void gf_clock_set_time(GF_Clock *ck, u32 TS) { if (!ck->clock_init) { ck->init_time = TS; ck->clock_init = 1; ck->drift = 0; /*update starttime and pausetime even in pause mode*/ ck->PauseTime = ck->StartTime = gf_term_get_time(ck->term); if (ck->term->play_state) ck->Paused ++; } #if 0 /*TODO: test with pure OCR streams*/ else if (ck->use_ocr) { /*just update the drift - we could also apply a drift algo*/ u32 now = gf_clock_real_time(ck); s32 drift = (s32) TS - (s32) now; ck->drift += drift; } #endif }
u32 gf_clock_time(GF_Clock *ck) { u32 time = gf_clock_real_time(ck); if ((ck->drift>0) && (time < (u32) ck->drift)) return 0; return time - ck->drift; }
/*handles reception of an SL-PDU, logical or physical*/ void gf_es_receive_sl_packet(GF_ClientService *serv, GF_Channel *ch, char *payload, u32 payload_size, GF_SLHeader *header, GF_Err reception_status) { GF_SLHeader hdr; u32 nbAU, OldLength, size, AUSeqNum; Bool EndAU, NewAU; if (ch->bypass_sl_and_db) { GF_SceneDecoder *sdec; ch->IsClockInit = 1; if (ch->odm->subscene) { sdec = (GF_SceneDecoder *)ch->odm->subscene->scene_codec->decio; } else { sdec = (GF_SceneDecoder *)ch->odm->codec->decio; } gf_mx_p(ch->mx); sdec->ProcessData(sdec, payload, payload_size, ch->esd->ESID, 0, 0); gf_mx_v(ch->mx); return; } if (ch->es_state != GF_ESM_ES_RUNNING) return; if (ch->skip_sl) { Channel_ReceiveSkipSL(serv, ch, payload, payload_size); return; } if (ch->is_raw_channel) { ch->CTS = ch->DTS = (u32) (ch->ts_offset + (header->compositionTimeStamp - ch->seed_ts) * 1000 / ch->ts_res); if (!ch->IsClockInit) { gf_es_check_timing(ch); } if (payload) gf_es_dispatch_raw_media_au(ch, payload, payload_size, ch->CTS); return; } /*physical SL-PDU - depacketize*/ if (!header) { u32 SLHdrLen; if (!payload_size) return; gf_sl_depacketize(ch->esd->slConfig, &hdr, payload, payload_size, &SLHdrLen); payload_size -= SLHdrLen; payload += SLHdrLen; } else { hdr = *header; } /*we ignore OCRs for the moment*/ if (hdr.OCRflag) { if (!ch->IsClockInit) { /*channel is the OCR, re-initialize the clock with the proper OCR*/ if (gf_es_owns_clock(ch)) { u32 OCR_TS; /*timestamps of PCR stream haven been shifted - shift the OCR as well*/ if (ch->seed_ts) { u64 diff_ts; Double scale = hdr.m2ts_pcr ? 27000000 : ch->esd->slConfig->OCRResolution; scale /= ch->ts_res; diff_ts = (u64) (ch->seed_ts * scale); hdr.objectClockReference -= diff_ts; } /*if SL is mapped from network module(eg not coded), OCR=PCR shall be given in 27Mhz units*/ if (hdr.m2ts_pcr) { OCR_TS = (u32) ( hdr.objectClockReference / 27000); } else { OCR_TS = (u32) ( (s64) (hdr.objectClockReference) * ch->ocr_scale); } OCR_TS += ch->ts_offset; ch->clock->clock_init = 0; gf_clock_set_time(ch->clock, OCR_TS); /*many TS streams deployed with HLS have broken PCRs - we will check their consistency when receiving the first AU with DTS/CTS on this channel*/ ch->clock->probe_ocr = 1; GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: initializing clock at STB %d from OCR TS %d (original TS "LLD") - %d buffering - OTB %d\n", ch->esd->ESID, gf_term_get_time(ch->odm->term), OCR_TS, hdr.objectClockReference, ch->clock->Buffering, gf_clock_time(ch->clock) )); if (ch->clock->clock_init) ch->IsClockInit = 1; } } #if 0 /*adjust clock if M2TS PCR discontinuity*/ else if (hdr.m2ts_pcr==2) { u32 ck; u32 OCR_TS = (u32) ( hdr.objectClockReference / 27000); ck = gf_clock_time(ch->clock); GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: At OTB %d - OCR Discontinuity OCR: adjusting to %d (original TS "LLD") - original clock %d\n", ch->esd->ESID, gf_clock_real_time(ch->clock), OCR_TS, hdr.objectClockReference, ck)); // gf_clock_set_time(ch->clock, (u32) OCR_TS); } /*compute clock drift*/ else { u32 ck; u32 OCR_TS; if (hdr.m2ts_pcr) { OCR_TS = (u32) ( hdr.objectClockReference / 27000); } else { OCR_TS = (u32) ( (s64) (hdr.objectClockReference) * ch->ocr_scale); } ck = gf_clock_time(ch->clock); GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: At OTB %d adjusting OCR to %d (original TS "LLD") - diff %d\n", ch->esd->ESID, gf_clock_real_time(ch->clock), OCR_TS, hdr.objectClockReference, (s32) OCR_TS - (s32) ck)); // gf_clock_set_time(ch->clock, (u32) OCR_TS); } #else { u32 ck; u32 OCR_TS; if (hdr.m2ts_pcr) { OCR_TS = (u32) ( hdr.objectClockReference / 27000); } else { OCR_TS = (u32) ( (s64) (hdr.objectClockReference) * ch->ocr_scale); } ck = gf_clock_time(ch->clock); GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: At OTB %d got OCR %d (original TS "LLD") - diff %d%s\n", ch->esd->ESID, gf_clock_real_time(ch->clock), OCR_TS, hdr.objectClockReference, (s32) OCR_TS - (s32) ck, (hdr.m2ts_pcr==2) ? " - PCR Discontinuity flag" : "" )); } #endif if (!payload_size) return; } /*check state*/ if (!ch->codec_resilient && (reception_status==GF_CORRUPTED_DATA)) { Channel_WaitRAP(ch); return; } if (!ch->esd->slConfig->useAccessUnitStartFlag) { /*no AU signaling - each packet is an AU*/ if (!ch->esd->slConfig->useAccessUnitEndFlag) hdr.accessUnitEndFlag = hdr.accessUnitStartFlag = 1; /*otherwise AU are signaled by end of previous packet*/ else hdr.accessUnitStartFlag = ch->NextIsAUStart; } /*get RAP*/ if (ch->esd->slConfig->hasRandomAccessUnitsOnlyFlag) { hdr.randomAccessPointFlag = 1; } else if ((ch->carousel_type!=GF_ESM_CAROUSEL_MPEG2) && (!ch->esd->slConfig->useRandomAccessPointFlag || ch->codec_resilient) ) { ch->stream_state = 0; } if (ch->esd->slConfig->packetSeqNumLength) { if (ch->pck_sn && hdr.packetSequenceNumber) { /*repeated -> drop*/ if (ch->pck_sn == hdr.packetSequenceNumber) { GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: repeated packet, droping\n", ch->esd->ESID)); return; } /*if codec has no resiliency check packet drops*/ if (!ch->codec_resilient && !hdr.accessUnitStartFlag) { if (ch->pck_sn == (u32) (1<<ch->esd->slConfig->packetSeqNumLength) ) { if (hdr.packetSequenceNumber) { GF_LOG(GF_LOG_WARNING, GF_LOG_SYNC, ("[SyncLayer] ES%d: packet loss, droping & wait RAP\n", ch->esd->ESID)); Channel_WaitRAP(ch); return; } } else if (ch->pck_sn + 1 != hdr.packetSequenceNumber) { GF_LOG(GF_LOG_WARNING, GF_LOG_SYNC, ("[SyncLayer] ES%d: packet loss, droping & wait RAP\n", ch->esd->ESID)); Channel_WaitRAP(ch); return; } } } ch->pck_sn = hdr.packetSequenceNumber; } /*if empty, skip the packet*/ if (hdr.paddingFlag && !hdr.paddingBits) { GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: Empty packet - skipping\n", ch->esd->ESID)); return; } /*IDLE stream shall be processed*/ NewAU = 0; if (hdr.accessUnitStartFlag) { NewAU = 1; ch->NextIsAUStart = 0; ch->skip_carousel_au = 0; /*if we have a pending AU, add it*/ if (ch->buffer) { if (ch->esd->slConfig->useAccessUnitEndFlag) { GF_LOG(GF_LOG_WARNING, GF_LOG_SYNC, ("[SyncLayer] ES%d: missed end of AU (DTS %d)\n", ch->esd->ESID, ch->DTS)); } if (ch->codec_resilient) { if (!ch->IsClockInit && !ch->skip_time_check_for_pending) gf_es_check_timing(ch); Channel_DispatchAU(ch, 0); } else { gf_free(ch->buffer); ch->buffer = NULL; ch->AULength = 0; ch->len = ch->allocSize = 0; } } ch->skip_time_check_for_pending = 0; AUSeqNum = hdr.AU_sequenceNumber; /*Get CTS */ if (ch->esd->slConfig->useTimestampsFlag) { if (hdr.compositionTimeStampFlag) { ch->net_dts = ch->net_cts = hdr.compositionTimeStamp; /*get DTS */ if (hdr.decodingTimeStampFlag) ch->net_dts = hdr.decodingTimeStamp; #if 0 /*until clock is not init check seed ts*/ if (!ch->IsClockInit && (ch->net_dts < ch->seed_ts)) ch->seed_ts = ch->net_dts; #endif if (ch->net_cts<ch->seed_ts) { u64 diff = ch->seed_ts - ch->net_cts; ch->CTS_past_offset = (u32) (diff * 1000 / ch->ts_res) + ch->ts_offset; ch->net_dts = ch->net_cts = 0; ch->CTS = ch->DTS = gf_clock_time(ch->clock); } else { if (ch->net_dts>ch->seed_ts) ch->net_dts -= ch->seed_ts; else ch->net_dts=0; ch->net_cts -= ch->seed_ts; ch->CTS_past_offset = 0; /*TS Wraping not tested*/ ch->CTS = (u32) (ch->ts_offset + (s64) (ch->net_cts) * 1000 / ch->ts_res); ch->DTS = (u32) (ch->ts_offset + (s64) (ch->net_dts) * 1000 / ch->ts_res); } if (ch->clock->probe_ocr && gf_es_owns_clock(ch)) { s32 diff_ts = ch->DTS; diff_ts -= ch->clock->init_time; if (ABS(diff_ts) > 10000) { GF_LOG(GF_LOG_ERROR, GF_LOG_SYNC, ("[SyncLayer] ES%d: invalid clock reference detected - DTS %d but OCR %d - using DTS as OCR\n", ch->esd->ESID, ch->DTS, ch->clock->init_time)); ch->clock->clock_init = 0; gf_clock_set_time(ch->clock, ch->DTS-1000); } ch->clock->probe_ocr = 0; } ch->no_timestamps = 0; } else { ch->no_timestamps = 1; } } else { /*use CU duration*/ if (!ch->IsClockInit) ch->DTS = ch->CTS = ch->ts_offset; if (!ch->esd->slConfig->AUSeqNumLength) { if (!ch->au_sn) { ch->CTS = ch->ts_offset; ch->au_sn = 1; } else { ch->CTS += ch->esd->slConfig->CUDuration; } } else { //use the sequence number to get the TS if (AUSeqNum < ch->au_sn) { nbAU = ( (1<<ch->esd->slConfig->AUSeqNumLength) - ch->au_sn) + AUSeqNum; } else { nbAU = AUSeqNum - ch->au_sn; } ch->CTS += nbAU * ch->esd->slConfig->CUDuration; } } /*if the AU Length is carried in SL, get its size*/ if (ch->esd->slConfig->AULength > 0) { ch->AULength = hdr.accessUnitLength; } else { ch->AULength = 0; } /*carousel for repeated AUs.*/ if (ch->carousel_type) { /* not used : Bool use_rap = hdr.randomAccessPointFlag; */ if (ch->carousel_type==GF_ESM_CAROUSEL_MPEG2) { AUSeqNum = hdr.m2ts_version_number_plus_one-1; /*mpeg-2 section carrouseling does not take into account the RAP nature of the tables*/ if (AUSeqNum==ch->au_sn) { if (ch->stream_state) { ch->stream_state=0; GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: MPEG-2 Carousel: tuning in\n", ch->esd->ESID)); } else { ch->skip_carousel_au = 1; GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: MPEG-2 Carousel: repeated AU (TS %d) - skipping\n", ch->esd->ESID, ch->CTS)); return; } } else { GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: MPEG-2 Carousel: updated AU (TS %d)\n", ch->esd->ESID, ch->CTS)); ch->stream_state=0; ch->au_sn = AUSeqNum; } } else { if (hdr.randomAccessPointFlag) { /*initial tune-in*/ if (ch->stream_state==1) { GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: RAP Carousel found (TS %d) - tuning in\n", ch->esd->ESID, ch->CTS)); ch->au_sn = AUSeqNum; ch->stream_state = 0; } /*carousel RAP*/ else if (AUSeqNum == ch->au_sn) { /*error recovery*/ if (ch->stream_state==2) { GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: RAP Carousel found (TS %d) - recovering\n", ch->esd->ESID, ch->CTS)); ch->stream_state = 0; } else { ch->skip_carousel_au = 1; GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: RAP Carousel found (TS %d) - skipping\n", ch->esd->ESID, ch->CTS)); return; } } /*regular RAP*/ else { if (ch->stream_state==2) { GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: RAP Carousel found (TS %d) - recovering from previous errors\n", ch->esd->ESID, ch->CTS)); } ch->au_sn = AUSeqNum; ch->stream_state = 0; } } /*regular AU but waiting for RAP*/ else if (ch->stream_state) { #if 0 ch->skip_carousel_au = 1; GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: Waiting for RAP Carousel - skipping\n", ch->esd->ESID)); return; #else GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: Tuning in before RAP\n", ch->esd->ESID)); #endif } /*previous packet(s) loss: check for critical or non-critical AUs*/ else if (reception_status == GF_REMOTE_SERVICE_ERROR) { if (ch->au_sn == AUSeqNum) { GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: Lost a non critical packet\n", ch->esd->ESID)); } /*Packet lost are critical*/ else { ch->stream_state = 2; GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: Lost a critical packet - skipping\n", ch->esd->ESID)); return; } } else { ch->au_sn = AUSeqNum; GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: NON-RAP AU received (TS %d)\n", ch->esd->ESID, ch->DTS)); } } } /*no carousel signaling, tune-in at first RAP*/ else if (hdr.randomAccessPointFlag) { ch->stream_state = 0; GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: RAP AU received\n", ch->esd->ESID)); } /*waiting for RAP, return*/ else if (ch->stream_state) { GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: Waiting for RAP - skipping AU (DTS %d)\n", ch->esd->ESID, ch->DTS)); return; } } /*update the RAP marker on a packet base (to cope with AVC/H264 NALU->AU reconstruction)*/ if (hdr.randomAccessPointFlag) ch->IsRap = 1; /*get AU end state*/ OldLength = ch->buffer ? ch->len : 0; EndAU = hdr.accessUnitEndFlag; if (ch->AULength == OldLength + payload_size) EndAU = 1; if (EndAU) ch->NextIsAUStart = 1; if (EndAU && !ch->IsClockInit) gf_es_check_timing(ch); /* we need to skip all the packets of the current AU in the carousel scenario */ if (ch->skip_carousel_au == 1) return; if (!payload_size && EndAU && ch->buffer) { GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: Empty packet, flushing buffer\n", ch->esd->ESID)); Channel_DispatchAU(ch, 0); return; } if (!payload_size) return; /*missed begining, unusable*/ if (!ch->buffer && !NewAU) { if (ch->esd->slConfig->useAccessUnitStartFlag) { GF_LOG(GF_LOG_ERROR, GF_LOG_SYNC, ("[SyncLayer] ES%d: missed begin of AU\n", ch->esd->ESID)); } if (ch->codec_resilient) NewAU = 1; else return; } /*Write the Packet payload to the buffer*/ if (NewAU) { /*we should NEVER have a bitstream at this stage*/ assert(!ch->buffer); /*ignore length fields*/ size = payload_size + ch->media_padding_bytes; ch->buffer = (char*)gf_malloc(sizeof(char) * size); if (!ch->buffer) { assert(0); return; } ch->allocSize = size; memset(ch->buffer, 0, sizeof(char) * size); ch->len = 0; } if (!ch->esd->slConfig->usePaddingFlag) hdr.paddingFlag = 0; if (ch->ipmp_tool) { GF_Err e; GF_IPMPEvent evt; memset(&evt, 0, sizeof(evt)); evt.event_type=GF_IPMP_TOOL_PROCESS_DATA; evt.channel = ch; evt.data = payload; evt.data_size = payload_size; evt.is_encrypted = hdr.isma_encrypted; evt.isma_BSO = hdr.isma_BSO; e = ch->ipmp_tool->process(ch->ipmp_tool, &evt); /*we discard undecrypted AU*/ if (e) { if (e==GF_EOS) { gf_es_on_eos(ch); /*restart*/ if (evt.restart_requested) { if (ch->odm->parentscene->is_dynamic_scene) { gf_scene_restart_dynamic(ch->odm->parentscene, 0); } else { mediacontrol_restart(ch->odm); } } } return; } } gf_es_lock(ch, 1); if (hdr.paddingFlag && !EndAU) { /*to do - this shouldn't happen anyway */ } else { /*check if enough space*/ size = ch->allocSize; if (size && (payload_size + ch->len <= size)) { memcpy(ch->buffer+ch->len, payload, payload_size); ch->len += payload_size; } else { size = payload_size + ch->len + ch->media_padding_bytes; ch->buffer = (char*)gf_realloc(ch->buffer, sizeof(char) * size); memcpy(ch->buffer+ch->len, payload, payload_size); ch->allocSize = size; ch->len += payload_size; } if (hdr.paddingFlag) ch->padingBits = hdr.paddingBits; } if (EndAU) Channel_DispatchAU(ch, hdr.au_duration); gf_es_lock(ch, 0); }
/*dispatch the AU in the DB*/ static void Channel_DispatchAU(GF_Channel *ch, u32 duration) { u32 time; GF_DBUnit *au; if (!ch->buffer || !ch->len) { if (ch->buffer) { gf_free(ch->buffer); ch->buffer = NULL; } return; } au = gf_db_unit_new(); if (!au) { gf_free(ch->buffer); ch->buffer = NULL; ch->len = 0; return; } au->CTS = ch->CTS; au->DTS = ch->DTS; if (ch->IsRap) au->flags |= GF_DB_AU_RAP; if (ch->CTS_past_offset) { au->CTS = ch->CTS_past_offset; au->flags |= GF_DB_AU_CTS_IN_PAST; ch->CTS_past_offset = 0; } if (ch->no_timestamps) { au->flags |= GF_DB_AU_NO_TIMESTAMPS; ch->no_timestamps=0; } au->data = ch->buffer; au->dataLength = ch->len; au->PaddingBits = ch->padingBits; ch->IsRap = 0; ch->padingBits = 0; au->next = NULL; ch->buffer = NULL; if (ch->len + ch->media_padding_bytes != ch->allocSize) { au->data = (char*)gf_realloc(au->data, sizeof(char) * (au->dataLength + ch->media_padding_bytes)); } if (ch->media_padding_bytes) memset(au->data + au->dataLength, 0, sizeof(char)*ch->media_padding_bytes); ch->len = ch->allocSize = 0; gf_es_lock(ch, 1); if (ch->service && ch->service->cache) { GF_SLHeader slh; memset(&slh, 0, sizeof(GF_SLHeader)); slh.accessUnitEndFlag = slh.accessUnitStartFlag = 1; slh.compositionTimeStampFlag = slh.decodingTimeStampFlag = 1; slh.decodingTimeStamp = ch->net_dts; slh.compositionTimeStamp = ch->net_cts; slh.randomAccessPointFlag = (au->flags & GF_DB_AU_RAP) ? 1 : 0; ch->service->cache->Write(ch->service->cache, ch, au->data, au->dataLength, &slh); } if (!ch->AU_buffer_first) { ch->AU_buffer_first = au; ch->AU_buffer_last = au; ch->AU_Count = 1; } else { if (ch->AU_buffer_last->DTS<=au->DTS) { ch->AU_buffer_last->next = au; ch->AU_buffer_last = ch->AU_buffer_last->next; } /*enable deinterleaving only for audio channels (some video transport may not be able to compute DTS, cf MPEG1-2/RTP) HOWEVER, we must recompute a monotone increasing DTS in case the decoder does perform frame reordering in which case the DTS is used for presentation time!!*/ else if (ch->esd->decoderConfig->streamType!=GF_STREAM_AUDIO) { #if 0 GF_DBUnit *au_prev, *ins_au; u32 DTS; #endif au->DTS = 0; /*append AU*/ ch->AU_buffer_last->next = au; ch->AU_buffer_last = ch->AU_buffer_last->next; #if 0 GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] Media deinterleaving OD %d ch %d\n", ch->esd->ESID, ch->odm->OD->objectDescriptorID)); DTS = au->DTS; au_prev = ch->AU_buffer_first; /*locate first AU in buffer with DTS greater than new unit CTS*/ while (au_prev->next && (au_prev->DTS < DTS) ) au_prev = au_prev->next; /*remember insertion point*/ ins_au = au_prev; /*shift all following frames DTS*/ while (au_prev->next) { au_prev->next->DTS = au_prev->DTS; au_prev = au_prev->next; } /*and apply*/ ins_au->DTS = DTS; #endif } else { GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] Audio deinterleaving OD %d ch %d\n", ch->esd->ESID, ch->odm->OD->objectDescriptorID)); /*de-interleaving of AUs*/ if (ch->AU_buffer_first->DTS > au->DTS) { au->next = ch->AU_buffer_first; ch->AU_buffer_first = au; } else { GF_DBUnit *au_prev = ch->AU_buffer_first; while (au_prev->next && au_prev->next->DTS<au->DTS) { au_prev = au_prev->next; } assert(au_prev); if (au_prev->next->DTS==au->DTS) { gf_free(au->data); gf_free(au); } else { au->next = au_prev->next; au_prev->next = au; } } } ch->AU_Count += 1; } Channel_UpdateBufferTime(ch); ch->au_duration = 0; if (duration) ch->au_duration = (u32) ((u64)1000 * duration / ch->ts_res); GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d - Dispatch AU DTS %d - CTS %d - size %d time %d Buffer %d Nb AUs %d - First AU relative timing %d\n", ch->esd->ESID, au->DTS, au->CTS, au->dataLength, gf_clock_real_time(ch->clock), ch->BufferTime, ch->AU_Count, ch->AU_buffer_first ? ch->AU_buffer_first->DTS - gf_clock_time(ch->clock) : 0 )); /*little optimisation: if direct dispatching is possible, try to decode the AU we must lock the media scheduler to avoid deadlocks with other codecs accessing the scene or media resources*/ if (ch->dispatch_after_db) { u32 retry = 100; u32 current_frame; GF_Terminal *term = ch->odm->term; ch_buffer_off(ch); gf_es_lock(ch, 0); if (gf_mx_try_lock(term->mm_mx)) { switch (ch->esd->decoderConfig->streamType) { case GF_STREAM_OD: gf_codec_process(ch->odm->subscene->od_codec, 100); break; case GF_STREAM_SCENE: if (ch->odm->codec) gf_codec_process(ch->odm->codec, 100); else gf_codec_process(ch->odm->subscene->scene_codec, 100); break; } gf_mx_v(term->mm_mx); } gf_es_lock(ch, 1); current_frame = term->compositor->frame_number; /*wait for initial setup to complete before giving back the hand to the caller service*/ while (retry) { /*Scene bootstrap: if the scene is attached, wait for first frame to complete so that initial PLAY on objects can be evaluated*/ if (term->compositor->scene && (term->compositor->frame_number==current_frame) ) { retry--; gf_sleep(1); continue; } /*Media bootstrap: wait for all pending requests on media objects are processed*/ if (gf_list_count(term->media_queue)) { retry--; gf_sleep(1); continue; } break; } } time = gf_term_get_time(ch->odm->term); if (ch->BufferOn) { ch->last_au_time = time; Channel_UpdateBuffering(ch, 1); } else { /*trigger the data progress every 500 ms*/ if (ch->last_au_time + 500 > time) { gf_term_service_media_event(ch->odm, GF_EVENT_MEDIA_PROGRESS); ch->last_au_time = time; } } gf_es_lock(ch, 0); }
u32 gf_clock_time(GF_Clock *ck) { u32 time = gf_clock_real_time(ck); if ((s32) time < ck->drift) return 0; return time - ck->drift; }
static GF_Err MediaCodec_Process(GF_Codec *codec, u32 TimeAvailable) { GF_CMUnit *CU; GF_DBUnit *AU; GF_Channel *ch, *prev_ch; u32 mmlevel, cts; u32 first, entryTime, now, obj_time, unit_size; GF_MediaDecoder *mdec = (GF_MediaDecoder*)codec->decio; GF_Err e = GF_OK; CU = NULL; /*if video codec muted don't decode (try to saves ressources) if audio codec muted we dispatch to keep sync in place*/ if (codec->Muted && (codec->type==GF_STREAM_VISUAL) ) return GF_OK; entryTime = gf_term_get_time(codec->odm->term); /*fetch next AU in DTS order for this codec*/ Decoder_GetNextAU(codec, &ch, &AU); /*no active channel return*/ if (!AU || !ch) { /*if the codec is in EOS state, assume we're done*/ if (codec->Status == GF_ESM_CODEC_EOS) { /*if codec is reordering, try to flush it*/ if (codec->is_reordering) { if ( LockCompositionUnit(codec, codec->last_unit_cts+1, &CU, &unit_size) == GF_OUT_OF_MEM) return GF_OK; assert( CU ); e = mdec->ProcessData(mdec, NULL, 0, 0, CU->data, &unit_size, 0, 0); if (e==GF_OK) e = UnlockCompositionUnit(codec, CU, unit_size); } gf_term_stop_codec(codec); if (codec->CB) gf_cm_set_eos(codec->CB); } /*if no data, and channel not buffering, ABORT CB buffer (data timeout or EOS not detectable)*/ else if (ch && !ch->BufferOn) gf_cm_abort_buffering(codec->CB); return GF_OK; } /*get the object time*/ obj_time = gf_clock_time(codec->ck); /*Media Time for media codecs is updated in the CB*/ if (!codec->CB) { gf_es_drop_au(ch); return GF_BAD_PARAM; } /*image codecs*/ if (codec->CB->Capacity == 1) { /*usually only one image is tolerated in the stream, but just in case force reset of CB*/ if (codec->CB->UnitCount && (obj_time>=AU->CTS)) { gf_mx_p(codec->odm->mx); codec->CB->output->dataLength = 0; codec->CB->UnitCount = 0; gf_mx_v(codec->odm->mx); } /*CB is already full*/ if (codec->CB->UnitCount) return GF_OK; /*a SHA signature is computed for each AU. This avoids decoding/recompositing when identical (for instance streaming a carousel)*/ { u8 new_unit_signature[20]; gf_sha1_csum(AU->data, AU->dataLength, new_unit_signature); if (!memcmp(codec->last_unit_signature, new_unit_signature, sizeof(new_unit_signature))) { codec->nb_repeted_frames++; gf_es_drop_au(ch); return GF_OK; } codec->nb_repeted_frames = 0; memcpy(codec->last_unit_signature, new_unit_signature, sizeof(new_unit_signature)); } } /*try to refill the full buffer*/ first = 1; while (codec->CB->Capacity > codec->CB->UnitCount) { /*set media processing level*/ mmlevel = GF_CODEC_LEVEL_NORMAL; /*SEEK: if the last frame had the same TS, we are seeking. Ask the codec to drop*/ if (!ch->skip_sl && codec->last_unit_cts && (codec->last_unit_cts == AU->CTS) && !ch->esd->dependsOnESID) { mmlevel = GF_CODEC_LEVEL_SEEK; /*object clock is paused by media control or terminal is paused: exact frame seek*/ if ( #ifndef GPAC_DISABLE_VRML (codec->ck->mc && codec->ck->mc->paused) || #endif (codec->odm->term->play_state) ) { gf_cm_rewind_input(codec->CB); mmlevel = GF_CODEC_LEVEL_NORMAL; /*force staying in step-mode*/ codec->odm->term->compositor->step_mode=1; } } /*only perform drop in normal playback*/ else if (codec->CB->Status == CB_PLAY) { /*extremely late, set the level to drop NOTE: the 100 ms safety gard is to avoid discarding audio*/ if (!ch->skip_sl && (AU->CTS + 100 < obj_time) ) { mmlevel = GF_CODEC_LEVEL_DROP; GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[%s] ODM%d: frame too late (%d vs %d) - using drop level\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, AU->CTS, obj_time)); if (ch->resync_drift && (AU->CTS + ch->resync_drift < obj_time)) { ch->clock->StartTime += (obj_time - AU->CTS); GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[%s] ODM%d: decoder too slow on OCR stream - rewinding clock of %d ms\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, obj_time - AU->CTS)); obj_time = gf_clock_time(codec->ck); } } /*we are late according to the media manager*/ else if (codec->PriorityBoost) { mmlevel = GF_CODEC_LEVEL_VERY_LATE; } /*otherwise we must have an idea of the load in order to set the right level use the composition buffer for that, only on the first frame*/ else if (first) { //if the CB is almost empty set to very late if (codec->CB->UnitCount <= codec->CB->Min+1) { mmlevel = GF_CODEC_LEVEL_VERY_LATE; } else if (codec->CB->UnitCount * 2 <= codec->CB->Capacity) { mmlevel = GF_CODEC_LEVEL_LATE; } first = 0; } } /*when using temporal scalability make sure we can decode*/ if (ch->esd->dependsOnESID && (codec->last_unit_dts > AU->DTS)){ // printf("SCALABLE STREAM DEAD!!\n"); goto drop; } if (ch->skip_sl) { if (codec->bytes_per_sec) { AU->CTS = codec->last_unit_cts + ch->ts_offset + codec->cur_audio_bytes * 1000 / codec->bytes_per_sec; } else if (codec->fps) { AU->CTS = codec->last_unit_cts + ch->ts_offset + (u32) (codec->cur_video_frames * 1000 / codec->fps); } } if ( LockCompositionUnit(codec, AU->CTS, &CU, &unit_size) == GF_OUT_OF_MEM) return GF_OK; scalable_retry: now = gf_term_get_time(codec->odm->term); assert( CU ); if (!CU->data && unit_size) e = GF_OUT_OF_MEM; else e = mdec->ProcessData(mdec, AU->data, AU->dataLength, ch->esd->ESID, CU->data, &unit_size, AU->PaddingBits, mmlevel); now = gf_term_get_time(codec->odm->term) - now; if (codec->Status == GF_ESM_CODEC_STOP) return GF_OK; /*input is too small, resize composition memory*/ switch (e) { case GF_BUFFER_TOO_SMALL: /*release but no dispatch*/ UnlockCompositionUnit(codec, CU, 0); ResizeCompositionBuffer(codec, unit_size); continue; /*this happens a lot when using non-MPEG-4 streams (ex: ffmpeg demuxer)*/ case GF_PACKED_FRAMES: /*in seek don't dispatch any output*/ if (mmlevel == GF_CODEC_LEVEL_SEEK) unit_size = 0; e = UnlockCompositionUnit(codec, CU, unit_size); GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI|GF_LOG_CODEC, ("[%s] ODM%d at %d decoded packed frame TS %d in %d ms\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, gf_clock_real_time(ch->clock), AU->CTS, now)); if (ch->skip_sl) { if (codec->bytes_per_sec) { codec->cur_audio_bytes += unit_size; } else if (codec->fps && unit_size) { codec->cur_video_frames += 1; } } else { u32 deltaTS = 0; if (codec->bytes_per_sec) { deltaTS = unit_size * 1000 / codec->bytes_per_sec; } /*else if (0 && codec->fps && unit_size) { deltaTS = (u32) (1000.0f / codec->fps); } */else { deltaTS = (AU->DTS - codec->last_unit_dts); } AU->CTS += deltaTS; } codec_update_stats(codec, 0, now); continue; /*for all cases below, don't release the composition buffer until we are sure we are not processing a scalable stream*/ case GF_OK: if (unit_size) { GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI|GF_LOG_CODEC, ("[%s] ODM%d at %d decoded frame TS %d in %d ms (DTS %d) - %d in CB\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, gf_clock_real_time(ch->clock), AU->CTS, now, AU->DTS, codec->CB->UnitCount + 1)); } /*if no size the decoder is not using the composition memory - if the object is in intitial buffering resume it!!*/ else if (codec->CB->Status == CB_BUFFER) { gf_cm_abort_buffering(codec->CB); } codec_update_stats(codec, AU->dataLength, now); if (ch->skip_sl) { if (codec->bytes_per_sec) { codec->cur_audio_bytes += unit_size; while (codec->cur_audio_bytes>codec->bytes_per_sec) { codec->cur_audio_bytes -= codec->bytes_per_sec; codec->last_unit_cts += 1000; } } else if (codec->fps && unit_size) { codec->cur_video_frames += 1; } } #ifndef GPAC_DISABLE_LOGS if (codec->odm->flags & GF_ODM_PREFETCH) { GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("[%s] ODM%d At %d decoding frame TS %d in prefetch mode\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, gf_clock_real_time(ch->clock) )); } #endif break; default: unit_size = 0; /*error - if the object is in intitial buffering resume it!!*/ gf_cm_abort_buffering(codec->CB); GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[%s] ODM%d At %d (frame TS %d - %d ms ): decoded error %s\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, gf_clock_real_time(ch->clock), AU->CTS, now, gf_error_to_string(e) )); e = GF_OK; break; } codec->last_unit_dts = AU->DTS; /*remember base layer timing*/ if (!ch->esd->dependsOnESID && !ch->skip_sl) codec->last_unit_cts = AU->CTS; drop: /*store current CTS*/ cts = AU->CTS; prev_ch = ch; gf_es_drop_au(ch); AU = NULL; if (e) { UnlockCompositionUnit(codec, CU, unit_size); return e; } Decoder_GetNextAU(codec, &ch, &AU); /*same CTS: same output, likely scalable stream so don't release the CB*/ if (AU && (AU->CTS == cts) && (ch !=prev_ch) ) { unit_size = codec->CB->UnitSize; goto scalable_retry; } /*in seek don't dispatch any output*/ if (mmlevel == GF_CODEC_LEVEL_SEEK) unit_size = 0; UnlockCompositionUnit(codec, CU, unit_size); if (!ch || !AU) return GF_OK; /*escape from decoding loop only if above critical limit - this is to avoid starvation on audio*/ if (!ch->esd->dependsOnESID && (codec->CB->UnitCount > codec->CB->Min)) { now = gf_term_get_time(codec->odm->term); if (now - entryTime >= TimeAvailable) { return GF_OK; } } Decoder_GetNextAU(codec, &ch, &AU); if (!ch || !AU) return GF_OK; } return GF_OK; }