Line data Source code
1 : /*
2 : * GPAC Multimedia Framework
3 : *
4 : * Authors: Jean Le Feuvre, Cyril Concolato
5 : * Copyright (c) Telecom ParisTech 2000-2012
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / ISO Media File Format 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_engine.h>
27 :
28 : #ifndef GPAC_DISABLE_SENG
29 : #include <gpac/scene_manager.h>
30 :
31 : #ifndef GPAC_DISABLE_BIFS_ENC
32 : #include <gpac/bifs.h>
33 : #endif
34 :
35 : #ifndef GPAC_DISABLE_VRML
36 : #include <gpac/nodes_mpeg4.h>
37 : #endif
38 :
39 : #ifndef GPAC_DISABLE_LASER
40 : #include <gpac/laser.h>
41 : #endif
42 :
43 : #include <gpac/constants.h>
44 : #include <gpac/base_coding.h>
45 :
46 :
47 : struct __tag_scene_engine
48 : {
49 : GF_SceneGraph *sg;
50 : GF_SceneManager *ctx;
51 : GF_SceneLoader loader;
52 : void *calling_object;
53 : Bool owns_context;
54 : #ifndef GPAC_DISABLE_BIFS_ENC
55 : GF_BifsEncoder *bifsenc;
56 : #endif
57 : #ifndef GPAC_DISABLE_LASER
58 : GF_LASeRCodec *lsrenc;
59 : #endif
60 :
61 : u32 start_time;
62 :
63 : char *dump_path;
64 :
65 : Bool embed_resources;
66 : Bool dump_rap;
67 : Bool first_dims_sent;
68 : };
69 :
70 : #ifndef GPAC_DISABLE_BIFS_ENC
71 1 : static GF_Err gf_sm_setup_bifsenc(GF_SceneEngine *seng, GF_StreamContext *sc, GF_ESD *esd)
72 : {
73 : u8 *data;
74 : u32 data_len;
75 : u32 nbb;
76 : Bool encode_names, delete_bcfg;
77 : GF_BIFSConfig *bcfg;
78 :
79 1 : if (!esd->decoderConfig || (esd->decoderConfig->streamType != GF_STREAM_SCENE)) return GF_BAD_PARAM;
80 :
81 1 : if (!seng->bifsenc)
82 1 : seng->bifsenc = gf_bifs_encoder_new(seng->ctx->scene_graph);
83 :
84 : delete_bcfg = 0;
85 : /*inputctx is not properly setup, do it*/
86 1 : if (!esd->decoderConfig->decoderSpecificInfo) {
87 0 : bcfg = (GF_BIFSConfig*)gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG);
88 0 : bcfg->pixelMetrics = seng->ctx->is_pixel_metrics;
89 0 : bcfg->pixelWidth = seng->ctx->scene_width;
90 0 : bcfg->pixelHeight = seng->ctx->scene_height;
91 : delete_bcfg = 1;
92 : }
93 : /*regular case*/
94 1 : else if (esd->decoderConfig->decoderSpecificInfo->tag == GF_ODF_BIFS_CFG_TAG) {
95 : bcfg = (GF_BIFSConfig *)esd->decoderConfig->decoderSpecificInfo;
96 : }
97 : /*happens when loading from MP4 in which case BIFSc is not decoded*/
98 : else {
99 0 : bcfg = gf_odf_get_bifs_config(esd->decoderConfig->decoderSpecificInfo, esd->decoderConfig->objectTypeIndication);
100 : delete_bcfg = 1;
101 : }
102 :
103 : /*NO CHANGE TO BIFSC otherwise the generated update will not match the input context
104 : The only case we modify the bifs config is when XXXBits is not specified*/
105 1 : nbb = gf_get_bit_size(seng->ctx->max_node_id);
106 1 : if (!bcfg->nodeIDbits) bcfg->nodeIDbits = nbb;
107 0 : else if (bcfg->nodeIDbits<nbb) {
108 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[BIFS] BIFSConfig.NodeIDBits too small (%d bits vs %d nodes)\n", bcfg->nodeIDbits, seng->ctx->max_node_id));
109 : }
110 1 : nbb = gf_get_bit_size(seng->ctx->max_route_id);
111 1 : if (!bcfg->routeIDbits) bcfg->routeIDbits = nbb;
112 0 : else if (bcfg->routeIDbits<nbb) {
113 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[BIFS] BIFSConfig.RouteIDBits too small (%d bits vs %d routes)\n", bcfg->routeIDbits, seng->ctx->max_route_id));
114 : }
115 1 : nbb = gf_get_bit_size(seng->ctx->max_proto_id);
116 1 : if (!bcfg->protoIDbits) bcfg->protoIDbits=nbb;
117 0 : else if (bcfg->protoIDbits<nbb) {
118 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[BIFS] BIFSConfig.ProtoIDBits too small (%d bits vs %d protos)\n", bcfg->protoIDbits, seng->ctx->max_proto_id));
119 : }
120 :
121 : /*this is the real pb, not stored in cfg or file level, set at EACH replaceScene*/
122 : encode_names = 0;
123 :
124 : /* The BIFS Config that is passed here should be the BIFSConfig from the IOD */
125 1 : gf_bifs_encoder_new_stream(seng->bifsenc, esd->ESID, bcfg, encode_names, 0);
126 1 : if (delete_bcfg) gf_odf_desc_del((GF_Descriptor *)bcfg);
127 :
128 1 : gf_bifs_encoder_get_config(seng->bifsenc, esd->ESID, &data, &data_len);
129 :
130 1 : if (esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo);
131 1 : esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);
132 1 : esd->decoderConfig->decoderSpecificInfo->data = data;
133 1 : esd->decoderConfig->decoderSpecificInfo->dataLength = data_len;
134 :
135 1 : sc->dec_cfg = gf_malloc(sizeof(char)*data_len);
136 1 : memcpy(sc->dec_cfg, data, data_len);
137 1 : sc->dec_cfg_len = data_len;
138 :
139 1 : esd->decoderConfig->objectTypeIndication = gf_bifs_encoder_get_version(seng->bifsenc, esd->ESID);
140 :
141 : return GF_OK;
142 : }
143 : #endif /*GPAC_DISABLE_BIFS_ENC*/
144 :
145 : #ifndef GPAC_DISABLE_LASER
146 0 : static GF_Err gf_sm_setup_lsrenc(GF_SceneEngine *seng, GF_StreamContext *sc, GF_ESD *esd)
147 : {
148 : u8 *data;
149 : u32 data_len;
150 : GF_LASERConfig lsr_cfg;
151 :
152 0 : if (!esd->decoderConfig || (esd->decoderConfig->streamType != GF_STREAM_SCENE)) return GF_BAD_PARAM;
153 :
154 0 : seng->lsrenc = gf_laser_encoder_new(seng->ctx->scene_graph);
155 :
156 : /*inputctx is not properly setup, do it*/
157 0 : if (!esd->decoderConfig->decoderSpecificInfo) {
158 : memset(&lsr_cfg, 0, sizeof(GF_LASERConfig));
159 : }
160 : /*regular case*/
161 0 : else if (esd->decoderConfig->decoderSpecificInfo->tag == GF_ODF_LASER_CFG_TAG) {
162 : memcpy(&lsr_cfg, (GF_LASERConfig *)esd->decoderConfig->decoderSpecificInfo, sizeof(GF_LASERConfig) );
163 : }
164 : /*happens when loading from MP4 in which case BIFSc is not decoded*/
165 : else {
166 0 : gf_odf_get_laser_config(esd->decoderConfig->decoderSpecificInfo, &lsr_cfg);
167 : }
168 :
169 0 : gf_laser_encoder_new_stream(seng->lsrenc, esd->ESID , &lsr_cfg);
170 :
171 0 : gf_laser_encoder_get_config(seng->lsrenc, esd->ESID, &data, &data_len);
172 :
173 0 : if (esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo);
174 0 : esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);
175 0 : esd->decoderConfig->decoderSpecificInfo->data = data;
176 0 : esd->decoderConfig->decoderSpecificInfo->dataLength = data_len;
177 :
178 0 : sc->dec_cfg = (char*)gf_malloc(sizeof(char)*data_len);
179 0 : memcpy(sc->dec_cfg, data, data_len);
180 0 : sc->dec_cfg_len = data_len;
181 : return GF_OK;
182 : }
183 : #endif /*GPAC_DISABLE_LASER*/
184 :
185 1 : static GF_Err gf_sm_live_setup(GF_SceneEngine *seng)
186 : {
187 : GF_Err e;
188 : GF_StreamContext *sc;
189 : GF_InitialObjectDescriptor *iod;
190 : GF_ESD *esd;
191 : u32 i, j;
192 :
193 : e = GF_OK;
194 :
195 1 : iod = (GF_InitialObjectDescriptor *) seng->ctx->root_od;
196 :
197 : /*build an IOD*/
198 1 : if (!iod) {
199 0 : seng->ctx->root_od = (GF_ObjectDescriptor*) gf_odf_desc_new(GF_ODF_IOD_TAG);
200 0 : iod = (GF_InitialObjectDescriptor *) seng->ctx->root_od;
201 :
202 0 : i=0;
203 0 : while ((sc = gf_list_enum(seng->ctx->streams, &i))) {
204 0 : if (sc->streamType != GF_STREAM_SCENE) continue;
205 :
206 0 : if (!sc->ESID) sc->ESID = 1;
207 :
208 0 : esd = gf_odf_desc_esd_new(2);
209 0 : gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo);
210 0 : esd->decoderConfig->decoderSpecificInfo = NULL;
211 0 : esd->ESID = sc->ESID;
212 0 : esd->decoderConfig->streamType = GF_STREAM_SCENE;
213 0 : esd->decoderConfig->objectTypeIndication = sc->codec_id;
214 0 : gf_list_add(iod->ESDescriptors, esd);
215 :
216 0 : if (!sc->timeScale) sc->timeScale = 1000;
217 0 : esd->slConfig->timestampResolution = sc->timeScale;
218 : }
219 : }
220 :
221 1 : i=0;
222 3 : while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
223 :
224 1 : j=0;
225 2 : while ((esd = gf_list_enum(seng->ctx->root_od->ESDescriptors, &j))) {
226 1 : if (sc->ESID==esd->ESID) {
227 : break;
228 : }
229 : }
230 1 : if (!esd) continue;
231 :
232 1 : if (!esd->slConfig) esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG);
233 1 : if (!esd->slConfig->timestampResolution) esd->slConfig->timestampResolution = 1000;
234 1 : if (!sc->timeScale) sc->timeScale = esd->slConfig->timestampResolution;
235 :
236 :
237 1 : if (sc->streamType == GF_STREAM_SCENE) {
238 1 : switch (sc->codec_id) {
239 : #ifndef GPAC_DISABLE_BIFS_ENC
240 1 : case GF_CODECID_BIFS:
241 : case GF_CODECID_BIFS_V2:
242 1 : e = gf_sm_setup_bifsenc(seng, sc, esd);
243 1 : break;
244 : #endif
245 :
246 : #ifndef GPAC_DISABLE_LASER
247 0 : case GF_CODECID_LASER:
248 0 : e = gf_sm_setup_lsrenc(seng, sc, esd);
249 0 : break;
250 : #endif
251 : case GF_CODECID_DIMS:
252 : /* Nothing to be done here */
253 : break;
254 : default:
255 : e = GF_NOT_SUPPORTED;
256 : break;
257 : }
258 1 : if (e) return e;
259 : }
260 : }
261 : return e;
262 : }
263 :
264 :
265 : GF_EXPORT
266 1 : GF_Err gf_seng_enable_aggregation(GF_SceneEngine *seng, u16 ESID, u16 onESID)
267 : {
268 : GF_StreamContext *sc;
269 :
270 1 : if (ESID) {
271 1 : u32 i=0;
272 2 : while (NULL != (sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
273 1 : if (0 != (sc->ESID==ESID)) break;
274 : }
275 : } else {
276 0 : sc = (GF_StreamContext*)gf_list_get(seng->ctx->streams, 0);
277 : }
278 1 : if (!sc) return GF_STREAM_NOT_FOUND;
279 :
280 1 : sc->aggregate_on_esid = onESID;
281 1 : return GF_OK;
282 : }
283 :
284 : /* Set to 1 if you want every dump with a timed file name */
285 : //#define DUMP_DIMS_LOG_WITH_TIME
286 :
287 0 : static GF_Err gf_seng_encode_dims_au(GF_SceneEngine *seng, u16 ESID, GF_List *commands, u8 **data, u32 *size, Bool compress_dims)
288 : {
289 : #ifndef GPAC_DISABLE_SCENE_DUMP
290 : GF_SceneDumper *dumper = NULL;
291 : #endif
292 : GF_Err e;
293 : char rad_name[4096];
294 : char file_name[4096+100];
295 : u32 fsize;
296 0 : u8 *buffer = NULL;
297 : GF_BitStream *bs = NULL;
298 : u8 dims_header;
299 : #ifdef DUMP_DIMS_LOG_WITH_TIME
300 : u32 do_dump_with_time = 1;
301 : #endif
302 : u32 buffer_len;
303 : const char *cache_dir;
304 : char *dump_name;
305 :
306 0 : if (!data) return GF_BAD_PARAM;
307 :
308 0 : if (!seng->dump_path) cache_dir = gf_get_default_cache_directory();
309 : else cache_dir = seng->dump_path;
310 :
311 : dump_name = "gpac_scene_engine_dump";
312 :
313 : #ifdef DUMP_DIMS_LOG_WITH_TIME
314 : start:
315 : #endif
316 :
317 0 : if (commands && gf_list_count(commands)) {
318 : sprintf(rad_name, "%s%c%s%s", cache_dir, GF_PATH_SEPARATOR, dump_name, "_update");
319 : } else {
320 : #ifndef DUMP_DIMS_LOG_WITH_TIME
321 : sprintf(rad_name, "%s%c%s%s", cache_dir, GF_PATH_SEPARATOR, "rap_", dump_name);
322 : #else
323 : char date_str[100], time_str[100];
324 : time_t now;
325 : struct tm *tm_tot;
326 : now = time(NULL);
327 : tm_tot = localtime(&now);
328 : strftime(date_str, 100, "%Yy%mm%dd", tm_tot);
329 : strftime(time_str, 100, "%Hh%Mm%Ss", tm_tot);
330 : sprintf(rad_name, "%s%c%s-%s-%s%s", cache_dir, GF_PATH_SEPARATOR, date_str, time_str, "rap_", dump_name);
331 : #endif
332 : }
333 :
334 : #ifndef GPAC_DISABLE_SCENE_DUMP
335 0 : dumper = gf_sm_dumper_new(seng->ctx->scene_graph, rad_name, GF_FALSE, ' ', GF_SM_DUMP_SVG);
336 0 : if (!dumper) {
337 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[SceneEngine] Cannot create SVG dumper for %s.svg\n", rad_name));
338 : e = GF_IO_ERR;
339 : goto exit;
340 : }
341 :
342 0 : if (commands && gf_list_count(commands)) {
343 0 : e = gf_sm_dump_command_list(dumper, commands, 0, 0);
344 : }
345 : else {
346 0 : e = gf_sm_dump_graph(dumper, 0, 0);
347 : }
348 0 : gf_sm_dumper_del(dumper);
349 :
350 : #if 0 //unused
351 : if(seng->dump_rap) {
352 : GF_SceneDumper *dumper = NULL;
353 :
354 : sprintf(rad_name, "%s%c%s%s", cache_dir, GF_PATH_SEPARATOR, "rap_", dump_name);
355 :
356 : dumper = gf_sm_dumper_new(seng->ctx->scene_graph, rad_name, GF_FALSE, ' ', GF_SM_DUMP_SVG);
357 : if (!dumper) {
358 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[SceneEngine] Cannot create SVG dumper for %s.svg\n", rad_name));
359 : e = GF_IO_ERR;
360 : goto exit;
361 : }
362 : e = gf_sm_dump_graph(dumper, 0, 0);
363 : gf_sm_dumper_del(dumper);
364 : }
365 : #endif
366 :
367 0 : if (e) {
368 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[SceneEngine] Cannot dump DIMS Commands\n"));
369 : goto exit;
370 : }
371 : #endif
372 :
373 : #ifdef DUMP_DIMS_LOG_WITH_TIME
374 : if (do_dump_with_time) {
375 : do_dump_with_time = 0;
376 : goto start;
377 : }
378 : #endif
379 :
380 : sprintf(file_name, "%s.svg", rad_name);
381 :
382 0 : e = gf_file_load_data(file_name, (u8 **) &buffer, &fsize);
383 0 : if (e) {
384 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[SceneEngine] Error loading SVG dump file %s\n", file_name));
385 : goto exit;
386 : }
387 :
388 : /* Then, set DIMS unit header - TODO: notify redundant units*/
389 0 : if (commands && gf_list_count(commands)) {
390 : dims_header = GF_DIMS_UNIT_P; /* streamer->all_non_rap_critical ? 0 : GF_DIMS_UNIT_P;*/
391 : } else {
392 : /*redundant RAP with complete scene*/
393 : dims_header = GF_DIMS_UNIT_M | GF_DIMS_UNIT_S | GF_DIMS_UNIT_I | GF_DIMS_UNIT_P;
394 : }
395 :
396 : /* Then, if compression is asked, we do it */
397 0 : buffer_len = (u32)fsize;
398 : assert(fsize < 0x80000000);
399 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[SceneEngine] Sending DIMS data - sizes: raw (%d)", buffer_len));
400 0 : if (compress_dims) {
401 : #ifndef GPAC_DISABLE_ZLIB
402 0 : dims_header |= GF_DIMS_UNIT_C;
403 0 : e = gf_gz_compress_payload(&buffer, buffer_len, &buffer_len);
404 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("/ compressed (%d)", buffer_len));
405 0 : if (e) goto exit;
406 : #else
407 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Error: your version of GPAC was compiled with no libz support. Abort."));
408 : e = GF_NOT_SUPPORTED;
409 : goto exit;
410 : #endif
411 : }
412 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\n"));
413 :
414 : /* Then, prepare the DIMS data using a bitstream instead of direct manipulation for endianness
415 : The new bitstream size should be:
416 : the size of the (compressed) data
417 : + 1 bytes for the header
418 : + 2 bytes for the size
419 : + 4 bytes if the size is greater than 65535
420 : */
421 0 : bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
422 0 : if (buffer_len > 65535) {
423 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[SceneEngine] Warning: DIMS Unit size too big !!!\n"));
424 0 : gf_bs_write_u16(bs, 0); /* internal GPAC hack to indicate that the size is larger than 65535 */
425 0 : gf_bs_write_u32(bs, buffer_len+1);
426 : } else {
427 0 : gf_bs_write_u16(bs, buffer_len+1);
428 : }
429 0 : gf_bs_write_u8(bs, dims_header);
430 0 : gf_bs_write_data(bs, buffer, buffer_len);
431 :
432 0 : gf_free(buffer);
433 0 : buffer = NULL;
434 :
435 0 : gf_bs_get_content(bs, data, size);
436 0 : gf_bs_del(bs);
437 :
438 0 : exit:
439 0 : if (buffer) gf_free(buffer);
440 : return e;
441 : }
442 :
443 1 : static Bool gf_sm_check_for_modif(GF_SceneEngine *seng, GF_AUContext *au)
444 : {
445 : GF_Command *com;
446 : Bool modified=0;
447 1 : u32 i=0;
448 : /*au is marked as modified - this happens when commands are concatenated into the au*/
449 1 : if (au->flags & GF_SM_AU_MODIFIED) {
450 0 : au->flags &= ~GF_SM_AU_MODIFIED;
451 : modified=1;
452 : }
453 : /*check each command*/
454 2 : while (NULL != (com = gf_list_enum(au->commands, &i))) {
455 1 : u32 j=0;
456 : GF_CommandField *field;
457 1 : if (!com->node) continue;
458 : /*check root node (for SCENE_REPLACE) */
459 1 : if (gf_node_dirty_get(com->node)) {
460 : modified=1;
461 1 : gf_node_dirty_reset(com->node, 1);
462 : }
463 : /*check all command fields of type SFNODE or MFNODE*/
464 1 : while (NULL != (field = gf_list_enum(com->command_fields, &j))) {
465 0 : switch (field->fieldType) {
466 0 : case GF_SG_VRML_SFNODE:
467 0 : if (field->new_node) {
468 0 : if (gf_node_dirty_get(field->new_node)) {
469 : modified=1;
470 0 : gf_node_dirty_reset(field->new_node, 1);
471 : }
472 : }
473 : break;
474 0 : case GF_SG_VRML_MFNODE:
475 0 : if (field->field_ptr) {
476 : GF_ChildNodeItem *child;
477 0 : child = field->node_list;
478 0 : while (child) {
479 0 : if (gf_node_dirty_get(child->node)) {
480 : modified=1;
481 0 : gf_node_dirty_reset(child->node, 1);
482 : }
483 0 : child = child->next;
484 : }
485 : }
486 : break;
487 : }
488 : }
489 : }
490 :
491 1 : if (!seng->first_dims_sent) {
492 1 : if (au->owner->codec_id==GF_CODECID_DIMS) {
493 0 : GF_Node *root = gf_sg_get_root_node(seng->ctx->scene_graph);
494 0 : if (gf_node_dirty_get(root)) {
495 : modified=1;
496 0 : gf_node_dirty_reset(root, 1);
497 : }
498 : } else {
499 : }
500 1 : seng->first_dims_sent = 1;
501 : }
502 1 : return modified;
503 : }
504 :
505 1 : static GF_Err gf_sm_live_encode_scene_au(GF_SceneEngine *seng, gf_seng_callback callback, Bool from_start)
506 : {
507 : GF_Err e;
508 : u32 i, j, size, count, nb_streams;
509 : u8 *data;
510 : GF_AUContext *au;
511 :
512 1 : if (!callback) return GF_BAD_PARAM;
513 :
514 : e = GF_OK;
515 :
516 1 : nb_streams = gf_list_count(seng->ctx->streams);
517 2 : for (i=0; i<nb_streams; i++) {
518 1 : GF_StreamContext *sc = gf_list_get(seng->ctx->streams, i);
519 1 : if (sc->streamType != GF_STREAM_SCENE) continue;
520 :
521 1 : count = gf_list_count(sc->AUs);
522 1 : j = from_start ? 0 : sc->current_au_count;
523 1 : for (; j<count; j++) {
524 1 : au = (GF_AUContext *)gf_list_get(sc->AUs, j);
525 1 : data = NULL;
526 1 : size = 0;
527 : /*in case using XMT*/
528 1 : if (au->timing_sec) au->timing = (u64) (au->timing_sec * sc->timeScale);
529 :
530 1 : if (from_start && !j && !gf_sm_check_for_modif(seng, au)) continue;
531 :
532 1 : switch (sc->codec_id) {
533 : #ifndef GPAC_DISABLE_BIFS_ENC
534 1 : case GF_CODECID_BIFS:
535 : case GF_CODECID_BIFS_V2:
536 1 : e = gf_bifs_encode_au(seng->bifsenc, sc->ESID, au->commands, &data, &size);
537 1 : break;
538 : #endif
539 :
540 : #ifndef GPAC_DISABLE_LASER
541 0 : case GF_CODECID_LASER:
542 0 : e = gf_laser_encode_au(seng->lsrenc, sc->ESID, au->commands, 0, &data, &size);
543 0 : break;
544 : #endif
545 0 : case GF_CODECID_DIMS:
546 0 : e = gf_seng_encode_dims_au(seng, sc->ESID, au->commands, &data, &size, GF_TRUE);
547 0 : break;
548 :
549 0 : default:
550 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("Cannot encode AU for Scene OTI %x\n", sc->codec_id));
551 : break;
552 : }
553 1 : callback(seng->calling_object, sc->ESID, data, size, au->timing);
554 1 : gf_free(data);
555 1 : data = NULL;
556 1 : if (e) break;
557 : }
558 : }
559 : return e;
560 : }
561 :
562 : GF_EXPORT
563 0 : GF_Err gf_seng_aggregate_context(GF_SceneEngine *seng, u16 ESID)
564 : {
565 0 : return gf_sm_aggregate(seng->ctx, ESID);
566 : }
567 :
568 : #if 0 //unused
569 : GF_Err gf_seng_dump_rap_on(GF_SceneEngine *seng, Bool dump_rap)
570 : {
571 : seng->dump_rap = dump_rap;
572 : return 0;
573 : }
574 : #endif
575 :
576 : GF_EXPORT
577 0 : GF_Err gf_seng_save_context(GF_SceneEngine *seng, char *ctxFileName)
578 : {
579 : #ifdef GPAC_DISABLE_SCENE_DUMP
580 : return GF_NOT_SUPPORTED;
581 : #else
582 : u32 d_mode, do_enc;
583 : char szF[GF_MAX_PATH], *ext;
584 : GF_Err e;
585 :
586 : /*check if we dump to BT, XMT or encode to MP4*/
587 : ext = NULL;
588 0 : if (ctxFileName) {
589 : strcpy(szF, ctxFileName);
590 0 : ext = gf_file_ext_start(szF);
591 : }
592 : d_mode = GF_SM_DUMP_BT;
593 : do_enc = 0;
594 0 : if (ext) {
595 0 : if (!stricmp(ext, ".xmt") || !stricmp(ext, ".xmta")) d_mode = GF_SM_DUMP_XMTA;
596 0 : else if (!stricmp(ext, ".mp4")) do_enc = 1;
597 0 : ext[0] = 0;
598 : }
599 :
600 0 : if (do_enc) {
601 : #ifndef GPAC_DISABLE_SCENE_ENCODER
602 : GF_ISOFile *mp4;
603 : strcat(szF, ".mp4");
604 0 : mp4 = gf_isom_open(szF, GF_ISOM_OPEN_WRITE, NULL);
605 0 : e = gf_sm_encode_to_file(seng->ctx, mp4, NULL);
606 0 : if (e) gf_isom_delete(mp4);
607 0 : else gf_isom_close(mp4);
608 : #else
609 : return GF_NOT_SUPPORTED;
610 : #endif
611 : } else {
612 0 : e = gf_sm_dump(seng->ctx, ctxFileName ? szF : NULL, GF_FALSE, d_mode);
613 : }
614 0 : return e;
615 : #endif
616 : }
617 :
618 0 : static GF_AUContext *gf_seng_create_new_au(GF_StreamContext *sc, u32 time)
619 : {
620 : GF_AUContext *new_au, *last_au;
621 0 : if (!sc) return NULL;
622 0 : last_au = gf_list_last(sc->AUs);
623 0 : if (last_au && last_au->timing == time) {
624 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SCENE, ("[SceneEngine] Forcing new AU\n"));
625 0 : time++;
626 : }
627 0 : new_au = gf_sm_stream_au_new(sc, time, 0, 0);
628 0 : return new_au;
629 : }
630 :
631 : GF_EXPORT
632 0 : GF_Err gf_seng_encode_from_string(GF_SceneEngine *seng, u16 ESID, Bool disable_aggregation, char *auString, gf_seng_callback callback)
633 : {
634 : GF_StreamContext *sc;
635 : u32 i;
636 : GF_Err e;
637 :
638 0 : i = 0;
639 0 : while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
640 0 : sc->current_au_count = gf_list_count(sc->AUs);
641 0 : sc->disable_aggregation = disable_aggregation;
642 : }
643 0 : seng->loader.flags |= GF_SM_LOAD_CONTEXT_READY;
644 0 : seng->loader.force_es_id = ESID;
645 :
646 : /* We need to create an empty AU for the parser to correctly parse a LASeR Command without SceneUnit */
647 0 : sc = gf_list_get(seng->ctx->streams, 0);
648 0 : if (sc->codec_id == GF_CODECID_DIMS) {
649 0 : gf_seng_create_new_au(sc, 0);
650 : }
651 :
652 0 : e = gf_sm_load_string(&seng->loader, auString, 0);
653 0 : if (e) goto exit;
654 :
655 0 : i = 0;
656 0 : while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
657 0 : sc->disable_aggregation = 0;
658 : }
659 0 : e = gf_sm_live_encode_scene_au(seng, callback, 0);
660 0 : exit:
661 0 : return e;
662 : }
663 :
664 :
665 : #if 0 //unused
666 : GF_Err gf_seng_encode_from_commands(GF_SceneEngine *seng, u16 ESID, Bool disable_aggregation, u32 time, GF_List *commands, gf_seng_callback callback)
667 : {
668 : GF_Err e;
669 : u32 size;
670 : char *data;
671 : GF_StreamContext *sc;
672 : u32 i, nb_streams;
673 : GF_AUContext *new_au;
674 :
675 : if (!callback) return GF_BAD_PARAM;
676 : if (!commands || !gf_list_count(commands)) return GF_BAD_PARAM;
677 :
678 : e = GF_OK;
679 :
680 : /* if the ESID is not provided we try to use the first scene stream */
681 : sc = NULL;
682 : nb_streams = gf_list_count(seng->ctx->streams);
683 : for (i=0; i<nb_streams; i++) {
684 : GF_StreamContext *tmp_sc = gf_list_get(seng->ctx->streams, i);
685 : if (tmp_sc->streamType != GF_STREAM_SCENE) continue;
686 : sc = tmp_sc;
687 : if (!ESID) break;
688 : else if (sc->ESID == ESID) break;
689 : }
690 : if (!sc) return GF_BAD_PARAM;
691 : /* We need to create an empty AU for the parser to correctly parse a LASeR Command without SceneUnit */
692 : new_au = gf_seng_create_new_au(sc, time);
693 :
694 : if (disable_aggregation) new_au->flags = GF_SM_AU_NOT_AGGREGATED;
695 :
696 :
697 :
698 : /* Removing the commands from the input list to avoid destruction
699 : and setting the RAP flag */
700 : while (gf_list_count(commands)) {
701 : GF_Command *com = gf_list_get(commands, 0);
702 : gf_list_rem(commands, 0);
703 : switch (com->tag) {
704 : #ifndef GPAC_DISABLE_VRML
705 : case GF_SG_SCENE_REPLACE:
706 : new_au->flags |= GF_SM_AU_RAP;
707 : break;
708 : #endif
709 : case GF_SG_LSR_NEW_SCENE:
710 : new_au->flags |= GF_SM_AU_RAP;
711 : break;
712 : }
713 : gf_list_add(new_au->commands, com);
714 : }
715 :
716 : data = NULL;
717 : size = 0;
718 :
719 : switch(sc->codec_id) {
720 : #ifndef GPAC_DISABLE_BIFS_ENC
721 : case GF_CODECID_BIFS:
722 : case GF_CODECID_BIFS_V2:
723 : e = gf_bifs_encode_au(seng->bifsenc, ESID, new_au->commands, &data, &size);
724 : break;
725 : #endif
726 :
727 : #ifndef GPAC_DISABLE_LASER
728 : case GF_CODECID_LASER:
729 : e = gf_laser_encode_au(seng->lsrenc, ESID, new_au->commands, 0, &data, &size);
730 : break;
731 : #endif
732 : case GF_CODECID_DIMS:
733 : e = gf_seng_encode_dims_au(seng, ESID, new_au->commands, &data, &size, GF_TRUE);
734 : break;
735 : default:
736 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("Cannot encode commands for Scene OTI %x\n", sc->codec_id));
737 : break;
738 : }
739 : callback(seng->calling_object, ESID, data, size, 0);
740 : gf_free(data);
741 : return e;
742 : }
743 : #endif
744 : GF_EXPORT
745 0 : GF_Err gf_seng_encode_from_file(GF_SceneEngine *seng, u16 ESID, Bool disable_aggregation, char *auFile, gf_seng_callback callback)
746 : {
747 : GF_Err e;
748 : GF_StreamContext *sc;
749 : u32 i;
750 : Bool dims = 0;
751 :
752 0 : seng->loader.fileName = auFile;
753 0 : seng->loader.ctx = seng->ctx;
754 0 : seng->loader.force_es_id = ESID;
755 :
756 : sc = NULL;
757 0 : i=0;
758 0 : while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
759 0 : sc->current_au_count = gf_list_count(sc->AUs);
760 0 : sc->disable_aggregation = disable_aggregation;
761 : }
762 : /* We need to create an empty AU for the parser to correctly parse a LASeR Command without SceneUnit */
763 0 : sc = gf_list_get(seng->ctx->streams, 0);
764 0 : if (sc->codec_id == GF_CODECID_DIMS) {
765 : dims = 1;
766 0 : gf_seng_create_new_au(sc, 0);
767 : }
768 0 : seng->loader.flags |= GF_SM_LOAD_CONTEXT_READY;
769 :
770 0 : if (dims) {
771 0 : seng->loader.type |= GF_SM_LOAD_DIMS;
772 : } else {
773 0 : seng->loader.flags |= GF_SM_LOAD_MPEG4_STRICT;
774 : }
775 0 : e = gf_sm_load_run(&seng->loader);
776 :
777 0 : if (e<0) {
778 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] cannot load AU File %s (error %s)\n", auFile, gf_error_to_string(e)));
779 : goto exit;
780 : }
781 :
782 0 : i = 0;
783 0 : while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
784 0 : sc->disable_aggregation = 0;
785 : }
786 :
787 0 : e = gf_sm_live_encode_scene_au(seng, callback, 0);
788 : if (e) goto exit;
789 0 : exit:
790 0 : return e;
791 : }
792 :
793 : GF_EXPORT
794 1 : GF_Err gf_seng_encode_context(GF_SceneEngine *seng, gf_seng_callback callback)
795 : {
796 1 : if (!seng) {
797 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] Cannot encode context. No seng provided\n"));
798 : return GF_BAD_PARAM;
799 : }
800 1 : return gf_sm_live_encode_scene_au(seng, callback, 1);
801 : }
802 :
803 : GF_EXPORT
804 1 : void gf_seng_terminate(GF_SceneEngine *seng)
805 : {
806 : #ifndef GPAC_DISABLE_BIFS_ENC
807 1 : if (seng->bifsenc) gf_bifs_encoder_del(seng->bifsenc);
808 : #endif
809 :
810 : #ifndef GPAC_DISABLE_LASER
811 1 : if (seng->lsrenc) gf_laser_encoder_del(seng->lsrenc);
812 : #endif
813 :
814 1 : gf_sm_load_done(&seng->loader);
815 :
816 1 : if (seng->owns_context) {
817 1 : if (seng->ctx) gf_sm_del(seng->ctx);
818 1 : if (seng->sg) gf_sg_del(seng->sg);
819 : }
820 1 : gf_free(seng);
821 1 : }
822 :
823 : GF_EXPORT
824 1 : GF_Err gf_seng_get_stream_config(GF_SceneEngine *seng, u32 idx, u16 *ESID, const u8 **config, u32 *config_len, u32 *streamType, u32 *codec_id, u32 *timeScale)
825 : {
826 1 : GF_StreamContext *sc = gf_list_get(seng->ctx->streams, idx);
827 1 : if (!sc || !ESID || !config || !config_len) return GF_BAD_PARAM;
828 1 : *ESID = sc->ESID;
829 1 : *config = sc->dec_cfg;
830 1 : *config_len = sc->dec_cfg_len;
831 1 : if (streamType) *streamType = sc->streamType;
832 1 : if (codec_id) *codec_id = sc->codec_id;
833 1 : if (timeScale) *timeScale = sc->timeScale;
834 : return GF_OK;
835 : }
836 :
837 :
838 : #ifndef GPAC_DISABLE_VRML
839 :
840 0 : static void seng_exec_conditional(M_Conditional *c, GF_SceneGraph *scene)
841 : {
842 0 : GF_List *clist = c->buffer.commandList;
843 0 : c->buffer.commandList = NULL;
844 :
845 0 : gf_sg_command_apply_list(gf_node_get_graph((GF_Node*)c), clist, 0.0);
846 :
847 0 : if (c->buffer.commandList != NULL) {
848 0 : while (gf_list_count(clist)) {
849 0 : GF_Command *sub_com = (GF_Command *)gf_list_get(clist, 0);
850 0 : gf_sg_command_del(sub_com);
851 0 : gf_list_rem(clist, 0);
852 : }
853 0 : gf_list_del(clist);
854 : } else {
855 0 : c->buffer.commandList = clist;
856 : }
857 0 : }
858 :
859 0 : static void seng_conditional_activate(GF_Node *node, GF_Route *route)
860 : {
861 0 : if (node) {
862 0 : GF_SceneEngine *seng = (GF_SceneEngine *) gf_node_get_private(node);
863 : M_Conditional *c = (M_Conditional*)node;
864 0 : if (c->activate) seng_exec_conditional(c, seng->sg);
865 : }
866 0 : }
867 :
868 0 : static void seng_conditional_reverse_activate(GF_Node *node, GF_Route *route)
869 : {
870 0 : if (node) {
871 0 : GF_SceneEngine *seng = (GF_SceneEngine *) gf_node_get_private(node);
872 : M_Conditional*c = (M_Conditional*)node;
873 0 : if (!c->reverseActivate) seng_exec_conditional(c, seng->sg);
874 : }
875 0 : }
876 : #endif //GPAC_DISABLE_VRML
877 :
878 :
879 99 : static void gf_seng_on_node_modified(void *_seng, GF_SGNodeCbkType type, GF_Node *node, void *ctxdata)
880 : {
881 99 : switch (type) {
882 : #ifndef GPAC_DISABLE_VRML
883 33 : case GF_SG_CALLBACK_INIT:
884 33 : if (gf_node_get_tag(node) == TAG_MPEG4_Conditional) {
885 : M_Conditional*c = (M_Conditional*)node;
886 0 : c->on_activate = seng_conditional_activate;
887 0 : c->on_reverseActivate = seng_conditional_reverse_activate;
888 0 : gf_node_set_private(node, _seng);
889 : }
890 : break;
891 : #endif
892 0 : case GF_SG_CALLBACK_MODIFIED:
893 0 : gf_node_dirty_parents(node);
894 0 : break;
895 : default:
896 : break;
897 : }
898 99 : }
899 :
900 : GF_EXPORT
901 1 : GF_SceneEngine *gf_seng_init(void *calling_object, char * inputContext, u32 load_type, char *dump_path, Bool embed_resources)
902 : {
903 : GF_SceneEngine *seng;
904 : GF_Err e = GF_OK;
905 :
906 1 : if (!inputContext) return NULL;
907 :
908 1 : GF_SAFEALLOC(seng, GF_SceneEngine)
909 1 : if (!seng) return NULL;
910 :
911 1 : seng->calling_object = calling_object;
912 :
913 : /*Step 1: create context and load input*/
914 1 : seng->sg = gf_sg_new();
915 1 : gf_sg_set_node_callback(seng->sg, gf_seng_on_node_modified);
916 1 : gf_sg_set_private(seng->sg, seng);
917 1 : seng->dump_path = dump_path;
918 1 : seng->ctx = gf_sm_new(seng->sg);
919 1 : seng->owns_context = 1;
920 1 : memset(&(seng->loader), 0, sizeof(GF_SceneLoader));
921 1 : seng->loader.ctx = seng->ctx;
922 1 : seng->loader.type = load_type;
923 : /*since we're encoding in BIFS we must get MPEG-4 nodes only*/
924 1 : seng->loader.flags = GF_SM_LOAD_MPEG4_STRICT;
925 1 : if (embed_resources) seng->loader.flags |= GF_SM_LOAD_EMBEDS_RES;
926 :
927 : #ifdef GPAC_ENABLE_COVERAGE
928 1 : if (gf_sys_is_cov_mode()) {
929 : seng_conditional_activate(NULL, NULL);
930 : seng_conditional_reverse_activate(NULL, NULL);
931 : gf_seng_create_new_au(NULL, 0);
932 :
933 : }
934 : #endif
935 :
936 1 : seng->loader.fileName = inputContext;
937 1 : e = gf_sm_load_init(&(seng->loader));
938 1 : if (!e) e = gf_sm_load_run(&(seng->loader));
939 :
940 1 : if (e<0) {
941 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] Cannot load context from %s (error %s)\n", inputContext, gf_error_to_string(e)));
942 : goto exit;
943 : }
944 1 : e = gf_sm_live_setup(seng);
945 1 : if (e!=GF_OK) {
946 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] cannot init scene encoder for context (error %s)\n", gf_error_to_string(e)));
947 : goto exit;
948 : }
949 : return seng;
950 :
951 0 : exit:
952 0 : gf_seng_terminate(seng);
953 0 : return NULL;
954 : }
955 :
956 : #if 0 //unused
957 : /**
958 : \param calling_object the calling object on which call back will be called
959 : \param ctx an already loaded scene manager
960 : \param dump_path the path where scenes are dumped
961 : *
962 : * must be called only one time (by process calling the DLL) before other calls
963 : */
964 : GF_SceneEngine *gf_seng_init_from_context(void *calling_object, GF_SceneManager *ctx, char *dump_path)
965 : {
966 : GF_SceneEngine *seng;
967 : GF_Err e = GF_OK;
968 :
969 : if (!ctx) return NULL;
970 :
971 : GF_SAFEALLOC(seng, GF_SceneEngine)
972 : if (!seng) return NULL;
973 :
974 : seng->calling_object = calling_object;
975 : seng->dump_path = dump_path;
976 : /*Step 1: create context and load input*/
977 : seng->sg = ctx->scene_graph;
978 : seng->ctx = ctx;
979 : seng->owns_context = 0;
980 :
981 : e = gf_sm_live_setup(seng);
982 : if (e!=GF_OK) {
983 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] cannot init scene encoder for context (error %s)\n", gf_error_to_string(e)));
984 : goto exit;
985 : }
986 : return seng;
987 :
988 : exit:
989 : gf_seng_terminate(seng);
990 : return NULL;
991 : }
992 : #endif
993 :
994 : #if 0 //unused
995 : /**
996 : \param calling_object is the calling object on which call back will be called
997 : \param inputContext is an UTF-8 scene description (with or without IOD) in BT or XMT-A format
998 : \param load_type is the preferred loader type for the content (e.g. SVG vs DIMS)
999 : \param width width of scene if no IOD is given in the context.
1000 : \param height height of scene if no IOD is given in the context.
1001 : \param usePixelMetrics metrics system used in the scene, if no IOD is given in the context.
1002 : \param dump_path the path where scenes are dumped
1003 : *
1004 : * must be called only one time (by process calling the DLL) before other calls
1005 : */
1006 :
1007 : GF_SceneEngine *gf_seng_init_from_string(void *calling_object, char * inputContext, u32 load_type, u32 width, u32 height, Bool usePixelMetrics, char *dump_path)
1008 : {
1009 : GF_SceneEngine *seng;
1010 : GF_Err e = GF_OK;
1011 :
1012 : if (!inputContext) return NULL;
1013 :
1014 : GF_SAFEALLOC(seng, GF_SceneEngine)
1015 : if (!seng) return NULL;
1016 :
1017 : seng->calling_object = calling_object;
1018 : seng->dump_path = dump_path;
1019 : /*Step 1: create context and load input*/
1020 : seng->sg = gf_sg_new();
1021 : seng->ctx = gf_sm_new(seng->sg);
1022 : seng->owns_context = 1;
1023 : memset(& seng->loader, 0, sizeof(GF_SceneLoader));
1024 : seng->loader.ctx = seng->ctx;
1025 : seng->loader.type = load_type;
1026 : /*since we're encoding in BIFS we must get MPEG-4 nodes only*/
1027 : seng->loader.flags = GF_SM_LOAD_MPEG4_STRICT;
1028 :
1029 : /* assign a loader type only if it was not requested (e.g. DIMS should not be overridden by SVG) */
1030 : if (!seng->loader.type) {
1031 : if (inputContext[0] == '<') {
1032 : if (strstr(inputContext, "<svg ")) seng->loader.type = GF_SM_LOAD_SVG;
1033 : else if (strstr(inputContext, "<saf ")) seng->loader.type = GF_SM_LOAD_XSR;
1034 : else if (strstr(inputContext, "XMT-A") || strstr(inputContext, "X3D")) seng->loader.type = GF_SM_LOAD_XMTA;
1035 : } else {
1036 : seng->loader.type = GF_SM_LOAD_BT;
1037 : }
1038 : }
1039 : e = gf_sm_load_string(&seng->loader, inputContext, 0);
1040 :
1041 : if (e) {
1042 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] cannot load context from %s (error %s)\n", inputContext, gf_error_to_string(e)));
1043 : goto exit;
1044 : }
1045 : if (!seng->ctx->root_od) {
1046 : seng->ctx->is_pixel_metrics = usePixelMetrics;
1047 : seng->ctx->scene_width = width;
1048 : seng->ctx->scene_height = height;
1049 : }
1050 :
1051 : e = gf_sm_live_setup(seng);
1052 : if (e!=GF_OK) {
1053 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] cannot init scene encoder for context (error %s)\n", gf_error_to_string(e)));
1054 : goto exit;
1055 : }
1056 : return seng;
1057 :
1058 : exit:
1059 : gf_seng_terminate(seng);
1060 : return NULL;
1061 : }
1062 : #endif
1063 :
1064 :
1065 : GF_EXPORT
1066 1 : u32 gf_seng_get_stream_count(GF_SceneEngine *seng)
1067 : {
1068 1 : return gf_list_count(seng->ctx->streams);
1069 : }
1070 :
1071 : GF_EXPORT
1072 1 : GF_Err gf_seng_get_stream_carousel_info(GF_SceneEngine *seng, u16 ESID, u32 *carousel_period, u16 *aggregate_on_es_id)
1073 : {
1074 1 : u32 i=0;
1075 : GF_StreamContext *sc;
1076 :
1077 1 : if (carousel_period) *carousel_period = (u32) -1;
1078 1 : if (aggregate_on_es_id) *aggregate_on_es_id = 0;
1079 :
1080 1 : while (NULL != (sc = gf_list_enum(seng->ctx->streams, &i))) {
1081 1 : if (sc->ESID==ESID) {
1082 1 : if (carousel_period) *carousel_period = sc->carousel_period;
1083 1 : if (aggregate_on_es_id) *aggregate_on_es_id = sc->aggregate_on_esid;
1084 : return GF_OK;
1085 : }
1086 : }
1087 : return GF_OK;
1088 : }
1089 :
1090 : GF_EXPORT
1091 1 : char *gf_seng_get_base64_iod(GF_SceneEngine *seng)
1092 : {
1093 : u32 size, size64;
1094 : u8 *buffer, *buf64;
1095 1 : u32 i=0;
1096 : GF_StreamContext*sc = NULL;
1097 :
1098 1 : if (!seng->ctx->root_od) return NULL;
1099 :
1100 1 : while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
1101 1 : if ((sc->streamType == GF_STREAM_SCENE) && (sc->codec_id != GF_CODECID_DIMS))
1102 : break;
1103 : }
1104 1 : if (!sc) return NULL;
1105 :
1106 1 : size = 0;
1107 1 : gf_odf_desc_write((GF_Descriptor *) seng->ctx->root_od, &buffer, &size);
1108 1 : buf64 = gf_malloc(size*2);
1109 1 : size64 = gf_base64_encode( buffer, size, buf64, size*2);
1110 1 : buf64[size64] = 0;
1111 1 : gf_free(buffer);
1112 1 : return buf64;
1113 : }
1114 :
1115 : GF_EXPORT
1116 1 : GF_Descriptor *gf_seng_get_iod(GF_SceneEngine *seng)
1117 : {
1118 1 : u32 i=0;
1119 1 : GF_Descriptor *out_iod = NULL;
1120 : GF_StreamContext*sc = NULL;
1121 :
1122 1 : if (!seng->ctx->root_od) return NULL;
1123 1 : while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
1124 1 : if ((sc->streamType == GF_STREAM_SCENE) && (sc->codec_id != GF_CODECID_DIMS))
1125 : break;
1126 : }
1127 1 : if (!sc) return NULL;
1128 1 : gf_odf_desc_copy((GF_Descriptor *)seng->ctx->root_od, &out_iod);
1129 1 : return out_iod;
1130 : }
1131 :
1132 :
1133 : #endif /*GPAC_DISABLE_SENG*/
1134 :
|