Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / Scene Compositor sub-project
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 :
27 :
28 : /*for OD service types*/
29 : #include <gpac/constants.h>
30 : /*for URL concatenation*/
31 : #include <gpac/network.h>
32 : #include <gpac/internal/compositor_dev.h>
33 : #include <gpac/nodes_x3d.h>
34 : #include <gpac/options.h>
35 :
36 : /*SVG properties*/
37 : #ifndef GPAC_DISABLE_SVG
38 : #include <gpac/scenegraph_svg.h>
39 : #endif
40 :
41 : GF_EXPORT
42 50382 : Double gf_scene_get_time(void *_is)
43 : {
44 : GF_Scene *scene = (GF_Scene *)_is;
45 : #if 1
46 : u32 ret;
47 : GF_Clock *ck;
48 : assert(scene);
49 : assert(scene->root_od);
50 50382 : ck = scene->root_od->ck;
51 50382 : if (!ck) return 0.0;
52 50382 : ret = gf_clock_time(ck);
53 50382 : if (scene->root_od->media_stop_time && (scene->root_od->media_stop_time<ret)) ret = (u32) scene->root_od->media_stop_time;
54 50382 : return ret/1000.0;
55 : #else
56 : return scene->simulation_time;
57 : #endif
58 : }
59 :
60 : #ifndef GPAC_DISABLE_VRML
61 :
62 : void gf_storage_save(M_Storage *storage);
63 : #endif
64 :
65 202 : static void inline_on_media_event(GF_Scene *scene, u32 type)
66 : {
67 202 : gf_odm_service_media_event(scene->root_od, type);
68 202 : }
69 :
70 :
71 3 : void gf_scene_message_ex(GF_Scene *scene, const char *service, const char *message, GF_Err error, Bool no_filtering)
72 : {
73 : GF_Event evt;
74 3 : if (!scene || !scene->compositor) return;
75 : memset(&evt, 0, sizeof(GF_Event));
76 3 : evt.type = GF_EVENT_MESSAGE;
77 3 : evt.message.service = service;
78 3 : evt.message.message = message;
79 3 : evt.message.error = error;
80 :
81 3 : if (no_filtering) {
82 0 : gf_filter_ui_event(scene->compositor->filter, &evt);
83 : } else {
84 3 : gf_filter_send_gf_event(scene->compositor->filter, &evt);
85 : }
86 : }
87 :
88 3 : void gf_scene_message(GF_Scene *scene, const char *service, const char *message, GF_Err error)
89 : {
90 3 : gf_scene_message_ex(scene, service, message, error, 0);
91 3 : }
92 :
93 :
94 14 : char *gf_scene_resolve_xlink(GF_Node *node, char *the_url)
95 : {
96 : char *url;
97 14 : GF_Scene *scene = gf_sg_get_private(gf_node_get_graph(node));
98 14 : if (!scene) return gf_strdup(the_url);
99 :
100 14 : url = gf_strdup(the_url);
101 : #ifndef GPAC_DISABLE_SVG
102 : /*apply XML:base*/
103 84 : while (node) {
104 : GF_FieldInfo info;
105 56 : if (gf_node_get_attribute_by_tag(node, TAG_XML_ATT_base, 0, 0, &info)==GF_OK) {
106 0 : char *new_url = gf_url_concatenate( ((XMLRI*)info.far_ptr)->string, url);
107 0 : if (new_url) {
108 0 : gf_free(url);
109 : url = new_url;
110 : }
111 : }
112 56 : node = gf_node_get_parent(node, 0);
113 : }
114 : #endif
115 :
116 : /*if this is a fragment and no XML:BASE was found, this is a fragment of the current document*/
117 14 : if (url[0]=='#') return url;
118 :
119 14 : if (scene->redirect_xml_base) {
120 0 : the_url = gf_url_concatenate(scene->redirect_xml_base, url);
121 : } else {
122 : // the_url = gf_url_concatenate(is->root_od->net_service->url, url);
123 : /*the root url of a document should be "." if not specified, so that the final URL resolve happens only once
124 : at the service level*/
125 14 : the_url = gf_strdup(url);
126 : }
127 14 : gf_free(url);
128 14 : return the_url;
129 : }
130 :
131 82 : static Bool gf_scene_script_action(void *opaque, GF_JSAPIActionType type, GF_Node *n, GF_JSAPIParam *param)
132 : {
133 : Bool ret;
134 : GF_Scene *root_scene;
135 : GF_Scene *scene = (GF_Scene *) opaque;
136 82 : if (!scene) return GF_FALSE;
137 : root_scene = gf_scene_get_root_scene(scene);
138 :
139 82 : if (type==GF_JSAPI_OP_MESSAGE) {
140 0 : gf_scene_message_ex(scene, scene->root_od->scene_ns->url, param->info.msg, param->info.e, 1);
141 0 : return 1;
142 : }
143 82 : if (type==GF_JSAPI_OP_GET_COMPOSITOR) {
144 4 : param->compositor = scene->compositor;
145 4 : return 1;
146 : }
147 78 : if (type==GF_JSAPI_OP_RESOLVE_XLINK) {
148 : #ifndef GPAC_DISABLE_SVG
149 0 : param->uri.url = (char *) gf_scene_resolve_xlink(n, (char *) param->uri.url);
150 0 : return 1;
151 : #else
152 : return 0;
153 : #endif
154 : }
155 78 : if (type==GF_JSAPI_OP_GET_OPT) {
156 0 : param->gpac_cfg.key_val = gf_opts_get_key(param->gpac_cfg.section, param->gpac_cfg.key);
157 0 : return 1;
158 : }
159 78 : if (type==GF_JSAPI_OP_SET_OPT) {
160 0 : gf_opts_set_key(param->gpac_cfg.section, param->gpac_cfg.key, param->gpac_cfg.key_val);
161 0 : return 1;
162 : }
163 78 : if (type==GF_JSAPI_OP_GET_DOWNLOAD_MANAGER) {
164 38 : param->dnld_man = gf_filter_get_download_manager(scene->compositor->filter);
165 38 : return 1;
166 : }
167 40 : if (type==GF_JSAPI_OP_SET_TITLE) {
168 : GF_Event evt;
169 0 : evt.type = GF_EVENT_SET_CAPTION;
170 0 : evt.caption.caption = param->uri.url;
171 0 : gf_filter_send_gf_event(scene->compositor->filter, &evt);
172 : return 1;
173 : }
174 40 : if (type==GF_JSAPI_OP_GET_SUBSCENE) {
175 0 : GF_Scene *a_scene = (GF_Scene *)gf_node_get_private(n);
176 0 : param->scene = a_scene->graph;
177 0 : return 1;
178 : }
179 :
180 40 : if (type==GF_JSAPI_OP_RESOLVE_URI) {
181 : char *url;
182 : char new_url[GF_MAX_PATH];
183 : #ifdef FILTER_FIXME
184 : char localized_url[GF_MAX_PATH];
185 : #endif
186 : Bool result=GF_FALSE;
187 38 : GF_Scene *a_scene = (GF_Scene *)gf_sg_get_private(gf_node_get_graph(n));
188 38 : url = (char *)param->uri.url;
189 38 : if (!url) {
190 0 : param->uri.url = gf_strdup(a_scene->root_od->scene_ns->url);
191 0 : param->uri.nb_params = 0;
192 0 : return 1;
193 : }
194 :
195 : new_url[0]=0;
196 : #ifdef FILTER_FIXME
197 : result = gf_term_relocate_url(term, url, a_scene->root_od->net_service->url, new_url, localized_url);
198 : #endif
199 : if (result) param->uri.url = gf_strdup(new_url);
200 38 : else param->uri.url = gf_url_concatenate(a_scene->root_od->scene_ns->url, url);
201 38 : return 1;
202 : }
203 :
204 : /*special case for pause/stop/resume*/
205 2 : if (type==GF_JSAPI_OP_PAUSE_SVG) {
206 0 : GF_SceneGraph *graph = gf_node_get_graph(n);
207 0 : if (n == gf_sg_get_root_node(graph)) {
208 0 : GF_Scene *a_scene = (GF_Scene *)gf_sg_get_private(graph);
209 0 : if (a_scene->root_od->ck) gf_clock_pause(a_scene->root_od->ck);
210 : return 1;
211 : }
212 : }
213 2 : if (type==GF_JSAPI_OP_RESUME_SVG) {
214 0 : GF_SceneGraph *graph = gf_node_get_graph(n);
215 0 : if (n == gf_sg_get_root_node(graph)) {
216 0 : GF_Scene *a_scene = (GF_Scene *)gf_sg_get_private(graph);
217 0 : if (a_scene->root_od->ck) gf_clock_resume(a_scene->root_od->ck);
218 : return 1;
219 : }
220 : }
221 2 : if (type==GF_JSAPI_OP_RESTART_SVG) {
222 0 : GF_SceneGraph *graph = gf_node_get_graph(n);
223 0 : if (n == gf_sg_get_root_node(graph)) {
224 0 : GF_Scene *a_scene = (GF_Scene *)gf_sg_get_private(graph);
225 0 : GF_Clock *ck = a_scene->root_od->ck;
226 0 : if (ck) {
227 0 : Bool is_paused = ck->nb_paused ? GF_TRUE : GF_FALSE;
228 0 : if (is_paused) gf_clock_resume(ck);
229 0 : gf_scene_restart_dynamic(a_scene, 0, 0, 0);
230 0 : if (is_paused) gf_clock_pause(ck);
231 : }
232 : return 1;
233 : }
234 : return 0;
235 : }
236 2 : if (type==GF_JSAPI_OP_SET_SCENE_SPEED) {
237 0 : GF_SceneGraph *graph = gf_node_get_graph(n);
238 0 : if (n == gf_sg_get_root_node(graph)) {
239 0 : GF_Scene *a_scene = (GF_Scene *)gf_sg_get_private(graph);
240 0 : GF_Clock *ck = a_scene->root_od->ck;
241 0 : if (ck) {
242 0 : gf_clock_set_speed(ck, param->val);
243 : }
244 : return 1;
245 : }
246 : return 0;
247 : }
248 :
249 :
250 2 : ret = gf_sc_script_action(scene->compositor, type, n, param);
251 2 : if (ret) return ret;
252 :
253 0 : if (type==GF_JSAPI_OP_LOAD_URL) {
254 0 : if (gf_sg_get_private(gf_node_get_graph(n)) == root_scene) {
255 : GF_Event evt;
256 0 : evt.type = GF_EVENT_NAVIGATE;
257 0 : evt.navigate.to_url = param->uri.url;
258 0 : evt.navigate.parameters = param->uri.params;
259 0 : evt.navigate.param_count = param->uri.nb_params;
260 0 : return gf_filter_send_gf_event(scene->compositor->filter, &evt);
261 : } else {
262 : /*TODO*/
263 : return 0;
264 : }
265 : }
266 : return 0;
267 : }
268 :
269 :
270 498 : Bool gf_scene_is_root(GF_Scene *scene)
271 : {
272 : GF_Scene *s = scene;
273 498 : while (s->root_od->parentscene) s = s->root_od->parentscene;
274 498 : return (s==scene) ? GF_TRUE : GF_FALSE;
275 : }
276 :
277 1891 : GF_Scene *gf_scene_get_root_scene(GF_Scene *scene)
278 : {
279 1973 : while (scene && scene->root_od && scene->root_od->parentscene)
280 : scene = scene->root_od->parentscene;
281 1891 : return scene;
282 : }
283 :
284 :
285 :
286 : GF_EXPORT
287 681 : GF_Scene *gf_scene_new(GF_Compositor *compositor, GF_Scene *parentScene)
288 : {
289 : GF_Scene *tmp;
290 681 : if (!compositor && !parentScene) return NULL;
291 :
292 681 : GF_SAFEALLOC(tmp, GF_Scene);
293 681 : if (! tmp) return NULL;
294 :
295 681 : tmp->resources = gf_list_new();
296 681 : tmp->scene_objects = gf_list_new();
297 681 : tmp->extra_scenes = gf_list_new();
298 681 : tmp->declared_addons = gf_list_new();
299 : /*init inline scene*/
300 681 : if (parentScene) {
301 74 : tmp->graph = gf_sg_new_subscene(parentScene->graph);
302 : assert(!compositor || (compositor==parentScene->compositor));
303 74 : tmp->compositor = parentScene->compositor;
304 : } else {
305 607 : tmp->graph = gf_sg_new();
306 607 : tmp->compositor = compositor;
307 : //only for the top scene
308 607 : tmp->namespaces = gf_list_new();
309 : }
310 :
311 681 : gf_sg_set_private(tmp->graph, tmp);
312 681 : gf_sg_set_node_callback(tmp->graph, gf_scene_node_callback);
313 681 : gf_sg_set_scene_time_callback(tmp->graph, gf_scene_get_time);
314 681 : if (tmp->compositor && !tmp->compositor->nojs)
315 655 : gf_sg_set_script_action(tmp->graph, gf_scene_script_action, tmp);
316 :
317 : //copy over pause_at_first_frame flag so that new subscene is not paused right away
318 681 : if (parentScene)
319 74 : tmp->first_frame_pause_type = parentScene->first_frame_pause_type;
320 :
321 : #ifndef GPAC_DISABLE_VRML
322 681 : tmp->extern_protos = gf_list_new();
323 681 : gf_sg_set_proto_loader(tmp->graph, gf_inline_get_proto_lib);
324 :
325 681 : tmp->storages = gf_list_new();
326 681 : tmp->keynavigators = gf_list_new();
327 681 : tmp->attached_inlines = gf_list_new();
328 : #endif
329 :
330 681 : tmp->on_media_event = inline_on_media_event;
331 681 : return tmp;
332 : }
333 :
334 1367 : static void gf_scene_reset_urls(GF_Scene *scene)
335 : {
336 : #define SFURL_RESET(__url) if (__url.url) gf_free(__url.url);\
337 : memset(&__url, 0, sizeof(SFURL));
338 :
339 1367 : SFURL_RESET(scene->audio_url);
340 1367 : SFURL_RESET(scene->visual_url);
341 1367 : SFURL_RESET(scene->text_url);
342 1367 : SFURL_RESET(scene->dims_url);
343 1367 : }
344 :
345 : GF_EXPORT
346 681 : void gf_scene_del(GF_Scene *scene)
347 : {
348 681 : gf_list_del(scene->resources);
349 : assert(!gf_list_count(scene->extra_scenes) );
350 681 : gf_list_del(scene->extra_scenes);
351 :
352 : #ifndef GPAC_DISABLE_VRML
353 1362 : while (gf_list_count(scene->extern_protos)) {
354 0 : GF_ProtoLink *pl = (GF_ProtoLink *)gf_list_get(scene->extern_protos, 0);
355 0 : gf_list_rem(scene->extern_protos, 0);
356 0 : gf_free(pl);
357 : }
358 681 : gf_list_del(scene->extern_protos);
359 : #endif
360 :
361 : /*delete the scene graph*/
362 681 : gf_sg_del(scene->graph);
363 :
364 : /*don't touch the root_od, will be deleted by the parent scene*/
365 :
366 : /*clean all remaining associations*/
367 1362 : while (gf_list_count(scene->scene_objects)) {
368 0 : GF_MediaObject *obj = (GF_MediaObject *)gf_list_get(scene->scene_objects, 0);
369 0 : if (obj->odm) obj->odm->mo = NULL;
370 0 : gf_list_rem(scene->scene_objects, 0);
371 0 : gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL);
372 0 : gf_mo_del(obj);
373 : }
374 681 : gf_list_del(scene->scene_objects);
375 : #ifndef GPAC_DISABLE_VRML
376 681 : gf_list_del(scene->storages);
377 681 : gf_list_del(scene->keynavigators);
378 : #endif
379 :
380 681 : gf_list_del(scene->declared_addons);
381 :
382 681 : gf_scene_reset_urls(scene);
383 :
384 681 : if (scene->fragment_uri) gf_free(scene->fragment_uri);
385 681 : if (scene->redirect_xml_base) gf_free(scene->redirect_xml_base);
386 :
387 681 : if (scene->namespaces) {
388 1219 : while (gf_list_count(scene->namespaces)) {
389 612 : GF_SceneNamespace *sns = gf_list_pop_back(scene->namespaces);
390 612 : gf_scene_ns_del(sns, scene);
391 : }
392 607 : gf_list_del(scene->namespaces);
393 : }
394 :
395 : #ifndef GPAC_DISABLE_VRML
396 681 : while (gf_list_count(scene->attached_inlines)) {
397 0 : GF_Node *n_inline = gf_list_pop_back(scene->attached_inlines);
398 0 : gf_node_set_private(n_inline, NULL);
399 : }
400 681 : gf_list_del(scene->attached_inlines);
401 : #endif
402 :
403 681 : if (scene->compositor->root_scene == scene)
404 598 : scene->compositor->root_scene = NULL;
405 :
406 681 : gf_free(scene);
407 681 : }
408 :
409 : GF_EXPORT
410 10 : GF_ObjectManager *gf_scene_find_odm(GF_Scene *scene, u16 OD_ID)
411 : {
412 : GF_ObjectManager *odm;
413 10 : u32 i=0;
414 32 : while ((odm = (GF_ObjectManager *)gf_list_enum(scene->resources, &i))) {
415 22 : if (odm->ID == OD_ID) return odm;
416 : }
417 : return NULL;
418 : }
419 :
420 : GF_EXPORT
421 686 : void gf_scene_disconnect(GF_Scene *scene, Bool for_shutdown)
422 : {
423 : u32 i;
424 : GF_MediaObject *obj;
425 : GF_ObjectManager *odm;
426 :
427 686 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Scene] disconnecting\n"));
428 :
429 :
430 : /*force unregistering of inline nodes (for safety)*/
431 686 : if (for_shutdown && scene->root_od->mo) {
432 : /*reset private stack of all inline nodes still registered*/
433 28 : while (gf_mo_event_target_count(scene->root_od->mo)) {
434 2 : gf_mo_event_target_remove_by_index(scene->root_od->mo, 0);
435 : #ifndef GPAC_DISABLE_VRML
436 2 : GF_Node *n = (GF_Node *)gf_event_target_get_node(gf_mo_event_target_get(scene->root_od->mo, 0));
437 2 : if (n) {
438 0 : switch (gf_node_get_tag(n)) {
439 0 : case TAG_MPEG4_Inline:
440 : #ifndef GPAC_DISABLE_X3D
441 : case TAG_X3D_Inline:
442 : #endif
443 0 : gf_node_set_private(n, NULL);
444 0 : break;
445 : }
446 : }
447 : #endif
448 : }
449 : }
450 :
451 : //Ivica patch: Remove all Registered InputSensor nodes -> shut down the InputSensor threads -> prevent illegal access on deleted pointers
452 : #ifndef GPAC_DISABLE_VRML
453 686 : if (for_shutdown) {
454 685 : i = 0;
455 1844 : while ((odm = (GF_ObjectManager *)gf_list_enum(scene->resources, &i))) {
456 474 : if (odm->mo) {
457 470 : odm->ck = NULL;
458 : obj = odm->mo;
459 1396 : while (gf_mo_event_target_count(obj)) {
460 456 : GF_Node *n = (GF_Node *)gf_event_target_get_node(gf_mo_event_target_get(obj, 0));
461 456 : if (n) {
462 456 : switch (gf_node_get_tag(n)) {
463 3 : case TAG_MPEG4_InputSensor:
464 : {
465 : M_InputSensor* is = (M_InputSensor*)n;
466 3 : is->enabled = 0;
467 3 : InputSensorModified(n);
468 3 : break;
469 : }
470 : }
471 : }
472 456 : gf_mo_event_target_remove_by_index(obj, 0);
473 : }
474 : }
475 : }
476 : }
477 : #endif
478 :
479 : /*remove all associated eventTargets*/
480 686 : i=0;
481 1856 : while ((obj = (GF_MediaObject *)gf_list_enum(scene->scene_objects, &i))) {
482 484 : gf_mo_event_target_reset(obj);
483 : }
484 :
485 : #ifndef GPAC_DISABLE_VRML
486 687 : while (gf_list_count(scene->storages)) {
487 1 : M_Storage *storage = (M_Storage *)gf_list_get(scene->storages, 0);
488 1 : gf_list_rem(scene->storages, 0);
489 1 : if (storage->_auto) gf_storage_save(storage);
490 : }
491 : #endif
492 :
493 686 : if (!scene->root_od->parentscene) {
494 607 : gf_sc_set_scene(scene->compositor, NULL);
495 : }
496 :
497 686 : gf_scene_reset_addons(scene);
498 :
499 : /*release the scene - at this stage, we no longer have any node stack referring to our media objects */
500 :
501 686 : gf_sc_node_destroy(scene->compositor, NULL, scene->graph);
502 686 : gf_sg_reset(scene->graph);
503 686 : scene->graph_attached = 0;
504 :
505 :
506 : /*disconnect all objects*/
507 686 : if (!for_shutdown) {
508 1 : i=0;
509 : /*stop all objects but DON'T DESTROY THEM*/
510 4 : while ((odm = (GF_ObjectManager *)gf_list_enum(scene->resources, &i))) {
511 2 : if (odm->state) gf_odm_disconnect(odm, GF_FALSE);
512 : //reset OD ID until we get a new OD re-attaching this object
513 2 : odm->ID = 0;
514 : }
515 : /*reset all stream associations*/
516 1 : i=0;
517 4 : while ((obj = (GF_MediaObject*)gf_list_enum(scene->scene_objects, &i))) {
518 2 : gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL);
519 2 : gf_mo_event_target_reset(obj);
520 : }
521 : }
522 : /*disconnect and remove all objects*/
523 : else {
524 1051 : while (gf_list_count(scene->resources)) {
525 366 : odm = (GF_ObjectManager *)gf_list_get(scene->resources, 0);
526 366 : if (odm->skip_disconnect_state) {
527 0 : gf_list_rem(scene->resources, 0);
528 : } else {
529 366 : gf_odm_disconnect(odm, for_shutdown ? 2 : 0);
530 : }
531 : }
532 : #ifndef GPAC_DISABLE_VRML
533 691 : while (gf_list_count(scene->extern_protos)) {
534 6 : GF_ProtoLink *pl = (GF_ProtoLink *)gf_list_get(scene->extern_protos, 0);
535 6 : gf_list_rem(scene->extern_protos, 0);
536 6 : gf_free(pl);
537 : }
538 : #endif
539 : }
540 :
541 : /*remove stream associations*/
542 710 : while (gf_list_count(scene->scene_objects)) {
543 24 : obj = (GF_MediaObject*)gf_list_get(scene->scene_objects, 0);
544 24 : gf_list_rem(scene->scene_objects, 0);
545 24 : if (obj->odm) obj->odm->mo = NULL;
546 24 : gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL);
547 24 : gf_mo_del(obj);
548 : }
549 :
550 : //reset URLs
551 686 : gf_scene_reset_urls(scene);
552 :
553 686 : scene->object_attached = 0;
554 686 : }
555 :
556 130 : static void gf_scene_insert_object(GF_Scene *scene, GF_MediaObject *mo, Bool lock_timelines, GF_MediaObject *sync_ref, Bool keep_fragment, GF_Scene *original_parent_scene)
557 : {
558 : GF_ObjectManager *odm;
559 : char *url, *final_url;
560 130 : if (!mo || !scene) return;
561 :
562 130 : odm = gf_odm_new();
563 : /*remember OD*/
564 130 : odm->mo = mo;
565 130 : mo->odm = odm;
566 130 : odm->parentscene = scene;
567 130 : odm->ID = GF_MEDIA_EXTERNAL_ID;
568 130 : if (scene->force_single_timeline) lock_timelines = GF_TRUE;
569 :
570 130 : url = mo->URLs.vals[0].url;
571 :
572 130 : if (!url) return;
573 :
574 130 : if (!stricmp(url, "KeySensor")) {
575 : final_url = "gpac://KeySensor";
576 130 : } else if (!stricmp(url, "StringSensor")) {
577 : final_url = "gpac://StringSensor";
578 130 : } else if (!stricmp(url, "Mouse")) {
579 : final_url = "gpac://Mouse";
580 : } else {
581 : final_url = mo->URLs.vals[0].url;
582 130 : if (lock_timelines) odm->flags |= GF_ODM_INHERIT_TIMELINE;
583 : }
584 :
585 : /*HACK - temp storage of sync ref*/
586 130 : if (sync_ref) odm->sync_ref = sync_ref;
587 :
588 130 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Scene] Inserting new MediaObject %08x for resource %s\n", odm->mo, url));
589 130 : gf_list_add(scene->resources, odm);
590 :
591 130 : gf_odm_setup_remote_object(odm, original_parent_scene ? original_parent_scene->root_od->scene_ns : NULL, final_url);
592 : }
593 :
594 0 : static void gf_scene_reinsert_object(GF_Scene *scene, GF_MediaObject *mo)
595 : {
596 : u32 i;
597 0 : gf_free(mo->URLs.vals[0].url);
598 0 : mo->URLs.vals[0].url = NULL;
599 0 : for (i=0; i<mo->URLs.count-1; i++) mo->URLs.vals[i].url = mo->URLs.vals[i+1].url;
600 0 : mo->URLs.vals[mo->URLs.count-1].url = NULL;
601 0 : mo->URLs.count-=1;
602 : /*FIXME - we should re-ananlyse whether the fragment is important or not ...*/
603 0 : gf_scene_insert_object(scene, mo, GF_FALSE, NULL, GF_FALSE, NULL);
604 0 : }
605 :
606 :
607 501 : void gf_scene_remove_object(GF_Scene *scene, GF_ObjectManager *odm, u32 for_shutdown)
608 : {
609 : u32 i;
610 : GF_MediaObject *obj;
611 :
612 501 : gf_list_del_item(scene->resources, odm);
613 :
614 501 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Scene] removing ODM %d\n", odm->ID ));
615 :
616 :
617 501 : i=0;
618 1393 : while ((obj = (GF_MediaObject*)gf_list_enum(scene->scene_objects, &i))) {
619 836 : if (
620 : /*assigned object*/
621 1237 : (obj->odm==odm) ||
622 : /*remote OD*/
623 497 : ((obj->OD_ID!=GF_MEDIA_EXTERNAL_ID) && (obj->OD_ID == odm->ID) )
624 : /*dynamic OD*/
625 391 : || (obj->URLs.count && odm->scene_ns && (odm->scene_ns!=scene->root_od->scene_ns) && !stricmp(obj->URLs.vals[0].url, odm->scene_ns->url))
626 : ) {
627 : u32 discard_obj = 0;
628 :
629 445 : obj->flags = 0;
630 445 : if (obj->odm) obj->odm->mo = NULL;
631 445 : odm->mo = NULL;
632 445 : obj->odm = NULL;
633 445 : if (obj->pck) {
634 6 : gf_filter_pck_unref(obj->pck);
635 6 : obj->pck = NULL;
636 : }
637 :
638 445 : obj->frame = NULL;
639 445 : obj->framesize = obj->timestamp = 0;
640 445 : obj->config_changed = GF_TRUE;
641 :
642 :
643 : /*if graph not attached we can remove the link (this is likely scene shutdown for some error)*/
644 445 : if (!scene->graph_attached) {
645 : #ifndef GPAC_DISABLE_VRML
646 : GF_ProtoLink *pl;
647 362 : u32 j=0;
648 736 : while ((pl = (GF_ProtoLink *)gf_list_enum(scene->extern_protos, &j))) {
649 18 : if (pl->mo==obj) {
650 6 : pl->mo = NULL;
651 6 : break;
652 : }
653 : }
654 : #endif
655 : discard_obj = 1;
656 83 : } else if (!for_shutdown) {
657 : /*if dynamic OD and more than 1 URLs resetup*/
658 0 : if ((obj->OD_ID==GF_MEDIA_EXTERNAL_ID) && (obj->URLs.count>1)) {
659 : discard_obj = 0;
660 0 : gf_scene_reinsert_object(scene, obj);
661 : } else {
662 : discard_obj = 2;
663 : }
664 : }
665 : /*discard media object*/
666 83 : else if (for_shutdown==2)
667 : discard_obj = 1;
668 :
669 : /*reset private stack of all inline nodes still registered*/
670 : if (discard_obj) {
671 447 : while (gf_mo_event_target_count(obj)) {
672 14 : gf_mo_event_target_remove_by_index(obj, 0);
673 : #ifndef GPAC_DISABLE_VRML
674 14 : GF_Node *n = (GF_Node *)gf_event_target_get_node(gf_mo_event_target_get(obj, 0));
675 14 : if (n) {
676 1 : switch (gf_node_get_tag(n)) {
677 0 : case TAG_MPEG4_Inline:
678 : #ifndef GPAC_DISABLE_X3D
679 : case TAG_X3D_Inline:
680 : #endif
681 0 : if (obj->num_open) gf_mo_stop(&obj);
682 0 : gf_node_set_private(n, NULL);
683 0 : break;
684 1 : default:
685 1 : gf_sc_mo_destroyed(n);
686 1 : break;
687 : }
688 13 : }
689 : #endif
690 : }
691 : }
692 :
693 445 : if ((discard_obj==1) && !obj->num_open) {
694 420 : gf_list_rem(scene->scene_objects, i-1);
695 420 : gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL);
696 420 : gf_mo_del(obj);
697 : }
698 445 : return;
699 : }
700 : }
701 : }
702 :
703 :
704 : //browse all channels and update buffering info
705 1878 : void gf_scene_buffering_info(GF_Scene *scene, Bool rebuffer_done)
706 : {
707 : GF_ODMExtraPid *xpid;
708 : u32 i, j;
709 : u64 max_buffer, cur_buffer, min_time, max_buff_val=0;
710 : u64 buf_val;
711 : GF_Event evt;
712 : GF_ObjectManager *odm;
713 1878 : if (!scene) return;
714 :
715 : max_buffer = cur_buffer = 0;
716 : min_time = (u64) -1;
717 :
718 : /*get buffering on root OD*/
719 1878 : odm = scene->root_od;
720 1878 : if (!scene->is_dynamic_scene && odm->pid && odm->buffer_playout_ms) {
721 1302 : if (max_buff_val < odm->buffer_playout_ms)
722 : max_buff_val = odm->buffer_playout_ms;
723 :
724 1302 : if (odm->nb_buffering) {
725 : max_buffer += odm->buffer_playout_ms;
726 12 : buf_val = gf_filter_pid_query_buffer_duration(odm->pid, GF_FALSE) / 1000;
727 : if (min_time>buf_val) min_time = buf_val;
728 :
729 12 : if (buf_val > max_buffer) buf_val = max_buffer;
730 12 : cur_buffer += (buf_val>0) ? buf_val : 1;
731 12 : i=0;
732 24 : while ((xpid = gf_list_enum(odm->extra_pids, &i))) {
733 0 : max_buffer += odm->buffer_playout_ms;
734 0 : buf_val = gf_filter_pid_query_buffer_duration(xpid->pid, GF_FALSE) / 1000;
735 0 : if (min_time>buf_val) min_time = buf_val;
736 :
737 0 : if (buf_val > max_buffer) buf_val = max_buffer;
738 0 : cur_buffer += (buf_val>0) ? buf_val : 1;
739 : }
740 : }
741 : }
742 :
743 : /*get buffering on all ODs*/
744 1878 : i=0;
745 5595 : while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) {
746 1839 : if (!odm->buffer_playout_ms) continue;
747 1648 : if (max_buff_val < odm->buffer_playout_ms)
748 : max_buff_val = odm->buffer_playout_ms;
749 :
750 1648 : if (!odm->nb_buffering) continue;
751 :
752 1021 : max_buffer += odm->buffer_playout_ms;
753 1021 : buf_val = gf_filter_pid_query_buffer_duration(odm->pid, GF_FALSE) / 1000;
754 1021 : if (min_time>buf_val) min_time = buf_val;
755 :
756 1021 : if (buf_val > max_buffer) buf_val = max_buffer;
757 1021 : cur_buffer += (buf_val>0) ? buf_val : 1;
758 1021 : j=0;
759 2042 : while ((xpid = gf_list_enum(odm->extra_pids, &j))) {
760 0 : max_buffer += odm->buffer_playout_ms;
761 0 : buf_val = gf_filter_pid_query_buffer_duration(xpid->pid, GF_FALSE) / 1000;
762 0 : if (min_time>buf_val) min_time = buf_val;
763 :
764 0 : if (buf_val > max_buffer) buf_val = max_buffer;
765 0 : cur_buffer += (buf_val>0) ? buf_val : 1;
766 : }
767 : }
768 : //likely local file playback with buffer disabled
769 1878 : if (!max_buff_val)
770 : return;
771 : //destruction
772 1878 : if (!scene->root_od->scene_ns)
773 : return;
774 :
775 : //if buffering, fire GF_EVENT_MEDIA_PROGRESS - use the min buffer we just computed
776 1878 : if ((rebuffer_done || scene->nb_buffering) && max_buffer) {
777 1029 : gf_odm_service_media_event_with_download(scene->root_od, GF_EVENT_MEDIA_PROGRESS, 0, 0, 0, (u32) (100 * cur_buffer / max_buffer) + 1, (u32) min_time);
778 : }
779 :
780 1878 : evt.type = GF_EVENT_PROGRESS;
781 1878 : evt.progress.progress_type = 0;
782 1878 : evt.progress.service = scene->root_od->scene_ns->url;
783 :
784 1878 : if (!max_buffer || !cur_buffer || (max_buffer <= cur_buffer)) {
785 1497 : if (!max_buffer) max_buffer=max_buff_val;
786 1497 : evt.progress.done = evt.progress.total = (u32) (max_buffer / 1000);
787 : } else {
788 381 : evt.progress.done = (u32) (cur_buffer / 1000);
789 381 : evt.progress.total = (u32) (max_buffer / 1000);
790 : }
791 1878 : gf_sc_send_event(scene->compositor, &evt);
792 : }
793 :
794 :
795 :
796 2425 : void gf_scene_notify_event(GF_Scene *scene, u32 event_type, GF_Node *n, void *_event, GF_Err code, Bool no_queuing)
797 : {
798 : /*fire resize event*/
799 : #ifndef GPAC_DISABLE_SVG
800 : GF_Node *root;
801 : u32 i, count;
802 : u32 w, h;
803 : GF_DOM_Event evt, *dom_event;
804 : dom_event = (GF_DOM_Event *)_event;
805 :
806 2425 : if (!scene) return;
807 2425 : root = gf_sg_get_root_node(scene->graph);
808 :
809 2425 : if (!dom_event) {
810 : memset(&evt, 0, sizeof(GF_DOM_Event));
811 : dom_event = &evt;
812 580 : w = h = 0;
813 580 : gf_sg_get_scene_size_info(scene->graph, &w, &h);
814 580 : evt.type = event_type;
815 580 : evt.screen_rect.width = INT2FIX(w);
816 580 : evt.screen_rect.height = INT2FIX(h);
817 580 : evt.key_flags = scene->is_dynamic_scene ? (scene->vr_type ? 2 : 1) : 0;
818 580 : if (root) {
819 : #ifndef GPAC_DISABLE_VRML
820 345 : switch (gf_node_get_tag(root)) {
821 2 : case TAG_MPEG4_Group:
822 : case TAG_MPEG4_Layer3D:
823 2 : evt.detail = 1;
824 2 : break;
825 : #ifndef GPAC_DISABLE_X3D
826 16 : case TAG_X3D_Group:
827 16 : evt.detail = 2;
828 16 : break;
829 : #endif
830 : }
831 : #endif
832 : }
833 :
834 580 : evt.error_state = code;
835 : }
836 2425 : if (n) {
837 56 : if (no_queuing) {
838 56 : gf_dom_event_fire(n, dom_event);
839 : } else {
840 0 : gf_sc_queue_dom_event(scene->compositor, n, dom_event);
841 : }
842 : } else {
843 2369 : if (root) {
844 2159 : if (no_queuing) {
845 1843 : gf_dom_event_fire(root, dom_event);
846 : } else {
847 316 : gf_sc_queue_dom_event(scene->compositor, root, dom_event);
848 : }
849 : }
850 :
851 2369 : count=scene->root_od->mo ? gf_mo_event_target_count(scene->root_od->mo) : 0;
852 3563 : for (i=0; i<count; i++) {
853 1194 : GF_Node *an = gf_event_target_get_node(gf_mo_event_target_get(scene->root_od->mo, i));
854 1194 : if (no_queuing) {
855 1132 : gf_dom_event_fire(an, dom_event);
856 : } else {
857 62 : gf_sc_queue_dom_event(scene->compositor, an, dom_event);
858 : }
859 : }
860 : }
861 : #endif
862 : }
863 :
864 :
865 : GF_EXPORT
866 906 : void gf_scene_attach_to_compositor(GF_Scene *scene)
867 : {
868 906 : if (!scene->root_od) return;
869 906 : if (scene->graph_attached==1) return;
870 :
871 444 : scene->graph_attached = 1;
872 444 : if (gf_sg_get_root_node(scene->graph)==NULL) {
873 1 : gf_sc_invalidate(scene->compositor, NULL);
874 1 : return;
875 : }
876 :
877 : /*locate fragment IRI*/
878 443 : if (scene->root_od->scene_ns && scene->root_od->scene_ns->url) {
879 : char *url;
880 443 : if (scene->fragment_uri) {
881 0 : gf_free(scene->fragment_uri);
882 0 : scene->fragment_uri = NULL;
883 : }
884 443 : url = strchr(scene->root_od->scene_ns->url, '#');
885 443 : if (url) scene->fragment_uri = gf_strdup(url+1);
886 : }
887 :
888 : /*main display scene, setup compositor*/
889 443 : if (!scene->root_od->parentscene) {
890 398 : gf_sc_set_scene(scene->compositor, scene->graph);
891 : }
892 : else {
893 : u32 i, count;
894 45 : count = scene->root_od->mo ? gf_mo_event_target_count(scene->root_od->mo) : 0;
895 84 : for (i=0; i<count; i++) {
896 39 : gf_node_dirty_parents( gf_event_target_get_node(gf_mo_event_target_get(scene->root_od->mo, i)));
897 : }
898 45 : gf_sc_invalidate(scene->compositor, NULL);
899 :
900 45 : if (scene->root_od->parentscene->is_dynamic_scene) {
901 : u32 w, h;
902 0 : gf_sg_get_scene_size_info(scene->graph, &w, &h);
903 0 : gf_sc_set_size(scene->compositor, w, h);
904 : }
905 : /*trigger a scene attach event*/
906 45 : gf_scene_notify_event(scene, GF_EVENT_SCENE_ATTACHED, NULL, NULL, GF_OK, GF_FALSE);
907 : }
908 : }
909 :
910 926 : static GF_MediaObject *IS_CheckExistingObject(GF_Scene *scene, MFURL *urls, u32 type)
911 : {
912 : GF_MediaObject *obj;
913 926 : u32 i = 0;
914 926 : while ((obj = (GF_MediaObject *)gf_list_enum(scene->scene_objects, &i))) {
915 926 : if (type && (type != obj->type)) continue;
916 926 : if ((obj->OD_ID == GF_MEDIA_EXTERNAL_ID) && gf_mo_is_same_url(obj, urls, NULL, 0)) return obj;
917 926 : else if ((obj->OD_ID != GF_MEDIA_EXTERNAL_ID) && (obj->OD_ID == urls->vals[0].OD_ID)) return obj;
918 : }
919 : return NULL;
920 : }
921 :
922 : static GFINLINE Bool is_match_obj_type(u32 type, u32 hint_type)
923 : {
924 879 : if (!hint_type) return GF_TRUE;
925 823 : if (type==hint_type) return GF_TRUE;
926 : /*TEXT are used by animation stream*/
927 208 : if ((type==GF_MEDIA_OBJECT_TEXT) && (hint_type==GF_MEDIA_OBJECT_UPDATES)) return GF_TRUE;
928 : return GF_FALSE;
929 : }
930 :
931 : GF_EXPORT
932 7469 : GF_MediaObject *gf_scene_get_media_object_ex(GF_Scene *scene, MFURL *url, u32 obj_type_hint, Bool lock_timelines, GF_MediaObject *sync_ref, Bool force_new_if_not_attached, GF_Node *node)
933 : {
934 : GF_MediaObject *obj;
935 : GF_Scene *original_parent_scene = NULL;
936 7469 : Bool keep_fragment = GF_TRUE;
937 7469 : Bool first_pass = force_new_if_not_attached ? GF_FALSE : GF_TRUE;
938 : u32 i, OD_ID;
939 :
940 7469 : OD_ID = gf_mo_get_od_id(url);
941 7469 : if (!OD_ID) return NULL;
942 :
943 : /*we may have overridden the time lines in parent scene, thus all objects in this scene have the same clock*/
944 2757 : if (scene->root_od->parentscene && scene->root_od->parentscene->force_single_timeline)
945 : lock_timelines = GF_TRUE;
946 :
947 : /*the first pass is needed to detect objects already inserted and registered with the given nodes, regardless of
948 : the force_new_if_not_attached flag. This ty^pically occurs when a resource is first created then linked to an animation/inline*/
949 2748 : restart:
950 : obj = NULL;
951 4073 : i=0;
952 10292 : while ((obj = (GF_MediaObject *)gf_list_enum(scene->scene_objects, &i))) {
953 : Bool odm_matches = GF_FALSE;
954 :
955 4636 : if (
956 : /*regular OD scheme*/
957 3757 : (OD_ID != GF_MEDIA_EXTERNAL_ID && (obj->OD_ID==OD_ID))
958 1154 : ||
959 : /*dynamic OD scheme - !! obj->OD_ID may different from GF_MEDIA_EXTERNAL_ID when ODs are
960 : directly added to the terminal by the service*/
961 : ((OD_ID == GF_MEDIA_EXTERNAL_ID)
962 : /*if object type unknown (media control, media sensor), return first obj matching URL
963 : otherwise check types*/
964 879 : && is_match_obj_type(obj->type, obj_type_hint)
965 : /*locate sub-url in given one and handle fragments (viewpoint/segments/...)*/
966 671 : && gf_mo_is_same_url(obj, url, &keep_fragment, obj_type_hint)
967 : )
968 : ) {
969 : odm_matches = GF_TRUE;
970 : }
971 :
972 1058 : if (!odm_matches) continue;
973 :
974 3578 : if (obj->odm) {
975 : Bool can_reuse = GF_TRUE;
976 1429 : Bool timeline_locked = (obj->odm->flags & GF_ODM_INHERIT_TIMELINE) ? GF_TRUE : GF_FALSE;
977 :
978 : //addon object always share the timeline
979 1429 : if (obj->odm->addon || obj->odm->parentscene->root_od->addon)
980 : timeline_locked = lock_timelines = 1;
981 :
982 1429 : if (timeline_locked != lock_timelines)
983 0 : continue;
984 :
985 1429 : if (obj->odm->flags & GF_ODM_DESTROYED) can_reuse = GF_FALSE;
986 :
987 0 : if (!can_reuse) continue;
988 :
989 : }
990 :
991 3578 : if (!first_pass && !force_new_if_not_attached) {
992 1074 : if (node && (gf_mo_event_target_find_by_node(obj, node)<0))
993 265 : gf_mo_event_target_add_node(obj, node);
994 : return obj;
995 : }
996 : /*special case where the URL is requested twice for the same node: use the existing resource*/
997 2504 : else if (node && (gf_mo_event_target_find_by_node(obj, node)>=0)) {
998 : return obj;
999 : }
1000 : }
1001 1583 : if (first_pass) {
1002 : first_pass = GF_FALSE;
1003 : goto restart;
1004 : }
1005 :
1006 : /*we cannot create an OD manager at this point*/
1007 258 : if (obj_type_hint==GF_MEDIA_OBJECT_UNDEF) {
1008 : return NULL;
1009 : }
1010 :
1011 : /*create a new object identification*/
1012 258 : obj = gf_mo_new();
1013 258 : obj->OD_ID = OD_ID;
1014 258 : obj->type = obj_type_hint;
1015 :
1016 : /*register node with object*/
1017 258 : if (node) {
1018 248 : gf_mo_event_target_add_node(obj, node);
1019 :
1020 248 : original_parent_scene = (GF_Scene*) gf_sg_get_private(gf_node_get_graph(node));
1021 : }
1022 :
1023 : /*if animation stream object, remember originating node
1024 : !! FIXME - this should be cleaned up !!
1025 : */
1026 258 : if (obj->type == GF_MEDIA_OBJECT_UPDATES)
1027 4 : obj->node_ptr = node;
1028 :
1029 258 : gf_list_add(scene->scene_objects, obj);
1030 258 : if (OD_ID == GF_MEDIA_EXTERNAL_ID) {
1031 130 : gf_sg_vrml_copy_mfurl(&obj->URLs, url);
1032 130 : gf_scene_insert_object(scene, obj, lock_timelines, sync_ref, keep_fragment, original_parent_scene);
1033 : /*safety check!!!*/
1034 130 : if (gf_list_find(scene->scene_objects, obj)<0) {
1035 : return NULL;
1036 : }
1037 :
1038 130 : if (obj->odm==NULL) {
1039 0 : gf_list_del_item(scene->scene_objects, obj);
1040 0 : gf_mo_event_target_reset(obj);
1041 0 : gf_mo_del(obj);
1042 0 : return NULL;
1043 : }
1044 : } else {
1045 : u32 j;
1046 24 : for (j=0; j<gf_list_count(scene->resources); j++) {
1047 126 : GF_ObjectManager *odm = gf_list_get(scene->resources, j);
1048 126 : if (odm->ID == obj->OD_ID) {
1049 102 : obj->odm = odm;
1050 102 : break;
1051 : }
1052 : }
1053 : }
1054 : return obj;
1055 : }
1056 :
1057 912 : GF_MediaObject *gf_scene_get_media_object(GF_Scene *scene, MFURL *url, u32 obj_type_hint, Bool lock_timelines)
1058 : {
1059 912 : return gf_scene_get_media_object_ex(scene, url, obj_type_hint, lock_timelines, NULL, GF_FALSE, NULL);
1060 : }
1061 :
1062 :
1063 1399 : static void gf_scene_get_video_size(GF_MediaObject *mo, u32 *w, u32 *h)
1064 : {
1065 : u32 pixel_ar;
1066 1399 : if (!gf_mo_get_visual_info(mo, w, h, NULL, &pixel_ar, NULL, NULL)) return;
1067 1399 : if (pixel_ar) {
1068 : u32 n, d;
1069 0 : n = (pixel_ar>>16) & 0x0000FFFF;
1070 0 : d = (pixel_ar) & 0x0000FFFF;
1071 0 : *w = (*w * n) / d;
1072 : }
1073 : #ifndef GPAC_DISABLE_3D
1074 1399 : if (mo->odm) {
1075 1399 : if (mo->odm->parentscene->compositor->fpack==GF_3D_STEREO_TOP) *h /= 2;
1076 1399 : else if (mo->odm->parentscene->compositor->fpack==GF_3D_STEREO_SIDE) *w /= 2;
1077 : }
1078 : #endif
1079 : }
1080 :
1081 :
1082 : GF_EXPORT
1083 469 : void gf_scene_setup_object(GF_Scene *scene, GF_ObjectManager *odm)
1084 : {
1085 : GF_MediaObject *obj;
1086 : u32 i;
1087 :
1088 469 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Scene] Setup object manager %d (MO %p)\n", odm->ID, odm->mo));
1089 :
1090 : /*an object may already be assigned (when using ESD URLs, setup is performed twice)*/
1091 469 : if (odm->mo != NULL) goto existing;
1092 :
1093 255 : i=0;
1094 548 : while ((obj = (GF_MediaObject*)gf_list_enum(scene->scene_objects, &i))) {
1095 : /*make sure services are different*/
1096 56 : if (obj->odm && (odm->source_id != obj->odm->source_id)) continue;
1097 :
1098 56 : if (obj->OD_ID==GF_MEDIA_EXTERNAL_ID) {
1099 : //assert(obj->odm);
1100 9 : if (obj->odm == odm) {
1101 : /*assign FINAL OD, not parent*/
1102 0 : obj->odm = odm;
1103 0 : odm->mo = obj;
1104 0 : goto existing;
1105 : }
1106 : }
1107 47 : else if (obj->OD_ID == odm->ID) {
1108 : GF_ObjectManager *old_odm = NULL;
1109 : //OD update may trigger reassignment of ODM
1110 27 : if (obj->odm && (obj->odm != odm)) {
1111 : old_odm = obj->odm;
1112 : }
1113 13 : obj->odm = odm;
1114 : if (old_odm) {
1115 5 : if (old_odm->mo) old_odm->mo->odm = NULL;
1116 5 : old_odm->mo = NULL;
1117 5 : gf_odm_disconnect(old_odm, GF_TRUE);
1118 : }
1119 18 : odm->mo = obj;
1120 18 : goto existing;
1121 : }
1122 : }
1123 : /*newly created OD*/
1124 237 : odm->mo = gf_mo_new();
1125 237 : gf_list_add(scene->scene_objects, odm->mo);
1126 237 : odm->mo->odm = odm;
1127 237 : odm->mo->OD_ID = odm->ID;
1128 :
1129 683 : existing:
1130 : /*setup object type*/
1131 469 : if (!odm->type) odm->type = GF_MEDIA_OBJECT_SCENE;
1132 469 : else if (odm->type == GF_STREAM_VISUAL) odm->mo->type = GF_MEDIA_OBJECT_VIDEO;
1133 90 : else if (odm->type == GF_STREAM_AUDIO) odm->mo->type = GF_MEDIA_OBJECT_AUDIO;
1134 71 : else if (odm->type == GF_STREAM_TEXT) odm->mo->type = GF_MEDIA_OBJECT_TEXT;
1135 61 : else if (odm->type == GF_STREAM_SCENE) odm->mo->type = GF_MEDIA_OBJECT_UPDATES;
1136 :
1137 : /*update info*/
1138 469 : gf_mo_update_caps(odm->mo);
1139 : /*media object playback has already been requested by the scene, trigger media start*/
1140 469 : if (odm->mo->num_open && !odm->state) {
1141 22 : gf_odm_start(odm);
1142 22 : if (odm->mo->speed != FIX_ONE) gf_odm_set_speed(odm, odm->mo->speed, GF_TRUE);
1143 : }
1144 469 : if ((odm->mo->type==GF_MEDIA_OBJECT_VIDEO) && scene->is_dynamic_scene && !odm->parentscene->root_od->addon) {
1145 204 : gf_scene_force_size_to_video(scene, odm->mo);
1146 : }
1147 265 : else if (!odm->scene_ns->source_filter && (odm->flags & GF_ODM_PASSTHROUGH)) {
1148 : u32 w, h;
1149 0 : gf_scene_get_video_size(odm->mo, &w, &h);
1150 0 : gf_sc_set_size(scene->compositor, w, h);
1151 : }
1152 : /*invalidate scene for all nodes using the OD*/
1153 469 : gf_sc_invalidate(odm->parentscene->compositor, NULL);
1154 469 : }
1155 :
1156 : GF_EXPORT
1157 216 : void gf_scene_set_duration(GF_Scene *scene)
1158 : {
1159 : Double dur;
1160 : u32 i;
1161 : u64 max_dur;
1162 : GF_ObjectManager *odm;
1163 : #ifndef GPAC_DISABLE_VRML
1164 : MediaSensorStack *media_sens;
1165 : #endif
1166 : GF_Clock *ck;
1167 :
1168 : /*this is not normative but works in so many cases... set the duration to the max duration
1169 : of all streams sharing the clock*/
1170 216 : ck = gf_odm_get_media_clock(scene->root_od);
1171 216 : max_dur = scene->root_od->duration;
1172 216 : i=0;
1173 678 : while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) {
1174 246 : if (!ck || gf_odm_shares_clock(odm, ck)) {
1175 162 : if (odm->duration>max_dur) max_dur = odm->duration;
1176 : }
1177 : }
1178 275 : if (scene->duration == max_dur) return;
1179 :
1180 157 : scene->duration = max_dur;
1181 157 : if (scene->is_dynamic_scene && !scene->root_od->duration) scene->root_od->duration = max_dur;
1182 :
1183 157 : dur = (Double) (s64) scene->duration;
1184 157 : dur /= 1000;
1185 :
1186 : #ifndef GPAC_DISABLE_VRML
1187 157 : i=0;
1188 316 : while ((media_sens = (MediaSensorStack*)gf_list_enum(scene->root_od->ms_stack, &i))) {
1189 2 : if (media_sens->sensor->isActive) {
1190 0 : media_sens->sensor->mediaDuration = dur;
1191 0 : gf_node_event_out((GF_Node *) media_sens->sensor, 3/*"mediaDuration"*/);
1192 : }
1193 : }
1194 : #endif
1195 :
1196 157 : if (!scene->root_od->parentscene) {
1197 : GF_Event evt;
1198 150 : evt.type = GF_EVENT_DURATION;
1199 150 : evt.duration.duration = dur;
1200 150 : evt.duration.can_seek = (scene->root_od->flags & GF_ODM_NO_TIME_CTRL) ? GF_FALSE : GF_TRUE;
1201 150 : if (dur<1.0) evt.duration.can_seek = 0;
1202 150 : gf_sc_send_event(scene->compositor, &evt);
1203 : }
1204 : }
1205 :
1206 : GF_EXPORT
1207 0 : void gf_scene_set_timeshift_depth(GF_Scene *scene)
1208 : {
1209 : u32 i;
1210 : u32 max_timeshift;
1211 : GF_ObjectManager *odm;
1212 : GF_Clock *ck;
1213 :
1214 0 : ck = gf_odm_get_media_clock(scene->root_od);
1215 0 : max_timeshift = scene->root_od->timeshift_depth;
1216 0 : i=0;
1217 0 : while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) {
1218 0 : if (!ck || gf_odm_shares_clock(odm, ck)) {
1219 0 : if (odm->timeshift_depth > max_timeshift) max_timeshift = odm->timeshift_depth;
1220 : }
1221 : }
1222 0 : if (scene->timeshift_depth == max_timeshift) return;
1223 :
1224 0 : scene->timeshift_depth = max_timeshift;
1225 0 : if (scene->is_dynamic_scene && !scene->root_od->timeshift_depth) scene->root_od->timeshift_depth = max_timeshift;
1226 0 : if (scene->root_od->addon && (scene->root_od->addon->addon_type==GF_ADDON_TYPE_MAIN)) {
1227 0 : if (scene->root_od->parentscene->is_dynamic_scene && (scene->root_od->parentscene->timeshift_depth < max_timeshift)) {
1228 0 : scene->root_od->parentscene->timeshift_depth = max_timeshift;
1229 0 : scene->root_od->parentscene->root_od->timeshift_depth = max_timeshift;
1230 0 : gf_scene_notify_event(scene->root_od->parentscene, GF_EVENT_TIMESHIFT_DEPTH, NULL, NULL, GF_OK, GF_FALSE);
1231 : }
1232 : } else {
1233 0 : gf_scene_notify_event(scene, GF_EVENT_TIMESHIFT_DEPTH, NULL, NULL, GF_OK, GF_FALSE);
1234 : }
1235 : }
1236 :
1237 :
1238 0 : GF_MediaObject *gf_scene_find_object(GF_Scene *scene, u16 ODID, char *url)
1239 : {
1240 : u32 i;
1241 : GF_MediaObject *mo;
1242 0 : if (!url && !ODID) return NULL;
1243 0 : i=0;
1244 0 : while ((mo = (GF_MediaObject *)gf_list_enum(scene->scene_objects, &i))) {
1245 0 : if ((ODID==GF_MEDIA_EXTERNAL_ID) && url) {
1246 0 : if (mo->URLs.count && !stricmp(mo->URLs.vals[0].url, url)) return mo;
1247 0 : } else if (mo->OD_ID==ODID) {
1248 : return mo;
1249 : }
1250 : }
1251 : return NULL;
1252 : }
1253 :
1254 :
1255 : GF_EXPORT
1256 30 : void gf_scene_register_extra_graph(GF_Scene *scene, GF_SceneGraph *extra_scene, Bool do_remove)
1257 : {
1258 30 : if (do_remove) {
1259 19 : if (gf_list_find(scene->extra_scenes, extra_scene)<0) return;
1260 11 : gf_list_del_item(scene->extra_scenes, extra_scene);
1261 : /*for root scene*/
1262 11 : if (! scene->root_od->parentscene) {
1263 11 : gf_sc_register_extra_graph(scene->compositor, extra_scene, 1);
1264 : }
1265 : } else {
1266 11 : if (gf_list_find(scene->extra_scenes, extra_scene)>=0) return;
1267 11 : gf_list_add(scene->extra_scenes, extra_scene);
1268 : /*for root scene*/
1269 11 : if (!scene->root_od->parentscene) {
1270 11 : gf_sc_register_extra_graph(scene->compositor, extra_scene, 0);
1271 : }
1272 : }
1273 : }
1274 :
1275 269 : void gf_scene_force_size_to_video(GF_Scene *scene, GF_MediaObject *mo)
1276 : {
1277 : u32 w, h;
1278 269 : gf_scene_get_video_size(mo, &w, &h);
1279 :
1280 269 : if (w && h) gf_scene_force_size(scene, w, h);
1281 269 : }
1282 :
1283 : #ifndef GPAC_DISABLE_VRML
1284 :
1285 681 : static void IS_UpdateVideoPos(GF_Scene *scene)
1286 : {
1287 : MFURL url;
1288 : M_Transform2D *tr;
1289 : GF_MediaObject *mo;
1290 : u32 w, h, v_w, v_h;
1291 899 : if (!scene->visual_url.OD_ID && !scene->visual_url.url) return;
1292 :
1293 467 : if (scene->vr_type) return;
1294 :
1295 463 : url.count = 1;
1296 463 : url.vals = &scene->visual_url;
1297 463 : mo = IS_CheckExistingObject(scene, &url, GF_MEDIA_OBJECT_VIDEO);
1298 463 : if (!mo) return;
1299 463 : tr = (M_Transform2D *) gf_sg_find_node_by_name(scene->graph, "DYN_TRANS");
1300 463 : if (!tr) return;
1301 :
1302 463 : gf_sg_get_scene_size_info(scene->graph, &w, &h);
1303 463 : if (!w || !h) return;
1304 :
1305 463 : gf_scene_get_video_size(mo, &v_w, &v_h);
1306 463 : if (scene->force_size_set) {
1307 9 : if (v_w && v_h) {
1308 9 : tr->scale.x = gf_divfix(INT2FIX(w), INT2FIX(v_w));
1309 9 : tr->scale.y = gf_divfix(INT2FIX(h), INT2FIX(v_h));
1310 : }
1311 9 : tr->translation.x = tr->translation.y = 0;
1312 : } else {
1313 454 : tr->scale.x = tr->scale.y = FIX_ONE;
1314 454 : tr->translation.x = INT2FIX((s32) (w - v_w)) / 2;
1315 454 : tr->translation.y = INT2FIX((s32) (h - v_h)) / 2;
1316 : }
1317 463 : gf_node_dirty_set((GF_Node *)tr, 0, 0);
1318 :
1319 463 : gf_scene_set_addon_layout_info(scene, scene->addon_position, scene->addon_size_factor);
1320 :
1321 : }
1322 :
1323 3541 : static GF_Node *is_create_node(GF_SceneGraph *sg, u32 tag, const char *def_name)
1324 : {
1325 3541 : GF_Node *n = gf_node_new(sg, tag);
1326 3541 : if (n) {
1327 3541 : if (def_name) gf_node_set_id(n, gf_sg_get_next_available_node_id(sg), def_name);
1328 3541 : gf_node_init(n);
1329 : }
1330 3541 : return n;
1331 : }
1332 :
1333 2 : static Bool is_odm_url(SFURL *url, GF_ObjectManager *odm)
1334 : {
1335 2 : if (!url->OD_ID && !url->url) return 0;
1336 1 : if (odm->ID != GF_MEDIA_EXTERNAL_ID) return (url->OD_ID==odm->ID) ? 1 : 0;
1337 :
1338 0 : if (!url->url || !odm->scene_ns || !odm->scene_ns->url) return 0;
1339 0 : return !stricmp(url->url, odm->scene_ns->url);
1340 : }
1341 :
1342 832 : static void set_media_url(GF_Scene *scene, SFURL *media_url, GF_Node *node, MFURL *node_url, u32 type)
1343 : {
1344 : u32 w, h;
1345 : SFURL *sfu;
1346 : Bool url_changed = 0;
1347 :
1348 : /*scene url is not set, find the first one*/
1349 832 : if (!media_url->OD_ID ) {
1350 : u32 count, i;
1351 : GF_ObjectManager *odm = NULL;
1352 832 : count = gf_list_count(scene->resources);
1353 1455 : for (i=0; i<count; i++) {
1354 832 : odm = (GF_ObjectManager*)gf_list_get(scene->resources, i);
1355 832 : if (odm->scalable_addon || !odm->ID)
1356 0 : continue;
1357 :
1358 832 : if (type==GF_STREAM_TEXT) {
1359 207 : if (odm->type!=type) continue;
1360 : }
1361 625 : else if (type==GF_STREAM_SCENE) {
1362 207 : if (!odm->subscene || !odm->subscene->is_dynamic_scene) continue;
1363 :
1364 0 : if (odm->subscene->root_od->addon)
1365 0 : continue;
1366 : }
1367 : else {
1368 418 : if (odm->type!=type) continue;
1369 : //todo: select preferred media format ?
1370 : }
1371 :
1372 209 : if (scene->selected_service_id && (scene->selected_service_id != odm->ServiceID)) {
1373 : //objects inserted from broadcast may have been played but not yet registered with the scene, we need to force a stop
1374 0 : if ((odm->mo && !odm->mo->num_open) || !odm->mo) {
1375 0 : if (odm->state==GF_ODM_STATE_PLAY) {
1376 :
1377 0 : gf_odm_stop(odm, GF_FALSE);
1378 : }
1379 : }
1380 0 : continue;
1381 : }
1382 :
1383 :
1384 209 : media_url->OD_ID = odm->ID;
1385 209 : if (media_url->OD_ID==GF_MEDIA_EXTERNAL_ID) media_url->url = gf_strdup(odm->scene_ns->url);
1386 :
1387 : //happens when switching service in a TS multiplex
1388 209 : if (!scene->root_od->ck) {
1389 0 : scene->root_od->ck = odm->ck;
1390 : }
1391 :
1392 209 : if (odm->mo && (type==GF_STREAM_VISUAL)) {
1393 204 : gf_scene_get_video_size(odm->mo, &w, &h);
1394 204 : if (w && h) {
1395 204 : scene->force_size_set = 0;
1396 204 : gf_sg_set_scene_size_info(scene->graph, w, h, 1);
1397 204 : gf_scene_force_size(scene, w, h);
1398 : }
1399 : }
1400 : break;
1401 : }
1402 832 : if (!odm) {
1403 0 : if (media_url->OD_ID ) url_changed = 1;
1404 0 : media_url->OD_ID = 0;
1405 0 : if (media_url->url) {
1406 0 : gf_free(media_url->url);
1407 0 : media_url->url = NULL;
1408 : }
1409 : }
1410 : }
1411 :
1412 832 : if (media_url->OD_ID) {
1413 209 : if (!node_url->count) url_changed = 1;
1414 0 : else if (node_url->vals[0].OD_ID!=media_url->OD_ID) url_changed = 1;
1415 0 : else if (media_url->OD_ID==GF_MEDIA_EXTERNAL_ID) {
1416 0 : if (!node_url->vals[0].url || !media_url->url || strcmp(node_url->vals[0].url, media_url->url) ) url_changed = 1;
1417 : }
1418 : } else {
1419 623 : if (node_url->count) url_changed = 1;
1420 : }
1421 :
1422 623 : if (url_changed) {
1423 209 : gf_sg_vrml_mf_reset(node_url, GF_SG_VRML_MFURL);
1424 209 : gf_sg_vrml_mf_append(node_url, GF_SG_VRML_MFURL, (void **) &sfu);
1425 209 : sfu->OD_ID = media_url->OD_ID;
1426 209 : if (media_url->url) sfu->url = gf_strdup(media_url->url);
1427 :
1428 209 : gf_node_changed(node, NULL);
1429 : }
1430 :
1431 832 : }
1432 :
1433 1 : static void scene_video_mouse_move(void *param, GF_FieldInfo *field)
1434 : {
1435 : u32 i, count;
1436 : Bool supported = GF_FALSE;
1437 : GF_Scene *scene = (GF_Scene *) param;
1438 1 : SFVec2f tx_coord = * ((SFVec2f *) field->far_ptr);
1439 1 : GF_Node *n = gf_sg_find_node_by_name(scene->graph, "DYN_TOUCH");
1440 :
1441 1 : if (!scene->visual_url.OD_ID) return;
1442 :
1443 1 : count = gf_list_count(scene->resources);
1444 2 : for (i=0; i<count; i++) {
1445 : const GF_PropertyValue *prop;
1446 1 : GF_ObjectManager *odm = gf_list_get(scene->resources, i);
1447 1 : if (!odm->mo) continue;
1448 :
1449 1 : prop = gf_filter_pid_get_property_str(odm->pid, "MouseEvents");
1450 1 : if (prop && prop->value.boolean) {
1451 : GF_FilterEvent evt;
1452 : supported = GF_TRUE;
1453 0 : GF_FEVT_INIT(evt, GF_FEVT_USER, odm->pid);
1454 :
1455 0 : evt.user_event.event.type = ((M_TouchSensor *)n)->isActive ? GF_EVENT_MOUSEDOWN : GF_EVENT_MOUSEUP;
1456 0 : evt.user_event.event.mouse.x = FIX2INT( tx_coord.x * odm->mo->width);
1457 0 : evt.user_event.event.mouse.y = FIX2INT( tx_coord.y * odm->mo->height);
1458 :
1459 0 : gf_filter_pid_send_event(odm->pid, &evt);
1460 :
1461 : }
1462 : }
1463 1 : if (!supported) {
1464 1 : if (n) ((M_TouchSensor *)n)->enabled = GF_FALSE;
1465 : }
1466 : }
1467 :
1468 2 : static GF_Node *load_vr_proto_node(GF_SceneGraph *sg, const char *name, const char *def_name)
1469 : {
1470 : GF_Proto *proto;
1471 : GF_Node *node;
1472 2 : if (!name) name = "urn:inet:gpac:builtin:VRGeometry";
1473 :
1474 2 : proto = gf_sg_find_proto(sg, 0, (char *) name);
1475 2 : if (!proto) {
1476 : MFURL *url;
1477 2 : proto = gf_sg_proto_new(sg, 0, (char *) name, GF_FALSE);
1478 2 : url = gf_sg_proto_get_extern_url(proto);
1479 2 : if (url)
1480 2 : url->vals = gf_malloc(sizeof(SFURL));
1481 2 : if (!url || !url->vals) {
1482 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Terminal] Failed to allocate VR proto\n"));
1483 : return NULL;
1484 : }
1485 2 : url->count=1;
1486 2 : url->vals[0].url = gf_strdup(name);
1487 : }
1488 2 : node = gf_sg_proto_create_instance(sg, proto);
1489 2 : if (node) {
1490 2 : if (def_name) gf_node_set_id(node, gf_sg_get_next_available_node_id(sg), def_name);
1491 2 : gf_node_init(node);
1492 : }
1493 : return node;
1494 : }
1495 :
1496 :
1497 209 : static void create_movie(GF_Scene *scene, GF_Node *root, const char *tr_name, const char *texture_name, const char *name_geo)
1498 : {
1499 : M_MovieTexture *mt;
1500 : GF_Node *n1, *n2;
1501 :
1502 : /*create a shape and bitmap node*/
1503 209 : n2 = is_create_node(scene->graph, TAG_MPEG4_Transform2D, tr_name);
1504 209 : gf_node_list_add_child( &((GF_ParentNode *)root)->children, n2);
1505 209 : gf_node_register(n2, root);
1506 : n1 = n2;
1507 209 : n2 = is_create_node(scene->graph, TAG_MPEG4_Shape, NULL);
1508 209 : gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2);
1509 209 : gf_node_register(n2, n1);
1510 : n1 = n2;
1511 209 : n2 = is_create_node(scene->graph, TAG_MPEG4_Appearance, NULL);
1512 209 : ((M_Shape *)n1)->appearance = n2;
1513 209 : gf_node_register(n2, n1);
1514 :
1515 : /*note we create a movie texture even for images...*/
1516 209 : mt = (M_MovieTexture *) is_create_node(scene->graph, TAG_MPEG4_MovieTexture, texture_name);
1517 209 : mt->startTime = gf_scene_get_time(scene);
1518 209 : ((M_Appearance *)n2)->texture = (GF_Node *)mt;
1519 209 : gf_node_register((GF_Node *)mt, n2);
1520 :
1521 209 : if (scene->srd_type) {
1522 : GF_Node *app = n2;
1523 :
1524 0 : if (scene->vr_type) {
1525 0 : n2 = load_vr_proto_node(scene->graph, NULL, name_geo);
1526 : } else {
1527 0 : n2 = is_create_node(scene->graph, TAG_MPEG4_Rectangle, name_geo);
1528 : }
1529 :
1530 0 : ((M_Shape *)n1)->geometry = n2;
1531 0 : gf_node_register(n2, n1);
1532 : //force appearance material2D.filled = TRUE
1533 0 : n2 = is_create_node(scene->graph, TAG_MPEG4_Material2D, NULL);
1534 0 : ((M_Material2D *)n2)->filled = GF_TRUE;
1535 0 : ((M_Appearance *)app)->material = n2;
1536 0 : gf_node_register(n2, app);
1537 209 : } else if (scene->vr_type) {
1538 2 : n2 = is_create_node(scene->graph, TAG_MPEG4_Sphere, name_geo);
1539 2 : ((M_Shape *)n1)->geometry = n2;
1540 2 : gf_node_register(n2, n1);
1541 : } else {
1542 207 : n2 = is_create_node(scene->graph, TAG_MPEG4_Bitmap, name_geo);
1543 207 : ((M_Shape *)n1)->geometry = n2;
1544 207 : gf_node_register(n2, n1);
1545 : }
1546 209 : }
1547 : /*regenerates the scene graph for dynamic scene.
1548 : This will also try to reload any previously presented streams. Note that in the usual case the scene is generated
1549 : just once when receiving the first OD AU (resources are NOT destroyed when seeking), but since the network may need
1550 : to update the OD resources, we still take care of it*/
1551 209 : void gf_scene_regenerate(GF_Scene *scene)
1552 : {
1553 : GF_Node *n1, *n2;
1554 : GF_Event evt;
1555 : M_AudioClip *ac;
1556 : M_MovieTexture *mt;
1557 : M_AnimationStream *as;
1558 : M_Inline *dims;
1559 209 : if (scene->is_dynamic_scene != 1) return;
1560 :
1561 209 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Inline] Regenerating scene graph for service %s\n", scene->root_od->scene_ns->url));
1562 :
1563 209 : ac = (M_AudioClip *) gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1");
1564 :
1565 : /*this is the first time, generate a scene graph*/
1566 209 : if (!ac) {
1567 : GF_Node *root;
1568 :
1569 : /*create an OrderedGroup*/
1570 209 : n1 = is_create_node(scene->graph, scene->vr_type ? TAG_MPEG4_Group : TAG_MPEG4_OrderedGroup, NULL);
1571 209 : gf_sg_set_root_node(scene->graph, n1);
1572 209 : gf_node_register(n1, NULL);
1573 : root = n1;
1574 :
1575 209 : if (! scene->root_od->parentscene && !scene->compositor->dyn_filter_mode) {
1576 205 : n2 = is_create_node(scene->graph, TAG_MPEG4_Background2D, "DYN_BACK");
1577 205 : gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2);
1578 205 : gf_node_register(n2, n1);
1579 : }
1580 :
1581 : //create VP info regardless of VR type
1582 209 : if (scene->vr_type) {
1583 2 : n2 = is_create_node(scene->graph, TAG_MPEG4_Viewpoint, "DYN_VP");
1584 2 : ((M_Viewpoint *)n2)->position.z = 0;
1585 :
1586 : #ifndef GPAC_DISABLE_3D
1587 2 : ((M_Viewpoint *)n2)->fieldOfView = scene->compositor->fov;
1588 : #else
1589 : ((M_Viewpoint *)n2)->fieldOfView = GF_PI/2;
1590 : #endif
1591 :
1592 2 : gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2);
1593 2 : gf_node_register(n2, n1);
1594 :
1595 2 : n2 = is_create_node(scene->graph, TAG_MPEG4_NavigationInfo, NULL);
1596 2 : gf_free( ((M_NavigationInfo *)n2)->type.vals[0] );
1597 2 : ((M_NavigationInfo *)n2)->type.vals[0] = gf_strdup("VR");
1598 2 : gf_free( ((M_NavigationInfo *)n2)->type.vals[1] );
1599 2 : ((M_NavigationInfo *)n2)->type.vals[1] = gf_strdup("NONE");
1600 2 : ((M_NavigationInfo *)n2)->type.count = 2;
1601 2 : ((M_NavigationInfo *)n2)->avatarSize.count = 0;
1602 :
1603 2 : gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2);
1604 2 : gf_node_register(n2, n1);
1605 : }
1606 :
1607 : /*create an sound2D and an audioClip node*/
1608 209 : n2 = is_create_node(scene->graph, TAG_MPEG4_Sound2D, NULL);
1609 209 : gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2);
1610 209 : gf_node_register(n2, n1);
1611 :
1612 209 : ac = (M_AudioClip *) is_create_node(scene->graph, TAG_MPEG4_AudioClip, "DYN_AUDIO1");
1613 209 : ac->startTime = gf_scene_get_time(scene);
1614 209 : ((M_Sound2D *)n2)->source = (GF_Node *)ac;
1615 209 : gf_node_register((GF_Node *)ac, n2);
1616 :
1617 :
1618 : /*transform for any translation due to scene resize (3GPP)*/
1619 209 : n2 = is_create_node(scene->graph, TAG_MPEG4_Transform2D, "DYN_TRANS");
1620 209 : gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2);
1621 209 : gf_node_register(n2, n1);
1622 : n1 = n2;
1623 :
1624 : /*create a touch sensor for the video*/
1625 209 : n2 = is_create_node(scene->graph, TAG_MPEG4_TouchSensor, "DYN_TOUCH");
1626 209 : gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2);
1627 209 : gf_node_register(n2, n1);
1628 209 : gf_sg_route_new_to_callback(scene->graph, n2, 3/*"hitTexCoord_changed"*/, scene, scene_video_mouse_move);
1629 :
1630 209 : create_movie(scene, n1, "TR1", "DYN_VIDEO1", "DYN_GEOM1");
1631 :
1632 209 : if (! scene->vr_type) {
1633 : M_Transform2D *addon_tr;
1634 : M_Layer2D *addon_layer;
1635 : M_Inline *addon_scene;
1636 :
1637 : /*text streams controlled through AnimationStream*/
1638 207 : n1 = gf_sg_get_root_node(scene->graph);
1639 207 : as = (M_AnimationStream *) is_create_node(scene->graph, TAG_MPEG4_AnimationStream, "DYN_TEXT");
1640 207 : gf_node_list_add_child( &((GF_ParentNode *)n1)->children, (GF_Node*)as);
1641 207 : gf_node_register((GF_Node *)as, n1);
1642 :
1643 :
1644 : /*3GPP DIMS streams controlled */
1645 207 : n1 = gf_sg_get_root_node(scene->graph);
1646 207 : dims = (M_Inline *) is_create_node(scene->graph, TAG_MPEG4_Inline, "DIMS_SCENE");
1647 207 : gf_node_list_add_child( &((GF_ParentNode *)n1)->children, (GF_Node*)dims);
1648 207 : gf_node_register((GF_Node *)dims, n1);
1649 :
1650 : /*PVR version of live content*/
1651 207 : n1 = gf_sg_get_root_node(scene->graph);
1652 207 : addon_scene = (M_Inline *) is_create_node(scene->graph, TAG_MPEG4_Inline, "PVR_SCENE");
1653 207 : gf_node_list_add_child( &((GF_ParentNode *)n1)->children, (GF_Node*)addon_scene);
1654 207 : gf_node_register((GF_Node *)addon_scene, (GF_Node *)n1);
1655 :
1656 : /*Media addon scene*/
1657 207 : n1 = gf_sg_get_root_node(scene->graph);
1658 207 : addon_tr = (M_Transform2D *) is_create_node(scene->graph, TAG_MPEG4_Transform2D, "ADDON_TRANS");
1659 207 : gf_node_list_add_child( &((GF_ParentNode *)n1)->children, (GF_Node*)addon_tr);
1660 207 : gf_node_register((GF_Node *)addon_tr, n1);
1661 :
1662 207 : addon_layer = (M_Layer2D *) is_create_node(scene->graph, TAG_MPEG4_Layer2D, "ADDON_LAYER");
1663 207 : gf_node_list_add_child( &((GF_ParentNode *)addon_tr)->children, (GF_Node*)addon_layer);
1664 207 : gf_node_register((GF_Node *)addon_layer, (GF_Node *)addon_tr);
1665 :
1666 207 : addon_scene = (M_Inline *) is_create_node(scene->graph, TAG_MPEG4_Inline, "ADDON_SCENE");
1667 207 : gf_node_list_add_child( &((GF_ParentNode *)addon_layer)->children, (GF_Node*)addon_scene);
1668 207 : gf_node_register((GF_Node *)addon_scene, (GF_Node *)addon_layer);
1669 : }
1670 : //VR mode, add VR headup
1671 : else {
1672 2 : GF_Node *vrhud = load_vr_proto_node(scene->graph, "urn:inet:gpac:builtin:VRHUD", NULL);
1673 2 : gf_node_list_add_child( &((GF_ParentNode *)root)->children, (GF_Node*)vrhud);
1674 2 : gf_node_register(vrhud, root);
1675 : }
1676 :
1677 : //send activation for sensors
1678 : memset(&evt, 0, sizeof(GF_Event));
1679 209 : evt.type = GF_EVENT_SENSOR_REQUEST;
1680 209 : evt.activate_sensor.activate = scene->vr_type;
1681 209 : evt.activate_sensor.sensor_type = GF_EVENT_SENSOR_ORIENTATION;
1682 209 : if (gf_sc_send_event(scene->compositor, &evt)==GF_TRUE) {
1683 0 : scene->compositor->orientation_sensors_active = scene->vr_type;
1684 : } else {
1685 209 : scene->compositor->orientation_sensors_active = GF_FALSE;
1686 : }
1687 : }
1688 :
1689 209 : if (scene->ambisonic_type) {
1690 : char szName[20];
1691 : SFURL url;
1692 : u32 i, count;
1693 0 : GF_Node *an, *root = gf_sg_get_root_node(scene->graph);
1694 0 : url.url = NULL;
1695 0 : url.OD_ID = 0;
1696 :
1697 0 : count = gf_list_count(scene->resources);
1698 0 : for (i=0; i<count; i++) {
1699 0 : GF_ObjectManager *odm = gf_list_get(scene->resources, i);
1700 0 : if (!odm->ambi_ch_id) continue;
1701 :
1702 : sprintf(szName, "DYN_AUDIO%d", odm->ambi_ch_id);
1703 0 : an = gf_sg_find_node_by_name(scene->graph, szName);
1704 0 : if (!an) {
1705 : /*create an sound2D and an audioClip node*/
1706 0 : an = is_create_node(scene->graph, TAG_MPEG4_Sound2D, NULL);
1707 0 : gf_node_list_add_child( &((GF_ParentNode *)root)->children, an);
1708 0 : gf_node_register(an, root);
1709 :
1710 0 : ac = (M_AudioClip *) is_create_node(scene->graph, TAG_MPEG4_AudioClip, szName);
1711 0 : ac->startTime = gf_scene_get_time(scene);
1712 0 : ((M_Sound2D *)an)->source = (GF_Node *)ac;
1713 0 : gf_node_register((GF_Node *)ac, an);
1714 : }
1715 0 : ac = (M_AudioClip *) gf_sg_find_node_by_name(scene->graph, szName);
1716 :
1717 0 : url.OD_ID = odm->ID;
1718 0 : set_media_url(scene, &url, (GF_Node*)ac, &ac->url, GF_STREAM_AUDIO);
1719 : }
1720 : } else {
1721 209 : ac = (M_AudioClip *) gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1");
1722 209 : set_media_url(scene, &scene->audio_url, (GF_Node*)ac, &ac->url, GF_STREAM_AUDIO);
1723 : }
1724 :
1725 :
1726 209 : if (scene->srd_type) {
1727 : char szName[20], szTex[20], szGeom[20];
1728 : u32 i, nb_srd = 0, srd_missing = 0;
1729 : GF_ObjectManager *a_odm;
1730 : SFURL url;
1731 : u32 sw, sh;
1732 : s32 min_x, max_x, min_y, max_y;
1733 0 : i=0;
1734 :
1735 : //we use 0 (and not INT_MAX) to always display the same thing regardless of holes in the srd description
1736 : min_x = min_y = 0;
1737 : max_x = max_y = 0;
1738 :
1739 0 : while ((a_odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) {
1740 0 : if (!a_odm->mo || !a_odm->mo->srd_w) {
1741 0 : srd_missing++;
1742 0 : continue;
1743 : }
1744 0 : if ((s32) a_odm->mo->srd_x < min_x) min_x = (s32) a_odm->mo->srd_x;
1745 0 : if ((s32) a_odm->mo->srd_y < min_y) min_y = (s32) a_odm->mo->srd_y;
1746 :
1747 0 : if (!max_x)
1748 0 : max_x = a_odm->mo->srd_full_w;
1749 0 : if ((s32) a_odm->mo->srd_x + (s32) a_odm->mo->srd_w > min_x + max_x)
1750 0 : max_x = (s32) a_odm->mo->srd_x + (s32) a_odm->mo->srd_w - min_x;
1751 :
1752 0 : if (!max_y)
1753 0 : max_y = a_odm->mo->srd_full_h;
1754 :
1755 0 : if ((s32) a_odm->mo->srd_y + (s32) a_odm->mo->srd_h > min_y + max_y)
1756 0 : max_y = (s32) a_odm->mo->srd_y + (s32) a_odm->mo->srd_h - min_y;
1757 :
1758 0 : nb_srd++;
1759 : }
1760 :
1761 0 : n1 = gf_sg_find_node_by_name(scene->graph, "DYN_TRANS");
1762 0 : for (i=1; i<nb_srd+srd_missing; i++) {
1763 0 : sprintf(szName, "TR%d", i+1);
1764 0 : sprintf(szTex, "DYN_VIDEO%d", i+1);
1765 0 : sprintf(szGeom, "DYN_GEOM%d", i+1);
1766 0 : n2 = gf_sg_find_node_by_name(scene->graph, szGeom);
1767 0 : if (!n2) {
1768 0 : create_movie(scene, n1, szName, szTex, szGeom);
1769 : }
1770 : }
1771 : assert(max_x>min_x);
1772 : assert(max_y>min_y);
1773 :
1774 0 : scene->srd_min_x = min_x;
1775 0 : scene->srd_min_y = min_y;
1776 0 : scene->srd_max_x = max_x;
1777 0 : scene->srd_max_y = max_y;
1778 :
1779 0 : url.url = NULL;
1780 0 : gf_sg_get_scene_size_info(scene->graph, &sw, &sh);
1781 0 : i=0;
1782 0 : while ((a_odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) {
1783 0 : if (a_odm->mo && a_odm->mo->srd_w) {
1784 : Fixed tw, th, tx, ty;
1785 :
1786 0 : sprintf(szName, "TR%d", i);
1787 0 : sprintf(szTex, "DYN_VIDEO%d", i);
1788 0 : sprintf(szGeom, "DYN_GEOM%d", i);
1789 0 : url.OD_ID = a_odm->ID;
1790 :
1791 0 : mt = (M_MovieTexture *) gf_sg_find_node_by_name(scene->graph, szTex);
1792 0 : if (!mt) continue;
1793 :
1794 0 : set_media_url(scene, &url, (GF_Node*)mt, &mt->url, GF_STREAM_VISUAL);
1795 :
1796 0 : if (!scene->root_od->ck && a_odm->ck) {
1797 0 : scene->root_od->ck = a_odm->ck;
1798 : }
1799 :
1800 0 : if (scene->vr_type) {
1801 0 : n2 = gf_sg_find_node_by_name(scene->graph, szGeom);
1802 0 : gf_node_changed(n2, NULL);
1803 : } else {
1804 : M_Transform2D *addon_tr;
1805 :
1806 0 : tw = INT2FIX( sw * a_odm->mo->srd_w) / (max_x - min_x);
1807 0 : th = INT2FIX(sh * a_odm->mo->srd_h) / (max_y - min_y);
1808 :
1809 0 : n2 = gf_sg_find_node_by_name(scene->graph, szGeom);
1810 0 : ((M_Rectangle *)n2)->size.x = tw;
1811 0 : ((M_Rectangle *)n2)->size.y = th;
1812 0 : gf_node_changed(n2, NULL);
1813 :
1814 0 : tx = INT2FIX(a_odm->mo->srd_x * sw) / (max_x - min_x);
1815 0 : tx = tx - INT2FIX(sw) / 2 + INT2FIX(tw) / 2;
1816 :
1817 0 : ty = INT2FIX(a_odm->mo->srd_y * sh) / (max_y - min_y);
1818 0 : ty = INT2FIX(sh) / 2 - ty - INT2FIX(th) / 2;
1819 :
1820 0 : addon_tr = (M_Transform2D *) gf_sg_find_node_by_name(scene->graph, szName);
1821 0 : addon_tr->translation.x = tx;
1822 0 : addon_tr->translation.y = ty;
1823 0 : gf_node_changed((GF_Node *)addon_tr, NULL);
1824 : }
1825 : }
1826 : }
1827 : } else {
1828 209 : mt = (M_MovieTexture *) gf_sg_find_node_by_name(scene->graph, "DYN_VIDEO1");
1829 209 : set_media_url(scene, &scene->visual_url, (GF_Node*)mt, &mt->url, GF_STREAM_VISUAL);
1830 : }
1831 :
1832 :
1833 209 : if (! scene->vr_type) {
1834 207 : as = (M_AnimationStream *) gf_sg_find_node_by_name(scene->graph, "DYN_TEXT");
1835 207 : set_media_url(scene, &scene->text_url, (GF_Node*)as, &as->url, GF_STREAM_TEXT);
1836 :
1837 207 : dims = (M_Inline *) gf_sg_find_node_by_name(scene->graph, "DIMS_SCENE");
1838 207 : set_media_url(scene, &scene->dims_url, (GF_Node*)dims, &dims->url, GF_STREAM_SCENE);
1839 : }
1840 :
1841 : /*disconnect to force resize*/
1842 209 : if (!scene->root_od->parentscene) {
1843 205 : gf_sc_set_scene(scene->compositor, scene->graph);
1844 205 : scene->graph_attached = 1;
1845 :
1846 205 : evt.type = GF_EVENT_STREAMLIST;
1847 205 : gf_sc_send_event(scene->compositor, &evt);
1848 :
1849 205 : IS_UpdateVideoPos(scene);
1850 : } else {
1851 4 : gf_scene_notify_event(scene, scene->graph_attached ? GF_EVENT_STREAMLIST : GF_EVENT_SCENE_ATTACHED, NULL, NULL, GF_OK, GF_FALSE);
1852 4 : scene->graph_attached = 1;
1853 4 : gf_sc_invalidate(scene->compositor, NULL);
1854 : }
1855 : }
1856 :
1857 0 : void gf_scene_toggle_addons(GF_Scene *scene, Bool show_addons)
1858 : {
1859 0 : M_Inline *dscene = (M_Inline *) gf_sg_find_node_by_name(scene->graph, "ADDON_SCENE");
1860 :
1861 0 : if (show_addons) {
1862 : #ifdef FILTER_FIXME
1863 : GF_AssociatedContentLocation addon_info;
1864 : memset(&addon_info, 0, sizeof(GF_AssociatedContentLocation));
1865 : addon_info.timeline_id = -100;
1866 : gf_scene_register_associated_media(scene, &addon_info);
1867 : #endif
1868 : } else {
1869 0 : gf_sg_vrml_mf_reset(&dscene->url, GF_SG_VRML_MFURL);
1870 : }
1871 0 : gf_node_changed((GF_Node *)dscene, NULL);
1872 0 : }
1873 :
1874 : #else
1875 : /*!!fixme - we would need an SVG scene in case no VRML support is present !!!*/
1876 : GF_EXPORT
1877 : void gf_scene_regenerate(GF_Scene *scene) {}
1878 : GF_EXPORT
1879 : void gf_scene_restart_dynamic(GF_Scene *scene, s64 from_time, Bool restart_only, Bool disable_addon_check) {}
1880 : GF_EXPORT
1881 : void gf_scene_select_object(GF_Scene *scene, GF_ObjectManager *odm) {}
1882 : GF_EXPORT
1883 : void gf_scene_toggle_addons(GF_Scene *scene, Bool show_addons) { }
1884 : GF_EXPORT
1885 : void gf_scene_resume_live(GF_Scene *subscene) { }
1886 : GF_EXPORT
1887 : void gf_scene_set_addon_layout_info(GF_Scene *scene, u32 position, u32 size_factor) {}
1888 : GF_EXPORT
1889 : void gf_scene_select_main_addon(GF_Scene *scene, GF_ObjectManager *odm, Bool set_on, u32 current_clock_time) { }
1890 :
1891 : #endif /*GPAC_DISABLE_VRML*/
1892 :
1893 : #ifndef GPAC_DISABLE_VRML
1894 :
1895 2 : static Bool check_odm_deactivate(SFURL *url, GF_ObjectManager *odm, GF_Node *n)
1896 : {
1897 : GF_FieldInfo info;
1898 : MFURL *mfurl;
1899 2 : if (!is_odm_url(url, odm) || !n) return 0;
1900 :
1901 1 : gf_node_get_field_by_name(n, "url", &info);
1902 1 : mfurl = (MFURL *)info.far_ptr;
1903 1 : if ((url->OD_ID!=GF_MEDIA_EXTERNAL_ID) && mfurl->count && (mfurl->vals[0].OD_ID==url->OD_ID))
1904 : return 1;
1905 :
1906 0 : if (url->url) gf_free(url->url);
1907 0 : url->url = NULL;
1908 0 : url->OD_ID = 0;
1909 :
1910 0 : gf_sg_vrml_mf_reset(info.far_ptr, GF_SG_VRML_MFURL);
1911 0 : gf_node_get_field_by_name(n, "stopTime", &info);
1912 0 : *((SFTime *)info.far_ptr) = gf_node_get_scene_time(n);
1913 0 : gf_node_changed(n, NULL);
1914 0 : return 1;
1915 : }
1916 :
1917 0 : static void odm_deactivate(GF_Node *n)
1918 : {
1919 : GF_FieldInfo info;
1920 :
1921 0 : gf_node_get_field_by_name(n, "url", &info);
1922 0 : gf_sg_vrml_mf_reset(info.far_ptr, GF_SG_VRML_MFURL);
1923 0 : gf_node_get_field_by_name(n, "stopTime", &info);
1924 0 : *((SFTime *)info.far_ptr) = gf_node_get_scene_time(n);
1925 0 : gf_node_changed(n, NULL);
1926 0 : }
1927 :
1928 0 : static void odm_activate(SFURL *url, GF_Node *n)
1929 : {
1930 : SFURL *sfu;
1931 : GF_FieldInfo info;
1932 :
1933 0 : gf_node_get_field_by_name(n, "url", &info);
1934 0 : gf_sg_vrml_mf_reset(info.far_ptr, GF_SG_VRML_MFURL);
1935 0 : if (url->OD_ID || url->url) {
1936 0 : gf_sg_vrml_mf_append(info.far_ptr, GF_SG_VRML_MFURL, (void **) &sfu);
1937 0 : sfu->OD_ID = url->OD_ID;
1938 0 : if (url->url) sfu->url = gf_strdup(url->url);
1939 :
1940 0 : gf_node_get_field_by_name(n, "startTime", &info);
1941 0 : *((SFTime *)info.far_ptr) = 0.0;
1942 0 : gf_node_get_field_by_name(n, "stopTime", &info);
1943 0 : *((SFTime *)info.far_ptr) = 0.0;
1944 : }
1945 :
1946 0 : gf_node_changed(n, NULL);
1947 0 : }
1948 :
1949 : GF_EXPORT
1950 3 : void gf_scene_set_service_id(GF_Scene *scene, u32 service_id)
1951 : {
1952 3 : if (!scene->is_dynamic_scene) return;
1953 :
1954 1 : gf_sc_lock(scene->compositor, 1);
1955 1 : if (scene->selected_service_id != service_id) {
1956 : u32 i;
1957 : GF_ObjectManager *odm, *remote_odm = NULL;
1958 : //delete all objects with given service ID
1959 0 : i=0;
1960 0 : while ((odm = gf_list_enum(scene->resources, &i))) {
1961 0 : if (odm->ServiceID != scene->selected_service_id) continue;
1962 0 : if (odm->redirect_url) {
1963 : remote_odm = odm;
1964 : assert(remote_odm->scene_ns->nb_odm_users);
1965 0 : remote_odm->scene_ns->nb_odm_users--;
1966 0 : remote_odm->scene_ns = scene->root_od->scene_ns;
1967 0 : remote_odm->scene_ns->nb_odm_users++;
1968 : }
1969 : //delete all objects from this service
1970 0 : else if (remote_odm) {
1971 0 : if (odm->scene_ns==remote_odm->scene_ns) odm->scene_ns->owner = odm;
1972 0 : gf_odm_disconnect(odm, 2);
1973 : }
1974 : }
1975 0 : GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[Scene] Switching %s from service %d to service %d (media time %g)\n", scene->root_od->scene_ns->url, scene->selected_service_id, service_id, (Double)scene->root_od->media_start_time/1000.0));
1976 :
1977 0 : scene->selected_service_id = service_id;
1978 0 : scene->audio_url.OD_ID = 0;
1979 0 : scene->visual_url.OD_ID = 0;
1980 0 : scene->text_url.OD_ID = 0;
1981 0 : scene->dims_url.OD_ID = 0;
1982 0 : scene->force_size_set = 0;
1983 : //reset clock since we change service IDs, but request a PLAY from the current time
1984 0 : if (scene->root_od->ck) {
1985 0 : scene->root_od->media_start_time = gf_clock_media_time(scene->root_od->ck);
1986 0 : scene->root_od->ck = NULL;
1987 : }
1988 :
1989 0 : if (remote_odm) {
1990 0 : i=0;
1991 0 : while ((odm = gf_list_enum(scene->resources, &i))) {
1992 0 : if (odm->ServiceID!=scene->selected_service_id) continue;
1993 0 : if (odm->redirect_url) {
1994 : //gf_odm_setup_object will increment the number of odms in net service (it's supposed to
1995 : //be called only upon startup, but we reuse the function). Since we are already registered
1996 : //with the service, decrement before calling
1997 0 : odm->scene_ns->nb_odm_users--;
1998 0 : gf_odm_setup_object(odm, odm->scene_ns, odm->pid);
1999 : }
2000 : break;
2001 : }
2002 : }
2003 0 : gf_scene_regenerate(scene);
2004 : }
2005 1 : gf_sc_lock(scene->compositor, 0);
2006 : }
2007 :
2008 : GF_EXPORT
2009 1 : void gf_scene_select_object(GF_Scene *scene, GF_ObjectManager *odm)
2010 : {
2011 : char *url;
2012 1 : if (!scene->is_dynamic_scene || !scene->graph_attached || !odm) return;
2013 :
2014 1 : if (!odm->ID) {
2015 0 : if (!odm->addon) return;
2016 : }
2017 :
2018 1 : if (odm->ServiceID && scene->selected_service_id && (scene->selected_service_id != odm->ServiceID)) {
2019 0 : gf_scene_set_service_id(scene, odm->ServiceID);
2020 0 : return;
2021 : }
2022 :
2023 :
2024 1 : if (odm->state) {
2025 1 : if (check_odm_deactivate(&scene->audio_url, odm, gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1")) ) return;
2026 1 : if (check_odm_deactivate(&scene->visual_url, odm, gf_sg_find_node_by_name(scene->graph, "DYN_VIDEO1") )) return;
2027 0 : if (check_odm_deactivate(&scene->text_url, odm, gf_sg_find_node_by_name(scene->graph, "DYN_TEXT") )) return;
2028 : }
2029 :
2030 :
2031 0 : if (!odm->ID && odm->subscene) {
2032 0 : M_Inline *dscene = (M_Inline *) gf_sg_find_node_by_name(scene->graph, "ADDON_SCENE");
2033 :
2034 0 : if (!dscene)
2035 : return;
2036 :
2037 0 : if (odm->addon && odm->addon->addon_type==GF_ADDON_TYPE_MAIN) {
2038 : return;
2039 : }
2040 :
2041 0 : gf_sg_vrml_field_copy(&dscene->url, &odm->mo->URLs, GF_SG_VRML_MFURL);
2042 0 : gf_node_changed((GF_Node *)dscene, NULL);
2043 : //do not update video pos for addons, this is done only when setting up the main video object
2044 0 : return;
2045 : }
2046 :
2047 0 : if (odm->type == GF_STREAM_AUDIO) {
2048 0 : M_AudioClip *ac = (M_AudioClip *) gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1");
2049 0 : if (!ac) return;
2050 0 : if (scene->audio_url.url) gf_free(scene->audio_url.url);
2051 0 : scene->audio_url.url = NULL;
2052 0 : scene->audio_url.OD_ID = odm->ID;
2053 0 : if (!ac->url.count) gf_sg_vrml_mf_alloc(&ac->url, GF_SG_VRML_MFURL, 1);
2054 0 : ac->url.vals[0].OD_ID = odm->ID;
2055 0 : if (ac->url.vals[0].url) {
2056 0 : gf_free(ac->url.vals[0].url);
2057 0 : ac->url.vals[0].url = NULL;
2058 : }
2059 0 : url = odm->mo->URLs.count ? odm->mo->URLs.vals[0].url : NULL;
2060 0 : if (url) {
2061 0 : scene->audio_url.url = gf_strdup(url);
2062 0 : ac->url.vals[0].url = gf_strdup(url);
2063 : }
2064 0 : ac->startTime = gf_scene_get_time(scene);
2065 0 : gf_node_changed((GF_Node *)ac, NULL);
2066 0 : return;
2067 : }
2068 :
2069 0 : if (odm->type == GF_STREAM_VISUAL) {
2070 0 : M_MovieTexture *mt = (M_MovieTexture*) gf_sg_find_node_by_name(scene->graph, "DYN_VIDEO1");
2071 0 : if (!mt) return;
2072 0 : if (scene->visual_url.url) gf_free(scene->visual_url.url);
2073 0 : scene->visual_url.url = NULL;
2074 0 : scene->visual_url.OD_ID = odm->ID;
2075 0 : if (!mt->url.count) gf_sg_vrml_mf_alloc(&mt->url, GF_SG_VRML_MFURL, 1);
2076 0 : mt->url.vals[0].OD_ID = odm->ID;
2077 0 : if (mt->url.vals[0].url) gf_free(mt->url.vals[0].url);
2078 0 : url = odm->mo->URLs.count ? odm->mo->URLs.vals[0].url : NULL;
2079 0 : if (url) {
2080 0 : scene->visual_url.url = gf_strdup(url);
2081 0 : mt->url.vals[0].url = gf_strdup(url);
2082 : }
2083 0 : mt->startTime = gf_scene_get_time(scene);
2084 0 : gf_node_changed((GF_Node *)mt, NULL);
2085 0 : if (odm->mo) gf_scene_force_size_to_video(scene, odm->mo);
2086 0 : scene->selected_service_id = odm->ServiceID;
2087 0 : return;
2088 : }
2089 :
2090 :
2091 0 : if (odm->type == GF_STREAM_TEXT) {
2092 0 : M_AnimationStream *as = (M_AnimationStream*) gf_sg_find_node_by_name(scene->graph, "DYN_TEXT");
2093 0 : if (!as) return;
2094 0 : if (scene->text_url.url) gf_free(scene->text_url.url);
2095 0 : scene->text_url.url = NULL;
2096 0 : scene->text_url.OD_ID = odm->ID;
2097 0 : if (!as->url.count) gf_sg_vrml_mf_alloc(&as->url, GF_SG_VRML_MFURL, 1);
2098 0 : as->url.vals[0].OD_ID = odm->ID;
2099 0 : if (as->url.vals[0].url) gf_free(as->url.vals[0].url);
2100 0 : url = odm->mo->URLs.count ? odm->mo->URLs.vals[0].url : NULL;
2101 0 : if (url) {
2102 0 : scene->text_url.url = gf_strdup(url);
2103 0 : as->url.vals[0].url = gf_strdup(url);
2104 : }
2105 0 : as->startTime = gf_scene_get_time(scene);
2106 0 : gf_node_changed((GF_Node *)as, NULL);
2107 0 : return;
2108 : }
2109 : }
2110 :
2111 0 : void gf_scene_select_main_addon(GF_Scene *scene, GF_ObjectManager *odm, Bool set_on, u32 current_clock_time)
2112 : {
2113 : GF_DOM_Event devt;
2114 0 : M_Inline *dscene = (M_Inline *) gf_sg_find_node_by_name(scene->graph, scene->compositor->dbgpvr ? "ADDON_SCENE" : "PVR_SCENE");
2115 :
2116 0 : if (scene->main_addon_selected==set_on) return;
2117 0 : scene->main_addon_selected = set_on;
2118 :
2119 0 : if (set_on) {
2120 0 : odm_deactivate(gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1"));
2121 0 : odm_deactivate(gf_sg_find_node_by_name(scene->graph, "DYN_VIDEO1"));
2122 0 : odm_deactivate(gf_sg_find_node_by_name(scene->graph, "DYN_TEXT"));
2123 :
2124 :
2125 0 : if (!odm->subscene->graph_attached) {
2126 0 : odm->flags &= ~GF_ODM_REGENERATE_SCENE;
2127 0 : gf_scene_regenerate(odm->subscene);
2128 : } else {
2129 0 : odm->subscene->needs_restart = 1;
2130 : }
2131 :
2132 : //main addon is vod not live, store clock
2133 0 : if (! odm->timeshift_depth && !scene->sys_clock_at_main_activation) {
2134 0 : scene->sys_clock_at_main_activation = gf_sys_clock();
2135 0 : scene->obj_clock_at_main_activation = current_clock_time;
2136 : }
2137 :
2138 :
2139 0 : gf_sg_vrml_field_copy(&dscene->url, &odm->mo->URLs, GF_SG_VRML_MFURL);
2140 0 : gf_node_changed((GF_Node *)dscene, NULL);
2141 : } else {
2142 0 : GF_Clock *ck = scene->root_od->ck;
2143 : //reactivating the main content will trigger a reset on the clock - remember where we are and resume from this point
2144 0 : scene->root_od->media_start_time = gf_clock_media_time(ck);
2145 :
2146 0 : scene->sys_clock_at_main_activation = 0;
2147 0 : scene->obj_clock_at_main_activation = 0;
2148 :
2149 0 : odm_activate(&scene->audio_url, gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1"));
2150 0 : odm_activate(&scene->visual_url, gf_sg_find_node_by_name(scene->graph, "DYN_VIDEO1"));
2151 0 : odm_activate(&scene->text_url, gf_sg_find_node_by_name(scene->graph, "DYN_TEXT"));
2152 :
2153 0 : gf_sg_vrml_mf_reset(&dscene->url, GF_SG_VRML_MFURL);
2154 0 : gf_node_changed((GF_Node *)dscene, NULL);
2155 : }
2156 :
2157 : memset(&devt, 0, sizeof(GF_DOM_Event));
2158 0 : devt.type = GF_EVENT_MAIN_ADDON_STATE;
2159 0 : devt.detail = set_on;
2160 0 : gf_scene_notify_event(scene, GF_EVENT_MAIN_ADDON_STATE, NULL, &devt, GF_OK, GF_FALSE);
2161 :
2162 : }
2163 :
2164 : GF_EXPORT
2165 463 : void gf_scene_set_addon_layout_info(GF_Scene *scene, u32 position, u32 size_factor)
2166 : {
2167 : MFURL url;
2168 : M_Transform2D *tr;
2169 : M_Layer2D *layer;
2170 : GF_MediaObject *mo;
2171 : s32 w, h, v_w, v_h;
2172 463 : if (!scene->visual_url.OD_ID && !scene->visual_url.url) return;
2173 :
2174 463 : url.count = 1;
2175 463 : url.vals = &scene->visual_url;
2176 463 : mo = IS_CheckExistingObject(scene, &url, GF_MEDIA_OBJECT_VIDEO);
2177 463 : if (!mo) return;
2178 :
2179 463 : scene->addon_position = position;
2180 463 : scene->addon_size_factor = size_factor;
2181 :
2182 463 : gf_scene_get_video_size(mo, (u32 *) &v_w, (u32 *) &v_h);
2183 463 : w = v_w;
2184 463 : h = v_h;
2185 463 : switch (size_factor) {
2186 463 : case 0:
2187 463 : v_w /= 2;
2188 463 : v_h /= 2;
2189 463 : break;
2190 0 : case 1:
2191 0 : v_w /= 3;
2192 0 : v_h /= 3;
2193 0 : break;
2194 0 : case 2:
2195 : default:
2196 0 : v_w /= 4;
2197 0 : v_h /= 4;
2198 0 : break;
2199 : }
2200 :
2201 463 : layer = (M_Layer2D *) gf_sg_find_node_by_name(scene->graph, "ADDON_LAYER");
2202 463 : if (!layer) return;
2203 463 : layer->size.x = INT2FIX(v_w);
2204 463 : layer->size.y = INT2FIX(v_h);
2205 463 : gf_node_dirty_set((GF_Node *)layer, 0, 0);
2206 :
2207 463 : tr = (M_Transform2D *) gf_sg_find_node_by_name(scene->graph, "ADDON_TRANS");
2208 463 : if (!tr) return;
2209 463 : switch (position) {
2210 463 : case 0:
2211 463 : tr->translation.x = INT2FIX(w - v_w) / 2;
2212 463 : tr->translation.y = INT2FIX(v_h - h) / 2;
2213 463 : break;
2214 0 : case 1:
2215 0 : tr->translation.x = INT2FIX(w - v_w) / 2;
2216 0 : tr->translation.y = INT2FIX(h - v_h) / 2;
2217 0 : break;
2218 0 : case 2:
2219 0 : tr->translation.x = INT2FIX(v_w - w) / 2;
2220 0 : tr->translation.y = INT2FIX(v_h - h) / 2;
2221 0 : break;
2222 0 : case 3:
2223 0 : tr->translation.x = INT2FIX(v_w - w) / 2;
2224 0 : tr->translation.y = INT2FIX(h - v_h) / 2;
2225 0 : break;
2226 : }
2227 463 : gf_node_dirty_set((GF_Node *)tr, 0, 0);
2228 : }
2229 :
2230 : GF_EXPORT
2231 0 : void gf_scene_resume_live(GF_Scene *subscene)
2232 : {
2233 0 : if (subscene->main_addon_selected)
2234 0 : mediacontrol_resume(subscene->root_od, 1);
2235 0 : }
2236 :
2237 0 : void gf_scene_restart_dynamic(GF_Scene *scene, s64 from_time, Bool restart_only, Bool disable_addon_check)
2238 : {
2239 : u32 i;
2240 : GF_Clock *ck;
2241 : GF_List *to_restart;
2242 : GF_ObjectManager *odm;
2243 0 : if (restart_only) {
2244 : from_time = 0;
2245 : }
2246 :
2247 0 : ck = scene->root_od->ck;
2248 : if (!scene->is_dynamic_scene) ck = scene->root_od->ck;
2249 0 : if (!ck) return;
2250 :
2251 : //first pass to check if we need to enable the addon acting as time shifting
2252 0 : if (!disable_addon_check) {
2253 0 : i=0;
2254 0 : while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) {
2255 :
2256 0 : if (odm->addon && (odm->addon->addon_type==GF_ADDON_TYPE_MAIN)) {
2257 : //assign clock if not yet available
2258 0 : if (odm->addon->root_od->subscene && !odm->addon->root_od->ck)
2259 0 : odm->addon->root_od->ck = scene->root_od->ck;
2260 :
2261 : //we're timeshifting through the main addon, activate it
2262 0 : if (from_time < -1) {
2263 0 : gf_scene_select_main_addon(scene, odm, GF_TRUE, gf_clock_time(ck));
2264 :
2265 : /*no timeshift, this is a VoD associated with the live broadcast: get current time*/
2266 0 : if (! odm->timeshift_depth) {
2267 0 : s64 live_clock = scene->obj_clock_at_main_activation + gf_sys_clock() - scene->sys_clock_at_main_activation;
2268 :
2269 0 : from_time += 1;
2270 0 : if (live_clock + from_time < 0) from_time = 0;
2271 : else from_time = live_clock + from_time;
2272 : }
2273 0 : } else if (scene->main_addon_selected) {
2274 0 : gf_scene_select_main_addon(scene, odm, GF_FALSE, 0);
2275 : }
2276 : }
2277 : }
2278 : }
2279 :
2280 0 : to_restart = gf_list_new();
2281 0 : i=0;
2282 0 : while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) {
2283 : //do not restart not selected objects
2284 0 : if (odm->state != GF_ODM_STATE_PLAY)
2285 0 : continue;
2286 :
2287 0 : if (gf_odm_shares_clock(odm, ck)) {
2288 : //object is not an addon and main addon is selected, do not add
2289 0 : if (!odm->addon && scene->main_addon_selected) {
2290 : }
2291 : //object is an addon and enabled, restart if main and main is enabled, or if not main
2292 0 : else if (odm->addon && odm->addon->enabled) {
2293 0 : if (odm->addon->addon_type==GF_ADDON_TYPE_MAIN) {
2294 0 : if (scene->main_addon_selected) {
2295 0 : gf_list_add(to_restart, odm);
2296 : }
2297 : } else {
2298 0 : gf_list_add(to_restart, odm);
2299 : }
2300 0 : } else if (!scene->selected_service_id || (scene->selected_service_id==odm->ServiceID) ) {
2301 0 : gf_odm_stop(odm, 1);
2302 0 : gf_list_add(to_restart, odm);
2303 : }
2304 : }
2305 : }
2306 :
2307 0 : if (!restart_only) {
2308 0 : GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[Scene] Restarting from "LLD"\n", from_time));
2309 : /*reset clock*/
2310 0 : gf_clock_reset(ck);
2311 :
2312 : //used by SVG for JSAPIs..;
2313 0 : if (!scene->is_dynamic_scene) gf_clock_set_time(ck, 0);
2314 : } else {
2315 0 : GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[Scene] Restarting scene from current clock %d\n", gf_clock_time(ck) ));
2316 : }
2317 :
2318 :
2319 : /*restart objects*/
2320 0 : i=0;
2321 0 : while ((odm = (GF_ObjectManager*)gf_list_enum(to_restart, &i))) {
2322 0 : if (from_time<0) {
2323 0 : odm->media_stop_time = from_time + 1;
2324 : } else {
2325 0 : odm->media_start_time = from_time;
2326 : }
2327 :
2328 0 : if (odm->subscene && odm->subscene->is_dynamic_scene) {
2329 0 : gf_scene_restart_dynamic(odm->subscene, from_time, 0, 0);
2330 : } else {
2331 0 : gf_odm_start(odm);
2332 : }
2333 : }
2334 0 : gf_list_del(to_restart);
2335 :
2336 : /*also check nodes since they may be deactivated (end of stream)*/
2337 0 : if (scene->is_dynamic_scene) {
2338 0 : M_AudioClip *ac = (M_AudioClip *) gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1");
2339 0 : M_MovieTexture *mt = (M_MovieTexture *) gf_sg_find_node_by_name(scene->graph, "DYN_VIDEO1");
2340 0 : M_AnimationStream *as = (M_AnimationStream *) gf_sg_find_node_by_name(scene->graph, "DYN_TEXT");
2341 0 : if (ac) {
2342 0 : ac->startTime = gf_scene_get_time(scene);
2343 0 : gf_node_changed((GF_Node *)ac, NULL);
2344 : }
2345 0 : if (mt) {
2346 0 : mt->startTime = gf_scene_get_time(scene);
2347 0 : gf_node_changed((GF_Node *)mt, NULL);
2348 : }
2349 0 : if (as) {
2350 0 : as->startTime = gf_scene_get_time(scene);
2351 0 : gf_node_changed((GF_Node *)as, NULL);
2352 : }
2353 : }
2354 : }
2355 :
2356 : #endif /*GPAC_DISABLE_VRML*/
2357 :
2358 :
2359 : GF_EXPORT
2360 478 : void gf_scene_force_size(GF_Scene *scene, u32 width, u32 height)
2361 : {
2362 : Bool skip_notif = GF_FALSE;
2363 :
2364 : /*for now only allowed when no scene info*/
2365 478 : if (!scene->is_dynamic_scene) return;
2366 :
2367 478 : GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Scene] Forcing scene size to %d x %d\n", width, height));
2368 :
2369 478 : if (scene->vr_type) {
2370 : /*for 360 don't set scene size to full-size , only half of it*/
2371 4 : width /= 2;
2372 4 : height /= 2;
2373 : /*if we already processed a force size in 360, don't do it again*/
2374 4 : if (scene->force_size_set)
2375 : return;
2376 :
2377 : #ifndef GPAC_DISABLE_VRML
2378 2 : scene->force_size_set = GF_TRUE;
2379 2 : if (! scene->srd_type) {
2380 2 : GF_Node *node = gf_sg_find_node_by_name(scene->graph, "DYN_GEOM1");
2381 2 : if (node && (((M_Sphere *)node)->radius == FIX_ONE)) {
2382 2 : u32 radius = MAX(width, height)/4;
2383 :
2384 2 : ((M_Sphere *)node)->radius = - INT2FIX(radius);
2385 2 : gf_node_changed(node, NULL);
2386 : }
2387 : }
2388 : #endif /* GPAC_DISABLE_VRML */
2389 : }
2390 :
2391 476 : if (scene->is_dynamic_scene) {
2392 : u32 serv_w=0, serv_h=0;
2393 476 : GF_FilterPid *pid = gf_filter_get_ipid(scene->compositor->filter, 0);
2394 : const GF_PropertyValue *prop;
2395 476 : prop = gf_filter_pid_get_property(pid, GF_PROP_SERVICE_WIDTH);
2396 476 : if (prop) serv_w = prop->value.uint;
2397 476 : prop = gf_filter_pid_get_property(pid, GF_PROP_SERVICE_HEIGHT);
2398 476 : if (prop) serv_h = prop->value.uint;
2399 :
2400 :
2401 476 : if (!scene->root_od->parentscene) {
2402 463 : if (serv_w && serv_h) {
2403 4 : gf_sc_set_scene_size(scene->compositor, width, height, 1);
2404 4 : if (!scene->force_size_set) {
2405 2 : gf_sc_set_size(scene->compositor, serv_w, serv_h);
2406 2 : scene->force_size_set = 1;
2407 : } else {
2408 2 : gf_sc_set_size(scene->compositor, 0, 0);
2409 : }
2410 : } else {
2411 459 : if (scene->vr_type) {
2412 0 : width = MAX(width, height) / 2;
2413 0 : gf_sg_set_scene_size_info(scene->graph, 0, 0, 1);
2414 : } else {
2415 : /*need output resize*/
2416 459 : gf_sg_set_scene_size_info(scene->graph, width, height, 1);
2417 459 : gf_sc_set_scene(scene->compositor, scene->graph);
2418 459 : gf_sc_set_size(scene->compositor, width, height);
2419 : }
2420 : }
2421 :
2422 13 : } else if (!scene->force_size_set) {
2423 8 : if (serv_w && serv_h) {
2424 : width = serv_w;
2425 : height = serv_h;
2426 : }
2427 8 : if (scene->vr_type) {
2428 0 : gf_sg_set_scene_size_info(scene->graph, 0, 0, 1);
2429 : } else {
2430 8 : gf_sg_set_scene_size_info(scene->graph, width, height, 1);
2431 : }
2432 8 : scene->force_size_set = 1;
2433 : } else {
2434 : u32 w, h;
2435 5 : gf_sg_get_scene_size_info(scene->graph, &w, &h);
2436 5 : if (!serv_w && !serv_h && ((w<width) || (h<height)) ) {
2437 1 : gf_sg_set_scene_size_info(scene->graph, width, height, 1);
2438 : } else {
2439 : GF_DOM_Event devt;
2440 : memset(&devt, 0, sizeof(GF_DOM_Event));
2441 4 : devt.type = GF_EVENT_SCENE_SIZE;
2442 4 : devt.screen_rect.width = INT2FIX(width);
2443 4 : devt.screen_rect.height = INT2FIX(height);
2444 :
2445 4 : devt.key_flags = scene->is_dynamic_scene ? (scene->vr_type ? 2 : 1) : 0;
2446 :
2447 4 : gf_scene_notify_event(scene, GF_EVENT_SCENE_SIZE, NULL, &devt, GF_OK, GF_FALSE);
2448 :
2449 : skip_notif = GF_TRUE;
2450 :
2451 4 : width = w;
2452 4 : height = h;
2453 : }
2454 : }
2455 : }
2456 0 : else if (scene->root_od->parentscene && scene->root_od->parentscene->is_dynamic_scene) {
2457 0 : gf_sg_set_scene_size_info(scene->root_od->parentscene->graph, width, height, gf_sg_use_pixel_metrics(scene->root_od->parentscene->graph));
2458 0 : if (!scene->root_od->parentscene) {
2459 0 : if (width && height) {
2460 0 : gf_sc_set_scene_size(scene->compositor, width, height, GF_TRUE);
2461 0 : gf_sc_set_size(scene->compositor, width, height);
2462 : }
2463 : }
2464 : }
2465 :
2466 476 : if (scene->vr_type) {
2467 2 : gf_sg_set_scene_size_info(scene->graph, 0, 0, GF_TRUE);
2468 : } else {
2469 474 : gf_sg_set_scene_size_info(scene->graph, width, height, GF_TRUE);
2470 : }
2471 476 : if (scene->srd_type)
2472 0 : gf_scene_regenerate(scene);
2473 :
2474 : #ifndef GPAC_DISABLE_VRML
2475 476 : IS_UpdateVideoPos(scene);
2476 : #endif
2477 :
2478 476 : if (skip_notif) return;
2479 :
2480 472 : gf_scene_notify_event(scene, GF_EVENT_SCENE_ATTACHED, NULL, NULL, GF_OK, GF_FALSE);
2481 : }
2482 :
2483 :
2484 : GF_EXPORT
2485 0 : Bool gf_scene_process_anchor(GF_Node *caller, GF_Event *evt)
2486 : {
2487 : #ifndef GPAC_DISABLE_VRML
2488 : u32 i;
2489 : M_Inline *inl;
2490 : #endif
2491 : GF_Scene *scene;
2492 0 : GF_SceneGraph *sg = gf_node_get_graph(caller);
2493 0 : if (!sg) return 1;
2494 0 : scene = (GF_Scene *)gf_sg_get_private(sg);
2495 0 : if (!scene) return 1;
2496 :
2497 : /*if main scene forward to user. If no params or first one not "self" forward to user*/
2498 0 : if (! scene->root_od->parentscene || !evt->navigate.parameters || !evt->navigate.param_count || (stricmp(evt->navigate.parameters[0], "self") && stricmp(evt->navigate.parameters[0], "_self"))) {
2499 :
2500 0 : return gf_filter_ui_event(scene->compositor->filter, evt);
2501 : }
2502 :
2503 0 : if (!scene->root_od->mo) return 1;
2504 :
2505 : /*FIXME this is too restrictive, we assume the navigate URL is really a presentation one...*/
2506 : #ifndef GPAC_DISABLE_VRML
2507 0 : i=0;
2508 0 : while ((inl = (M_Inline*)gf_mo_event_target_enum_node(scene->root_od->mo, &i))) {
2509 0 : switch (gf_node_get_tag((GF_Node *)inl)) {
2510 0 : case TAG_MPEG4_Inline:
2511 : #ifndef GPAC_DISABLE_X3D
2512 : case TAG_X3D_Inline:
2513 : #endif
2514 0 : gf_sg_vrml_mf_reset(&inl->url, GF_SG_VRML_MFURL);
2515 0 : gf_sg_vrml_mf_alloc(&inl->url, GF_SG_VRML_MFURL, 1);
2516 0 : inl->url.vals[0].url = gf_strdup(evt->navigate.to_url ? evt->navigate.to_url : "");
2517 : /*signal URL change but don't destroy inline scene now since we got this event from inside the scene,
2518 : this could crash compositors*/
2519 0 : scene->needs_restart = 2;
2520 0 : break;
2521 : }
2522 : }
2523 : #endif
2524 :
2525 : return 1;
2526 : }
2527 :
2528 : GF_EXPORT
2529 15817 : GF_Compositor *gf_sc_get_compositor(GF_Node *node)
2530 : {
2531 : GF_Scene *scene;
2532 15817 : GF_SceneGraph *sg = gf_node_get_graph(node);
2533 15817 : if (!sg) return NULL;
2534 15817 : scene = (GF_Scene *)gf_sg_get_private(sg);
2535 15817 : if (!scene) return NULL;
2536 15817 : return scene->compositor;
2537 : }
2538 :
2539 943 : const char *gf_scene_get_fragment_uri(GF_Node *node)
2540 : {
2541 943 : GF_SceneGraph *sg = gf_node_get_graph(node);
2542 943 : GF_Scene *scene = sg ? (GF_Scene *) gf_sg_get_private(sg) : NULL;
2543 943 : if (!scene) return NULL;
2544 943 : return scene->fragment_uri;
2545 : }
2546 0 : void gf_scene_set_fragment_uri(GF_Node *node, const char *uri)
2547 : {
2548 0 : GF_SceneGraph *sg = gf_node_get_graph(node);
2549 0 : GF_Scene *scene = sg ? (GF_Scene *) gf_sg_get_private(sg) : NULL;
2550 0 : if (!scene) return;
2551 0 : if (scene->fragment_uri) {
2552 0 : gf_free(scene->fragment_uri);
2553 0 : scene->fragment_uri = NULL;
2554 : }
2555 0 : if (uri) scene->fragment_uri = gf_strdup(uri);
2556 : }
2557 :
2558 :
2559 0 : GF_Node *gf_scene_get_subscene_root(GF_Node *node)
2560 : {
2561 : GF_Scene *scene;
2562 0 : if (!node) return NULL;
2563 0 : switch (gf_node_get_tag(node)) {
2564 : #ifndef GPAC_DISABLE_VRML
2565 : case TAG_MPEG4_Inline:
2566 : #ifndef GPAC_DISABLE_X3D
2567 : case TAG_X3D_Inline:
2568 : #endif
2569 : break;
2570 : #endif
2571 : default:
2572 : return NULL;
2573 : }
2574 0 : scene = (GF_Scene *)gf_node_get_private(node);
2575 0 : if (!scene) return NULL;
2576 0 : if (!scene->graph) return NULL;
2577 0 : return gf_sg_get_root_node(scene->graph);
2578 : }
2579 :
2580 : /*returns 0 if any of the clock still hasn't seen EOS*/
2581 23762 : Bool gf_scene_check_clocks(GF_SceneNamespace *ns, GF_Scene *scene, Bool check_buffering)
2582 : {
2583 : GF_Clock *ck;
2584 : Bool initialized = GF_FALSE;
2585 : u32 i;
2586 :
2587 23762 : if (scene) {
2588 : GF_ObjectManager *odm;
2589 14893 : if (scene->root_od->scene_ns != ns) {
2590 0 : if (!gf_scene_check_clocks(scene->root_od->scene_ns, scene, check_buffering)) return 0;
2591 : }
2592 14893 : i=0;
2593 35310 : while ( (odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i)) ) {
2594 9350 : switch (odm->type) {
2595 12 : case GF_STREAM_OCR:
2596 : case GF_STREAM_INTERACT:
2597 12 : continue;
2598 : default:
2599 : break;
2600 : }
2601 :
2602 9338 : if (odm->scene_ns && (odm->scene_ns != ns)) {
2603 8869 : if (!gf_scene_check_clocks(odm->scene_ns, NULL, check_buffering)) return 0;
2604 469 : } else if (odm->ck) {
2605 : initialized = GF_TRUE;
2606 459 : if (!check_buffering) {
2607 459 : if (! odm->has_seen_eos && (odm->state != GF_ODM_STATE_STOP) ) {
2608 : return 0;
2609 : }
2610 : } else {
2611 0 : if (odm->ck->nb_buffering) {
2612 : return 0;
2613 : }
2614 : }
2615 : }
2616 : }
2617 : }
2618 :
2619 19936 : i=0;
2620 55892 : while (ns->clocks && (ck = (GF_Clock *)gf_list_enum(ns->clocks, &i) ) ) {
2621 : initialized = GF_TRUE;
2622 19799 : if (!check_buffering) {
2623 19799 : Bool is_eos = ck->has_seen_eos;
2624 : //object not active consider it done
2625 19799 : if (!is_eos && ns->owner->mo && !ns->owner->mo->num_open)
2626 : is_eos = GF_TRUE;
2627 :
2628 19796 : if (!is_eos) return 0;
2629 : } else {
2630 0 : if (ck->nb_buffering) return 0;
2631 : }
2632 :
2633 : }
2634 :
2635 16157 : if (!check_buffering && scene) {
2636 11035 : if (scene->root_od->ID) {
2637 : initialized = GF_TRUE;
2638 11035 : if (scene->root_od->parentscene && (scene->root_od->state != GF_ODM_STATE_STOP)) return 0;
2639 : }
2640 : }
2641 5122 : if (!initialized) return 0;
2642 :
2643 16078 : return 1;
2644 : }
2645 :
2646 5 : const char *gf_scene_get_service_url(GF_SceneGraph *sg)
2647 : {
2648 5 : GF_Scene *scene = gf_sg_get_private(sg);
2649 5 : if (scene) return scene->root_od->scene_ns->url;
2650 : return NULL;
2651 : }
2652 :
2653 5649 : Bool gf_scene_is_over(GF_SceneGraph *sg)
2654 : {
2655 : u32 i, count;
2656 5649 : GF_Scene *scene = gf_sg_get_private(sg);
2657 5649 : if (!scene) return GF_FALSE;
2658 5649 : if (scene->root_od->has_seen_eos)
2659 5106 : return scene->root_od->ck->has_seen_eos;
2660 :
2661 543 : count = gf_list_count(scene->resources);
2662 543 : for (i=0; i<count; i++) {
2663 540 : GF_ObjectManager *odm = gf_list_get(scene->resources, i);
2664 540 : if (!odm->has_seen_eos || !odm->ck->has_seen_eos) return GF_FALSE;
2665 0 : if (odm->subscene && !gf_scene_is_over(odm->subscene->graph) ) return GF_FALSE;
2666 : }
2667 : return GF_TRUE;
2668 : }
2669 :
2670 5663 : GF_SceneGraph *gf_scene_enum_extra_scene(GF_SceneGraph *sg, u32 *i)
2671 : {
2672 5663 : GF_Scene *scene = gf_sg_get_private(sg);
2673 5663 : if (!scene) return NULL;
2674 5663 : return gf_list_enum(scene->extra_scenes, i);
2675 : }
2676 :
2677 45 : Bool gf_scene_is_dynamic_scene(GF_SceneGraph *sg)
2678 : {
2679 45 : GF_Scene *scene = gf_sg_get_private(sg);
2680 45 : if (!scene) return 0;
2681 45 : return scene->is_dynamic_scene ? 1 : 0;
2682 : }
2683 :
2684 : #define USE_TEXTURES 0
2685 :
2686 0 : void gf_scene_generate_views(GF_Scene *scene, char *url, char *parent_path)
2687 : {
2688 : #ifndef GPAC_DISABLE_VRML
2689 : char *url_search;
2690 : Bool use_old_syntax = 1;
2691 : GF_Node *n1, *switcher;
2692 : #if USE_TEXTURES
2693 : GF_Node *n2;
2694 : M_MovieTexture *mt;
2695 : #else
2696 : M_Inline *inl;
2697 : #endif
2698 : GF_Event evt;
2699 0 : gf_sc_node_destroy(scene->compositor, NULL, scene->graph);
2700 0 : gf_sg_reset(scene->graph);
2701 :
2702 0 : scene->force_single_timeline = 1;
2703 0 : n1 = is_create_node(scene->graph, TAG_MPEG4_OrderedGroup, NULL);
2704 0 : gf_sg_set_root_node(scene->graph, n1);
2705 0 : gf_node_register(n1, NULL);
2706 :
2707 0 : switcher = is_create_node(scene->graph, TAG_MPEG4_Switch, NULL);
2708 0 : gf_node_register(switcher, n1);
2709 0 : gf_node_list_add_child( &((GF_ParentNode *)n1)->children, switcher);
2710 0 : ((M_Switch*)switcher)->whichChoice = -2;
2711 :
2712 0 : if (strstr(url, "::")) use_old_syntax = 0;
2713 :
2714 : url_search = url;
2715 : while (1) {
2716 : char *sep;
2717 :
2718 0 : if (use_old_syntax) {
2719 0 : sep = gf_url_colon_suffix(url_search);
2720 : } else {
2721 0 : sep = strstr(url_search, "::");
2722 : }
2723 0 : if (sep) sep[0] = 0;
2724 :
2725 : #if USE_TEXTURES
2726 : /*create a shape and bitmap node*/
2727 : n2 = is_create_node(scene->graph, TAG_MPEG4_Shape, NULL);
2728 : gf_node_list_add_child( &((M_Switch *)switcher)->choice, n2);
2729 : gf_node_register(n2, switcher);
2730 : n1 = n2;
2731 : n2 = is_create_node(scene->graph, TAG_MPEG4_Appearance, NULL);
2732 : ((M_Shape *)n1)->appearance = n2;
2733 : gf_node_register(n2, n1);
2734 :
2735 : /*note we create a movie texture even for images...*/
2736 : mt = (M_MovieTexture *) is_create_node(scene->graph, TAG_MPEG4_MovieTexture, NULL);
2737 : mt->startTime = gf_scene_get_time(scene);
2738 : ((M_Appearance *)n2)->texture = (GF_Node *)mt;
2739 : gf_node_register((GF_Node *)mt, n2);
2740 :
2741 : n2 = is_create_node(scene->graph, TAG_MPEG4_Bitmap, NULL);
2742 : ((M_Shape *)n1)->geometry = n2;
2743 : gf_node_register(n2, n1);
2744 :
2745 : gf_sg_vrml_mf_reset(&mt->url, GF_SG_VRML_MFURL);
2746 : gf_sg_vrml_mf_append(&mt->url, GF_SG_VRML_MFURL, NULL);
2747 : mt->url.vals[0].url = gf_url_concatenate(parent_path, url);
2748 : #else
2749 0 : inl = (M_Inline *) is_create_node(scene->graph, TAG_MPEG4_Inline, NULL);
2750 0 : gf_node_list_add_child( &((M_Switch *)switcher)->choice, (GF_Node *)inl);
2751 0 : gf_node_register((GF_Node*) inl, switcher);
2752 :
2753 0 : gf_sg_vrml_mf_reset(&inl->url, GF_SG_VRML_MFURL);
2754 0 : gf_sg_vrml_mf_append(&inl->url, GF_SG_VRML_MFURL, NULL);
2755 0 : inl->url.vals[0].url = gf_url_concatenate(parent_path, url);
2756 : #endif
2757 :
2758 0 : if (!sep) break;
2759 0 : sep[0] = ':';
2760 0 : if (use_old_syntax) {
2761 0 : url = sep+1;
2762 : } else {
2763 0 : url = sep+2;
2764 : }
2765 : url_search = url;
2766 : }
2767 :
2768 0 : gf_sc_set_option(scene->compositor, GF_OPT_USE_OPENGL, 1);
2769 :
2770 0 : scene->is_dynamic_scene = 2;
2771 0 : gf_sg_set_scene_size_info(scene->graph, 0, 0, 1);
2772 :
2773 0 : gf_scene_attach_to_compositor(scene);
2774 :
2775 0 : evt.type = GF_EVENT_CONNECT;
2776 0 : evt.connect.is_connected = 1;
2777 0 : gf_sc_send_event(scene->compositor, &evt);
2778 : #endif
2779 0 : }
2780 :
2781 0 : void gf_scene_generate_mosaic(GF_Scene *scene, char *url, char *parent_path)
2782 : {
2783 : #ifndef GPAC_DISABLE_VRML
2784 : char *url_search, *cur_url;
2785 : Bool use_old_syntax = 1;
2786 : GF_Node *n1;
2787 : M_Inline *inl;
2788 : Bool first_pass = GF_TRUE;
2789 : u32 nb_items=0, nb_rows=0, nb_cols=0;
2790 : s32 width=1920, height=1080, x=0, y=0, tw=1920, th=1080;
2791 :
2792 : GF_Event evt;
2793 0 : gf_sc_node_destroy(scene->compositor, NULL, scene->graph);
2794 0 : gf_sg_reset(scene->graph);
2795 :
2796 0 : scene->force_single_timeline = GF_FALSE;
2797 0 : n1 = is_create_node(scene->graph, TAG_MPEG4_OrderedGroup, NULL);
2798 0 : gf_sg_set_root_node(scene->graph, n1);
2799 0 : gf_node_register(n1, NULL);
2800 :
2801 0 : if (strstr(url, "::")) use_old_syntax = 0;
2802 :
2803 0 : restart:
2804 : url_search = cur_url = url;
2805 : x = y = 0;
2806 : while (1) {
2807 : char *sep;
2808 :
2809 0 : if (use_old_syntax) {
2810 0 : sep = strchr(url_search, ':');
2811 : /*if :// or :\ is found, skip it*/
2812 0 : if (sep && ( ((sep[1] == '/') && (sep[2] == '/')) || (sep[1] == '\\') ) ) {
2813 0 : url_search = sep+1;
2814 0 : continue;
2815 : }
2816 : } else {
2817 0 : sep = strstr(url_search, "::");
2818 : }
2819 0 : if (sep) sep[0] = 0;
2820 :
2821 0 : if (first_pass) {
2822 0 : nb_items ++;
2823 : } else {
2824 0 : GF_Node *tr = is_create_node(scene->graph, TAG_MPEG4_Transform2D, NULL);
2825 0 : GF_Node *layer = is_create_node(scene->graph, TAG_MPEG4_Layer2D, NULL);
2826 0 : gf_node_register(tr, n1);
2827 0 : gf_node_list_add_child( &((GF_ParentNode *)n1)->children, tr);
2828 :
2829 0 : ((M_Transform2D *)tr)->translation.x = INT2FIX( -width/2 + tw/2 + x*tw);
2830 0 : ((M_Transform2D *)tr)->translation.y = INT2FIX( height/2 - th/2 - y*th);
2831 :
2832 0 : x++;
2833 0 : if (x==nb_cols) {
2834 0 : y++;
2835 : x=0;
2836 : }
2837 :
2838 0 : gf_node_register(layer, tr);
2839 0 : gf_node_list_add_child( &((M_Transform2D *)tr)->children, layer);
2840 0 : ((M_Layer2D *)layer)->size.x = INT2FIX(tw);
2841 0 : ((M_Layer2D *)layer)->size.y = INT2FIX(th);
2842 :
2843 0 : inl = (M_Inline *) is_create_node(scene->graph, TAG_MPEG4_Inline, NULL);
2844 0 : gf_node_list_add_child( &((M_Layer2D *)layer)->children, (GF_Node *)inl);
2845 0 : gf_node_register((GF_Node*) inl, layer);
2846 :
2847 0 : gf_sg_vrml_mf_reset(&inl->url, GF_SG_VRML_MFURL);
2848 0 : gf_sg_vrml_mf_append(&inl->url, GF_SG_VRML_MFURL, NULL);
2849 0 : inl->url.vals[0].url = gf_url_concatenate(parent_path, cur_url);
2850 : }
2851 :
2852 0 : if (!sep) break;
2853 0 : sep[0] = ':';
2854 0 : if (use_old_syntax) {
2855 0 : cur_url = sep+1;
2856 : } else {
2857 0 : cur_url = sep+2;
2858 : }
2859 : url_search = cur_url;
2860 : }
2861 0 : if (first_pass) {
2862 : first_pass = GF_FALSE;
2863 0 : nb_cols=(u32) gf_ceil( gf_sqrt(nb_items) );
2864 0 : nb_rows=nb_items/nb_cols;
2865 0 : if (nb_cols * nb_rows < nb_items) nb_rows++;
2866 0 : tw = width/nb_cols;
2867 0 : th = height/nb_rows;
2868 0 : goto restart;
2869 : }
2870 :
2871 0 : scene->is_dynamic_scene = 2;
2872 0 : gf_sg_set_scene_size_info(scene->graph, width, height, 1);
2873 :
2874 0 : gf_scene_attach_to_compositor(scene);
2875 :
2876 0 : evt.type = GF_EVENT_CONNECT;
2877 0 : evt.connect.is_connected = 1;
2878 0 : gf_sc_send_event(scene->compositor, &evt);
2879 : #endif
2880 0 : }
2881 :
2882 :
2883 0 : void gf_scene_reset_addon(GF_AddonMedia *addon, Bool disconnect)
2884 : {
2885 0 : if (addon->root_od) {
2886 0 : addon->root_od->addon = NULL;
2887 0 : if (disconnect) {
2888 0 : gf_scene_remove_object(addon->root_od->parentscene, addon->root_od, 2);
2889 0 : gf_odm_disconnect(addon->root_od, 1);
2890 : }
2891 : }
2892 :
2893 0 : if (addon->url) gf_free(addon->url);
2894 0 : gf_free(addon);
2895 0 : }
2896 :
2897 686 : void gf_scene_reset_addons(GF_Scene *scene)
2898 : {
2899 1372 : while (gf_list_count(scene->declared_addons)) {
2900 0 : GF_AddonMedia *addon = gf_list_last(scene->declared_addons);
2901 0 : gf_list_rem_last(scene->declared_addons);
2902 :
2903 0 : gf_scene_reset_addon(addon, GF_FALSE);
2904 : }
2905 686 : }
2906 : #ifdef FILTER_FIXME
2907 :
2908 : static void load_associated_media(GF_Scene *scene, GF_AddonMedia *addon)
2909 : {
2910 : GF_MediaObject *mo;
2911 : MFURL url;
2912 : SFURL sfurl;
2913 :
2914 : if (!addon->enabled) return;
2915 :
2916 : url.count=1;
2917 : url.vals = &sfurl;
2918 : url.vals[0].OD_ID = GF_MEDIA_EXTERNAL_ID;
2919 : url.vals[0].url = (char *)addon->url;
2920 :
2921 : //we may need to change the object type once we have more ideas what the external resource is about.
2922 : //By default we start with scene
2923 : //we force the timeline of the addon to be locked with the main scene
2924 : mo = gf_scene_get_media_object(scene, &url, GF_MEDIA_OBJECT_SCENE, GF_TRUE);
2925 :
2926 : if (!mo || !mo->odm) {
2927 : assert(0);
2928 : return;
2929 : }
2930 :
2931 : addon->root_od = mo->odm;
2932 : mo->odm->addon = addon;
2933 : }
2934 :
2935 :
2936 : GF_EXPORT
2937 : void gf_scene_register_associated_media(GF_Scene *scene, GF_AssociatedContentLocation *addon_info)
2938 : {
2939 : GF_AddonMedia *addon = NULL;
2940 : GF_Event evt;
2941 : u32 i, count;
2942 : Bool new_addon = 0;
2943 :
2944 : if (!scene->is_dynamic_scene) return;
2945 :
2946 : count = gf_list_count(scene->declared_addons);
2947 : for (i=0; i<count; i++) {
2948 : Bool my_addon = 0;
2949 : addon = gf_list_get(scene->declared_addons, i);
2950 : if ((addon_info->timeline_id>=0) && addon->timeline_id==addon_info->timeline_id) {
2951 : my_addon = 1;
2952 : } else if (addon->url && addon_info->external_URL && !strcmp(addon->url, addon_info->external_URL)) {
2953 : my_addon = 1;
2954 : //send message to service handler
2955 : }
2956 : //this is an already received addon
2957 : if (my_addon) {
2958 : if (addon_info->disable_if_defined) {
2959 : addon->enabled = GF_FALSE;
2960 :
2961 : if (addon->root_od) {
2962 : gf_scene_toggle_addons(scene, GF_FALSE);
2963 : gf_scene_remove_object(addon->root_od->parentscene, addon->root_od, 2);
2964 : gf_odm_disconnect(addon->root_od, 1);
2965 :
2966 : addon->root_od->addon = NULL;
2967 : }
2968 : return;
2969 : }
2970 :
2971 : if (addon_info->enable_if_defined)
2972 : addon->enabled = GF_TRUE;
2973 :
2974 : //declaration of start time
2975 : if (addon->is_splicing && (addon->splice_start<0) && addon_info->is_splicing) {
2976 : addon->splice_in_pts = addon_info->splice_time_pts;
2977 :
2978 : if (addon->splice_in_pts) {
2979 : addon->media_pts = (u64) (addon_info->splice_start_time);
2980 : addon->splice_start = addon_info->splice_start_time / 90;
2981 : addon->splice_end = addon_info->splice_end_time / 90;
2982 : } else {
2983 : addon->media_pts = (u64)(addon_info->splice_start_time * 90000);
2984 : addon->splice_start = addon_info->splice_start_time * 1000;
2985 : addon->splice_end = addon_info->splice_end_time * 1000;
2986 : }
2987 : }
2988 :
2989 : //restart addon
2990 : if (!addon->root_od && addon->timeline_ready && addon->enabled) {
2991 : load_associated_media(scene, addon);
2992 : }
2993 : //nothing associated, deactivate addon
2994 : if (!addon_info->external_URL || !strlen(addon_info->external_URL) ) {
2995 : gf_list_rem(scene->declared_addons, i);
2996 : gf_scene_reset_addon(addon, GF_TRUE);
2997 : gf_scene_toggle_addons(scene, GF_FALSE);
2998 : } else if (strcmp(addon_info->external_URL, addon->url)) {
2999 : //reconfigure addon
3000 : gf_free(addon->url);
3001 : addon->url = NULL;
3002 : break;
3003 : }
3004 : return;
3005 : }
3006 : addon = NULL;
3007 : }
3008 :
3009 : if (!addon_info->external_URL || !strlen(addon_info->external_URL) ) {
3010 : return;
3011 : }
3012 :
3013 : if (!addon) {
3014 : GF_SAFEALLOC(addon, GF_AddonMedia);
3015 : if (!addon) {
3016 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Terminal] Failed to allocate media addon\n"));
3017 : return;
3018 : }
3019 : addon->timeline_id = addon_info->timeline_id;
3020 : gf_list_add(scene->declared_addons, addon);
3021 : new_addon = 1;
3022 : }
3023 :
3024 : addon->is_splicing = addon_info->is_splicing;
3025 : addon->activation_time = gf_scene_get_time(scene)+addon_info->activation_countdown;
3026 : addon->url = gf_strdup(addon_info->external_URL);
3027 : addon->media_timescale = 1;
3028 : addon->timeline_ready = (addon_info->timeline_id<0) ? 1 : 0;
3029 : addon->splice_in_pts = addon_info->splice_time_pts;
3030 : if (addon_info->is_splicing) {
3031 : addon->addon_type = GF_ADDON_TYPE_SPLICED;
3032 : scene->has_splicing_addons = GF_TRUE;
3033 :
3034 : if (addon->splice_in_pts) {
3035 : addon->media_pts = (u64) (addon_info->splice_start_time);
3036 : addon->splice_start = addon_info->splice_start_time / 90;
3037 : addon->splice_end = addon_info->splice_end_time / 90;
3038 : } else {
3039 : addon->media_pts = (u64)(addon_info->splice_start_time * 90000);
3040 :
3041 : addon->splice_start = addon_info->splice_start_time * 1000;
3042 : addon->splice_end = addon_info->splice_end_time * 1000;
3043 : }
3044 : } else {
3045 : addon->splice_start = addon_info->splice_start_time;
3046 : addon->splice_end = addon_info->splice_end_time;
3047 : }
3048 :
3049 : if (!new_addon) return;
3050 :
3051 : //notify we found a new addon
3052 :
3053 : if (! scene->root_od->parentscene) {
3054 : evt.type = GF_EVENT_ADDON_DETECTED;
3055 : evt.addon_connect.addon_url = addon->url;
3056 : addon->enabled = gf_sc_send_event(scene->compositor, &evt);
3057 :
3058 : if (addon->timeline_ready)
3059 : load_associated_media(scene, addon);
3060 : } else {
3061 : GF_DOM_Event devt;
3062 : memset(&devt, 0, sizeof(GF_DOM_Event));
3063 : devt.type = GF_EVENT_ADDON_DETECTED;
3064 : devt.addon_url = addon->url;
3065 : addon->enabled = 0;
3066 :
3067 : gf_scene_notify_event(scene, GF_EVENT_SCENE_ATTACHED, NULL, &devt, GF_OK, GF_TRUE);
3068 : }
3069 :
3070 : }
3071 :
3072 : void gf_scene_notify_associated_media_timeline(GF_Scene *scene, GF_AssociatedContentTiming *addon_time)
3073 : {
3074 : Double prev_time;
3075 : GF_AddonMedia *addon = NULL;
3076 :
3077 : u32 i, count = gf_list_count(scene->declared_addons);
3078 : for (i=0; i<count; i++) {
3079 : addon = gf_list_get(scene->declared_addons, i);
3080 : if (addon->timeline_id==addon_time->timeline_id)
3081 : break;
3082 : addon = NULL;
3083 : }
3084 : if (!addon) return;
3085 :
3086 : count = i;
3087 : for (i=0; i<count; i++) {
3088 : GF_AddonMedia *prev_addon = gf_list_get(scene->declared_addons, i);
3089 : //we are adding a non splicing point: discard all previously declared addons
3090 : if (!addon->is_splicing
3091 : //this is a splicing point, discard all previsously declared splicing addons
3092 : || prev_addon->is_splicing
3093 : ) {
3094 : gf_scene_reset_addon(prev_addon, GF_TRUE);
3095 : gf_list_rem(scene->declared_addons, i);
3096 : i--;
3097 : count--;
3098 : }
3099 : }
3100 :
3101 :
3102 : prev_time = (Double) addon->media_timestamp;
3103 : prev_time /= addon->media_timescale;
3104 :
3105 : //loop has been detected
3106 : if ( prev_time * addon_time->media_timescale > addon_time->media_timestamp + 1.5 * addon_time->media_timescale ) {
3107 : if (!addon->loop_detected) {
3108 : addon->loop_detected = GF_TRUE;
3109 : GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("Loop detected in addon - PTS "LLD" (CTS %d) - media time "LLD"\n", addon_time->media_pts, addon_time->media_pts/90, addon_time->media_timestamp));
3110 : addon->past_media_pts = addon_time->media_pts;
3111 : addon->past_media_timestamp = addon_time->media_timestamp;
3112 : addon->past_media_timescale = addon_time->media_timescale;
3113 : addon->past_media_pts_scaled = addon_time->media_pts/90;
3114 : }
3115 : } else if (!addon->loop_detected) {
3116 : addon->media_pts = addon_time->media_pts;
3117 : addon->media_timestamp = addon_time->media_timestamp;
3118 : addon->media_timescale = addon_time->media_timescale;
3119 : assert(addon_time->media_timescale);
3120 : assert(!addon->loop_detected);
3121 : }
3122 :
3123 : if (!addon->timeline_ready) {
3124 : addon->timeline_ready = GF_TRUE;
3125 : load_associated_media(scene, addon);
3126 : }
3127 :
3128 : if ((addon->addon_type==GF_ADDON_TYPE_MAIN) && addon->root_od && addon->root_od->duration && !addon->root_od->timeshift_depth) {
3129 : Double dur, tsb;
3130 : dur = (Double) addon->root_od->duration;
3131 : dur /= 1000;
3132 : tsb = (Double) addon->media_timestamp;
3133 : tsb /= addon->media_timescale;
3134 : if (tsb>dur) tsb = dur;
3135 : addon->root_od->parentscene->root_od->timeshift_depth = (u32) (1000*tsb);
3136 : gf_scene_set_timeshift_depth(scene);
3137 : }
3138 :
3139 : //and forward ntp if any to underlying service
3140 : if (addon_time->ntp && addon->root_od && addon->root_od->net_service) {
3141 : GF_NetworkCommand com;
3142 : memset(&com, 0, sizeof(com));
3143 : com.addon_time = *addon_time;
3144 : gf_term_service_command(addon->root_od->net_service, &com);
3145 : }
3146 : }
3147 :
3148 : #endif
3149 :
3150 0 : Bool gf_scene_check_addon_restart(GF_AddonMedia *addon, u64 cts, u64 dts)
3151 : {
3152 : u32 i;
3153 : GF_ObjectManager*odm;
3154 : GF_Scene *subscene;
3155 : GF_List *to_restart = NULL;
3156 :
3157 0 : if (!addon || !addon->loop_detected) return GF_FALSE;
3158 : //warning, we need to compare to media PTS/90 since we already rounded the media_ts to milliseconds (otherwise we would get rounding errors).
3159 0 : if ((cts == addon->past_media_pts_scaled) || (dts >= addon->past_media_pts_scaled) ) {
3160 : } else {
3161 0 : GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("Loop not yet active - CTS "LLD" DTS "LLD" media TS "LLD" \n", cts, dts, addon->past_media_pts_scaled));
3162 : return GF_FALSE;
3163 : }
3164 :
3165 0 : addon->loop_detected = 0;
3166 0 : addon->media_pts = addon->past_media_pts;
3167 0 : addon->media_timestamp = addon->past_media_timestamp;
3168 0 : addon->media_timescale = addon->past_media_timescale;
3169 : assert(addon->past_media_timescale);
3170 0 : addon->past_media_pts = 0;
3171 0 : addon->past_media_timestamp = 0;
3172 0 : addon->past_media_timescale = 0;
3173 :
3174 0 : subscene = addon->root_od->subscene;
3175 :
3176 0 : GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("Looping addon - CTS "LLD" - addon media TS "LLD" (CTS "LLD") addon media time "LLD"\n", cts, addon->media_pts, addon->media_pts/90, addon->media_timestamp));
3177 :
3178 0 : to_restart = gf_list_new();
3179 :
3180 0 : i=0;
3181 0 : while ((odm = (GF_ObjectManager*)gf_list_enum(subscene->resources, &i))) {
3182 0 : if (odm->state == GF_ODM_STATE_PLAY) {
3183 0 : gf_list_add(to_restart, odm);
3184 : }
3185 0 : gf_odm_stop(odm, GF_FALSE);
3186 : }
3187 :
3188 0 : i=0;
3189 0 : while ((odm = (GF_ObjectManager*)gf_list_enum(to_restart, &i))) {
3190 0 : gf_odm_start(odm);
3191 : }
3192 0 : gf_list_del(to_restart);
3193 0 : return GF_TRUE;
3194 : }
3195 :
3196 0 : Double gf_scene_adjust_time_for_addon(GF_AddonMedia *addon, Double clock_time, u8 *timestamp_based)
3197 : {
3198 : Double media_time;
3199 0 : if (!addon->timeline_ready)
3200 : return clock_time;
3201 :
3202 0 : if (timestamp_based)
3203 0 : *timestamp_based = (addon->timeline_id>=0) ? 0 : 1;
3204 :
3205 0 : if (addon->is_splicing) {
3206 0 : return ((Double)addon->media_timestamp) / addon->media_timescale;
3207 : }
3208 :
3209 : //get PTS diff (clock is in ms, pt is in 90k)
3210 : media_time = clock_time;
3211 0 : media_time -= addon->media_pts/90000.0;
3212 :
3213 0 : media_time += ((Double)addon->media_timestamp) / addon->media_timescale;
3214 0 : GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("Addon about to start - media time %g\n", media_time));
3215 : return media_time;
3216 : }
3217 :
3218 0 : s64 gf_scene_adjust_timestamp_for_addon(GF_AddonMedia *addon, u64 orig_ts)
3219 : {
3220 : s64 media_ts_ms;
3221 : assert(addon->timeline_ready);
3222 0 : if (addon->is_splicing) {
3223 0 : if (!addon->min_dts_set || (orig_ts<addon->splice_min_dts)) {
3224 0 : addon->splice_min_dts = orig_ts;
3225 0 : addon->min_dts_set = GF_TRUE;
3226 : }
3227 0 : orig_ts -= addon->splice_min_dts;
3228 : }
3229 : media_ts_ms = orig_ts;
3230 0 : media_ts_ms -= (addon->media_timestamp*1000) / addon->media_timescale;
3231 0 : media_ts_ms += (addon->media_pts/90);
3232 0 : return media_ts_ms;
3233 : }
3234 :
3235 0 : void gf_scene_select_scalable_addon(GF_Scene *scene, GF_ObjectManager *odm)
3236 : {
3237 : #ifdef FILTER_FIXME
3238 : Bool nalu_annex_b;
3239 : Bool force_attach=GF_FALSE;
3240 : #endif
3241 : GF_ObjectManager *odm_base = NULL;
3242 : u32 i, count;
3243 :
3244 0 : count = gf_list_count(scene->resources);
3245 0 : for (i=0; i<count; i++) {
3246 0 : odm_base = gf_list_get(scene->resources, i);
3247 0 : if ((odm->type==odm_base->type) && odm_base->ID)
3248 : break;
3249 : odm_base=NULL;
3250 : //todo
3251 : //1- check if we use compatible formats, for now we only do demos with hevc/lhvc
3252 : //2- check dependency IDs if any, for now we only do demos with 2 layers hevc/lhvc
3253 : }
3254 0 : if (!odm_base) return;
3255 :
3256 0 : odm_base->upper_layer_odm = odm;
3257 0 : odm->lower_layer_odm = odm_base;
3258 :
3259 0 : switch (odm_base->original_oti) {
3260 0 : case GF_CODECID_AVC:
3261 : case GF_CODECID_SVC:
3262 : case GF_CODECID_MVC:
3263 0 : switch (odm->original_oti) {
3264 0 : case GF_CODECID_LHVC:
3265 : if (!odm_base->hybrid_layered_coded) {
3266 : #ifdef FILTER_FIXME
3267 : force_attach=GF_TRUE;
3268 : #endif
3269 :
3270 : }
3271 0 : odm_base->hybrid_layered_coded=GF_TRUE;
3272 0 : break;
3273 : }
3274 : break;
3275 : case GF_CODECID_HEVC:
3276 : #ifdef FILTER_FIXME
3277 : force_attach=GF_TRUE;
3278 : #endif
3279 : break;
3280 : }
3281 0 : odm->lower_layer_odm = odm_base;
3282 :
3283 : #ifdef FILTER_FIXME
3284 : GF_NetworkCommand com;
3285 :
3286 : if (odm_base->upper_layer_odm) {
3287 : force_attach=GF_FALSE;
3288 : } else {
3289 : odm_base->upper_layer_odm = odm;
3290 : }
3291 : odm->lower_layer_odm = odm_base;
3292 :
3293 : nalu_annex_b = 1;
3294 : if (base_ch->esd->decoderConfig->decoderSpecificInfo && base_ch->esd->decoderConfig->decoderSpecificInfo->dataLength)
3295 : nalu_annex_b = 0;
3296 :
3297 : if (odm_base->hybrid_layered_coded && ch->esd->decoderConfig->decoderSpecificInfo && ch->esd->decoderConfig->decoderSpecificInfo->dataLength) {
3298 :
3299 : nalu_annex_b = 0;
3300 : if (force_attach) {
3301 : odm_base->codec->decio->AttachStream(odm_base->codec->decio, ch->esd);
3302 : }
3303 : } else if (force_attach) {
3304 : //we force annexB mode, delete avcC/hvcC
3305 : if (nalu_annex_b && ch->esd->decoderConfig->decoderSpecificInfo) {
3306 : gf_odf_desc_del((GF_Descriptor *)ch->esd->decoderConfig->decoderSpecificInfo);
3307 : ch->esd->decoderConfig->decoderSpecificInfo=NULL;
3308 : }
3309 : odm_base->codec->decio->AttachStream(odm_base->codec->decio, ch->esd);
3310 : }
3311 :
3312 : memset(&com, 0, sizeof(GF_NetworkCommand));
3313 : com.command_type = GF_NET_CHAN_NALU_MODE;
3314 : //force AnnexB mode and no sync sample seeking
3315 : com.nalu_mode.extract_mode = nalu_annex_b ? 2 : 0;
3316 : count = gf_list_count(odm->channels);
3317 : for (i=0; i<count; i++) {
3318 : com.base.on_channel = ch = gf_list_get(odm->channels, i);
3319 : //we must wait for RAP otherwise we won't be able to detect temporal scalability correctly
3320 : ch->stream_state = 1;
3321 : ch->media_padding_bytes = base_ch->media_padding_bytes;
3322 : gf_term_service_command(ch->service, &com);
3323 : }
3324 :
3325 : caps.CapCode = GF_CODEC_MEDIA_SWITCH_QUALITY;
3326 : // splicing, signal to the base decoder that we will want low quality and wait for splice activation
3327 : if (odm->parentscene->root_od->addon->is_splicing) {
3328 : caps.cap.valueInt = 0;
3329 : }
3330 : //not splicing, signal to the base decoder that we will want full quality right now
3331 : else {
3332 : caps.cap.valueInt = 2;
3333 : }
3334 : odm_base->codec->decio->SetCapabilities(odm_base->codec->decio, caps);
3335 : #endif
3336 : }
3337 :
3338 :
3339 : GF_EXPORT
3340 13 : void gf_scene_switch_quality(GF_Scene *scene, Bool up)
3341 : {
3342 : u32 i;
3343 : GF_ObjectManager *odm;
3344 : GF_FilterEvent evt;
3345 :
3346 13 : if (!scene) return;
3347 :
3348 13 : GF_FEVT_INIT(evt, GF_FEVT_QUALITY_SWITCH, NULL);
3349 13 : evt.quality_switch.up = up;
3350 :
3351 13 : if (scene->root_od->pid) {
3352 3 : gf_filter_pid_send_event(scene->root_od->pid, &evt);
3353 3 : if (scene->root_od->extra_pids) {
3354 : GF_ODMExtraPid *xpid;
3355 0 : i=0;
3356 0 : while ( (xpid = gf_list_enum(scene->root_od->extra_pids, &i) ) ) {
3357 0 : gf_filter_pid_send_event(xpid->pid, &evt);
3358 : }
3359 : }
3360 : }
3361 :
3362 13 : i=0;
3363 37 : while (NULL != (odm = gf_list_enum(scene->resources, &i))) {
3364 11 : if (odm->pid) {
3365 1 : gf_filter_pid_send_event(odm->pid, &evt);
3366 : }
3367 11 : if (odm->subscene)
3368 10 : gf_scene_switch_quality(odm->subscene, up);
3369 :
3370 : #ifdef FILTER_FIXME
3371 : if (odm->scalable_addon) {
3372 : if (up)
3373 : gf_odm_start(odm, 0);
3374 : else
3375 : gf_odm_stop(odm, GF_FALSE);
3376 : }
3377 : #endif
3378 : }
3379 : }
|