Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2012
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 490 : GF_SceneManager *gf_sm_new(GF_SceneGraph *graph)
37 : {
38 : GF_SceneManager *tmp;
39 :
40 490 : if (!graph) return NULL;
41 490 : GF_SAFEALLOC(tmp, GF_SceneManager);
42 490 : if (!tmp) return NULL;
43 490 : tmp->streams = gf_list_new();
44 490 : tmp->scene_graph = graph;
45 490 : return tmp;
46 : }
47 :
48 : GF_EXPORT
49 1027 : GF_StreamContext *gf_sm_stream_new(GF_SceneManager *ctx, u16 ES_ID, u8 streamType, u32 codec_id)
50 : {
51 : u32 i;
52 : GF_StreamContext *tmp;
53 :
54 1027 : i=0;
55 2603 : while ((tmp = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) {
56 : /*we MUST use the same ST*/
57 892 : 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 397 : if ( tmp->ESID==ES_ID ) {
62 : //tmp->objectType = objectType;
63 : return tmp;
64 : }
65 : }
66 :
67 684 : GF_SAFEALLOC(tmp, GF_StreamContext);
68 684 : if (!tmp) return NULL;
69 684 : tmp->AUs = gf_list_new();
70 684 : tmp->ESID = ES_ID;
71 684 : tmp->streamType = streamType;
72 684 : tmp->codec_id = codec_id ? codec_id : 1;
73 684 : tmp->timeScale = 1000;
74 684 : gf_list_add(ctx->streams, tmp);
75 684 : return tmp;
76 : }
77 :
78 180 : GF_StreamContext *gf_sm_stream_find(GF_SceneManager *ctx, u16 ES_ID)
79 : {
80 : u32 i, count;
81 180 : if (!ES_ID) return NULL;
82 180 : count = gf_list_count(ctx->streams);
83 360 : for (i=0; i<count; i++) {
84 360 : GF_StreamContext *tmp = (GF_StreamContext *)gf_list_get(ctx->streams, i);
85 360 : if (tmp->ESID==ES_ID) return tmp;
86 : }
87 : return NULL;
88 : }
89 :
90 : GF_EXPORT
91 866 : GF_MuxInfo *gf_sm_get_mux_info(GF_ESD *src)
92 : {
93 : u32 i;
94 : GF_MuxInfo *mux;
95 866 : i=0;
96 1738 : while ((mux = (GF_MuxInfo *)gf_list_enum(src->extensionDescriptors, &i))) {
97 158 : if (mux->tag == GF_ODF_MUXINFO_TAG) return mux;
98 : }
99 : return NULL;
100 : }
101 :
102 :
103 3361 : static void gf_sm_au_del(GF_StreamContext *sc, GF_AUContext *au)
104 : {
105 8827 : while (gf_list_count(au->commands)) {
106 5466 : void *comptr = gf_list_last(au->commands);
107 5466 : gf_list_rem_last(au->commands);
108 5466 : switch (sc->streamType) {
109 126 : case GF_STREAM_OD:
110 126 : gf_odf_com_del((GF_ODCom**) & comptr);
111 : break;
112 5340 : case GF_STREAM_SCENE:
113 5340 : gf_sg_command_del((GF_Command *)comptr);
114 : break;
115 : }
116 : }
117 3361 : gf_list_del(au->commands);
118 3361 : gf_free(au);
119 3361 : }
120 :
121 684 : static void gf_sm_reset_stream(GF_StreamContext *sc)
122 : {
123 4723 : while (gf_list_count(sc->AUs)) {
124 3355 : GF_AUContext *au = (GF_AUContext *)gf_list_last(sc->AUs);
125 3355 : gf_list_rem_last(sc->AUs);
126 3355 : gf_sm_au_del(sc, au);
127 :
128 : }
129 684 : }
130 :
131 684 : static void gf_sm_delete_stream(GF_StreamContext *sc)
132 : {
133 684 : gf_sm_reset_stream(sc);
134 684 : gf_list_del(sc->AUs);
135 684 : if (sc->name) gf_free(sc->name);
136 684 : if (sc->dec_cfg) gf_free(sc->dec_cfg);
137 684 : gf_free(sc);
138 684 : }
139 :
140 : GF_EXPORT
141 490 : void gf_sm_del(GF_SceneManager *ctx)
142 : {
143 : u32 count;
144 1664 : while ( (count = gf_list_count(ctx->streams)) ) {
145 684 : GF_StreamContext *sc = (GF_StreamContext *)gf_list_get(ctx->streams, count-1);
146 684 : gf_list_rem(ctx->streams, count-1);
147 684 : gf_sm_delete_stream(sc);
148 : }
149 490 : gf_list_del(ctx->streams);
150 490 : if (ctx->root_od) gf_odf_desc_del((GF_Descriptor *) ctx->root_od);
151 490 : gf_free(ctx);
152 490 : }
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 4044 : GF_AUContext *gf_sm_stream_au_new(GF_StreamContext *stream, u64 timing, Double time_sec, Bool isRap)
169 : {
170 : u32 i;
171 : GF_AUContext *tmp;
172 : u64 tmp_timing;
173 :
174 4044 : tmp_timing = timing ? timing : (u64) (time_sec*1000);
175 4044 : if (stream->imp_exp_time >= tmp_timing) {
176 : /*look for existing AU*/
177 724 : i=0;
178 2033 : while ((tmp = (GF_AUContext *)gf_list_enum(stream->AUs, &i))) {
179 684 : if (timing && (tmp->timing==timing)) return tmp;
180 684 : else if (time_sec && (tmp->timing_sec == time_sec)) return tmp;
181 599 : else if (!time_sec && !timing && !tmp->timing && !tmp->timing_sec) return tmp;
182 : /*insert AU*/
183 585 : 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 : 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 : }
195 : }
196 : }
197 3945 : GF_SAFEALLOC(tmp, GF_AUContext);
198 3945 : if (!tmp) return NULL;
199 3945 : tmp->commands = gf_list_new();
200 3945 : if (isRap) tmp->flags = GF_SM_AU_RAP;
201 3945 : tmp->timing = timing;
202 3945 : tmp->timing_sec = time_sec;
203 3945 : tmp->owner = stream;
204 3945 : if (stream->disable_aggregation) tmp->flags |= GF_SM_AU_NOT_AGGREGATED;
205 3945 : gf_list_add(stream->AUs, tmp);
206 3945 : stream->imp_exp_time = tmp_timing;
207 3945 : return tmp;
208 : }
209 :
210 3 : static Bool node_in_commands_subtree(GF_Node *node, GF_List *commands)
211 : {
212 : #ifndef GPAC_DISABLE_VRML
213 : u32 i, j, count, nb_fields;
214 :
215 3 : count = gf_list_count(commands);
216 4 : for (i=0; i<count; i++) {
217 1 : GF_Command *com = gf_list_get(commands, i);
218 1 : 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 : return 0;
221 : }
222 1 : if (com->tag==GF_SG_SCENE_REPLACE) {
223 0 : if (gf_node_parent_of(com->node, node)) return 1;
224 0 : continue;
225 : }
226 1 : nb_fields = gf_list_count(com->command_fields);
227 2 : for (j=0; j<nb_fields; j++) {
228 1 : GF_CommandField *field = gf_list_get(com->command_fields, j);
229 1 : 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 : }
234 : break;
235 0 : case GF_SG_VRML_MFNODE:
236 0 : if (field->field_ptr) {
237 : 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 : }
243 : }
244 : break;
245 : }
246 : }
247 : }
248 : #endif
249 : return 0;
250 : }
251 :
252 3 : static u32 store_or_aggregate(GF_StreamContext *sc, GF_Command *com, GF_List *commands, Bool *has_modif)
253 : {
254 : #ifndef GPAC_DISABLE_VRML
255 : u32 i, count, j, nb_fields;
256 : GF_CommandField *field, *check_field;
257 :
258 : /*if our command deals with a node inserted in the commands list, apply command list*/
259 3 : 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 3 : count = gf_list_count(commands);
263 0 : for (i=0; i<count; i++) {
264 1 : GF_Command *check = gf_list_get(commands, i);
265 :
266 1 : if (sc->streamType == GF_STREAM_SCENE) {
267 : Bool check_index=0;
268 : Bool original_is_index = 0;
269 : Bool apply;
270 1 : switch (com->tag) {
271 0 : case GF_SG_INDEXED_REPLACE:
272 : check_index=1;
273 1 : case GF_SG_MULTIPLE_INDEXED_REPLACE:
274 : case GF_SG_FIELD_REPLACE:
275 : case GF_SG_MULTIPLE_REPLACE:
276 1 : if (check->node != com->node) break;
277 : /*we may aggregate an indexed insertion and a replace one*/
278 1 : if (check_index) {
279 0 : if (check->tag == GF_SG_INDEXED_REPLACE) {}
280 0 : else if (check->tag == GF_SG_INDEXED_INSERT) {
281 : original_is_index = 1;
282 : }
283 : else {
284 : break;
285 : }
286 : } else {
287 1 : if (check->tag != com->tag) break;
288 : }
289 1 : nb_fields = gf_list_count(com->command_fields);
290 1 : if (gf_list_count(check->command_fields) != nb_fields) break;
291 : apply=1;
292 1 : for (j=0; j<nb_fields; j++) {
293 1 : field = gf_list_get(com->command_fields, j);
294 1 : check_field = gf_list_get(check->command_fields, j);
295 1 : if ((field->pos != check_field->pos) || (field->fieldIndex != check_field->fieldIndex)) {
296 : apply=0;
297 : break;
298 : }
299 : }
300 : /*same target node+fields, destroy first command and store new one*/
301 1 : if (apply) {
302 : /*if indexed, change command tag*/
303 1 : if (original_is_index) com->tag = GF_SG_INDEXED_INSERT;
304 :
305 1 : gf_sg_command_del((GF_Command *)check);
306 1 : gf_list_rem(commands, i);
307 1 : if (has_modif) *has_modif = 1;
308 : return 1;
309 : }
310 : break;
311 :
312 0 : case GF_SG_NODE_REPLACE:
313 0 : if (check->tag != GF_SG_NODE_REPLACE) {
314 : break;
315 : }
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 : break;
319 : }
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 : 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 : 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 : break;
344 : }
345 : }
346 : }
347 : /*the command modifies another stream than associated current carousel stream, we have to store it.*/
348 2 : if (has_modif) *has_modif=1;
349 : #endif
350 : return 1;
351 : }
352 :
353 2 : static GF_StreamContext *gf_sm_get_stream(GF_SceneManager *ctx, u16 ESID)
354 : {
355 : u32 i, count;
356 2 : count = gf_list_count(ctx->streams);
357 6 : for (i=0; i<count; i++) {
358 6 : GF_StreamContext *sc = gf_list_get(ctx->streams, i);
359 6 : if (sc->ESID==ESID) return sc;
360 : }
361 : return NULL;
362 : }
363 :
364 : GF_EXPORT
365 2 : GF_Err gf_sm_aggregate(GF_SceneManager *ctx, u16 ESID)
366 : {
367 : GF_Err e;
368 : u32 i, stream_count;
369 : #ifndef GPAC_DISABLE_VRML
370 : u32 j;
371 : GF_AUContext *au;
372 : GF_Command *com;
373 : #endif
374 :
375 : 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 2 : stream_count = gf_list_count(ctx->streams);
394 8 : for (i=0; i<stream_count; i++) {
395 : GF_AUContext *carousel_au;
396 : GF_List *carousel_commands;
397 : GF_StreamContext *aggregate_on_stream;
398 6 : GF_StreamContext *sc = (GF_StreamContext *)gf_list_get(ctx->streams, i);
399 6 : if (ESID && (sc->ESID!=ESID)) continue;
400 :
401 : /*locate the AU in which our commands will be aggregated*/
402 : carousel_au = NULL;
403 : carousel_commands = NULL;
404 6 : aggregate_on_stream = sc->aggregate_on_esid ? gf_sm_get_stream(ctx, sc->aggregate_on_esid) : NULL;
405 6 : if (aggregate_on_stream==sc) {
406 2 : carousel_commands = gf_list_new();
407 4 : } 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 : } else {
411 : /* assert we already performed aggregation */
412 : assert(gf_list_count(aggregate_on_stream->AUs)==1);
413 0 : carousel_au = gf_list_get(aggregate_on_stream->AUs, 0);
414 : }
415 0 : carousel_commands = carousel_au->commands;
416 : }
417 : /*TODO - do this as well for ODs*/
418 : #ifndef GPAC_DISABLE_VRML
419 6 : if (sc->streamType == GF_STREAM_SCENE) {
420 4 : 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 : Bool base_stream_found = 0;
423 :
424 : /*in DIMS we use an empty initial AU with no commands to signal the RAP*/
425 4 : if (sc->codec_id == GF_CODECID_DIMS) base_stream_found = 1;
426 :
427 : /*apply all commands - this will also apply the SceneReplace*/
428 10 : while (gf_list_count(sc->AUs)) {
429 : u32 count;
430 6 : au = (GF_AUContext *) gf_list_get(sc->AUs, 0);
431 6 : gf_list_rem(sc->AUs, 0);
432 :
433 : /*AU not aggregated*/
434 6 : if (au->flags & GF_SM_AU_NOT_AGGREGATED) {
435 0 : gf_sm_au_del(sc, au);
436 0 : continue;
437 : }
438 :
439 6 : count = gf_list_count(au->commands);
440 :
441 15 : for (j=0; j<count; j++) {
442 : u32 store=0;
443 9 : com = gf_list_get(au->commands, j);
444 9 : if (!base_stream_found) {
445 5 : switch (com->tag) {
446 : case GF_SG_SCENE_REPLACE:
447 : case GF_SG_LSR_NEW_SCENE:
448 : case GF_SG_LSR_REFRESH_SCENE:
449 : base_stream_found = 1;
450 : break;
451 : }
452 : }
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 7 : if (base_stream_found || !sc->aggregate_on_esid) {
458 : store = 0;
459 : }
460 : /*otherwise, check wether the command should be kept in this stream as is, or can be aggregated on this stream*/
461 : else {
462 3 : 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 : case GF_SG_ROUTE_REPLACE:
466 : case GF_SG_ROUTE_DELETE:
467 : case GF_SG_ROUTE_INSERT:
468 : case GF_SG_PROTO_INSERT:
469 : case GF_SG_PROTO_DELETE:
470 : case GF_SG_PROTO_DELETE_ALL:
471 : case GF_SG_GLOBAL_QUANTIZER:
472 : case GF_SG_LSR_RESTORE:
473 : case GF_SG_LSR_SAVE:
474 : case GF_SG_LSR_SEND_EVENT:
475 : 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 : store = 1;
480 : 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 3 : default:
490 : /*check if we can directly store the command*/
491 : assert(carousel_commands);
492 3 : store = store_or_aggregate(sc, com, carousel_commands, &has_modif);
493 3 : break;
494 : }
495 : }
496 :
497 5 : switch (store) {
498 : /*command has been merged with a previous command in carousel and needs to be destroyed*/
499 0 : case 2:
500 0 : gf_list_rem(au->commands, j);
501 0 : j--;
502 0 : count--;
503 0 : gf_sg_command_del((GF_Command *)com);
504 0 : break;
505 : /*command shall be moved to carousel without being applied*/
506 3 : case 1:
507 3 : gf_list_insert(carousel_commands, com, 0);
508 3 : gf_list_rem(au->commands, j);
509 3 : j--;
510 3 : count--;
511 3 : break;
512 : /*command can be applied*/
513 6 : default:
514 6 : e = gf_sg_command_apply(ctx->scene_graph, com, 0);
515 6 : break;
516 : }
517 : }
518 6 : gf_sm_au_del(sc, au);
519 : }
520 :
521 : /*and recreate scene replace*/
522 4 : if (base_stream_found) {
523 2 : au = gf_sm_stream_au_new(sc, 0, 0, 1);
524 :
525 2 : switch (sc->codec_id) {
526 2 : case GF_CODECID_BIFS:
527 : case GF_CODECID_BIFS_V2:
528 2 : com = gf_sg_command_new(ctx->scene_graph, GF_SG_SCENE_REPLACE);
529 2 : break;
530 0 : case GF_CODECID_LASER:
531 0 : com = gf_sg_command_new(ctx->scene_graph, GF_SG_LSR_NEW_SCENE);
532 0 : break;
533 : case GF_CODECID_DIMS:
534 : /* We do not create a new command, empty AU is enough in DIMS*/
535 : default:
536 : com = NULL;
537 : break;
538 : }
539 :
540 2 : if (com) {
541 2 : com->node = ctx->scene_graph->RootNode;
542 2 : ctx->scene_graph->RootNode = NULL;
543 2 : gf_list_del(com->new_proto_list);
544 2 : com->new_proto_list = ctx->scene_graph->protos;
545 2 : ctx->scene_graph->protos = NULL;
546 : /*indicate the command is the aggregated scene graph, so that PROTOs and ROUTEs
547 : are taken from the scenegraph when encoding*/
548 2 : com->aggregated = 1;
549 2 : gf_list_add(au->commands, com);
550 : }
551 : }
552 : /*update carousel flags of the AU*/
553 2 : else if (carousel_commands) {
554 : /*if current stream caries its own carousel*/
555 2 : if (!carousel_au) {
556 2 : carousel_au = gf_sm_stream_au_new(sc, 0, 0, 1);
557 2 : gf_list_del(carousel_au->commands);
558 2 : carousel_au->commands = carousel_commands;
559 : }
560 2 : carousel_au->flags |= GF_SM_AU_RAP | GF_SM_AU_CAROUSEL;
561 2 : if (has_modif) carousel_au->flags |= GF_SM_AU_MODIFIED;
562 : }
563 : }
564 : #endif
565 : }
566 2 : return e;
567 : }
568 :
569 : #ifndef GPAC_DISABLE_LOADER_BT
570 : GF_Err gf_sm_load_init_bt(GF_SceneLoader *load);
571 : #endif
572 :
573 : #ifndef GPAC_DISABLE_LOADER_XMT
574 : GF_Err gf_sm_load_init_xmt(GF_SceneLoader *load);
575 : #endif
576 :
577 : #ifndef GPAC_DISABLE_LOADER_ISOM
578 : GF_Err gf_sm_load_init_isom(GF_SceneLoader *load);
579 : #endif
580 :
581 : #ifndef GPAC_DISABLE_SVG
582 :
583 : GF_Err gf_sm_load_init_svg(GF_SceneLoader *load);
584 :
585 : #endif
586 :
587 : #ifndef GPAC_DISABLE_SWF_IMPORT
588 : GF_Err gf_sm_load_init_swf(GF_SceneLoader *load);
589 : #endif
590 :
591 :
592 : #ifndef GPAC_DISABLE_QTVR
593 :
594 : GF_Err gf_sm_load_init_qt(GF_SceneLoader *load);
595 : #endif
596 :
597 :
598 :
599 : GF_EXPORT
600 3 : GF_Err gf_sm_load_string(GF_SceneLoader *load, const char *str, Bool do_clean)
601 : {
602 : GF_Err e;
603 3 : if (!load->type) e = GF_BAD_PARAM;
604 3 : else if (load->parse_string) e = load->parse_string(load, str);
605 : else {
606 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[Scene Manager] string parsing not supported by loader\n"));
607 : e = GF_NOT_SUPPORTED;
608 : }
609 3 : return e;
610 : }
611 :
612 :
613 : /*initializes the context loader*/
614 : GF_EXPORT
615 540 : GF_Err gf_sm_load_init(GF_SceneLoader *load)
616 : {
617 540 : GF_Err e = GF_NOT_SUPPORTED;
618 : char *ext;
619 : /*we need at least a scene graph*/
620 540 : if (!load || (!load->ctx && !load->scene_graph)
621 : #ifndef GPAC_DISABLE_ISOM
622 540 : || (!load->fileName && !load->isom && !(load->flags & GF_SM_LOAD_FOR_PLAYBACK) )
623 : #endif
624 : ) return GF_BAD_PARAM;
625 :
626 540 : if (!load->type) {
627 : #ifndef GPAC_DISABLE_ISOM
628 80 : if (load->isom) {
629 18 : load->type = GF_SM_LOAD_MP4;
630 : } else
631 : #endif
632 : {
633 : char szExt[50];
634 62 : ext = gf_file_ext_start(load->fileName);
635 62 : if (!ext) return GF_NOT_SUPPORTED;
636 62 : if (strlen(ext) < 2 || strlen(ext) > sizeof(szExt)) {
637 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[Scene Manager] invalid extension %s in file name %s\n", ext, load->fileName));
638 : return GF_NOT_SUPPORTED;
639 : }
640 62 : strcpy(szExt, &ext[1]);
641 62 : strlwr(szExt);
642 62 : if (strstr(szExt, "bt")) load->type = GF_SM_LOAD_BT;
643 35 : else if (strstr(szExt, "wrl")) load->type = GF_SM_LOAD_VRML;
644 35 : else if (strstr(szExt, "x3dv")) load->type = GF_SM_LOAD_X3DV;
645 : #ifndef GPAC_DISABLE_LOADER_XMT
646 35 : else if (strstr(szExt, "xmt") || strstr(szExt, "xmta")) load->type = GF_SM_LOAD_XMTA;
647 31 : else if (strstr(szExt, "x3d")) load->type = GF_SM_LOAD_X3D;
648 : #endif
649 31 : else if (strstr(szExt, "swf")) load->type = GF_SM_LOAD_SWF;
650 23 : else if (strstr(szExt, "mov")) load->type = GF_SM_LOAD_QT;
651 20 : else if (strstr(szExt, "svg")) load->type = GF_SM_LOAD_SVG;
652 2 : else if (strstr(szExt, "xsr")) load->type = GF_SM_LOAD_XSR;
653 2 : else if (strstr(szExt, "xml")) {
654 2 : char *rtype = gf_xml_get_root_type(load->fileName, &e);
655 2 : if (rtype) {
656 2 : if (!strcmp(rtype, "SAFSession")) load->type = GF_SM_LOAD_XSR;
657 0 : else if (!strcmp(rtype, "XMT-A")) load->type = GF_SM_LOAD_XMTA;
658 0 : else if (!strcmp(rtype, "X3D")) load->type = GF_SM_LOAD_X3D;
659 2 : gf_free(rtype);
660 : }
661 : }
662 : }
663 : }
664 540 : if (!load->type) return e;
665 :
666 540 : if (!load->scene_graph) load->scene_graph = load->ctx->scene_graph;
667 :
668 540 : switch (load->type) {
669 : #ifndef GPAC_DISABLE_LOADER_BT
670 437 : case GF_SM_LOAD_BT:
671 : case GF_SM_LOAD_VRML:
672 : case GF_SM_LOAD_X3DV:
673 437 : return gf_sm_load_init_bt(load);
674 : #endif
675 :
676 : #ifndef GPAC_DISABLE_LOADER_XMT
677 5 : case GF_SM_LOAD_XMTA:
678 : case GF_SM_LOAD_X3D:
679 5 : return gf_sm_load_init_xmt(load);
680 : #endif
681 :
682 : #ifndef GPAC_DISABLE_SVG
683 69 : case GF_SM_LOAD_SVG:
684 : case GF_SM_LOAD_XSR:
685 : case GF_SM_LOAD_DIMS:
686 69 : return gf_sm_load_init_svg(load);
687 :
688 : #endif
689 :
690 : #ifndef GPAC_DISABLE_SWF_IMPORT
691 8 : case GF_SM_LOAD_SWF:
692 8 : return gf_sm_load_init_swf(load);
693 : #endif
694 :
695 : #ifndef GPAC_DISABLE_LOADER_ISOM
696 18 : case GF_SM_LOAD_MP4:
697 18 : return gf_sm_load_init_isom(load);
698 : #endif
699 :
700 : #ifndef GPAC_DISABLE_QTVR
701 3 : case GF_SM_LOAD_QT:
702 3 : return gf_sm_load_init_qt(load);
703 : #endif
704 : default:
705 : return GF_NOT_SUPPORTED;
706 : }
707 : return GF_NOT_SUPPORTED;
708 : }
709 :
710 : GF_EXPORT
711 996 : void gf_sm_load_done(GF_SceneLoader *load)
712 : {
713 996 : if (load->done) load->done(load);
714 996 : }
715 :
716 : GF_EXPORT
717 537 : GF_Err gf_sm_load_run(GF_SceneLoader *load)
718 : {
719 537 : if (load->process) return load->process(load);
720 : return GF_OK;
721 : }
722 :
723 : #if 0 //unused
724 : GF_Err gf_sm_load_suspend(GF_SceneLoader *load, Bool suspend)
725 : {
726 : if (load->suspend) return load->suspend(load, suspend);
727 : return GF_OK;
728 : }
729 : #endif
730 :
731 : #if !defined(GPAC_DISABLE_LOADER_BT) || !defined(GPAC_DISABLE_LOADER_XMT)
732 : #include <gpac/base_coding.h>
733 15 : void gf_sm_update_bitwrapper_buffer(GF_Node *node, const char *fileName)
734 : {
735 15 : u32 data_size = 0;
736 15 : char *data = NULL;
737 : char *buffer;
738 : M_BitWrapper *bw = (M_BitWrapper *)node;
739 :
740 15 : if (!bw->buffer.buffer) return;
741 : buffer = bw->buffer.buffer;
742 15 : if (!strnicmp(buffer, "file:", 5) ) {
743 : char *url;
744 5 : if (!strnicmp(buffer, "file://", 7)) buffer += 7;
745 0 : else buffer += 5;
746 :
747 5 : url = gf_url_concatenate(fileName, buffer);
748 5 : if (url) {
749 5 : if (gf_file_load_data(url, (u8 **) &data, &data_size) != GF_OK) {
750 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[Scene Manager] error reading bitwrapper file %s\n", url));
751 : }
752 5 : gf_free(url);
753 : }
754 : } else {
755 : Bool base_64 = 0;
756 10 : if (!strnicmp(buffer, "data:application/octet-string", 29)) {
757 5 : char *sep = strchr(bw->buffer.buffer, ',');
758 5 : base_64 = strstr(bw->buffer.buffer, ";base64") ? 1 : 0;
759 5 : if (sep) buffer = sep+1;
760 : }
761 :
762 5 : if (base_64) {
763 5 : data_size = 2 * (u32) strlen(buffer);
764 5 : data = (char*)gf_malloc(sizeof(char)*data_size);
765 5 : if (data)
766 5 : data_size = gf_base64_decode(buffer, (u32) strlen(buffer), data, data_size);
767 : } else {
768 : u32 i, c;
769 5 : if (!strnicmp(buffer, "0x", 2)) buffer += 2;
770 5 : data_size = (u32) strlen(buffer) / 2;
771 5 : data = (char*)gf_malloc(sizeof(char) * data_size);
772 5 : if (data) {
773 : char s[3];
774 5 : s[2] = 0;
775 20 : for (i=0; i<data_size; i++) {
776 15 : s[0] = buffer[2*i];
777 15 : s[1] = buffer[2*i+1];
778 15 : sscanf(s, "%02X", &c);
779 15 : data[i] = (unsigned char) c;
780 : }
781 : }
782 : }
783 : }
784 15 : gf_free(bw->buffer.buffer);
785 15 : bw->buffer.buffer = NULL;
786 15 : bw->buffer_len = 0;
787 15 : if (data) {
788 15 : bw->buffer.buffer = data;
789 15 : bw->buffer_len = data_size;
790 : }
791 :
792 : }
793 : #endif //!defined(GPAC_DISABLE_LOADER_BT) || !defined(GPAC_DISABLE_LOADER_XMT)
794 :
795 :
|