Coverage Report

Created: 2025-12-05 07:06

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