| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2023 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / Scene Compositor sub-project | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include <gpac/internal/compositor_dev.h> | |
| #include <gpac/network.h> | |
| #if !defined(GPAC_DISABLE_COMPOSITOR) | |
| Bool scene_ns_on_setup_error(GF_Filter *failed_filter, void *udta, GF_Err err) | |
| { | |
| GF_SceneNamespace *scene_ns; | |
| GF_Scene *scene; | |
| GF_ObjectManager *root = (GF_ObjectManager *)udta; | |
| if (!root) return GF_FALSE;; | |
| scene_ns = root->scene_ns; | |
| gf_fatal_assert(scene_ns); | |
| scene = root->subscene ? root->subscene : root->parentscene; | |
| gf_fatal_assert(scene); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[ODM] Service connection failure received from %s - %s\n", scene_ns->url, gf_error_to_string(err) )); | |
| if (root->scene_ns->owner != root) { | |
| gf_scene_message(scene, scene_ns->url, "Incompatible module type", GF_SERVICE_ERROR); | |
| return GF_FALSE; | |
| } | |
| /*this is service connection*/ | |
| gf_odm_service_media_event(root, GF_EVENT_MEDIA_SETUP_DONE); | |
| scene_ns->connect_ack = GF_TRUE; | |
| if (err) { | |
| char msg[5000]; | |
| snprintf(msg, sizeof(msg), "Cannot open %s", scene_ns->url); | |
| gf_scene_message(scene, scene_ns->url, msg, err); | |
| if (root->mo) root->mo->connect_state = MO_CONNECT_FAILED; | |
| gf_odm_service_media_event(root, GF_EVENT_ERROR); | |
| /*destroy service only if attached*/ | |
| if (root) { | |
| GF_Scene *top_scene; | |
| //notify before disconnecting | |
| if (root->subscene) gf_scene_notify_event(root->subscene, GF_EVENT_SCENE_ATTACHED, NULL, NULL, err, GF_FALSE); | |
| //detach clocks before destroying namspace | |
| root->ck = NULL; | |
| if (root->subscene) { | |
| u32 i, count = gf_list_count(root->subscene->resources); | |
| for (i=0; i<count; i++) { | |
| GF_ObjectManager *anodm = gf_list_get(root->subscene->resources, i); | |
| anodm->ck = NULL; | |
| if (anodm->scene_ns==root->scene_ns) | |
| anodm->scene_ns = NULL; | |
| } | |
| } | |
| root->scene_ns = NULL; | |
| if (scene_ns->owner && scene_ns->nb_odm_users) scene_ns->nb_odm_users--; | |
| scene_ns->owner = NULL; | |
| top_scene = gf_scene_get_root_scene(scene); | |
| //source filter no longer exists | |
| scene_ns->source_filter = NULL; | |
| gf_scene_ns_del(scene_ns, top_scene); | |
| if (!root->parentscene) { | |
| GF_Event evt; | |
| evt.type = GF_EVENT_CONNECT; | |
| evt.connect.is_connected = 0; | |
| gf_filter_send_gf_event(scene->compositor->filter, &evt); | |
| } else { | |
| /*try to reinsert OD for VRML/X3D with multiple URLs: | |
| 1- first remove from parent scene without destroying object, this will trigger a re-setup | |
| if other URLs are present | |
| 2- then destroy object*/ | |
| gf_scene_remove_object(root->parentscene, root, 0); | |
| gf_odm_disconnect(root, 1); | |
| } | |
| return GF_FALSE; | |
| } | |
| } | |
| return GF_FALSE; | |
| } | |
| void gf_scene_insert_pid(GF_Scene *scene, GF_SceneNamespace *sns, GF_FilterPid *pid, Bool is_in_iod) | |
| { | |
| u32 i, min_od_id; | |
| GF_MediaObject *the_mo; | |
| GF_ObjectManager *odm, *root; | |
| const GF_PropertyValue *v; | |
| u32 mtype=0; | |
| u32 pid_odid=0; | |
| u32 pid_id=0; | |
| u32 ServiceID=0; | |
| root = sns->owner; | |
| if (!root) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, ("[Service %s] has no root, aborting !\n", sns->url)); | |
| return; | |
| } | |
| if (root->flags & GF_ODM_DESTROYED) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, ("[Service %s] root has been scheduled for destruction - aborting !\n", sns->url)); | |
| return; | |
| } | |
| scene = root->subscene ? root->subscene : root->parentscene; | |
| if (scene->root_od->addon && (scene->root_od->addon->addon_type == GF_ADDON_TYPE_MAIN)) { | |
| scene->root_od->flags |= GF_ODM_REGENERATE_SCENE; | |
| } | |
| if (scene->is_dynamic_scene) { | |
| if (!root->ID) | |
| root->ID = 1; | |
| } | |
| v = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE); | |
| if (!v) return; | |
| mtype = v->value.uint; | |
| v = gf_filter_pid_get_property(pid, GF_PROP_PID_SERVICE_ID); | |
| if (v) ServiceID = v->value.uint; | |
| v = gf_filter_pid_get_property(pid, GF_PROP_PID_ID); | |
| if (v) pid_id = v->value.uint; | |
| if (scene->compositor->autofps && (mtype==GF_STREAM_VISUAL) ) { | |
| v = gf_filter_pid_get_property(pid, GF_PROP_PID_FPS); | |
| if (v && v->value.frac.den && v->value.frac.num && (v->value.frac.den!=v->value.frac.num)) { | |
| scene->compositor->autofps = GF_FALSE; | |
| gf_sc_set_fps(scene->compositor, v->value.frac); | |
| } | |
| } | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[Service %s] Adding new media\n", sns->url)); | |
| /*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 = NULL; | |
| char *ext, *url; | |
| u32 match_esid = 0; | |
| Bool type_matched = GF_FALSE; | |
| 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; | |
| //got the service, check the type | |
| /*match type*/ | |
| switch (mtype) { | |
| case GF_STREAM_VISUAL: | |
| if (mo->type == GF_MEDIA_OBJECT_VIDEO) type_matched = GF_TRUE; | |
| break; | |
| case GF_STREAM_AUDIO: | |
| if (mo->type == GF_MEDIA_OBJECT_AUDIO) type_matched = GF_TRUE; | |
| break; | |
| case GF_STREAM_SCENE: | |
| if (mo->type == GF_MEDIA_OBJECT_UPDATES) type_matched = GF_TRUE; | |
| break; | |
| default: | |
| break; | |
| } | |
| if (!mo->odm) { | |
| u32 j; | |
| //if not external OD, look for ODM with same ID | |
| if (mo->OD_ID != GF_MEDIA_EXTERNAL_ID) { | |
| for (j=0; j<gf_list_count(scene->resources); j++) { | |
| GF_ObjectManager *an_odm = gf_list_get(scene->resources, j); | |
| if (an_odm->ID == mo->OD_ID) { | |
| mo->odm = an_odm; | |
| break; | |
| } | |
| } | |
| } | |
| if (!mo->odm) { | |
| continue; | |
| } | |
| } | |
| /*if object is attached to a service, don't bother looking in a different one*/ | |
| if (mo->odm->scene_ns && (mo->odm->scene_ns != sns)) { | |
| Bool mine = 0; | |
| if (mo->odm->scene_ns->source_filter) { | |
| mine = gf_filter_pid_is_filter_in_parents(pid, mo->odm->scene_ns->source_filter); | |
| } | |
| //passthrough ODM not yet assigned, consider it ours | |
| else if (!mo->odm->pid && (mo->odm->flags & GF_ODM_PASSTHROUGH)) { | |
| mine = GF_TRUE; | |
| } | |
| if (!mine) continue; | |
| if (type_matched) { | |
| the_mo = mo; | |
| odm = mo->odm; | |
| pid_odid = odm->ID = mo->OD_ID; | |
| break; | |
| } | |
| } | |
| /*already assigned object*/ | |
| if (mo->odm->ID) continue; | |
| /*we cannot match yet the object, we need the OD command for that*/ | |
| if (mo->OD_ID != GF_MEDIA_EXTERNAL_ID) { | |
| continue; | |
| } | |
| if (!mo->URLs.count || !mo->URLs.vals[0].url) continue; | |
| 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 && mo->odm->scene_ns && !strstr(mo->odm->scene_ns->url, url)) { | |
| if (ext) ext[0] = '#'; | |
| continue; | |
| } | |
| if (ext) ext[0] = '#'; | |
| if (match_esid && (pid_id != match_esid)) | |
| continue; | |
| /*match type*/ | |
| switch (mtype) { | |
| 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_SCENE: | |
| if (mo->type != GF_MEDIA_OBJECT_UPDATES) continue; | |
| break; | |
| default: | |
| continue; | |
| } | |
| if (frag) { | |
| u32 frag_id = 0; | |
| u32 ID = mo->OD_ID; | |
| if (ID==GF_MEDIA_EXTERNAL_ID) ID = pid_id; | |
| frag++; | |
| frag_id = atoi(frag); | |
| if (ID!=frag_id) continue; | |
| } | |
| the_mo = mo; | |
| odm = mo->odm; | |
| pid_odid = odm->ID = mo->OD_ID; | |
| if (mo->OD_ID == GF_MEDIA_EXTERNAL_ID) | |
| odm->flags |= GF_ODM_NOT_IN_OD_STREAM; | |
| 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->ID == GF_MEDIA_EXTERNAL_ID) continue; | |
| if (min_od_id < an_odm->ID) | |
| min_od_id = an_odm->ID; | |
| if (odm) continue; | |
| //check for OD replacement, look for ODMs with no PID assigned and a source filter origin for this pid | |
| if (an_odm->pid) continue; | |
| if (!an_odm->scene_ns->source_filter) continue; | |
| if (! gf_filter_pid_is_filter_in_parents(pid, an_odm->scene_ns->source_filter)) | |
| continue; | |
| odm = an_odm; | |
| pid_odid = an_odm->ID; | |
| break; | |
| } | |
| if (is_in_iod) { | |
| odm = scene->root_od; | |
| //for inline cases, the media object may already be setup | |
| the_mo = odm->mo; | |
| if (the_mo) pid_odid = the_mo->OD_ID; | |
| } | |
| if (!odm) { | |
| odm = gf_odm_new(); | |
| odm->parentscene = scene; | |
| gf_list_add(scene->resources, odm); | |
| } | |
| odm->flags |= GF_ODM_NOT_SETUP; | |
| if (!pid_odid) { | |
| pid_odid = min_od_id + 1; | |
| } | |
| odm->ID = pid_odid; | |
| odm->mo = the_mo; | |
| odm->ServiceID = ServiceID; | |
| if (!odm->pid) { | |
| odm->type = mtype; | |
| if (mtype == GF_STREAM_AUDIO) { | |
| scene->compositor->audio_renderer->nb_audio_objects++; | |
| scene->compositor->audio_renderer->scene_ready = GF_FALSE; | |
| } | |
| } | |
| if (gf_filter_pid_is_sparse(pid)) { | |
| odm->flags |= GF_ODM_IS_SPARSE; | |
| } | |
| //register PID with ODM, but don't call setup object | |
| gf_odm_register_pid(odm, pid, GF_TRUE); | |
| gf_filter_pid_set_udta(pid, odm); | |
| v = gf_filter_pid_get_property(pid, GF_PROP_PID_PLAYBACK_MODE); | |
| if (!v || !v->value.uint) odm->flags |= GF_ODM_NO_TIME_CTRL; | |
| if (the_mo) the_mo->OD_ID = odm->ID; | |
| if (!scene->selected_service_id) | |
| scene->selected_service_id = ServiceID; | |
| if (odm->parentscene && odm->parentscene->is_dynamic_scene) | |
| odm->flags |= GF_ODM_NOT_IN_OD_STREAM; | |
| //we insert right away the PID as a new object if the scene is dynamic | |
| //if the scene is not dynamic, we wait for the corresponding OD update | |
| //otherwise if subscene, this is an IOD | |
| if (odm->subscene || (odm->flags & GF_ODM_NOT_IN_OD_STREAM) ) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[ODM%d] setup object - MO %08x\n", odm->ID, odm->mo)); | |
| gf_odm_setup_object(odm, sns, pid); | |
| } else { | |
| //cannot setup until we get the associated OD_Update | |
| odm->ID = 0; | |
| } | |
| if (scene->is_dynamic_scene && !root->scene_ns->connect_ack) { | |
| /*othewise send a connect ack for top level*/ | |
| GF_Event evt; | |
| root->scene_ns->connect_ack = GF_TRUE; | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[ODM] Root object connected (%s) !\n", root->scene_ns->url)); | |
| evt.type = GF_EVENT_CONNECT; | |
| evt.connect.is_connected = GF_TRUE; | |
| gf_sc_send_event(scene->compositor, &evt); | |
| } | |
| } | |
| GF_SceneNamespace *gf_scene_ns_new(GF_Scene *scene, GF_ObjectManager *owner, const char *url, const char *parent_url) | |
| { | |
| char *frag; | |
| GF_SceneNamespace *sns; | |
| GF_SAFEALLOC(sns, GF_SceneNamespace); | |
| if (!sns) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, ("[Compose] Failed to allocate namespace\n")); | |
| return NULL; | |
| } | |
| sns->owner = owner; | |
| sns->url = gf_url_concatenate(parent_url, url); | |
| sns->clocks = gf_list_new(); | |
| frag = strchr(sns->url, '#'); | |
| if (frag) { | |
| sns->url_frag = gf_strdup(frag+1); | |
| frag[0] = 0; | |
| } | |
| //move to top scene | |
| scene = gf_scene_get_root_scene(scene); | |
| gf_list_add(scene->namespaces, sns); | |
| return sns; | |
| } | |
| void gf_scene_ns_del(GF_SceneNamespace *sns, GF_Scene *root_scene) | |
| { | |
| gf_list_del_item(root_scene->namespaces, sns); | |
| if (sns->source_filter) { | |
| gf_filter_remove_src(root_scene->compositor->filter, sns->source_filter); | |
| } | |
| if (sns->clocks) { | |
| while (gf_list_count(sns->clocks)) { | |
| GF_Clock *ck = gf_list_pop_back(sns->clocks); | |
| gf_clock_del(ck); | |
| } | |
| gf_list_del(sns->clocks); | |
| } | |
| if (sns->url) gf_free(sns->url); | |
| if (sns->url_frag) gf_free(sns->url_frag); | |
| gf_free(sns); | |
| } | |
| Bool scene_ns_remove_object(GF_Filter *filter, void *callback, u32 *reschedule_ms) | |
| { | |
| GF_ObjectManager *odm = (GF_ObjectManager *)callback; | |
| odm->has_pending_remove_task = GF_FALSE; | |
| if (odm->flags & GF_ODM_DESTROYED) { | |
| gf_odm_del(odm); | |
| return GF_FALSE; | |
| } | |
| gf_odm_disconnect(odm, 2); | |
| return GF_FALSE; | |
| } | |
| /*connects given OD manager to its URL*/ | |
| void gf_scene_ns_connect_object(GF_Scene *scene, GF_ObjectManager *odm, char *serviceURL, char *parent_url, GF_SceneNamespace *addon_parent_ns) | |
| { | |
| GF_Err e; | |
| char *frag; | |
| char *url_with_args = NULL; | |
| Bool reloc_result=GF_FALSE; | |
| if (addon_parent_ns) { | |
| parent_url = NULL; | |
| } | |
| /*check cache*/ | |
| if (parent_url) { | |
| u32 i, count; | |
| count = gf_opts_get_section_count(); | |
| for (i=0; i<count; i++) { | |
| u32 exp, sec, frac; | |
| const char *opt, *service_cache; | |
| const char *name = gf_opts_get_section_name(i); | |
| if (strncmp(name, "@cache=", 7)) continue; | |
| opt = gf_opts_get_key(name, "serviceURL"); | |
| if (!opt || stricmp(opt, parent_url)) continue; | |
| opt = gf_opts_get_key(name, "cacheName"); | |
| if (!opt || stricmp(opt, serviceURL)) continue; | |
| service_cache = (char*)gf_opts_get_key(name, "cacheFile"); | |
| opt = gf_opts_get_key(name, "expireAfterNTP"); | |
| if (opt) { | |
| sscanf(opt, "%u", &exp); | |
| gf_net_get_ntp(&sec, &frac); | |
| if (exp && (exp<sec)) { | |
| opt = gf_opts_get_key(name, "cacheFile"); | |
| if (opt) gf_file_delete((char*) opt); | |
| gf_opts_del_section(name); | |
| service_cache = NULL; | |
| } | |
| } | |
| if (service_cache) serviceURL = (char *)service_cache; | |
| break; | |
| } | |
| } | |
| frag = strchr(serviceURL, '#'); | |
| if (frag) { | |
| frag[0] = 0; | |
| u32 i, count = gf_list_count(scene->compositor->root_scene->namespaces); | |
| for (i=0; i<count; i++) { | |
| GF_SceneNamespace *sns = gf_list_get(scene->compositor->root_scene->namespaces, i); | |
| if (sns->url && !strcmp(sns->url, serviceURL)) { | |
| frag[0] = '#'; | |
| odm->scene_ns = sns; | |
| sns->nb_odm_users++; | |
| return; | |
| } | |
| } | |
| } | |
| odm->scene_ns = gf_scene_ns_new(scene->compositor->root_scene, odm, serviceURL, (odm->addon || reloc_result) ? NULL : parent_url); | |
| if (!odm->scene_ns) { | |
| gf_scene_message(scene, serviceURL, "Cannot create scene service", GF_OUT_OF_MEM); | |
| gf_odm_disconnect(odm, 1); | |
| if (frag) frag[0] = '#'; | |
| return; | |
| } | |
| odm->scene_ns->nb_odm_users++; | |
| gf_assert(odm->scene_ns->owner == odm); | |
| if (frag && !odm->scene_ns->url_frag) { | |
| odm->scene_ns->url_frag = gf_strdup(frag+1); | |
| } | |
| if (!strncmp(serviceURL, "gpid://", 7)) { | |
| gf_odm_service_media_event(odm, GF_EVENT_MEDIA_SETUP_BEGIN); | |
| gf_odm_service_media_event(odm, GF_EVENT_MEDIA_SETUP_DONE); | |
| odm->scene_ns->connect_ack = GF_TRUE; | |
| odm->flags |= GF_ODM_NOT_IN_OD_STREAM | GF_ODM_PASSTHROUGH; | |
| if (frag) frag[0] = '#'; | |
| return; | |
| } | |
| if (!addon_parent_ns && !parent_url && odm->parentscene && odm->parentscene->root_od->scene_ns) | |
| parent_url = odm->parentscene->root_od->scene_ns->url; | |
| //we setup an addon, use the same filter chain (so copy source id) as orginial content | |
| //so that any scalable streams in the addon can connect to decoders | |
| if (addon_parent_ns) { | |
| const char *fid = gf_filter_get_id(addon_parent_ns->source_filter); | |
| if (fid) { | |
| char szExt[100]; | |
| char sep_a = gf_filter_get_sep(scene->compositor->filter, GF_FS_SEP_ARGS); | |
| char sep_v = gf_filter_get_sep(scene->compositor->filter, GF_FS_SEP_NAME); | |
| gf_dynstrcat(&url_with_args, serviceURL, NULL); | |
| sprintf(szExt, "%cgpac%cFID%c", sep_a, sep_a, sep_v); | |
| gf_dynstrcat(&url_with_args, szExt, NULL); | |
| gf_dynstrcat(&url_with_args, fid, NULL); | |
| } | |
| } | |
| gf_filter_lock_all(scene->compositor->filter, GF_TRUE); | |
| odm->scene_ns->source_filter = gf_filter_connect_source(scene->compositor->filter, url_with_args ? url_with_args : serviceURL, parent_url, GF_FALSE, &e); | |
| if (url_with_args) gf_free(url_with_args); | |
| if (frag) frag[0] = '#'; | |
| if (!odm->scene_ns->source_filter) { | |
| Bool remove_scene=GF_FALSE; | |
| Bool remove_filter=GF_FALSE; | |
| GF_Compositor *compositor = scene->compositor; | |
| GF_Scene *target_scene = odm->subscene ? NULL : odm->parentscene; | |
| gf_filter_lock_all(scene->compositor->filter, GF_FALSE); | |
| if (!odm->mo && target_scene) { | |
| u32 i; | |
| for (i=0; i<gf_list_count(target_scene->scene_objects); i++) { | |
| GF_MediaObject *mo = gf_list_get(target_scene->scene_objects, i); | |
| if (mo->odm) continue; | |
| if ((mo->OD_ID != GF_MEDIA_EXTERNAL_ID) && (mo->OD_ID==odm->ID)) { | |
| odm->mo = mo; | |
| mo->odm = odm; | |
| break; | |
| } | |
| } | |
| } | |
| if (compositor->player && !odm->parentscene) remove_filter = GF_TRUE; | |
| if (odm->mo) odm->mo->connect_state = MO_CONNECT_FAILED; | |
| odm->skip_disconnect_state = 1; | |
| //prevent scene from being disconnected - this can happen if a script catches the event and triggers a disonnection of the parent scene | |
| if (target_scene) target_scene->root_od->skip_disconnect_state = 1; | |
| gf_scene_notify_event(scene, GF_EVENT_SCENE_ATTACHED, NULL, NULL, e, GF_TRUE); | |
| gf_scene_message(scene, serviceURL, "Cannot find filter for service", e); | |
| odm->skip_disconnect_state = 0; | |
| if (target_scene) { | |
| if (target_scene->root_od->skip_disconnect_state==2) remove_scene = GF_TRUE; | |
| target_scene->root_od->skip_disconnect_state = 0; | |
| } | |
| if (remove_scene) { | |
| target_scene->root_od->has_pending_remove_task = GF_TRUE; | |
| gf_filter_post_task(scene->compositor->filter, scene_ns_remove_object, target_scene->root_od, "remove_odm"); | |
| } else { | |
| odm->has_pending_remove_task = GF_TRUE; | |
| gf_filter_post_task(scene->compositor->filter, scene_ns_remove_object, odm, "remove_odm"); | |
| } | |
| if (remove_filter) { | |
| gf_filter_remove(compositor->filter); | |
| compositor->check_eos_state = 3; | |
| } | |
| return; | |
| } | |
| //make sure we only connect this filter to ourselves | |
| gf_filter_set_source_restricted(scene->compositor->filter, odm->scene_ns->source_filter, NULL); | |
| gf_filter_set_setup_failure_callback(scene->compositor->filter, odm->scene_ns->source_filter, scene_ns_on_setup_error, odm); | |
| gf_filter_lock_all(scene->compositor->filter, GF_FALSE); | |
| /*OK connect*/ | |
| gf_odm_service_media_event(odm, GF_EVENT_MEDIA_SETUP_BEGIN); | |
| } | |
| #endif //!defined(GPAC_DISABLE_COMPOSITOR) |