/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 | | |