/*pause all objects*/ void mediacontrol_set_speed(GF_ObjectManager *odm, Fixed speed) { u32 i; GF_ObjectManager *ctrl_od; GF_Scene *in_scene; GF_Clock *ck; if (odm->flags & GF_ODM_NO_TIME_CTRL) return; /*locate all objects sharing the clock*/ ck = gf_odm_get_media_clock(odm); if (!ck) return; in_scene = odm->parentscene; if (odm->subscene) { assert(odm->subscene->root_od==odm); in_scene = odm->subscene; //dynamic scene with speed direction, we need to re-start everything to issue new PLAY requests if (in_scene->is_dynamic_scene && (gf_mulfix(ck->speed, speed) < 0)) { u32 time = gf_clock_time(ck); gf_clock_set_speed(ck, speed); //enable main addon if (speed<0) { i=0; while ((ctrl_od = (GF_ObjectManager*)gf_list_enum(in_scene->resources, &i))) { if (ctrl_od->addon && (ctrl_od->addon->addon_type==GF_ADDON_TYPE_MAIN)) { gf_scene_select_main_addon(in_scene, ctrl_od, GF_TRUE, gf_clock_time(ck) ); break; } } } gf_scene_restart_dynamic(in_scene, time, 0, 1); return; } gf_clock_set_speed(ck, speed); gf_odm_set_speed(odm, speed, GF_TRUE); } i=0; while ((ctrl_od = (GF_ObjectManager*)gf_list_enum(in_scene->resources, &i))) { if (!gf_odm_shares_clock(ctrl_od, ck)) continue; if (ctrl_od->subscene) { mediacontrol_set_speed(ctrl_od, speed); } else { gf_odm_set_speed(ctrl_od, speed, GF_TRUE); } } }
void Channel_ReceiveSkipSL(GF_ClientService *serv, GF_Channel *ch, const char *StreamBuf, u32 StreamLength) { GF_DBUnit *au; if (!StreamLength) return; gf_es_lock(ch, 1); au = gf_db_unit_new(); au->flags = GF_DB_AU_RAP; au->DTS = gf_clock_time(ch->clock); au->data = (char*)gf_malloc(sizeof(char) * (ch->media_padding_bytes + StreamLength)); memcpy(au->data, StreamBuf, sizeof(char) * StreamLength); if (ch->media_padding_bytes) memset(au->data + StreamLength, 0, sizeof(char)*ch->media_padding_bytes); au->dataLength = StreamLength; au->next = NULL; /*if channel owns the clock, start it*/ if (ch->clock && !ch->IsClockInit) { if (gf_es_owns_clock(ch)) { gf_clock_set_time(ch->clock, 0); ch->IsClockInit = 1; ch->seed_ts = 0; } if (ch->clock->clock_init && !ch->IsClockInit) { ch->IsClockInit = 1; ch->seed_ts = gf_clock_time(ch->clock); } } if (!ch->AU_buffer_first) { ch->AU_buffer_first = au; ch->AU_buffer_last = au; ch->AU_Count = 1; } else { ch->AU_buffer_last->next = au; ch->AU_buffer_last = ch->AU_buffer_last->next; ch->AU_Count += 1; } Channel_UpdateBufferTime(ch); if (ch->BufferOn) { ch->last_au_time = gf_term_get_time(ch->odm->term); Channel_UpdateBuffering(ch, 1); } gf_es_lock(ch, 0); }
Bool uir_on_event_record(GF_UIRecord *uir , GF_Event *event, Bool consumed_by_compositor) { switch (event->type) { case GF_EVENT_CONNECT: if (event->connect.is_connected) { uir->ck = uir->term->root_scene->scene_codec ? uir->term->root_scene->scene_codec->ck : uir->term->root_scene->dyn_ck; } break; case GF_EVENT_CLICK: case GF_EVENT_MOUSEUP: case GF_EVENT_MOUSEDOWN: case GF_EVENT_MOUSEOVER: case GF_EVENT_MOUSEOUT: /*!! ALL MOUSE EVENTS SHALL BE DECLARED BEFORE MOUSEMOVE !! */ case GF_EVENT_MOUSEMOVE: /*mouse wheel event*/ case GF_EVENT_MOUSEWHEEL: gf_bs_write_u32(uir->bs, gf_clock_time(uir->ck) ); gf_bs_write_u8(uir->bs, event->type); gf_bs_write_u8(uir->bs, event->mouse.button); gf_bs_write_u32(uir->bs, event->mouse.x); gf_bs_write_u32(uir->bs, event->mouse.y); gf_bs_write_float(uir->bs, FIX2FLT(event->mouse.wheel_pos) ); gf_bs_write_u8(uir->bs, event->mouse.key_states); break; /*Key Events*/ case GF_EVENT_KEYUP: case GF_EVENT_KEYDOWN: case GF_EVENT_LONGKEYPRESS: gf_bs_write_u32(uir->bs, gf_clock_time(uir->ck) ); gf_bs_write_u8(uir->bs, event->type); gf_bs_write_u32(uir->bs, event->key.key_code); gf_bs_write_u32(uir->bs, event->key.hw_code); gf_bs_write_u32(uir->bs, event->key.flags); break; /*character input*/ case GF_EVENT_TEXTINPUT: gf_bs_write_u32(uir->bs, gf_clock_time(uir->ck) ); gf_bs_write_u8(uir->bs, event->type); gf_bs_write_u32(uir->bs, event->character.unicode_char); break; } return 0; }
static Bool uir_process(GF_TermExt *termext, u32 action, void *param) { const char *opt, *uifile; GF_UIRecord *uir = termext->udta; switch (action) { case GF_TERM_EXT_START: uir->term = (GF_Terminal *) param; opt = gf_modules_get_option((GF_BaseInterface*)termext, "UIRecord", "Mode"); if (!opt) return 0; uifile = gf_modules_get_option((GF_BaseInterface*)termext, "UIRecord", "File"); if (!opt) return 0; if (!strcmp(opt, "Play")) { uir->uif = gf_f64_open(uifile, "rb"); if (!uir->uif) return 0; uir->bs = gf_bs_from_file(uir->uif, GF_BITSTREAM_READ); termext->caps |= GF_TERM_EXTENSION_NOT_THREADED; uir->evt_filter.on_event = uir_on_event_play; uir->evt_filter.udta = uir; gf_term_add_event_filter(uir->term, &uir->evt_filter); uir_load_event(uir); } else if (!strcmp(opt, "Record")) { uir->uif = fopen(uifile, "wb"); if (!uir->uif) return 0; uir->bs = gf_bs_from_file(uir->uif, GF_BITSTREAM_WRITE); uir->evt_filter.on_event = uir_on_event_record; uir->evt_filter.udta = uir; gf_term_add_event_filter(uir->term, &uir->evt_filter); } else { return 0; } return 1; case GF_TERM_EXT_STOP: if (uir->uif) fclose(uir->uif); if (uir->bs) gf_bs_del(uir->bs); gf_term_remove_event_filter(uir->term, &uir->evt_filter); uir->term = NULL; /*auto-disable the plugin by default*/ gf_modules_set_option((GF_BaseInterface*)termext, "UIRecord", "Mode", "Disable"); break; case GF_TERM_EXT_PROCESS: /*flush all events until current time if reached*/ while (uir->evt_loaded && uir->ck && (uir->next_time <= gf_clock_time(uir->ck) )) { uir->term->compositor->video_out->on_event(uir->term->compositor->video_out->evt_cbk_hdl, &uir->next_event); uir_load_event(uir); } break; } return 0; }
Bool gf_odm_check_segment_switch(GF_ObjectManager *odm) { u32 count, i; GF_Segment *cur, *next; MediaControlStack *ctrl = gf_odm_get_mediacontrol(odm); /*if no control or control not on this object ignore segment switch*/ if (!ctrl || (ctrl->stream->odm != odm)) return 0; count = gf_list_count(ctrl->seg); /*reached end of controled stream (no more segments)*/ if (ctrl->current_seg>=count) return 0; /*synth media, trigger if end of segment run-time*/ if (!odm->codec || ((odm->codec->type!=GF_STREAM_VISUAL) && (odm->codec->type!=GF_STREAM_AUDIO))) { GF_Clock *ck = gf_odm_get_media_clock(odm); u32 now = gf_clock_time(ck); u64 dur = odm->subscene ? odm->subscene->duration : odm->duration; cur = (GF_Segment *)gf_list_get(ctrl->seg, ctrl->current_seg); if (odm->subscene && odm->subscene->needs_restart) return 0; if (cur) dur = (u32) ((cur->Duration+cur->startTime)*1000); if (now<=dur) return 0; } else { /*FIXME - for natural media with scalability, we should only process when all streams of the object are done*/ } /*get current segment and move to next one*/ cur = (GF_Segment *)gf_list_get(ctrl->seg, ctrl->current_seg); ctrl->current_seg ++; /*resync in case we have been issuing a play range over several segments*/ for (i=ctrl->current_seg; i<count; i++) { next = (GF_Segment *)gf_list_get(ctrl->seg, i); if ( /*if next seg start is after cur seg start*/ (cur->startTime < next->startTime) /*if next seg start is before cur seg end*/ && (cur->startTime + cur->Duration > next->startTime) /*if next seg start is already passed*/ && (1000*next->startTime < odm->media_current_time) /*then next segment was taken into account when requesting play*/ ) { cur = next; ctrl->current_seg ++; } } /*if last segment in ctrl is done, end of stream*/ if (ctrl->current_seg >= count) return 0; next = (GF_Segment *)gf_list_get(ctrl->seg, ctrl->current_seg); /*if next seg start is not in current seg, media needs restart*/ if ((next->startTime < cur->startTime) || (cur->startTime + cur->Duration < next->startTime)) mediacontrol_restart(odm); return 1; }
static void validator_on_video_frame(void *udta, u32 time) { GF_Validator *validator = (GF_Validator *)udta; if (validator->snapshot_next_frame) { char *snap_name = validator_create_snapshot(validator); validator_xvs_add_snapshot_node(validator, snap_name, gf_clock_time(validator->ck)); gf_free(snap_name); validator->snapshot_next_frame = 0; } }
void gf_clock_set_speed(GF_Clock *ck, Fixed speed) { u32 time; if (speed==ck->speed) return; time = gf_term_get_time(ck->term); /*adjust start time*/ ck->discontinuity_time = gf_clock_time(ck) - ck->init_time; ck->PauseTime = ck->StartTime = time; ck->speed = speed; }
/*render : setup media sensor and update timing in case of inline scenes*/ void RenderMediaSensor(GF_Node *node, void *rs, Bool is_destroy) { GF_Clock *ck; MediaSensorStack *st = (MediaSensorStack *)gf_node_get_private(node); if (is_destroy) { /*unlink from OD*/ if (st->stream && st->stream->odm) gf_list_del_item(st->stream->odm->ms_stack, st); gf_list_del(st->seg); gf_free(st); return; } if (!st->stream) st->stream = gf_mo_register(node, &st->sensor->url, 0, 0); if (!st->stream || !st->stream->odm) return; if (!st->is_init) { gf_list_add(st->stream->odm->ms_stack, st); gf_odm_init_segments(st->stream->odm, st->seg, &st->sensor->url); st->is_init = 1; st->active_seg = 0; } /*media sensor bound to natural media (audio, video) is updated when fetching the stream data for rendering.*/ ck = NULL; /*check inline scenes - if the scene is set to restart DON'T MODIFY SENSOR: since we need a 2 render passes to restart inline, scene is considered as not running*/ if (st->stream->odm->subscene && !st->stream->odm->subscene->needs_restart) { if (st->stream->odm->subscene->scene_codec) ck = st->stream->odm->subscene->scene_codec->ck; /*dynamic scene*/ else ck = st->stream->odm->subscene->dyn_ck; /*since audio may be used alone through an inline scene, we need to refresh the graph*/ if (ck && !ck->has_seen_eos && st->stream->odm->state) gf_term_invalidate_compositor(st->stream->odm->term); } /*check anim streams*/ else if (st->stream->odm->codec && (st->stream->odm->codec->type==GF_STREAM_SCENE)) ck = st->stream->odm->codec->ck; /*check OCR streams*/ else if (st->stream->odm->ocr_codec) ck = st->stream->odm->ocr_codec->ck; if (ck && gf_clock_is_started(ck) ) { st->stream->odm->current_time = gf_clock_time(ck); mediasensor_update_timing(st->stream->odm, 0); } }
/*drop the output CU*/ void gf_cm_drop_output(GF_CompositionMemory *cb) { gf_cm_output_kept(cb); /*WARNING: in RAW mode, we (for the moment) only have one unit - setting output->dataLength to 0 means the input is available for the raw channel - we have to make sure the output is completely reseted before releasing the sema*/ /*on visual streams (except raw oness), always keep the last AU*/ if (cb->output->dataLength && (cb->odm->codec->type == GF_STREAM_VISUAL) ) { if ( !cb->output->next->dataLength || (cb->Capacity == 1) ) { Bool no_drop = 1; if (cb->no_allocation ) { if (cb->odm->term->bench_mode) no_drop = 0; else if (gf_clock_time(cb->odm->codec->ck) > cb->output->TS) no_drop = 0; } if (no_drop) { if (cb->odm->raw_frame_sema) { cb->output->dataLength = 0; gf_sema_notify(cb->odm->raw_frame_sema, 1); } return; } } } /*reset the output*/ cb->output->dataLength = 0; if (cb->output->frame) { cb->output->frame->Release(cb->output->frame); cb->output->frame = NULL; } cb->output->TS = 0; cb->output = cb->output->next; cb->UnitCount -= 1; if (!cb->HasSeenEOS && cb->UnitCount <= cb->Min) { cb->odm->codec->PriorityBoost = 1; } if (cb->odm->raw_frame_sema) { gf_sema_notify(cb->odm->raw_frame_sema, 1); } }
/*scalable browsing of input channels: find the AU with the lowest DTS on all input channels*/ static void Decoder_GetNextAU(GF_Codec *codec, GF_Channel **activeChannel, GF_DBUnit **nextAU) { GF_Channel *ch; GF_DBUnit *AU; u32 count, minDTS, i; count = gf_list_count(codec->inChannels); *nextAU = NULL; *activeChannel = NULL; if (!count) return; minDTS = (u32) -1; /*reverse browsing to make sure we fill enhancement before base layer*/ for (i=count;i>0;i--) { ch = (GF_Channel*)gf_list_get(codec->inChannels, i-1); if ((codec->type==GF_STREAM_OCR) && ch->IsClockInit) { /*check duration - we assume that scalable OCR streams are just pure nonsense...*/ if (ch->is_pulling && codec->odm->duration) { if (gf_clock_time(codec->ck) > codec->odm->duration) gf_es_on_eos(ch); } return; } AU = gf_es_get_au(ch); if (!AU) { if (! (*activeChannel)) *activeChannel = ch; continue; } /*we use <= to make sure we first fetch data on base layer if same DTS (which is possible in spatial scalability)*/ if (AU->DTS <= minDTS) { minDTS = AU->DTS; *activeChannel = ch; *nextAU = AU; } } /*FIXME - we're breaking sync (couple of frames delay)*/ if (codec->is_reordering && *nextAU) (*nextAU)->CTS = (*nextAU)->DTS; }
/*pause all objects*/ void mediacontrol_pause(GF_ObjectManager *odm) { u32 i; GF_ObjectManager *ctrl_od; GF_Scene *in_scene; GF_Clock *ck; if (odm->flags & GF_ODM_NO_TIME_CTRL) return; /*otherwise locate all objects sharing the clock*/ ck = gf_odm_get_media_clock(odm); if (!ck) { odm->flags |= GF_ODM_PAUSE_QUEUED; return; } in_scene = odm->parentscene; if (odm->subscene) { assert(odm->subscene->root_od==odm); assert(odm->subscene->is_dynamic_scene || gf_odm_shares_clock(odm, ck) ); /*pause root*/ gf_odm_pause(odm); in_scene = odm->subscene; } i=0; while ((ctrl_od = (GF_ObjectManager*)gf_list_enum(in_scene->resources, &i))) { if (!odm->subscene && !gf_odm_shares_clock(ctrl_od, ck)) continue; if (ctrl_od->addon && (ctrl_od->addon->addon_type==GF_ADDON_TYPE_MAIN)) { gf_clock_pause(ck); gf_scene_select_main_addon(in_scene, ctrl_od, GF_TRUE, gf_clock_time(ck) ); } if (ctrl_od->subscene) { mediacontrol_pause(ctrl_od); } else { gf_odm_pause(ctrl_od); } } }
static void gf_es_check_timing(GF_Channel *ch) { /*the first data received inits the clock - this is needed to handle clock dependencies on non-initialized streams (eg, bifs/od depends on audio/video clock)*/ if (!ch->clock->clock_init) { if (!ch->clock->use_ocr) { gf_clock_set_time(ch->clock, ch->CTS); GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: Forcing clock initialization at STB %d - AU DTS %d\n", ch->esd->ESID, gf_term_get_time(ch->odm->term), ch->DTS)); ch->IsClockInit = 1; } } /*channel is the OCR, force a re-init of the clock since we cannot assume the AU used to init the clock was not sent ahead of time*/ else if (gf_es_owns_clock(ch)) { if (!ch->clock->use_ocr) { ch->clock->clock_init = 0; gf_clock_set_time(ch->clock, ch->DTS); GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: initializing clock at STB %d - AU DTS %d - %d buffering - OTB %d\n", ch->esd->ESID, gf_term_get_time(ch->odm->term), ch->DTS, ch->clock->Buffering, gf_clock_time(ch->clock) )); ch->IsClockInit = 1; } } /*if channel is not the OCR, shift all time stamps to match the current time at clock init*/ else if (!ch->IsClockInit ) { // ch->ts_offset += gf_clock_real_time(ch->clock); if (ch->clock->clock_init) { ch->IsClockInit = 1; if (ch->odm->flags & GF_ODM_INHERIT_TIMELINE) { // ch->ts_offset += gf_clock_real_time(ch->clock) - ch->CTS; } } } /*deal with some broken DMB streams were the timestamps on BIFS/OD are not set (0) or completely out of sync of the OCR clock (usually audio). If the audio codec (BSAC ...) is not found, we force re-initializing of the clock so that video can play back correctly*/ else if (gf_clock_time(ch->clock) * 1000 < ch->DTS) { ch->clock->clock_init = 0; gf_clock_set_time(ch->clock, ch->DTS); GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: re-initializing clock at STB %d - AU DTS %d - %d buffering\n", ch->esd->ESID, gf_term_get_time(ch->odm->term), ch->DTS, ch->clock->Buffering)); ch->IsClockInit = 1; } }
static void codec_update_stats(GF_Codec *codec, u32 dataLength, u32 dec_time) { codec->total_dec_time += dec_time; codec->nb_dec_frames++; if (dec_time>codec->max_dec_time) codec->max_dec_time = dec_time; if (dataLength) { u32 now = gf_clock_time(codec->ck); if (codec->last_stat_start + 1000 <= now) { if (!codec->cur_bit_size) { codec->last_stat_start = now; } else { codec->avg_bit_rate = codec->cur_bit_size; if (codec->avg_bit_rate > codec->max_bit_rate) codec->max_bit_rate = codec->avg_bit_rate; codec->last_stat_start = now; codec->cur_bit_size = 0; } } codec->cur_bit_size += 8*dataLength; } }
static void gf_inline_check_restart(GF_Scene *scene) { /*no ctrl if no duration*/ if (!scene->duration) return; if (!scene->needs_restart) gf_odm_check_segment_switch(scene->root_od); if (scene->needs_restart) return; if (scene->root_od->media_ctrl && scene->root_od->media_ctrl->control->loop) { GF_Clock *ck = gf_odm_get_media_clock(scene->root_od); if (ck->has_seen_eos) { u32 now = gf_clock_time(ck); u64 dur = scene->duration; if (scene->root_od->media_ctrl->current_seg) { /*only process when all segments are played*/ if (gf_list_count(scene->root_od->media_ctrl->seg) <= scene->root_od->media_ctrl->current_seg) { scene->needs_restart = 1; scene->root_od->media_ctrl->current_seg = 0; } } else { Double s, e; s = now; s/=1000; e = -1; MC_GetRange(scene->root_od->media_ctrl, &s, &e); if ((e>=0) && (e<GF_MAX_FLOAT)) dur = (u32) (e*1000); if (dur<=now) { scene->needs_restart = 1; scene->root_od->media_ctrl->current_seg = 0; } else { /*trigger render until to watch for restart...*/ gf_term_invalidate_compositor(scene->root_od->term); } } } else { /*trigger render until to watch for restart...*/ gf_term_invalidate_compositor(scene->root_od->term); } } }
static void Channel_UpdateBufferTime(GF_Channel *ch) { if (!ch->AU_buffer_first || !ch->IsClockInit) { ch->BufferTime = 0; } else if (ch->skip_sl) { GF_DBUnit *au; /*compute buffer size from avg bitrate*/ u32 avg_rate = ch->esd->decoderConfig->avgBitrate; if (!avg_rate && ch->odm->codec) avg_rate = ch->odm->codec->avg_bit_rate; if (avg_rate) { u32 bsize = 0; au = ch->AU_buffer_first; while (1) { bsize += au->dataLength*8; if (!au->next) break; au = au->next; } ch->BufferTime = 1000*bsize/avg_rate; } else { /*we're in the dark, so don't buffer too much (assume 50ms per unit) so that we start decoding asap*/ ch->BufferTime = 50*ch->AU_Count; } } else { s32 bt = ch->AU_buffer_last->DTS - gf_clock_time(ch->clock); if (bt>0) { ch->BufferTime = (u32) bt; if (ch->clock->speed != FIX_ONE) { ch->BufferTime = FIX2INT( gf_divfix( INT2FIX(ch->AU_buffer_last->DTS - ch->AU_buffer_first->DTS) , ch->clock->speed)) ; } } else { ch->BufferTime = 0; } } ch->BufferTime += ch->au_duration; }
void gf_cm_set_eos(GF_CompositionMemory *cb) { gf_odm_lock(cb->odm, 1); /*we may have a pb if the stream is so short that the EOS is signaled while we're buffering. In this case we shall turn the clock on and keep a trace of the EOS notif*/ if (cb->Status == CB_BUFFER) { cb->Status = CB_BUFFER_DONE; gf_clock_buffer_off(cb->odm->codec->ck); GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] CB EOS - ODM%d: buffering off at OTB %u (STB %d) (nb wait on clock: %d)\n", cb->odm->OD->objectDescriptorID, gf_clock_time(cb->odm->codec->ck), gf_term_get_time(cb->odm->term), cb->odm->codec->ck->Buffering)); } cb->HasSeenEOS = 1; //in bench mode eos cannot be signaled through flush of composition memory since it is always empty - do it here if (cb->odm->term->bench_mode==2) { cb->Status = CB_STOP; gf_odm_signal_eos(cb->odm); } gf_term_invalidate_compositor(cb->odm->term); gf_odm_lock(cb->odm, 0); }
GF_EXPORT GF_Err gf_term_get_object_info(GF_Terminal *term, GF_ObjectManager *odm, GF_MediaInfo *info) { GF_Channel *ch; if (!term || !odm || !odm->OD || !info) return GF_BAD_PARAM; if (!gf_term_check_odm(term, odm)) return GF_BAD_PARAM; memset(info, 0, sizeof(GF_MediaInfo)); info->od = odm->OD; info->duration = (Double) (s64)odm->duration; info->duration /= 1000; if (odm->codec) { /*since we don't remove ODs that failed setup, check for clock*/ if (odm->codec->ck) info->current_time = odm->codec->CB ? odm->current_time : gf_clock_time(odm->codec->ck); info->current_time /= 1000; info->nb_droped = odm->codec->nb_droped; } else if (odm->subscene) { if (odm->subscene->scene_codec) { if (odm->subscene->scene_codec->ck) { info->current_time = gf_clock_time(odm->subscene->scene_codec->ck); info->current_time /= 1000; } info->duration = (Double) (s64)odm->subscene->duration; info->duration /= 1000; info->nb_droped = odm->subscene->scene_codec->nb_droped; } else if (odm->subscene->is_dynamic_scene && odm->subscene->dyn_ck) { info->current_time = gf_clock_time(odm->subscene->dyn_ck); info->current_time /= 1000; } } info->buffer = -2; info->db_unit_count = 0; /*Warning: is_open==2 means object setup, don't check then*/ if (odm->state==GF_ODM_STATE_IN_SETUP) { info->status = 3; } else if (odm->state==GF_ODM_STATE_BLOCKED) { info->status = 0; info->protection = 2; } else if (odm->state) { u32 i, buf; GF_Clock *ck; ck = gf_odm_get_media_clock(odm); /*no clock means setup failed*/ if (!ck) { info->status = 4; } else { info->status = gf_clock_is_started(ck) ? 1 : 2; info->clock_drift = ck->drift; info->buffer = -1; buf = 0; i=0; while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &i))) { info->db_unit_count += ch->AU_Count; if (!ch->is_pulling) { if (ch->MaxBuffer) info->buffer = 0; buf += ch->BufferTime; } if (ch->is_protected) info->protection = ch->ipmp_tool ? 1 : 2; } if (buf) info->buffer = (s32) buf; } } info->has_profiles = (odm->flags & GF_ODM_HAS_PROFILES) ? 1 : 0; if (info->has_profiles) { info->inline_pl = (odm->flags & GF_ODM_INLINE_PROFILES) ? 1 : 0; info->OD_pl = odm->OD_PL; info->scene_pl = odm->Scene_PL; info->audio_pl = odm->Audio_PL; info->visual_pl = odm->Visual_PL; info->graphics_pl = odm->Graphics_PL; } if (odm->net_service) { info->service_handler = odm->net_service->ifce->module_name; info->service_url = odm->net_service->url; if (odm->net_service->owner == odm) info->owns_service = 1; } else if ((odm->subscene && odm->subscene->graph_attached) || (odm->codec)) { info->service_url = "No associated network Service"; } else { info->service_url = "Service not found or error"; } if (odm->codec && odm->codec->decio) { if (!odm->codec->decio->GetName) { info->codec_name = odm->codec->decio->module_name; } else { info->codec_name = odm->codec->decio->GetName(odm->codec->decio); } info->od_type = odm->codec->type; if (odm->codec->CB) { info->cb_max_count = odm->codec->CB->Capacity; info->cb_unit_count = odm->codec->CB->UnitCount; } } if (odm->subscene && odm->subscene->scene_codec) { GF_BaseDecoder *dec = odm->subscene->scene_codec->decio; assert(odm->subscene->root_od==odm) ; info->od_type = odm->subscene->scene_codec->type; if (!dec->GetName) { info->codec_name = dec->module_name; } else { info->codec_name = dec->GetName(dec); } gf_sg_get_scene_size_info(odm->subscene->graph, &info->width, &info->height); } else if (odm->mo) { switch (info->od_type) { case GF_STREAM_VISUAL: gf_mo_get_visual_info(odm->mo, &info->width, &info->height, NULL, &info->par, &info->pixelFormat, NULL); break; case GF_STREAM_AUDIO: gf_mo_get_audio_info(odm->mo, &info->sample_rate, &info->bits_per_sample, &info->num_channels, NULL); info->clock_drift = 0; break; case GF_STREAM_TEXT: gf_mo_get_visual_info(odm->mo, &info->width, &info->height, NULL, NULL, NULL, NULL); break; } } if (odm->subscene && odm->subscene->scene_codec) get_codec_stats(odm->subscene->scene_codec, info); else if (odm->codec) get_codec_stats(odm->codec, info); ch = (GF_Channel*)gf_list_get(odm->channels, 0); if (ch && ch->esd->langDesc) info->lang = ch->esd->langDesc->langCode; if (odm->mo && odm->mo->URLs.count) info->media_url = odm->mo->URLs.vals[0].url; return GF_OK; }
static void term_on_command(void *user_priv, GF_ClientService *service, GF_NetworkCommand *com, GF_Err response) { GF_Channel *ch; GET_TERM(); if (com->command_type==GF_NET_BUFFER_QUERY) { GF_List *od_list; u32 i; GF_ObjectManager *odm; com->buffer.max = 0; com->buffer.min = com->buffer.occupancy = (u32) -1; if (!service->owner) { com->buffer.occupancy = 0; return; } /*browse all channels in the scene, running on this service, and get buffer info*/ od_list = NULL; if (service->owner->subscene) { od_list = service->owner->subscene->resources; } else if (service->owner->parentscene) { od_list = service->owner->parentscene->resources; } if (!od_list) { com->buffer.occupancy = 0; return; } /*get exclusive access to media scheduler, to make sure ODs are not being manipulated*/ gf_mx_p(term->mm_mx); if (!gf_list_count(od_list)) GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[ODM] No object manager found for the scene (URL: %s), buffer occupancy will remain unchanged\n", service->url)); i=0; while ((odm = (GF_ObjectManager*)gf_list_enum(od_list, &i))) { u32 j, count; if (!odm->codec) continue; count = gf_list_count(odm->channels); for (j=0; j<count; j++) { GF_Channel *ch = (GF_Channel *)gf_list_get(odm->channels, j); if (ch->service != service) continue; if (ch->es_state != GF_ESM_ES_RUNNING) continue; if (/*!ch->MaxBuffer || */ch->dispatch_after_db || ch->bypass_sl_and_db || ch->IsEndOfStream) continue; //perform buffer management only on base layer -this is because we don't signal which ESs are on/off in the underlying service ... if (ch->esd->dependsOnESID) continue; if (ch->MaxBuffer>com->buffer.max) com->buffer.max = ch->MaxBuffer; if (ch->MinBuffer<com->buffer.min) com->buffer.min = ch->MinBuffer; if (ch->IsClockInit && (u32) ch->BufferTime < com->buffer.occupancy) { /*if we don't have more units (compressed or not) than requested max for the composition memory, request more data*/ if (odm->codec->CB->UnitCount + ch->AU_Count <= odm->codec->CB->Capacity) { // com->buffer.occupancy = 0; com->buffer.occupancy = ch->BufferTime; } else { com->buffer.occupancy = ch->BufferTime; } } } } gf_mx_v(term->mm_mx); // fprintf(stderr, "Buffer occupancy %d\n", com->buffer.occupancy); if (com->buffer.occupancy==(u32) -1) com->buffer.occupancy = 0; return; } if (com->command_type==GF_NET_SERVICE_INFO) { GF_Event evt; evt.type = GF_EVENT_METADATA; gf_term_send_event(term, &evt); return; } if (!com->base.on_channel) return; ch = gf_term_get_channel(service, com->base.on_channel); if (!ch) return; switch (com->command_type) { /*SL reconfiguration*/ case GF_NET_CHAN_RECONFIG: gf_term_lock_net(term, 1); gf_es_reconfig_sl(ch, &com->cfg.sl_config, com->cfg.use_m2ts_sections); gf_term_lock_net(term, 0); return; /*time mapping (TS to media-time)*/ case GF_NET_CHAN_MAP_TIME: if (ch->esd->dependsOnESID) { //ignore everything } else { u32 i; GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: before mapping: seed TS %d - TS offset %d\n", ch->esd->ESID, ch->seed_ts, ch->ts_offset)); ch->seed_ts = com->map_time.timestamp; ch->ts_offset = (u32) (com->map_time.media_time*1000); GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: mapping TS "LLD" to media time %f - current time %d\n", ch->esd->ESID, com->map_time.timestamp, com->map_time.media_time, gf_clock_time(ch->clock))); GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: after mapping: seed TS %d - TS offset %d\n", ch->esd->ESID, ch->seed_ts, ch->ts_offset)); if (com->map_time.reset_buffers) { gf_es_reset_buffers(ch); } /*if we were reassembling an AU, do not perform clock init check when dispatching it since we computed its timestamps according to the previous clock origin*/ else { gf_mx_p(ch->mx); ch->skip_time_check_for_pending = 1; gf_mx_v(ch->mx); } /*if the channel is the clock, force a re-init*/ if (gf_es_owns_clock(ch)) { ch->IsClockInit = 0; gf_clock_reset(ch->clock); } else if (ch->odm->flags & GF_ODM_INHERIT_TIMELINE) { ch->IsClockInit = 0; // ch->ts_offset -= ch->seed_ts*1000/ch->ts_res; } for (i=0; i<gf_list_count(ch->odm->channels); i++) { GF_Channel *a_ch = gf_list_get(ch->odm->channels, i); if (ch==a_ch) continue; if (! a_ch->esd->dependsOnESID) continue; a_ch->seed_ts = ch->seed_ts; a_ch->IsClockInit = 0; a_ch->ts_offset = ch->ts_offset; } } break; /*duration changed*/ case GF_NET_CHAN_DURATION: gf_odm_set_duration(ch->odm, ch, (u32) (1000*com->duration.duration)); break; case GF_NET_CHAN_BUFFER_QUERY: if (ch->IsEndOfStream) { com->buffer.max = com->buffer.min = com->buffer.occupancy = 0; } else { com->buffer.max = ch->MaxBuffer; com->buffer.min = ch->MinBuffer; com->buffer.occupancy = ch->BufferTime; } break; case GF_NET_CHAN_DRM_CFG: gf_term_lock_net(term, 1); gf_es_config_drm(ch, &com->drm_cfg); gf_term_lock_net(term, 0); return; case GF_NET_CHAN_GET_ESD: gf_term_lock_net(term, 1); com->cache_esd.esd = ch->esd; com->cache_esd.is_iod_stream = (ch->odm->subscene /*&& (ch->odm->subscene->root_od==ch->odm)*/) ? 1 : 0; gf_term_lock_net(term, 0); return; default: return; } }
u32 gf_clock_elapse_time(GF_Clock *ck) { if (ck->no_time_ctrl) return gf_clock_time(ck) - ck->init_time; return gf_clock_time(ck); }
Bool validator_on_event_record(void *udta, GF_Event *event, Bool consumed_by_compositor) { GF_Validator *validator = (GF_Validator *)udta; Bool rec_event = 1; switch (event->type) { case GF_EVENT_CONNECT: if (event->connect.is_connected) { if (!validator->trace_mode) { gf_sc_add_video_listener(validator->term->compositor, &validator->video_listener); } validator->ck = validator->term->root_scene->scene_codec ? validator->term->root_scene->scene_codec->ck : validator->term->root_scene->dyn_ck; } break; case GF_EVENT_KEYDOWN: if (event->key.key_code == GF_KEY_INSERT) { rec_event = 0; } else if (event->key.key_code == GF_KEY_PAGEDOWN) { rec_event = 0; } else if (event->key.key_code == GF_KEY_PAGEUP) { rec_event = 0; } else if (event->key.key_code == GF_KEY_END) { rec_event = 0; } else if (event->key.key_code == GF_KEY_CONTROL) { rec_event = 0; } else if (event->key.flags & GF_KEY_MOD_CTRL) { rec_event = 0; } break; case GF_EVENT_KEYUP: if (event->key.flags & GF_KEY_MOD_CTRL) { rec_event = 0; if (event->key.key_code == GF_KEY_INSERT) { char *snap_name = validator_create_snapshot(validator); validator_xvs_add_snapshot_node(validator, snap_name, gf_clock_time(validator->ck)); gf_free(snap_name); } else if (event->key.key_code == GF_KEY_END) { GF_Event evt; memset(&evt, 0, sizeof(GF_Event)); evt.type = GF_EVENT_QUIT; validator->term->compositor->video_out->on_event(validator->term->compositor->video_out->evt_cbk_hdl, &evt); } else if (event->key.key_code == GF_KEY_F1) { validator->snapshot_next_frame = 1; } } else if (event->key.key_code == GF_KEY_PAGEDOWN) { rec_event = 0; validator_xvs_close(validator); gf_term_disconnect(validator->term); gf_sc_remove_video_listener(validator->term->compositor, &validator->video_listener); validator_xvs_next(validator, 0); } else if (event->key.key_code == GF_KEY_PAGEUP) { rec_event = 0; validator_xvs_close(validator); gf_term_disconnect(validator->term); gf_sc_remove_video_listener(validator->term->compositor, &validator->video_listener); validator_xvs_next(validator, 1); } else if (event->key.key_code == GF_KEY_CONTROL) { rec_event = 0; } break; } if (rec_event) { validator_xvs_add_event_dom(validator, event); } return 0; }
static Bool validator_process(GF_TermExt *termext, u32 action, void *param) { const char *opt; GF_Validator *validator = termext->udta; switch (action) { /* Upon starting of the terminal, we parse (possibly an XVL file), an XVS file, and start the first test sequence */ case GF_TERM_EXT_START: validator->term = (GF_Terminal *) param; /* if the validator is loaded, we switch off anti-aliasing for image comparison and we put a low framerate, but we store the previous value to restore it upon termination of the validator */ opt = (char *)gf_modules_get_option((GF_BaseInterface*)termext, "Compositor", "FrameRate"); if (opt) validator->prev_fps = gf_strdup(opt); opt = (char *)gf_modules_get_option((GF_BaseInterface*)termext, "Compositor", "AntiAlias"); if (opt) validator->prev_alias = gf_strdup(opt); /* Check if the validator should be loaded and in which mode */ opt = gf_modules_get_option((GF_BaseInterface*)termext, "Validator", "Mode"); if (!opt) { GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("Validator missing configuration, stopping.\n")); return 0; } else if (!strcmp(opt, "Play")) { GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator starting in playback mode.\n")); validator->is_recording = 0; } else if (!strcmp(opt, "Record")) { GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator starting in recording mode.\n")); validator->is_recording = 1; } else if (!strcmp(opt, "Disable")) { GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator is disabled.\n")); return 0; } else { GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("Validator configuration using wrong mode, stopping.\n")); return 0; } /* initializes the validator and starts */ validator->xvs_filename = NULL; validator->xvl_filename = (char *)gf_modules_get_option((GF_BaseInterface*)termext, "Validator", "XVL"); if (!validator->xvl_filename) { validator->xvs_filename = (char *)gf_modules_get_option((GF_BaseInterface*)termext, "Validator", "XVS"); if (!validator->xvs_filename) { validator->xvs_filename = (char *)gf_modules_get_option((GF_BaseInterface*)termext, "Validator", "Trace"); if (!validator->xvs_filename) { GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("Validator configuration without input, stopping.\n")); return 0; } validator->test_filename = validator->xvs_filename; validator->trace_mode = 1; GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator using trace file: %s\n", validator->xvs_filename)); } else { GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator using scenario file: %s\n", validator->xvs_filename)); } } else { GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator using scenario playlist: %s\n", validator->xvl_filename)); } /* since we changed parameters of the compositor, we need to trigger a reconfiguration */ // gf_modules_set_option((GF_BaseInterface*)termext, "Compositor", "FrameRate", "5.0"); // gf_modules_set_option((GF_BaseInterface*)termext, "Compositor", "AntiAlias", "None"); // gf_term_set_option(validator->term, GF_OPT_RELOAD_CONFIG, 1); /* TODO: if start returns 0, the module is not loaded, so the above init (filter registration) is not removed, should probably return 1 all the time, to make sure stop is called */ if (validator->xvl_filename) { validator_xvl_open(validator); if (!validator->xvl_node) { return 0; } validator_xvs_next(validator, 0); if (!validator->xvs_node) { return 0; } } else if (validator->xvs_filename) { validator_xvs_open(validator); if (!validator->xvs_node) { return 0; } if (validator->test_filename) { validator_test_open(validator); } else { validator_xvs_close(validator); return 0; } } else { return 0; } validator->evt_filter.udta = validator; if (!validator->is_recording) { validator->evt_filter.on_event = validator_on_event_play; termext->caps |= GF_TERM_EXTENSION_NOT_THREADED; } else { validator->evt_filter.on_event = validator_on_event_record; } gf_term_add_event_filter(validator->term, &validator->evt_filter); validator->video_listener.udta = validator; validator->video_listener.on_video_frame = validator_on_video_frame; validator->video_listener.on_video_reconfig = validator_on_video_reconfig; if (!validator->is_recording) { validator_load_event(validator); } return 1; /* when the terminal stops, we close the XVS parser and XVL parser if any, restore the config, and free all validator data (the validator will be destroyed when the module is unloaded) Note: we don't need to disconnect the terminal since it's already stopping */ case GF_TERM_EXT_STOP: gf_term_remove_event_filter(validator->term, &validator->evt_filter); validator_xvs_close(validator); validator_xvl_close(validator); validator->term = NULL; if (validator->test_base) { gf_free(validator->test_base); validator->test_base = NULL; } /*auto-disable the recording by default*/ if (!validator->trace_mode) { if (validator->is_recording ) { gf_modules_set_option((GF_BaseInterface*)termext, "Validator", "Mode", "Play"); } else { gf_modules_set_option((GF_BaseInterface*)termext, "Validator", "Mode", "Disable"); } } GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Stopping validator\n")); if (validator->prev_fps) { gf_modules_set_option((GF_BaseInterface*)termext, "Compositor", "FrameRate", validator->prev_fps); gf_free(validator->prev_fps); validator->prev_fps = NULL; } if (validator->prev_alias) { gf_modules_set_option((GF_BaseInterface*)termext, "Compositor", "AntiAlias", validator->prev_alias); gf_free(validator->prev_alias); validator->prev_alias = NULL; } break; /* When called in the main loop of the terminal, we don't do anything in the recording mode. In the playing/validating mode, we need to check if an event needs to be dispatched or if snapshots need to be made, until there is no more event, in which case we trigger either the load of the next XVS or the quit */ case GF_TERM_EXT_PROCESS: /* if the time is right, dispatch the event and load the next one */ while (!validator->is_recording && validator->evt_loaded && validator->ck && (validator->next_time <= gf_clock_time(validator->ck) )) { Bool has_more_events; //u32 diff = gf_clock_time(validator->ck) - validator->next_time; //GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Time diff: evt_time=%d clock_time = %d, diff=%d\n", validator->next_time, gf_clock_time(validator->ck), diff)); if (validator->next_event_snapshot) { Bool res; char *snap_name = validator_create_snapshot(validator); gf_free(snap_name); res = validator_compare_snapshots(validator); validator->xvs_result &= res; validator->next_event_snapshot = 0; } else { validator->term->compositor->video_out->on_event(validator->term->compositor->video_out->evt_cbk_hdl, &validator->next_event); } has_more_events = validator_load_event(validator); if (!has_more_events) { validator_xvs_close(validator); if (! validator->trace_mode) { gf_term_disconnect(validator->term); gf_sc_remove_video_listener(validator->term->compositor, &validator->video_listener); validator_xvs_next(validator, 0); if (!validator->xvs_node) { GF_Event evt; memset(&evt, 0, sizeof(GF_Event)); evt.type = GF_EVENT_QUIT; validator->term->compositor->video_out->on_event(validator->term->compositor->video_out->evt_cbk_hdl, &evt); } else { if (!validator->is_recording) { validator_load_event(validator); } } } } } break; } return 0; }
static GF_Err SystemCodec_Process(GF_Codec *codec, u32 TimeAvailable) { GF_DBUnit *AU; GF_Channel *ch; u32 now, obj_time, mm_level, au_time, cts; GF_Scene *scene_locked; Bool check_next_unit; GF_SceneDecoder *sdec = (GF_SceneDecoder *)codec->decio; GF_Err e = GF_OK; scene_locked = NULL; /*for resync, if needed - the logic behind this is that there is no composition memory on sytems codecs so "frame dropping" is done by preventing the compositor from redrawing after an update and decoding following AU so that the compositor is always woken up once all late systems AUs are decoded. This flag is overriden when seeking*/ check_next_unit = (codec->odm->term->flags & GF_TERM_SYSDEC_RESYNC) ? 1 : 0; check_unit: /*muting systems codec means we don't decode until mute is off - likely there will be visible however there is no other way to decode system AUs without modifying the content, which is what mute is about on visual...*/ if (codec->Muted) goto exit; /*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, move to STOP*/ if (codec->Status == GF_ESM_CODEC_EOS) { GF_CodecCapability cap; cap.CapCode = GF_CODEC_MEDIA_NOT_OVER; cap.cap.valueInt = 0; sdec->GetCapabilities(codec->decio, &cap); if (!cap.cap.valueInt) { gf_term_stop_codec(codec); if ((codec->type==GF_STREAM_OD) && (codec->nb_dec_frames==1)) { /*this is just by safety, since seeking is only allowed when a single clock is present in the scene*/ if (gf_list_count(codec->odm->net_service->Clocks)==1) codec->odm->subscene->static_media_ressources=1; } } } goto exit; } #ifndef GPAC_DISABLE_VRML if (ch && ch->odm->media_ctrl && !ch->odm->media_ctrl->media_speed) goto exit; #endif /*get the object time*/ obj_time = gf_clock_time(codec->ck); /*clock is not started*/ // if (ch->first_au_fetched && !gf_clock_is_started(ch->clock)) goto exit; /*check timing based on the input channel and main FPS*/ if (AU->DTS > obj_time /*+ codec->odm->term->half_frame_duration*/) goto exit; cts = AU->CTS; /*in cases where no CTS was set for the BIFS (which may be interpreted as "now", although not compliant), use the object clock*/ if (AU->flags & GF_DB_AU_NO_TIMESTAMPS) au_time = obj_time; /*case where CTS is in the past (carousels) */ else if (AU->flags & GF_DB_AU_CTS_IN_PAST) { au_time = - (s32) AU->CTS; cts = AU->DTS; } /*regular case, SFTime will be from CTS (since DTS == CTS)*/ else au_time = AU->DTS; /*check seeking and update timing - do NOT use the base layer, since BIFS streams may depend on other streams not on the same clock*/ if (codec->last_unit_cts == cts) { /*seeking for systems is done by not releasing the graph until seek is done*/ check_next_unit = 1; mm_level = GF_CODEC_LEVEL_SEEK; } /*set system stream timing*/ else { codec->last_unit_cts = AU->CTS; /*we're droping the frame*/ if (scene_locked) codec->nb_droped ++; mm_level = GF_CODEC_LEVEL_NORMAL; } /*lock scene*/ if (!scene_locked) { scene_locked = codec->odm->subscene ? codec->odm->subscene : codec->odm->parentscene; if (!gf_mx_try_lock(scene_locked->root_od->term->compositor->mx)) return GF_OK; /*if terminal is paused, force step-mode: it won't hurt in regular pause/play and ensures proper frame dumping*/ if (codec->odm->term->play_state) codec->odm->term->compositor->step_mode=1; } /*current media time for system objects is the clock time, since the media is likely to have random updates in time*/ codec->odm->current_time = gf_clock_time(codec->ck); now = gf_term_get_time(codec->odm->term); e = sdec->ProcessData(sdec, AU->data, AU->dataLength, ch->esd->ESID, au_time, mm_level); now = gf_term_get_time(codec->odm->term) - now; GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[%s] ODM%d#CH%d at %d decoded AU TS %d in %d ms\n", sdec->module_name, codec->odm->OD->objectDescriptorID, ch->esd->ESID, codec->odm->current_time, AU->CTS, now)); codec_update_stats(codec, AU->dataLength, now); codec->prev_au_size = AU->dataLength; /*destroy this AU*/ gf_es_drop_au(ch); if (e) { GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[SysDec] Codec %s AU CTS %d Decode error %s\n", sdec->module_name , cts, gf_error_to_string(e) )); if (e<0) ch->stream_state = 2; goto exit; } /*in broadcast mode, generate a scene if none is available*/ if (codec->ck->no_time_ctrl) { GF_Scene *scene = codec->odm->subscene ? codec->odm->subscene : codec->odm->parentscene; /*static OD resources (embedded in ESD) in broadcast mode, reset time*/ if (codec->flags & GF_ESM_CODEC_IS_STATIC_OD) gf_clock_reset(codec->ck); /*generate a temp scene if none is in place*/ if (scene->graph_attached != 1) { Bool prev_dyn = scene->is_dynamic_scene; scene->is_dynamic_scene = 1; gf_scene_regenerate(scene); scene->graph_attached = 2; scene->is_dynamic_scene = prev_dyn; GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("[Decoder] Got OD resources before scene - generating temporary scene\n")); } } /*if no release restart*/ if (check_next_unit) goto check_unit; exit: if (scene_locked) { gf_mx_v(scene_locked->root_od->term->compositor->mx); } return e; }
static void MediaDecoder_GetNextAU(GF_Codec *codec, GF_Channel **activeChannel, GF_DBUnit **nextAU) { GF_Channel *ch; GF_DBUnit *AU; u32 count, minDTS, i; count = gf_list_count(codec->inChannels); *nextAU = NULL; *activeChannel = NULL; if (!count) return; minDTS = 0; /*browse from base to top layer*/ for (i=0;i<count;i++) { ch = (GF_Channel*)gf_list_get(codec->inChannels, i); if ((codec->type==GF_STREAM_OCR) && ch->IsClockInit) { /*check duration - we assume that scalable OCR streams are just pure nonsense...*/ if (ch->is_pulling && codec->odm->duration) { if (gf_clock_time(codec->ck) > codec->odm->duration) gf_es_on_eos(ch); } return; } AU = gf_es_get_au(ch); if (!AU) { if (! (*activeChannel)) *activeChannel = ch; continue; } /*aggregate all AUs with the same timestamp on the base AU and delete the upper layers)*/ if (! *nextAU) { GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[%s] ODM%d#CH%d AU CTS %d selected as first layer\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, ch->esd->ESID, AU->CTS)); *nextAU = AU; *activeChannel = ch; minDTS = AU->DTS; } else if (AU->DTS == minDTS) { GF_DBUnit *baseAU = *nextAU; assert(baseAU); baseAU->data = gf_realloc(baseAU->data, baseAU->dataLength + AU->dataLength); memcpy(baseAU->data + baseAU->dataLength , AU->data, AU->dataLength); baseAU->dataLength += AU->dataLength; GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[%s] ODM%d#CH%d AU CTS %d reaggregated on base layer %d\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, ch->esd->ESID, AU->CTS, (*activeChannel)->esd->ESID)); gf_es_drop_au(ch); } else { break; } } if (codec->is_reordering && *nextAU && codec->first_frame_dispatched) { if ((*activeChannel)->esd->slConfig->no_dts_signaling) { u32 CTS = (*nextAU)->CTS; /*reordering !!*/ u32 prev_ts_diff; u32 diff = 0; if (codec->recomputed_cts && (codec->recomputed_cts > (*nextAU)->CTS)) { diff = codec->recomputed_cts - CTS; } prev_ts_diff = (CTS > codec->last_unit_cts) ? (CTS - codec->last_unit_cts) : (codec->last_unit_cts - CTS); if (!diff) diff = prev_ts_diff; else if (prev_ts_diff && (prev_ts_diff < diff) ) diff = prev_ts_diff; if (!codec->min_au_duration || (diff < codec->min_au_duration)) codec->min_au_duration = diff; } else { codec->min_au_duration = 0; /*FIXME - we're breaking sync (couple of frames delay)*/ (*nextAU)->CTS = (*nextAU)->DTS; } } }
/*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); }
static void term_on_command(GF_ClientService *service, GF_NetworkCommand *com, GF_Err response) { GF_Channel *ch; GF_Terminal *term = service->term; if (com->command_type==GF_NET_BUFFER_QUERY) { GF_Scene *scene; u32 i, max_buffer_time; GF_ObjectManager *odm; com->buffer.max = 0; com->buffer.min = com->buffer.occupancy = (u32) -1; com->buffer.buffering = GF_FALSE; if (!service->owner) { com->buffer.occupancy = 0; return; } /*browse all channels in the scene, running on this service, and get buffer info*/ scene = NULL; if (service->owner->subscene) { scene = service->owner->subscene; } else if (service->owner->parentscene) { scene = service->owner->parentscene; } if (!scene) { com->buffer.occupancy = 0; return; } /*get exclusive access to scene resources , to make sure ODs are not being inserted/remove*/ gf_mx_p(scene->mx_resources); max_buffer_time=0; if (!gf_list_count(scene->resources)) GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[ODM] No object manager found for the scene (URL: %s), buffer occupancy will remain unchanged\n", service->url)); i=0; while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) { gather_buffer_level(odm, service, com, &max_buffer_time); } gf_mx_v(scene->mx_resources); if (com->buffer.occupancy==(u32) -1) com->buffer.occupancy = 0; //in bench mode return the 1 if one of the buffer is full (eg sleep until all buffers are not full), 0 otherwise if (term->bench_mode) { com->buffer.occupancy = (max_buffer_time>com->buffer.max) ? 2 : 0; com->buffer.max = 1; com->buffer.min = 0; } return; } if (com->command_type==GF_NET_SERVICE_INFO) { GF_Event evt; evt.type = GF_EVENT_METADATA; gf_term_send_event(term, &evt); return; } if (com->command_type==GF_NET_SERVICE_MEDIA_CAP_QUERY) { gf_sc_get_av_caps(term->compositor, &com->mcaps.width, &com->mcaps.height, &com->mcaps.display_bit_depth, &com->mcaps.audio_bpp, &com->mcaps.channels, &com->mcaps.sample_rate); return; } if (com->command_type==GF_NET_SERVICE_EVENT) { /*check for UDP timeout*/ if (com->send_event.evt.message.error == GF_IP_UDP_TIMEOUT) { const char *sOpt = gf_cfg_get_key(term->user->config, "Network", "AutoReconfigUDP"); if (sOpt && !stricmp(sOpt, "yes")) { char szMsg[1024]; sprintf(szMsg, "!! UDP down (%s) - Retrying with TCP !!\n", com->send_event.evt.message.message); gf_term_message(term, service->url, szMsg, GF_IP_NETWORK_FAILURE); /*reload scene - FIXME this shall work on inline nodes, not on the root !*/ if (term->reload_url) gf_free(term->reload_url); term->reload_state = 1; term->reload_url = gf_strdup(term->root_scene->root_od->net_service->url); gf_cfg_set_key(term->user->config, "Network", "UDPNotAvailable", "yes"); return; } } com->send_event.res = 0; gf_term_send_event(term, &com->send_event.evt); return; } if (com->command_type==GF_NET_ASSOCIATED_CONTENT_LOCATION) { GF_Scene *scene = NULL; if (service->owner->subscene) { scene = service->owner->subscene; } else if (service->owner->parentscene) { scene = service->owner->parentscene; } if (scene) gf_scene_register_associated_media(scene, &com->addon_info); return; } if (com->command_type==GF_NET_ASSOCIATED_CONTENT_TIMING) { GF_Scene *scene = NULL; if (service->owner->subscene) { scene = service->owner->subscene; } else if (service->owner->parentscene) { scene = service->owner->parentscene; } if (scene) gf_scene_notify_associated_media_timeline(scene, &com->addon_time); return; } if (com->command_type==GF_NET_SERVICE_SEEK) { GF_Scene *scene = NULL; if (service->owner->subscene) { scene = service->owner->subscene; } else if (service->owner->parentscene) { scene = service->owner->parentscene; } if (scene && scene->is_dynamic_scene) { gf_sc_lock(term->compositor, 1); gf_scene_restart_dynamic(scene, (u64) (com->play.start_range*1000), 0, 0); gf_sc_lock(term->compositor, 0); } return; } if (com->command_type == GF_NET_SERVICE_CODEC_STAT_QUERY) { GF_List *od_list; u32 i; GF_ObjectManager *odm; com->codec_stat.avg_dec_time = 0; com->codec_stat.max_dec_time = 0; com->codec_stat.irap_avg_dec_time = 0; com->codec_stat.irap_max_dec_time = 0; if (!service->owner) return; /*browse all channels in the scene, running on this service, and get codec stat*/ od_list = NULL; if (service->owner->subscene) { od_list = service->owner->subscene->resources; } else if (service->owner->parentscene) { od_list = service->owner->parentscene->resources; } if (!od_list) return; /*get exclusive access to media scheduler, to make sure ODs are not being manipulated*/ i=0; while ((odm = (GF_ObjectManager*)gf_list_enum(od_list, &i))) { u32 avg_dec_time; /*the decoder statistics are reliable only if we decoded at least 1s*/ if (!odm->codec || !odm->codec->nb_dec_frames || (odm->codec->ck->speed > 0 ? odm->codec->stat_start + 1000 > odm->codec->last_unit_dts : odm->codec->stat_start - 1000 < odm->codec->last_unit_dts)) continue; avg_dec_time = (u32) (odm->codec->total_dec_time / odm->codec->nb_dec_frames); if (avg_dec_time > com->codec_stat.avg_dec_time) { com->codec_stat.avg_dec_time = avg_dec_time; com->codec_stat.max_dec_time = odm->codec->max_dec_time; com->codec_stat.irap_avg_dec_time = odm->codec->nb_iframes ? (u32) (odm->codec->total_iframes_time / odm->codec->nb_iframes) : 0; com->codec_stat.irap_max_dec_time = odm->codec->max_iframes_time; } if (odm->codec->codec_reset) { com->codec_stat.codec_reset = GF_TRUE; odm->codec->codec_reset = GF_FALSE; } com->codec_stat.decode_only_rap = odm->codec->decode_only_rap ? GF_TRUE : GF_FALSE; } return; } if (!com->base.on_channel) return; ch = gf_term_get_channel(service, com->base.on_channel); if (!ch) return; switch (com->command_type) { /*SL reconfiguration*/ case GF_NET_CHAN_RECONFIG: gf_term_lock_net(term, 1); gf_es_reconfig_sl(ch, &com->cfg.sl_config, com->cfg.use_m2ts_sections); gf_term_lock_net(term, 0); return; case GF_NET_CHAN_SET_MEDIA_TIME: if (gf_es_owns_clock(ch) || !ch->clock->has_media_time_shift) { Double mtime = com->map_time.media_time; if (ch->clock->clock_init) { Double t = (Double) com->map_time.timestamp; t /= ch->esd->slConfig->timestampResolution; t -= ((Double) ch->clock->init_time) /1000; mtime += t; } ch->clock->media_time_at_init = (u32) (1000*mtime); ch->clock->has_media_time_shift = 1; } return; /*time mapping (TS to media-time)*/ case GF_NET_CHAN_MAP_TIME: if (ch->esd->dependsOnESID) { //ignore everything } else { u32 i; GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: before mapping: seed TS %d - TS offset %d\n", ch->esd->ESID, ch->seed_ts, ch->ts_offset)); ch->seed_ts = com->map_time.timestamp; ch->ts_offset = (u32) (com->map_time.media_time*1000); GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: mapping TS "LLD" to media time %f - current time %d\n", ch->esd->ESID, com->map_time.timestamp, com->map_time.media_time, gf_clock_time(ch->clock))); GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: after mapping: seed TS %d - TS offset %d\n", ch->esd->ESID, ch->seed_ts, ch->ts_offset)); if (com->map_time.reset_buffers) { gf_es_reset_buffers(ch); } /*if we were reassembling an AU, do not perform clock init check when dispatching it since we computed its timestamps according to the previous clock origin*/ else { gf_mx_p(ch->mx); ch->skip_time_check_for_pending = 1; gf_mx_v(ch->mx); } /*if the channel is the clock, force a re-init*/ if (gf_es_owns_clock(ch)) { ch->IsClockInit = 0; gf_clock_reset(ch->clock); } else if (ch->odm->flags & GF_ODM_INHERIT_TIMELINE) { ch->IsClockInit = 0; // ch->ts_offset -= ch->seed_ts*1000/ch->ts_res; } for (i=0; i<gf_list_count(ch->odm->channels); i++) { GF_Channel *a_ch = gf_list_get(ch->odm->channels, i); if (ch==a_ch) continue; if (! a_ch->esd->dependsOnESID) continue; a_ch->seed_ts = ch->seed_ts; a_ch->IsClockInit = 0; a_ch->ts_offset = ch->ts_offset; } } break; /*duration changed*/ case GF_NET_CHAN_DURATION: gf_odm_set_duration(ch->odm, ch, (u32) (1000*com->duration.duration)); break; case GF_NET_CHAN_BUFFER_QUERY: if (ch->IsEndOfStream) { com->buffer.max = com->buffer.min = com->buffer.occupancy = 0; } else { com->buffer.max = ch->MaxBuffer; com->buffer.min = ch->MinBuffer; com->buffer.occupancy = (u32) (ch->BufferTime / FIX2FLT(ch->clock->speed) ); } break; case GF_NET_CHAN_DRM_CFG: gf_term_lock_net(term, 1); gf_es_config_drm(ch, &com->drm_cfg); gf_term_lock_net(term, 0); return; case GF_NET_CHAN_GET_ESD: gf_term_lock_net(term, 1); com->cache_esd.esd = ch->esd; com->cache_esd.is_iod_stream = (ch->odm->subscene /*&& (ch->odm->subscene->root_od==ch->odm)*/) ? 1 : 0; gf_term_lock_net(term, 0); return; case GF_NET_CHAN_RESET: gf_es_reset_buffers(ch); break; case GF_NET_CHAN_PAUSE: ch->MaxBuffer = com->buffer.max; ch->MinBuffer = com->buffer.min; ch->BufferTime = com->buffer.max; gf_es_buffer_on(ch); break; case GF_NET_CHAN_RESUME: ch->BufferTime = ch->MaxBuffer; gf_es_update_buffering(ch, 1); gf_es_buffer_off(ch); break; case GF_NET_CHAN_BUFFER: ch->MaxBuffer = com->buffer.max; ch->MinBuffer = com->buffer.min; ch->BufferTime = com->buffer.occupancy; gf_es_update_buffering(ch, 1); break; default: return; } }
void gf_es_dispatch_raw_media_au(GF_Channel *ch, char *payload, u32 payload_size, u32 cts) { u32 now; GF_CompositionMemory *cb; GF_CMUnit *cu; if (!payload || !ch->odm->codec->CB) return; if (!ch->odm->codec->CB->no_allocation) return; now = gf_clock_real_time(ch->clock); if (cts + ch->MinBuffer < now) { if (ch->MinBuffer && (ch->is_raw_channel==2)) { ch->clock->clock_init = 0; gf_clock_set_time(ch->clock, cts); GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[ODM%d] Raw Frame dispatched at OTB %d but frame TS is %d ms - adjusting clock\n", ch->odm->OD->objectDescriptorID, now, cts)); } else { GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[ODM%d] Raw Frame dispatched at OTB %d but frame TS is %d ms - DROPPING\n", ch->odm->OD->objectDescriptorID, now, cts)); } return; } cb = ch->odm->codec->CB; cu = gf_cm_lock_input(cb, cts, 1); if (cu) { u32 size = 0; assert(cu->RenderedLength==0); if (cb->UnitSize >= payload_size) { cu->data = payload; size = payload_size; cu->TS = cts; GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM%d] Raw Frame dispatched to CB - TS %d ms - OTB %d ms - OTB_drift %d ms\n", ch->odm->OD->objectDescriptorID, cu->TS, gf_clock_real_time(ch->clock), gf_clock_time(ch->clock) )); } gf_cm_unlock_input(cb, cu, size, 1); if (ch->BufferOn) { ch->BufferOn = 0; gf_clock_buffer_off(ch->clock); gf_cm_abort_buffering(cb); } /*since the CB is a simple pointer to the input frame, wait until it is released before getting back to the caller module*/ if (size) { gf_sema_wait(ch->odm->raw_frame_sema); assert(cb->output->dataLength == 0); } } }
/*special handling of decoders not using ESM*/ static GF_Err PrivateScene_Process(GF_Codec *codec, u32 TimeAvailable) { Bool resume_clock; u32 now; GF_Channel *ch; GF_Scene *scene_locked; GF_SceneDecoder *sdec = (GF_SceneDecoder *)codec->decio; GF_Err e = GF_OK; /*muting systems codec means we don't decode until mute is off - likely there will be visible however there is no other way to decode system AUs without modifying the content, which is what mute is about on visual...*/ if (codec->Muted) return GF_OK; if (codec->Status == GF_ESM_CODEC_EOS) { gf_term_stop_codec(codec); return GF_OK; } scene_locked = codec->odm->subscene ? codec->odm->subscene : codec->odm->parentscene; ch = (GF_Channel*)gf_list_get(codec->inChannels, 0); if (!ch) return GF_OK; resume_clock = 0; /*init channel clock*/ if (!ch->IsClockInit) { Bool started; /*signal seek*/ if (!gf_mx_try_lock(scene_locked->root_od->term->compositor->mx)) return GF_OK; gf_es_init_dummy(ch); sdec->ProcessData(sdec, NULL, 0, ch->esd->ESID, -1, GF_CODEC_LEVEL_NORMAL); gf_mx_v(scene_locked->root_od->term->compositor->mx); started = gf_clock_is_started(ch->clock); /*let's be nice to the scene loader (that usually involves quite some parsing), pause clock while parsing*/ gf_clock_pause(ch->clock); codec->last_unit_dts = 0; if (!started) return GF_OK; } codec->odm->current_time = codec->last_unit_cts = gf_clock_time(codec->ck); /*lock scene*/ GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[PrivateDec] Codec %s Processing at %d\n", sdec->module_name , codec->odm->current_time)); if (!gf_mx_try_lock(scene_locked->root_od->term->compositor->mx)) return GF_OK; now = gf_term_get_time(codec->odm->term); e = sdec->ProcessData(sdec, NULL, 0, ch->esd->ESID, codec->odm->current_time, GF_CODEC_LEVEL_NORMAL); now = gf_term_get_time(codec->odm->term) - now; codec->last_unit_dts ++; /*resume on error*/ if (e && (codec->last_unit_dts<2) ) { gf_clock_resume(ch->clock); codec->last_unit_dts = 2; } /*resume clock on 2nd decode (we assume parsing is done in 2 steps, one for first frame display, one for complete parse)*/ else if (codec->last_unit_dts==2) { gf_clock_resume(ch->clock); } codec_update_stats(codec, 0, now); gf_mx_v(scene_locked->root_od->term->compositor->mx); if (e==GF_EOS) { /*first end of stream, evaluate duration*/ //if (!codec->odm->duration) gf_odm_set_duration(codec->odm, ch, codec->odm->current_time); gf_es_on_eos(ch); return GF_OK; } return e; }
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; }
/*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); }
static void ch_buffer_off(GF_Channel *ch) { /*just in case*/ if (ch->BufferOn) { ch->BufferOn = 0; gf_clock_buffer_off(ch->clock); GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: buffering off at STB %d (OTB %d) (nb buffering on clock: %d)\n", ch->esd->ESID, gf_term_get_time(ch->odm->term), gf_clock_time(ch->clock), ch->clock->Buffering)); } }