static void term_on_media_add(void *user_priv, GF_ClientService *service, GF_Descriptor *media_desc, Bool no_scene_check) { u32 i, min_od_id; GF_MediaObject *the_mo; GF_Scene *scene; GF_ObjectManager *odm, *root; GF_ObjectDescriptor *od; GET_TERM(); root = service->owner; if (!root) { GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Service %s] has not root, aborting !\n", service->url)); return; } if (root->flags & GF_ODM_DESTROYED) { GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Service %s] root has been scheduled for destruction - aborting !\n", service->url)); return; } scene = root->subscene ? root->subscene : root->parentscene; GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Service %s] %s\n", service->url, media_desc ? "Adding new media object" : "Regenerating scene graph")); if (!media_desc) { if (!no_scene_check) gf_scene_regenerate(scene); return; } switch (media_desc->tag) { case GF_ODF_OD_TAG: case GF_ODF_IOD_TAG: if (root && (root->net_service == service)) { od = (GF_ObjectDescriptor *) media_desc; break; } default: gf_odf_desc_del(media_desc); return; } gf_term_lock_net(term, 1); /*object declared this way are not part of an OD stream and are considered as dynamic*/ /* od->objectDescriptorID = GF_MEDIA_EXTERNAL_ID; */ /*check if we have a mediaObject in the scene not attached and matching this object*/ the_mo = NULL; odm = NULL; min_od_id = 0; for (i=0; i<gf_list_count(scene->scene_objects); i++) { char *frag, *ext; GF_ESD *esd; char *url; u32 match_esid = 0; GF_MediaObject *mo = gf_list_get(scene->scene_objects, i); if ((mo->OD_ID != GF_MEDIA_EXTERNAL_ID) && (min_od_id<mo->OD_ID)) min_od_id = mo->OD_ID; if (!mo->odm) continue; /*if object is attached to a service, don't bother looking in a different one*/ if (mo->odm->net_service && (mo->odm->net_service != service)) continue; /*already assigned object - this may happen since the compositor has no control on when objects are declared by the service, therefore opening file#video and file#audio may result in the objects being declared twice if the service doesn't keep track of declared objects*/ if (mo->odm->OD) { if (od->objectDescriptorID && is_same_od(mo->odm->OD, od)) { /*reassign OD ID*/ mo->OD_ID = od->objectDescriptorID; gf_odf_desc_del(media_desc); gf_term_lock_net(term, 0); return; } continue; } if (mo->OD_ID != GF_MEDIA_EXTERNAL_ID) { if (mo->OD_ID == od->objectDescriptorID) { the_mo = mo; odm = mo->odm; break; } continue; } if (!mo->URLs.count || !mo->URLs.vals[0].url) continue; frag = NULL; ext = strrchr(mo->URLs.vals[0].url, '#'); if (ext) { frag = strchr(ext, '='); ext[0] = 0; } url = mo->URLs.vals[0].url; if (!strnicmp(url, "file://localhost", 16)) url += 16; else if (!strnicmp(url, "file://", 7)) url += 7; else if (!strnicmp(url, "gpac://", 7)) url += 7; else if (!strnicmp(url, "pid://", 6)) match_esid = atoi(url+6); if (!match_esid && !strstr(service->url, url)) { if (ext) ext[0] = '#'; continue; } if (ext) ext[0] = '#'; esd = gf_list_get(od->ESDescriptors, 0); if (match_esid && (esd->ESID != match_esid)) continue; /*match type*/ switch (esd->decoderConfig->streamType) { case GF_STREAM_VISUAL: if (mo->type != GF_MEDIA_OBJECT_VIDEO) continue; break; case GF_STREAM_AUDIO: if (mo->type != GF_MEDIA_OBJECT_AUDIO) continue; break; case GF_STREAM_PRIVATE_MEDIA: if ((mo->type != GF_MEDIA_OBJECT_AUDIO) && (mo->type != GF_MEDIA_OBJECT_VIDEO)) continue; break; case GF_STREAM_SCENE: if (mo->type != GF_MEDIA_OBJECT_UPDATES) continue; break; default: continue; } if (frag) { u32 frag_id = 0; u32 ID = od->objectDescriptorID; if (ID==GF_MEDIA_EXTERNAL_ID) ID = esd->ESID; frag++; frag_id = atoi(frag); if (ID!=frag_id) continue; } the_mo = mo; odm = mo->odm; break; } /*add a pass on scene->resource to check for min_od_id, otherwise we may have another modules declaring an object with ID 0 from another thread, which will assert (only one object with a givne OD ID)*/ for (i=0; i<gf_list_count(scene->resources); i++) { GF_ObjectManager *an_odm = gf_list_get(scene->resources, i); if (an_odm->OD && (an_odm->OD->objectDescriptorID != GF_MEDIA_EXTERNAL_ID) && (min_od_id < an_odm->OD->objectDescriptorID)) min_od_id = an_odm->OD->objectDescriptorID; } if (!odm) { odm = gf_odm_new(); odm->term = term; odm->parentscene = scene; gf_list_add(scene->resources, odm); } odm->OD = od; odm->mo = the_mo; odm->flags |= GF_ODM_NOT_IN_OD_STREAM; if (!od->objectDescriptorID) { od->objectDescriptorID = min_od_id + 1; } if (the_mo) the_mo->OD_ID = od->objectDescriptorID; if (!scene->selected_service_id) scene->selected_service_id = od->ServiceID; /*net is unlocked before seting up the object as this might trigger events going into JS and deadlocks with the compositor*/ gf_term_lock_net(term, 0); GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM%d] setup object - MO %08x\n", odm->OD->objectDescriptorID, odm->mo)); gf_odm_setup_object(odm, service); /*OD inserted by service: resetup scene*/ if (!no_scene_check && scene->is_dynamic_scene) gf_scene_regenerate(scene); }
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; }