Coverage Report

Created: 2026-10-08 07:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/scene_manager/scene_manager.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2000-2026
6
 *          All rights reserved
7
 *
8
 *  This file is part of GPAC / Scene Management 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
#include <gpac/scene_manager.h>
27
#include <gpac/constants.h>
28
#include <gpac/media_tools.h>
29
#include <gpac/bifs.h>
30
#include <gpac/xml.h>
31
#include <gpac/internal/scenegraph_dev.h>
32
#include <gpac/network.h>
33
34
35
GF_EXPORT
36
GF_SceneManager *gf_sm_new(GF_SceneGraph *graph)
37
0
{
38
0
  GF_SceneManager *tmp;
39
40
0
  if (!graph) return NULL;
41
0
  GF_SAFEALLOC(tmp, GF_SceneManager);
42
0
  if (!tmp) return NULL;
43
0
  tmp->streams = gf_list_new();
44
0
  tmp->scene_graph = graph;
45
0
  return tmp;
46
0
}
47
48
GF_EXPORT
49
GF_StreamContext *gf_sm_stream_new(GF_SceneManager *ctx, u16 ES_ID, u8 streamType, u32 codec_id)
50
0
{
51
0
  u32 i;
52
0
  GF_StreamContext *tmp;
53
54
0
  i=0;
55
0
  while ((tmp = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) {
56
    /*we MUST use the same ST*/
57
0
    if (tmp->streamType!=streamType) continue;
58
    /*if no ESID/OTI specified this is a base layer (default stream created by parsers)
59
    if ESID/OTI specified this is a stream already setup
60
    */
61
0
    if ( tmp->ESID==ES_ID ) {
62
      //tmp->objectType = objectType;
63
0
      return tmp;
64
0
    }
65
0
  }
66
67
0
  GF_SAFEALLOC(tmp, GF_StreamContext);
68
0
  if (!tmp) return NULL;
69
0
  tmp->AUs = gf_list_new();
70
0
  tmp->ESID = ES_ID;
71
0
  tmp->streamType = streamType;
72
0
  tmp->codec_id = codec_id ? codec_id : 1;
73
0
  tmp->timeScale = 1000;
74
0
  gf_list_add(ctx->streams, tmp);
75
0
  return tmp;
76
0
}
77
78
GF_StreamContext *gf_sm_stream_find(GF_SceneManager *ctx, u16 ES_ID)
79
0
{
80
0
  u32 i, count;
81
0
  if (!ES_ID) return NULL;
82
0
  count = gf_list_count(ctx->streams);
83
0
  for (i=0; i<count; i++) {
84
0
    GF_StreamContext *tmp = (GF_StreamContext *)gf_list_get(ctx->streams, i);
85
0
    if (tmp->ESID==ES_ID) return tmp;
86
0
  }
87
0
  return NULL;
88
0
}
89
90
GF_EXPORT
91
GF_MuxInfo *gf_sm_get_mux_info(GF_ESD *src)
92
0
{
93
0
  u32 i;
94
0
  GF_MuxInfo *mux;
95
0
  i=0;
96
0
  while ((mux = (GF_MuxInfo *)gf_list_enum(src->extensionDescriptors, &i))) {
97
0
    if (mux->tag == GF_ODF_MUXINFO_TAG) return mux;
98
0
  }
99
0
  return NULL;
100
0
}
101
102
103
static void gf_sm_au_del(GF_StreamContext *sc, GF_AUContext *au)
104
0
{
105
0
  while (gf_list_count(au->commands)) {
106
0
    void *comptr = gf_list_last(au->commands);
107
0
    gf_list_rem_last(au->commands);
108
0
    switch (sc->streamType) {
109
0
    case GF_STREAM_OD:
110
0
      gf_odf_com_del((GF_ODCom**) & comptr);
111
0
      break;
112
0
    case GF_STREAM_SCENE:
113
0
      gf_sg_command_del((GF_Command *)comptr);
114
0
      break;
115
0
    }
116
0
  }
117
0
  gf_list_del(au->commands);
118
0
  gf_free(au);
119
0
}
120
121
static void gf_sm_reset_stream(GF_StreamContext *sc)
122
0
{
123
0
  while (gf_list_count(sc->AUs)) {
124
0
    GF_AUContext *au = (GF_AUContext *)gf_list_last(sc->AUs);
125
0
    gf_list_rem_last(sc->AUs);
126
0
    gf_sm_au_del(sc, au);
127
128
0
  }
129
0
}
130
131
static void gf_sm_delete_stream(GF_StreamContext *sc)
132
0
{
133
0
  gf_sm_reset_stream(sc);
134
0
  gf_list_del(sc->AUs);
135
0
  if (sc->name) gf_free(sc->name);
136
0
  if (sc->dec_cfg) gf_free(sc->dec_cfg);
137
0
  gf_free(sc);
138
0
}
139
140
GF_EXPORT
141
void gf_sm_del(GF_SceneManager *ctx)
142
0
{
143
0
  u32 count;
144
0
  while ( (count = gf_list_count(ctx->streams)) ) {
145
0
    GF_StreamContext *sc = (GF_StreamContext *)gf_list_get(ctx->streams, count-1);
146
0
    gf_list_rem(ctx->streams, count-1);
147
0
    gf_sm_delete_stream(sc);
148
0
  }
149
0
  gf_list_del(ctx->streams);
150
0
  if (ctx->root_od) gf_odf_desc_del((GF_Descriptor *) ctx->root_od);
151
0
  gf_free(ctx);
152
0
}
153
154
#if 0 //unused
155
void gf_sm_reset(GF_SceneManager *ctx)
156
{
157
  GF_StreamContext *sc;
158
  u32 i=0;
159
  while ( (sc = gf_list_enum(ctx->streams, &i)) ) {
160
    gf_sm_reset_stream(sc);
161
  }
162
  if (ctx->root_od) gf_odf_desc_del((GF_Descriptor *) ctx->root_od);
163
  ctx->root_od = NULL;
164
}
165
#endif
166
167
GF_EXPORT
168
GF_AUContext *gf_sm_stream_au_new(GF_StreamContext *stream, u64 timing, Double time_sec, Bool isRap)
169
0
{
170
0
  u32 i;
171
0
  GF_AUContext *tmp;
172
0
  u64 tmp_timing;
173
174
0
  tmp_timing = timing ? timing : (u64) (time_sec*1000);
175
0
  if (stream->imp_exp_time >= tmp_timing) {
176
    /*look for existing AU*/
177
0
    i=0;
178
0
    while ((tmp = (GF_AUContext *)gf_list_enum(stream->AUs, &i))) {
179
0
      if (timing && (tmp->timing==timing)) return tmp;
180
0
      else if (time_sec && (tmp->timing_sec == time_sec)) return tmp;
181
0
      else if (!time_sec && !timing && !tmp->timing && !tmp->timing_sec) return tmp;
182
      /*insert AU*/
183
0
      else if ((time_sec && time_sec<tmp->timing_sec) || (timing && timing<tmp->timing)) {
184
0
        tmp = gf_malloc(sizeof(GF_AUContext));
185
0
        if (!tmp) return NULL;
186
0
        memset(tmp, 0, sizeof(GF_AUContext));
187
0
        tmp->commands = gf_list_new();
188
0
        if (isRap) tmp->flags = GF_SM_AU_RAP;
189
0
        tmp->timing = timing;
190
0
        tmp->timing_sec = time_sec;
191
0
        tmp->owner = stream;
192
0
        gf_list_insert(stream->AUs, tmp, i-1);
193
0
        return tmp;
194
0
      }
195
0
    }
196
0
  }
197
0
  GF_SAFEALLOC(tmp, GF_AUContext);
198
0
  if (!tmp) return NULL;
199
0
  tmp->commands = gf_list_new();
200
0
  if (isRap) tmp->flags = GF_SM_AU_RAP;
201
0
  tmp->timing = timing;
202
0
  tmp->timing_sec = time_sec;
203
0
  tmp->owner = stream;
204
0
  if (stream->disable_aggregation) tmp->flags |= GF_SM_AU_NOT_AGGREGATED;
205
0
  gf_list_add(stream->AUs, tmp);
206
0
  stream->imp_exp_time = tmp_timing;
207
0
  return tmp;
208
0
}
209
210
static Bool node_in_commands_subtree(GF_Node *node, GF_List *commands)
211
0
{
212
0
#ifndef GPAC_DISABLE_VRML
213
0
  u32 i, j, count, nb_fields;
214
215
0
  count = gf_list_count(commands);
216
0
  for (i=0; i<count; i++) {
217
0
    GF_Command *com = gf_list_get(commands, i);
218
0
    if (com->tag>=GF_SG_LAST_BIFS_COMMAND) {
219
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[Scene Manager] Command check for LASeR/DIMS not supported\n"));
220
0
      return 0;
221
0
    }
222
0
    if (com->tag==GF_SG_SCENE_REPLACE) {
223
0
      if (gf_node_parent_of(com->node, node)) return 1;
224
0
      continue;
225
0
    }
226
0
    nb_fields = gf_list_count(com->command_fields);
227
0
    for (j=0; j<nb_fields; j++) {
228
0
      GF_CommandField *field = gf_list_get(com->command_fields, j);
229
0
      switch (field->fieldType) {
230
0
      case GF_SG_VRML_SFNODE:
231
0
        if (field->new_node) {
232
0
          if (gf_node_parent_of(field->new_node, node)) return 1;
233
0
        }
234
0
        break;
235
0
      case GF_SG_VRML_MFNODE:
236
0
        if (field->field_ptr) {
237
0
          GF_ChildNodeItem *child;
238
0
          child = field->node_list;
239
0
          while (child) {
240
0
            if (gf_node_parent_of(child->node, node)) return 1;
241
0
            child = child->next;
242
0
          }
243
0
        }
244
0
        break;
245
0
      }
246
0
    }
247
0
  }
248
0
#endif
249
0
  return 0;
250
0
}
251
252
static u32 store_or_aggregate(GF_StreamContext *sc, GF_Command *com, GF_List *commands, Bool *has_modif)
253
0
{
254
0
#ifndef GPAC_DISABLE_VRML
255
0
  u32 i, count, j, nb_fields;
256
0
  GF_CommandField *field, *check_field;
257
258
  /*if our command deals with a node inserted in the commands list, apply command list*/
259
0
  if (node_in_commands_subtree(com->node, commands)) return 0;
260
261
  /*otherwise, check if we can substitute a previous command with this one*/
262
0
  count = gf_list_count(commands);
263
0
  for (i=0; i<count; i++) {
264
0
    GF_Command *check = gf_list_get(commands, i);
265
266
0
    if (sc->streamType == GF_STREAM_SCENE) {
267
0
      Bool check_index=0;
268
0
      Bool original_is_index = 0;
269
0
      Bool apply;
270
0
      switch (com->tag) {
271
0
      case GF_SG_INDEXED_REPLACE:
272
0
        check_index=1;
273
0
      case GF_SG_MULTIPLE_INDEXED_REPLACE:
274
0
      case GF_SG_FIELD_REPLACE:
275
0
      case GF_SG_MULTIPLE_REPLACE:
276
0
        if (check->node != com->node) break;
277
        /*we may aggregate an indexed insertion and a replace one*/
278
0
        if (check_index) {
279
0
          if (check->tag == GF_SG_INDEXED_REPLACE) {}
280
0
          else if (check->tag == GF_SG_INDEXED_INSERT) {
281
0
            original_is_index = 1;
282
0
          }
283
0
          else {
284
0
            break;
285
0
          }
286
0
        } else {
287
0
          if (check->tag != com->tag) break;
288
0
        }
289
0
        nb_fields = gf_list_count(com->command_fields);
290
0
        if (gf_list_count(check->command_fields) != nb_fields) break;
291
0
        apply=1;
292
0
        for (j=0; j<nb_fields; j++) {
293
0
          field = gf_list_get(com->command_fields, j);
294
0
          check_field = gf_list_get(check->command_fields, j);
295
0
          if ((field->pos != check_field->pos) || (field->fieldIndex != check_field->fieldIndex)) {
296
0
            apply=0;
297
0
            break;
298
0
          }
299
0
        }
300
        /*same target node+fields, destroy first command and store new one*/
301
0
        if (apply) {
302
          /*if indexed, change command tag*/
303
0
          if (original_is_index) com->tag = GF_SG_INDEXED_INSERT;
304
305
0
          gf_sg_command_del((GF_Command *)check);
306
0
          gf_list_rem(commands, i);
307
0
          if (has_modif) *has_modif = 1;
308
0
          return 1;
309
0
        }
310
0
        break;
311
312
0
      case GF_SG_NODE_REPLACE:
313
0
        if (check->tag != GF_SG_NODE_REPLACE) {
314
0
          break;
315
0
        }
316
        /*TODO - THIS IS NOT SUPPORTED IN GPAC SINCE WE NEVER ALLOW FOR DUPLICATE NODE IDs IN THE SCENE !!!*/
317
0
        if (gf_node_get_id(check->node) != gf_node_get_id(com->node) ) {
318
0
          break;
319
0
        }
320
        /*same node ID, destroy first command and store new one*/
321
0
        gf_sg_command_del((GF_Command *)check);
322
0
        gf_list_rem(commands, i);
323
0
        if (has_modif) *has_modif = 1;
324
0
        return 1;
325
326
0
      case GF_SG_INDEXED_DELETE:
327
        /*look for an indexed insert before the indexed delete with same target pos and node. If found, discard both commands!*/
328
0
        if (check->tag != GF_SG_INDEXED_INSERT) break;
329
0
        if (com->node != check->node) break;
330
0
        field = gf_list_get(com->command_fields, 0);
331
0
        check_field = gf_list_get(check->command_fields, 0);
332
0
        if (!field || !check_field) break;
333
0
        if (field->pos != check_field->pos) break;
334
0
        if (field->fieldIndex != check_field->fieldIndex) break;
335
336
0
        gf_sg_command_del((GF_Command *)check);
337
0
        gf_list_rem(commands, i);
338
0
        if (has_modif) *has_modif = 1;
339
0
        return 2;
340
341
0
      default:
342
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[Scene Manager] Stream Aggregation not implemented for command - aggregating on main scene\n"));
343
0
        break;
344
0
      }
345
0
    }
346
0
  }
347
  /*the command modifies another stream than associated current carousel stream, we have to store it.*/
348
0
  if (has_modif) *has_modif=1;
349
0
#endif
350
0
  return 1;
351
0
}
352
353
static GF_StreamContext *gf_sm_get_stream(GF_SceneManager *ctx, u16 ESID)
354
0
{
355
0
  u32 i, count;
356
0
  count = gf_list_count(ctx->streams);
357
0
  for (i=0; i<count; i++) {
358
0
    GF_StreamContext *sc = gf_list_get(ctx->streams, i);
359
0
    if (sc->ESID==ESID) return sc;
360
0
  }
361
0
  return NULL;
362
0
}
363
364
GF_EXPORT
365
GF_Err gf_sm_aggregate(GF_SceneManager *ctx, u16 ESID)
366
0
{
367
0
  GF_Err e;
368
0
  u32 i, stream_count;
369
0
#ifndef GPAC_DISABLE_VRML
370
0
  u32 j;
371
0
  GF_AUContext *au;
372
0
  GF_Command *com;
373
0
#endif
374
375
0
  e = GF_OK;
376
377
#if DEBUG_RAP
378
  com_count = 0;
379
  stream_count = gf_list_count(ctx->streams);
380
  for (i=0; i<stream_count; i++) {
381
    GF_StreamContext *sc = (GF_StreamContext *)gf_list_get(ctx->streams, i);
382
    if (sc->streamType == GF_STREAM_SCENE) {
383
      au_count = gf_list_count(sc->AUs);
384
      for (j=0; j<au_count; j++) {
385
        au = (GF_AUContext *)gf_list_get(sc->AUs, j);
386
        com_count += gf_list_count(au->commands);
387
      }
388
    }
389
  }
390
  GF_LOG(GF_LOG_INFO, GF_LOG_SCENE, ("[SceneManager] Making RAP with %d commands\n", com_count));
391
#endif
392
393
0
  stream_count = gf_list_count(ctx->streams);
394
0
  for (i=0; i<stream_count; i++) {
395
0
    GF_AUContext *carousel_au;
396
0
    GF_List *carousel_commands;
397
0
    GF_StreamContext *aggregate_on_stream;
398
0
    GF_StreamContext *sc = (GF_StreamContext *)gf_list_get(ctx->streams, i);
399
0
    if (ESID && (sc->ESID!=ESID)) continue;
400
401
    /*locate the AU in which our commands will be aggregated*/
402
0
    carousel_au = NULL;
403
0
    carousel_commands = NULL;
404
0
    aggregate_on_stream = sc->aggregate_on_esid ? gf_sm_get_stream(ctx, sc->aggregate_on_esid) : NULL;
405
0
    if (aggregate_on_stream==sc) {
406
0
      carousel_commands = gf_list_new();
407
0
    } else if (aggregate_on_stream) {
408
0
      if (!gf_list_count(aggregate_on_stream->AUs)) {
409
0
        carousel_au = gf_sm_stream_au_new(aggregate_on_stream, 0, 0, 1);
410
0
      } else {
411
        /* assert we already performed aggregation */
412
0
        gf_assert(gf_list_count(aggregate_on_stream->AUs)==1);
413
0
        carousel_au = gf_list_get(aggregate_on_stream->AUs, 0);
414
0
      }
415
0
      carousel_commands = carousel_au->commands;
416
0
    }
417
    /*TODO - do this as well for ODs*/
418
0
#ifndef GPAC_DISABLE_VRML
419
0
    if (sc->streamType == GF_STREAM_SCENE) {
420
0
      Bool has_modif = 0;
421
      /*we check for each stream if it is a base stream (SceneReplace ...) - several streams may carry RAPs if inline nodes are used*/
422
0
      Bool base_stream_found = 0;
423
424
      /*in DIMS we use an empty initial AU with no commands to signal the RAP*/
425
0
      if (sc->codec_id == GF_CODECID_DIMS) base_stream_found = 1;
426
427
      /*apply all commands - this will also apply the SceneReplace*/
428
0
      while (gf_list_count(sc->AUs)) {
429
0
        u32 count;
430
0
        au = (GF_AUContext *) gf_list_get(sc->AUs, 0);
431
0
        gf_list_rem(sc->AUs, 0);
432
433
        /*AU not aggregated*/
434
0
        if (au->flags & GF_SM_AU_NOT_AGGREGATED) {
435
0
          gf_sm_au_del(sc, au);
436
0
          continue;
437
0
        }
438
439
0
        count = gf_list_count(au->commands);
440
441
0
        for (j=0; j<count; j++) {
442
0
          u32 store=0;
443
0
          com = gf_list_get(au->commands, j);
444
0
          if (!base_stream_found) {
445
0
            switch (com->tag) {
446
0
            case GF_SG_SCENE_REPLACE:
447
0
            case GF_SG_LSR_NEW_SCENE:
448
0
            case GF_SG_LSR_REFRESH_SCENE:
449
0
              base_stream_found = 1;
450
0
              break;
451
0
            }
452
0
          }
453
454
          /*aggregate the command*/
455
456
          /*if stream doesn't carry a carousel or carries the base carousel (scene replace), always apply the command*/
457
0
          if (base_stream_found || !sc->aggregate_on_esid) {
458
0
            store = 0;
459
0
          }
460
          /*otherwise, check whether the command should be kept in this stream as is, or can be aggregated on this stream*/
461
0
          else {
462
0
            switch (com->tag) {
463
            /*the following commands do not impact a sub-tree (eg do not deal with nodes), we cannot
464
            aggregate them... */
465
0
            case GF_SG_ROUTE_REPLACE:
466
0
            case GF_SG_ROUTE_DELETE:
467
0
            case GF_SG_ROUTE_INSERT:
468
0
            case GF_SG_PROTO_INSERT:
469
0
            case GF_SG_PROTO_DELETE:
470
0
            case GF_SG_PROTO_DELETE_ALL:
471
0
            case GF_SG_GLOBAL_QUANTIZER:
472
0
            case GF_SG_LSR_RESTORE:
473
0
            case GF_SG_LSR_SAVE:
474
0
            case GF_SG_LSR_SEND_EVENT:
475
0
            case GF_SG_LSR_CLEAN:
476
              /*todo check in which category to put these commands*/
477
//            case GF_SG_LSR_ACTIVATE:
478
//            case GF_SG_LSR_DEACTIVATE:
479
0
              store = 1;
480
0
              break;
481
            /*other commands:
482
              !!! we need to know if the target node of the command has been inserted in this stream !!!
483
484
            This is a tedious task, for now we will consider the following cases:
485
              - locate a similar command in the stored list: remove the similar one and aggregate on stream
486
              - by default all AUs are stored if the stream is in aggregate mode - we should fix that by checking insertion points:
487
               if a command apllies on a node that has been inserted in this stream, we can aggregate, otherwise store
488
            */
489
0
            default:
490
              /*check if we can directly store the command*/
491
0
              if (carousel_commands)
492
0
                store = store_or_aggregate(sc, com, carousel_commands, &has_modif);
493
0
              else {
494
0
                gf_assert(0);
495
0
              }
496
0
              break;
497
0
            }
498
0
          }
499
500
0
          switch (store) {
501
          /*command has been merged with a previous command in carousel and needs to be destroyed*/
502
0
          case 2:
503
0
            gf_list_rem(au->commands, j);
504
0
            j--;
505
0
            count--;
506
0
            gf_sg_command_del((GF_Command *)com);
507
0
            break;
508
          /*command shall be moved to carousel without being applied*/
509
0
          case 1:
510
0
            gf_list_insert(carousel_commands, com, 0);
511
0
            gf_list_rem(au->commands, j);
512
0
            j--;
513
0
            count--;
514
0
            break;
515
          /*command can be applied*/
516
0
          default:
517
0
            e = gf_sg_command_apply(ctx->scene_graph, com, 0);
518
0
            break;
519
0
          }
520
0
        }
521
0
        gf_sm_au_del(sc, au);
522
0
      }
523
524
      /*and recreate scene replace*/
525
0
      if (base_stream_found) {
526
0
        au = gf_sm_stream_au_new(sc, 0, 0, 1);
527
528
0
        switch (sc->codec_id) {
529
0
        case GF_CODECID_BIFS:
530
0
        case GF_CODECID_BIFS_V2:
531
0
          com = gf_sg_command_new(ctx->scene_graph, GF_SG_SCENE_REPLACE);
532
0
          break;
533
0
        case GF_CODECID_LASER:
534
0
          com = gf_sg_command_new(ctx->scene_graph, GF_SG_LSR_NEW_SCENE);
535
0
          break;
536
0
        case GF_CODECID_DIMS:
537
        /* We do not create a new command, empty AU is enough in DIMS*/
538
0
        default:
539
0
          com = NULL;
540
0
          break;
541
0
        }
542
543
0
        if (com) {
544
0
          com->node = ctx->scene_graph->RootNode;
545
0
          ctx->scene_graph->RootNode = NULL;
546
0
          gf_list_del(com->new_proto_list);
547
0
          com->new_proto_list = ctx->scene_graph->protos;
548
0
          ctx->scene_graph->protos = NULL;
549
          /*indicate the command is the aggregated scene graph, so that PROTOs and ROUTEs
550
          are taken from the scenegraph when encoding*/
551
0
          com->aggregated = 1;
552
0
          gf_list_add(au->commands, com);
553
0
        }
554
0
      }
555
      /*update carousel flags of the AU*/
556
0
      else if (carousel_commands) {
557
        /*if current stream caries its own carousel*/
558
0
        if (!carousel_au) {
559
0
          carousel_au = gf_sm_stream_au_new(sc, 0, 0, 1);
560
0
          gf_list_del(carousel_au->commands);
561
0
          carousel_au->commands = carousel_commands;
562
0
        }
563
0
        carousel_au->flags |= GF_SM_AU_RAP | GF_SM_AU_CAROUSEL;
564
0
        if (has_modif) carousel_au->flags |= GF_SM_AU_MODIFIED;
565
0
      }
566
0
    }
567
0
#endif
568
0
  }
569
0
  return e;
570
0
}
571
572
#ifndef GPAC_DISABLE_LOADER_BT
573
GF_Err gf_sm_load_init_bt(GF_SceneLoader *load);
574
#endif
575
576
#ifndef GPAC_DISABLE_LOADER_XMT
577
GF_Err gf_sm_load_init_xmt(GF_SceneLoader *load);
578
#endif
579
580
#ifndef GPAC_DISABLE_LOADER_ISOM
581
GF_Err gf_sm_load_init_isom(GF_SceneLoader *load);
582
#endif
583
584
#ifndef GPAC_DISABLE_SVG
585
586
GF_Err gf_sm_load_init_svg(GF_SceneLoader *load);
587
588
#endif
589
590
#ifndef GPAC_DISABLE_SWF_IMPORT
591
GF_Err gf_sm_load_init_swf(GF_SceneLoader *load);
592
#endif
593
594
595
#ifndef GPAC_DISABLE_QTVR
596
597
GF_Err gf_sm_load_init_qt(GF_SceneLoader *load);
598
#endif
599
600
601
602
GF_EXPORT
603
GF_Err gf_sm_load_string(GF_SceneLoader *load, const char *str, Bool do_clean)
604
0
{
605
0
  GF_Err e;
606
0
  if (!load->type) e = GF_BAD_PARAM;
607
0
  else if (load->parse_string) e = load->parse_string(load, str);
608
0
  else {
609
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[Scene Manager] string parsing not supported by loader\n"));
610
0
    e = GF_NOT_SUPPORTED;
611
0
  }
612
0
  return e;
613
0
}
614
615
616
/*initializes the context loader*/
617
GF_EXPORT
618
GF_Err gf_sm_load_init(GF_SceneLoader *load)
619
0
{
620
0
  GF_Err e = GF_NOT_SUPPORTED;
621
0
  char *ext;
622
  /*we need at least a scene graph*/
623
0
  if (!load || (!load->ctx && !load->scene_graph)
624
0
#ifndef GPAC_DISABLE_ISOM
625
0
          || (!load->fileName && !load->isom && !(load->flags & GF_SM_LOAD_FOR_PLAYBACK) )
626
0
#endif
627
0
     ) return GF_BAD_PARAM;
628
629
0
  if (!load->type) {
630
0
#ifndef GPAC_DISABLE_ISOM
631
0
    if (load->isom) {
632
0
      load->type = GF_SM_LOAD_MP4;
633
0
    } else
634
0
#endif
635
0
    {
636
0
      char szExt[50];
637
0
      ext = gf_file_ext_start(load->fileName);
638
0
      if (!ext) return GF_NOT_SUPPORTED;
639
0
      if (strlen(ext) < 2 || strlen(ext) > sizeof(szExt)) {
640
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[Scene Manager] invalid extension %s in file name %s\n", ext, load->fileName));
641
0
        return GF_NOT_SUPPORTED;
642
0
      }
643
0
      gf_strcpy(szExt, &ext[1]);
644
0
      strlwr(szExt);
645
0
      if (strstr(szExt, "bt")) load->type = GF_SM_LOAD_BT;
646
0
      else if (strstr(szExt, "wrl")) load->type = GF_SM_LOAD_VRML;
647
0
      else if (strstr(szExt, "x3dv")) load->type = GF_SM_LOAD_X3DV;
648
0
#ifndef GPAC_DISABLE_LOADER_XMT
649
0
      else if (strstr(szExt, "xmt") || strstr(szExt, "xmta")) load->type = GF_SM_LOAD_XMTA;
650
0
      else if (strstr(szExt, "x3d")) load->type = GF_SM_LOAD_X3D;
651
0
#endif
652
0
      else if (strstr(szExt, "swf")) load->type = GF_SM_LOAD_SWF;
653
0
      else if (strstr(szExt, "mov")) load->type = GF_SM_LOAD_QT;
654
0
      else if (strstr(szExt, "svg")) load->type = GF_SM_LOAD_SVG;
655
0
      else if (strstr(szExt, "xsr")) load->type = GF_SM_LOAD_XSR;
656
0
      else if (strstr(szExt, "xml")) {
657
0
        char *rtype = gf_xml_get_root_type(load->fileName, &e);
658
0
        if (rtype) {
659
0
          if (!strcmp(rtype, "SAFSession")) load->type = GF_SM_LOAD_XSR;
660
0
          else if (!strcmp(rtype, "XMT-A")) load->type = GF_SM_LOAD_XMTA;
661
0
          else if (!strcmp(rtype, "X3D")) load->type = GF_SM_LOAD_X3D;
662
0
          gf_free(rtype);
663
0
        }
664
0
      }
665
0
    }
666
0
  }
667
0
  if (!load->type) return e;
668
669
0
  if (!load->scene_graph) load->scene_graph = load->ctx->scene_graph;
670
671
0
  switch (load->type) {
672
0
#ifndef GPAC_DISABLE_LOADER_BT
673
0
  case GF_SM_LOAD_BT:
674
0
  case GF_SM_LOAD_VRML:
675
0
  case GF_SM_LOAD_X3DV:
676
0
    return gf_sm_load_init_bt(load);
677
0
#endif
678
679
0
#ifndef GPAC_DISABLE_LOADER_XMT
680
0
  case GF_SM_LOAD_XMTA:
681
0
  case GF_SM_LOAD_X3D:
682
0
    return gf_sm_load_init_xmt(load);
683
0
#endif
684
685
0
#ifndef GPAC_DISABLE_SVG
686
0
  case GF_SM_LOAD_SVG:
687
0
  case GF_SM_LOAD_XSR:
688
0
  case GF_SM_LOAD_DIMS:
689
0
    return gf_sm_load_init_svg(load);
690
691
0
#endif
692
693
0
#ifndef GPAC_DISABLE_SWF_IMPORT
694
0
  case GF_SM_LOAD_SWF:
695
0
    return gf_sm_load_init_swf(load);
696
0
#endif
697
698
0
#ifndef GPAC_DISABLE_LOADER_ISOM
699
0
  case GF_SM_LOAD_MP4:
700
0
    return gf_sm_load_init_isom(load);
701
0
#endif
702
703
0
#ifndef GPAC_DISABLE_QTVR
704
0
  case GF_SM_LOAD_QT:
705
0
    return gf_sm_load_init_qt(load);
706
0
#endif
707
0
  default:
708
0
    return GF_NOT_SUPPORTED;
709
0
  }
710
0
  return GF_NOT_SUPPORTED;
711
0
}
712
713
GF_EXPORT
714
void gf_sm_load_done(GF_SceneLoader *load)
715
0
{
716
0
  if (load->done) load->done(load);
717
0
}
718
719
GF_EXPORT
720
GF_Err gf_sm_load_run(GF_SceneLoader *load)
721
0
{
722
0
  if (load->process) return load->process(load);
723
0
  return GF_OK;
724
0
}
725
726
#if 0 //unused
727
GF_Err gf_sm_load_suspend(GF_SceneLoader *load, Bool suspend)
728
{
729
  if (load->suspend) return load->suspend(load, suspend);
730
  return GF_OK;
731
}
732
#endif
733
734
#if !defined(GPAC_DISABLE_LOADER_BT) || !defined(GPAC_DISABLE_LOADER_XMT)
735
#include <gpac/base_coding.h>
736
void gf_sm_update_bitwrapper_buffer(GF_Node *node, const char *fileName)
737
0
{
738
0
  u32 data_size = 0;
739
0
  char *data = NULL;
740
0
  char *buffer;
741
0
  M_BitWrapper *bw = (M_BitWrapper *)node;
742
743
0
  if (!bw->buffer.buffer) return;
744
0
  buffer = bw->buffer.buffer;
745
0
  if (!strnicmp(buffer, "file:", 5) ) {
746
0
    char *url;
747
0
    if (!strnicmp(buffer, "file://", 7)) buffer += 7;
748
0
    else buffer += 5;
749
750
0
    url = gf_url_concatenate(fileName, buffer);
751
0
    if (url) {
752
0
      if (gf_file_load_data(url, (u8 **) &data, &data_size) != GF_OK) {
753
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[Scene Manager] error reading bitwrapper file %s\n", url));
754
0
      }
755
0
      gf_free(url);
756
0
    }
757
0
  } else {
758
0
    Bool base_64 = 0;
759
0
    if (!strnicmp(buffer, "data:application/octet-string", 29)) {
760
0
      char *sep = strchr(bw->buffer.buffer, ',');
761
0
      base_64 = strstr(bw->buffer.buffer, ";base64") ? 1 : 0;
762
0
      if (sep) buffer = sep+1;
763
0
    }
764
765
0
    if (base_64) {
766
0
      data_size = 2 * (u32) strlen(buffer);
767
0
      data = (char*)gf_malloc(sizeof(char)*data_size);
768
0
      if (data)
769
0
        data_size = gf_base64_decode(buffer, (u32) strlen(buffer), data, data_size);
770
0
    } else {
771
0
      u32 i, c;
772
0
      if (!strnicmp(buffer, "0x", 2)) buffer += 2;
773
0
      data_size = (u32) strlen(buffer) / 2;
774
0
      data = (char*)gf_malloc(sizeof(char) * data_size);
775
0
      if (data) {
776
0
        char s[3];
777
0
        s[2] = 0;
778
0
        for (i=0; i<data_size; i++) {
779
0
          s[0] = buffer[2*i];
780
0
          s[1] = buffer[2*i+1];
781
0
          sscanf(s, "%02X", &c);
782
0
          data[i] = (unsigned char) c;
783
0
        }
784
0
      }
785
0
    }
786
0
  }
787
0
  gf_free(bw->buffer.buffer);
788
0
  bw->buffer.buffer = NULL;
789
0
  bw->buffer_len = 0;
790
0
  if (data) {
791
0
    bw->buffer.buffer = data;
792
0
    bw->buffer_len = data_size;
793
0
  }
794
795
0
}
796
#endif //!defined(GPAC_DISABLE_LOADER_BT) || !defined(GPAC_DISABLE_LOADER_XMT)
797
798