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
gf_sm_setup_bifsenc(GF_SceneEngine * seng,GF_StreamContext * sc,GF_ESD * esd)71 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 	if (!esd->decoderConfig || (esd->decoderConfig->streamType != GF_STREAM_SCENE)) return GF_BAD_PARAM;
80 
81 	if (!seng->bifsenc)
82 		seng->bifsenc = gf_bifs_encoder_new(seng->ctx->scene_graph);
83 
84 	delete_bcfg = 0;
85 	/*inputctx is not properly setup, do it*/
86 	if (!esd->decoderConfig->decoderSpecificInfo) {
87 		bcfg = (GF_BIFSConfig*)gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG);
88 		bcfg->pixelMetrics = seng->ctx->is_pixel_metrics;
89 		bcfg->pixelWidth = seng->ctx->scene_width;
90 		bcfg->pixelHeight = seng->ctx->scene_height;
91 		delete_bcfg = 1;
92 	}
93 	/*regular case*/
94 	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 		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 	nbb = gf_get_bit_size(seng->ctx->max_node_id);
106 	if (!bcfg->nodeIDbits) bcfg->nodeIDbits = nbb;
107 	else if (bcfg->nodeIDbits<nbb) {
108 		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 	nbb = gf_get_bit_size(seng->ctx->max_route_id);
111 	if (!bcfg->routeIDbits) bcfg->routeIDbits = nbb;
112 	else if (bcfg->routeIDbits<nbb) {
113 		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 	nbb = gf_get_bit_size(seng->ctx->max_proto_id);
116 	if (!bcfg->protoIDbits) bcfg->protoIDbits=nbb;
117 	else if (bcfg->protoIDbits<nbb) {
118 		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 	gf_bifs_encoder_new_stream(seng->bifsenc, esd->ESID, bcfg, encode_names, 0);
126 	if (delete_bcfg) gf_odf_desc_del((GF_Descriptor *)bcfg);
127 
128 	gf_bifs_encoder_get_config(seng->bifsenc, esd->ESID, &data, &data_len);
129 
130 	if (esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo);
131 	esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);
132 	esd->decoderConfig->decoderSpecificInfo->data = data;
133 	esd->decoderConfig->decoderSpecificInfo->dataLength = data_len;
134 
135 	sc->dec_cfg = gf_malloc(sizeof(char)*data_len);
136 	memcpy(sc->dec_cfg, data, data_len);
137 	sc->dec_cfg_len = data_len;
138 
139 	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
gf_sm_setup_lsrenc(GF_SceneEngine * seng,GF_StreamContext * sc,GF_ESD * esd)146 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 	if (!esd->decoderConfig || (esd->decoderConfig->streamType != GF_STREAM_SCENE)) return GF_BAD_PARAM;
153 
154 	seng->lsrenc = gf_laser_encoder_new(seng->ctx->scene_graph);
155 
156 	/*inputctx is not properly setup, do it*/
157 	if (!esd->decoderConfig->decoderSpecificInfo) {
158 		memset(&lsr_cfg, 0, sizeof(GF_LASERConfig));
159 	}
160 	/*regular case*/
161 	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 		gf_odf_get_laser_config(esd->decoderConfig->decoderSpecificInfo, &lsr_cfg);
167 	}
168 
169 	gf_laser_encoder_new_stream(seng->lsrenc, esd->ESID , &lsr_cfg);
170 
171 	gf_laser_encoder_get_config(seng->lsrenc, esd->ESID, &data, &data_len);
172 
173 	if (esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo);
174 	esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);
175 	esd->decoderConfig->decoderSpecificInfo->data = data;
176 	esd->decoderConfig->decoderSpecificInfo->dataLength = data_len;
177 
178 	sc->dec_cfg = (char*)gf_malloc(sizeof(char)*data_len);
179 	memcpy(sc->dec_cfg, data, data_len);
180 	sc->dec_cfg_len = data_len;
181 	return GF_OK;
182 }
183 #endif /*GPAC_DISABLE_LASER*/
184 
gf_sm_live_setup(GF_SceneEngine * seng)185 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 	iod = (GF_InitialObjectDescriptor *) seng->ctx->root_od;
196 
197 	/*build an IOD*/
198 	if (!iod) {
199 		seng->ctx->root_od = (GF_ObjectDescriptor*) gf_odf_desc_new(GF_ODF_IOD_TAG);
200 		iod = (GF_InitialObjectDescriptor *) seng->ctx->root_od;
201 
202 		i=0;
203 		while ((sc = gf_list_enum(seng->ctx->streams, &i))) {
204 			if (sc->streamType != GF_STREAM_SCENE) continue;
205 
206 			if (!sc->ESID) sc->ESID = 1;
207 
208 			esd = gf_odf_desc_esd_new(2);
209 			gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo);
210 			esd->decoderConfig->decoderSpecificInfo = NULL;
211 			esd->ESID = sc->ESID;
212 			esd->decoderConfig->streamType = GF_STREAM_SCENE;
213 			esd->decoderConfig->objectTypeIndication = sc->codec_id;
214 			gf_list_add(iod->ESDescriptors, esd);
215 
216 			if (!sc->timeScale) sc->timeScale = 1000;
217 			esd->slConfig->timestampResolution = sc->timeScale;
218 		}
219 	}
220 
221 	i=0;
222 	while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
223 
224 		j=0;
225 		while ((esd = gf_list_enum(seng->ctx->root_od->ESDescriptors, &j))) {
226 			if (sc->ESID==esd->ESID) {
227 				break;
228 			}
229 		}
230 		if (!esd) continue;
231 
232 		if (!esd->slConfig) esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG);
233 		if (!esd->slConfig->timestampResolution) esd->slConfig->timestampResolution = 1000;
234 		if (!sc->timeScale) sc->timeScale = esd->slConfig->timestampResolution;
235 
236 
237 		if (sc->streamType == GF_STREAM_SCENE) {
238 			switch (sc->codec_id) {
239 #ifndef GPAC_DISABLE_BIFS_ENC
240 			case GF_CODECID_BIFS:
241 			case GF_CODECID_BIFS_V2:
242 				e = gf_sm_setup_bifsenc(seng, sc, esd);
243 				break;
244 #endif
245 
246 #ifndef GPAC_DISABLE_LASER
247 			case GF_CODECID_LASER:
248 				e = gf_sm_setup_lsrenc(seng, sc, esd);
249 				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 			if (e) return e;
259 		}
260 	}
261 	return e;
262 }
263 
264 
265 GF_EXPORT
gf_seng_enable_aggregation(GF_SceneEngine * seng,u16 ESID,u16 onESID)266 GF_Err gf_seng_enable_aggregation(GF_SceneEngine *seng, u16 ESID, u16 onESID)
267 {
268 	GF_StreamContext *sc;
269 
270 	if (ESID) {
271 		u32 i=0;
272 		while (NULL != (sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
273 			if (0 != (sc->ESID==ESID)) break;
274 		}
275 	} else {
276 		sc = (GF_StreamContext*)gf_list_get(seng->ctx->streams, 0);
277 	}
278 	if (!sc) return GF_STREAM_NOT_FOUND;
279 
280 	sc->aggregate_on_esid = onESID;
281 	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 
gf_seng_encode_dims_au(GF_SceneEngine * seng,u16 ESID,GF_List * commands,u8 ** data,u32 * size,Bool compress_dims)287 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 	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 	if (!data) return GF_BAD_PARAM;
307 
308 	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 	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 	dumper = gf_sm_dumper_new(seng->ctx->scene_graph, rad_name, GF_FALSE, ' ', GF_SM_DUMP_SVG);
336 	if (!dumper) {
337 		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 	if (commands && gf_list_count(commands)) {
343 		e = gf_sm_dump_command_list(dumper, commands, 0, 0);
344 	}
345 	else {
346 		e = gf_sm_dump_graph(dumper, 0, 0);
347 	}
348 	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 	if (e) {
368 		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 	e = gf_file_load_data(file_name, (u8 **) &buffer, &fsize);
383 	if (e) {
384 		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 	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 	buffer_len = (u32)fsize;
398 	assert(fsize < 0x80000000);
399 	GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[SceneEngine] Sending DIMS data - sizes: raw (%d)", buffer_len));
400 	if (compress_dims) {
401 #ifndef GPAC_DISABLE_ZLIB
402 		dims_header |= GF_DIMS_UNIT_C;
403 		e = gf_gz_compress_payload(&buffer, buffer_len, &buffer_len);
404 		GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("/ compressed (%d)", buffer_len));
405 		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 	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 	bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
422 	if (buffer_len > 65535) {
423 		GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[SceneEngine] Warning: DIMS Unit size too big !!!\n"));
424 		gf_bs_write_u16(bs, 0); /* internal GPAC hack to indicate that the size is larger than 65535 */
425 		gf_bs_write_u32(bs, buffer_len+1);
426 	} else {
427 		gf_bs_write_u16(bs, buffer_len+1);
428 	}
429 	gf_bs_write_u8(bs, dims_header);
430 	gf_bs_write_data(bs, buffer, buffer_len);
431 
432 	gf_free(buffer);
433 	buffer = NULL;
434 
435 	gf_bs_get_content(bs, data, size);
436 	gf_bs_del(bs);
437 
438 exit:
439 	if (buffer) gf_free(buffer);
440 	return e;
441 }
442 
gf_sm_check_for_modif(GF_SceneEngine * seng,GF_AUContext * au)443 static Bool gf_sm_check_for_modif(GF_SceneEngine *seng, GF_AUContext *au)
444 {
445 	GF_Command *com;
446 	Bool modified=0;
447 	u32 i=0;
448 	/*au is marked as modified - this happens when commands are concatenated into the au*/
449 	if (au->flags & GF_SM_AU_MODIFIED) {
450 		au->flags &= ~GF_SM_AU_MODIFIED;
451 		modified=1;
452 	}
453 	/*check each command*/
454 	while (NULL != (com = gf_list_enum(au->commands, &i))) {
455 		u32 j=0;
456 		GF_CommandField *field;
457 		if (!com->node) continue;
458 		/*check root node (for SCENE_REPLACE) */
459 		if (gf_node_dirty_get(com->node)) {
460 			modified=1;
461 			gf_node_dirty_reset(com->node, 1);
462 		}
463 		/*check all command fields of type SFNODE or MFNODE*/
464 		while (NULL != (field = gf_list_enum(com->command_fields, &j))) {
465 			switch (field->fieldType) {
466 			case GF_SG_VRML_SFNODE:
467 				if (field->new_node) {
468 					if (gf_node_dirty_get(field->new_node)) {
469 						modified=1;
470 						gf_node_dirty_reset(field->new_node, 1);
471 					}
472 				}
473 				break;
474 			case GF_SG_VRML_MFNODE:
475 				if (field->field_ptr) {
476 					GF_ChildNodeItem *child;
477 					child = field->node_list;
478 					while (child) {
479 						if (gf_node_dirty_get(child->node)) {
480 							modified=1;
481 							gf_node_dirty_reset(child->node, 1);
482 						}
483 						child = child->next;
484 					}
485 				}
486 				break;
487 			}
488 		}
489 	}
490 
491 	if (!seng->first_dims_sent) {
492 		if (au->owner->codec_id==GF_CODECID_DIMS) {
493 			GF_Node *root = gf_sg_get_root_node(seng->ctx->scene_graph);
494 			if (gf_node_dirty_get(root)) {
495 				modified=1;
496 				gf_node_dirty_reset(root, 1);
497 			}
498 		} else {
499 		}
500 		seng->first_dims_sent = 1;
501 	}
502 	return modified;
503 }
504 
gf_sm_live_encode_scene_au(GF_SceneEngine * seng,gf_seng_callback callback,Bool from_start)505 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 	if (!callback) return GF_BAD_PARAM;
513 
514 	e = GF_OK;
515 
516 	nb_streams = gf_list_count(seng->ctx->streams);
517 	for (i=0; i<nb_streams; i++) {
518 		GF_StreamContext *sc = gf_list_get(seng->ctx->streams, i);
519 		if (sc->streamType != GF_STREAM_SCENE) continue;
520 
521 		count = gf_list_count(sc->AUs);
522 		j = from_start ? 0 : sc->current_au_count;
523 		for (; j<count; j++) {
524 			au = (GF_AUContext *)gf_list_get(sc->AUs, j);
525 			data = NULL;
526 			size = 0;
527 			/*in case using XMT*/
528 			if (au->timing_sec) au->timing = (u64) (au->timing_sec * sc->timeScale);
529 
530 			if (from_start && !j && !gf_sm_check_for_modif(seng, au)) continue;
531 
532 			switch (sc->codec_id) {
533 #ifndef GPAC_DISABLE_BIFS_ENC
534 			case GF_CODECID_BIFS:
535 			case GF_CODECID_BIFS_V2:
536 				e = gf_bifs_encode_au(seng->bifsenc, sc->ESID, au->commands, &data, &size);
537 				break;
538 #endif
539 
540 #ifndef GPAC_DISABLE_LASER
541 			case GF_CODECID_LASER:
542 				e = gf_laser_encode_au(seng->lsrenc, sc->ESID, au->commands, 0, &data, &size);
543 				break;
544 #endif
545 			case GF_CODECID_DIMS:
546 				e = gf_seng_encode_dims_au(seng, sc->ESID, au->commands, &data, &size, GF_TRUE);
547 				break;
548 
549 			default:
550 				GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("Cannot encode AU for Scene OTI %x\n", sc->codec_id));
551 				break;
552 			}
553 			callback(seng->calling_object, sc->ESID, data, size, au->timing);
554 			gf_free(data);
555 			data = NULL;
556 			if (e) break;
557 		}
558 	}
559 	return e;
560 }
561 
562 GF_EXPORT
gf_seng_aggregate_context(GF_SceneEngine * seng,u16 ESID)563 GF_Err gf_seng_aggregate_context(GF_SceneEngine *seng, u16 ESID)
564 {
565 	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
gf_seng_save_context(GF_SceneEngine * seng,char * ctxFileName)577 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 	if (ctxFileName) {
589 		strcpy(szF, ctxFileName);
590 		ext = gf_file_ext_start(szF);
591 	}
592 	d_mode = GF_SM_DUMP_BT;
593 	do_enc = 0;
594 	if (ext) {
595 		if (!stricmp(ext, ".xmt") || !stricmp(ext, ".xmta")) d_mode = GF_SM_DUMP_XMTA;
596 		else if (!stricmp(ext, ".mp4")) do_enc = 1;
597 		ext[0] = 0;
598 	}
599 
600 	if (do_enc) {
601 #ifndef GPAC_DISABLE_SCENE_ENCODER
602 		GF_ISOFile *mp4;
603 		strcat(szF, ".mp4");
604 		mp4 = gf_isom_open(szF, GF_ISOM_OPEN_WRITE, NULL);
605 		e = gf_sm_encode_to_file(seng->ctx, mp4, NULL);
606 		if (e) gf_isom_delete(mp4);
607 		else gf_isom_close(mp4);
608 #else
609 		return GF_NOT_SUPPORTED;
610 #endif
611 	} else {
612 		e = gf_sm_dump(seng->ctx, ctxFileName ? szF : NULL, GF_FALSE, d_mode);
613 	}
614 	return e;
615 #endif
616 }
617 
gf_seng_create_new_au(GF_StreamContext * sc,u32 time)618 static GF_AUContext *gf_seng_create_new_au(GF_StreamContext *sc, u32 time)
619 {
620 	GF_AUContext *new_au, *last_au;
621 	if (!sc) return NULL;
622 	last_au = gf_list_last(sc->AUs);
623 	if (last_au && last_au->timing == time) {
624 		GF_LOG(GF_LOG_DEBUG, GF_LOG_SCENE, ("[SceneEngine] Forcing new AU\n"));
625 		time++;
626 	}
627 	new_au = gf_sm_stream_au_new(sc, time, 0, 0);
628 	return new_au;
629 }
630 
631 GF_EXPORT
gf_seng_encode_from_string(GF_SceneEngine * seng,u16 ESID,Bool disable_aggregation,char * auString,gf_seng_callback callback)632 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 	i = 0;
639 	while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
640 		sc->current_au_count = gf_list_count(sc->AUs);
641 		sc->disable_aggregation = disable_aggregation;
642 	}
643 	seng->loader.flags |= GF_SM_LOAD_CONTEXT_READY;
644 	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 	sc = gf_list_get(seng->ctx->streams, 0);
648 	if (sc->codec_id == GF_CODECID_DIMS) {
649 		gf_seng_create_new_au(sc, 0);
650 	}
651 
652 	e = gf_sm_load_string(&seng->loader, auString, 0);
653 	if (e) goto exit;
654 
655 	i = 0;
656 	while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
657 		sc->disable_aggregation = 0;
658 	}
659 	e = gf_sm_live_encode_scene_au(seng, callback, 0);
660 exit:
661 	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
gf_seng_encode_from_file(GF_SceneEngine * seng,u16 ESID,Bool disable_aggregation,char * auFile,gf_seng_callback callback)745 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 	seng->loader.fileName = auFile;
753 	seng->loader.ctx = seng->ctx;
754 	seng->loader.force_es_id = ESID;
755 
756 	sc = NULL;
757 	i=0;
758 	while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
759 		sc->current_au_count = gf_list_count(sc->AUs);
760 		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 	sc = gf_list_get(seng->ctx->streams, 0);
764 	if (sc->codec_id == GF_CODECID_DIMS) {
765 		dims = 1;
766 		gf_seng_create_new_au(sc, 0);
767 	}
768 	seng->loader.flags |= GF_SM_LOAD_CONTEXT_READY;
769 
770 	if (dims) {
771 		seng->loader.type |= GF_SM_LOAD_DIMS;
772 	} else {
773 		seng->loader.flags |= GF_SM_LOAD_MPEG4_STRICT;
774 	}
775 	e = gf_sm_load_run(&seng->loader);
776 
777 	if (e<0) {
778 		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 	i = 0;
783 	while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
784 		sc->disable_aggregation = 0;
785 	}
786 
787 	e = gf_sm_live_encode_scene_au(seng, callback, 0);
788 	if (e) goto exit;
789 exit:
790 	return e;
791 }
792 
793 GF_EXPORT
gf_seng_encode_context(GF_SceneEngine * seng,gf_seng_callback callback)794 GF_Err gf_seng_encode_context(GF_SceneEngine *seng, gf_seng_callback callback)
795 {
796 	if (!seng) {
797 		GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] Cannot encode context. No seng provided\n"));
798 		return GF_BAD_PARAM;
799 	}
800 	return gf_sm_live_encode_scene_au(seng, callback, 1);
801 }
802 
803 GF_EXPORT
gf_seng_terminate(GF_SceneEngine * seng)804 void gf_seng_terminate(GF_SceneEngine *seng)
805 {
806 #ifndef GPAC_DISABLE_BIFS_ENC
807 	if (seng->bifsenc) gf_bifs_encoder_del(seng->bifsenc);
808 #endif
809 
810 #ifndef GPAC_DISABLE_LASER
811 	if (seng->lsrenc) gf_laser_encoder_del(seng->lsrenc);
812 #endif
813 
814 	gf_sm_load_done(&seng->loader);
815 
816 	if (seng->owns_context) {
817 		if (seng->ctx) gf_sm_del(seng->ctx);
818 		if (seng->sg) gf_sg_del(seng->sg);
819 	}
820 	gf_free(seng);
821 }
822 
823 GF_EXPORT
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)824 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 	GF_StreamContext *sc = gf_list_get(seng->ctx->streams, idx);
827 	if (!sc || !ESID || !config || !config_len) return GF_BAD_PARAM;
828 	*ESID = sc->ESID;
829 	*config = sc->dec_cfg;
830 	*config_len = sc->dec_cfg_len;
831 	if (streamType) *streamType = sc->streamType;
832 	if (codec_id) *codec_id = sc->codec_id;
833 	if (timeScale) *timeScale = sc->timeScale;
834 	return GF_OK;
835 }
836 
837 
838 #ifndef GPAC_DISABLE_VRML
839 
seng_exec_conditional(M_Conditional * c,GF_SceneGraph * scene)840 static void seng_exec_conditional(M_Conditional *c, GF_SceneGraph *scene)
841 {
842 	GF_List *clist = c->buffer.commandList;
843 	c->buffer.commandList = NULL;
844 
845 	gf_sg_command_apply_list(gf_node_get_graph((GF_Node*)c), clist, 0.0);
846 
847 	if (c->buffer.commandList != NULL) {
848 		while (gf_list_count(clist)) {
849 			GF_Command *sub_com = (GF_Command *)gf_list_get(clist, 0);
850 			gf_sg_command_del(sub_com);
851 			gf_list_rem(clist, 0);
852 		}
853 		gf_list_del(clist);
854 	} else {
855 		c->buffer.commandList = clist;
856 	}
857 }
858 
seng_conditional_activate(GF_Node * node,GF_Route * route)859 static void seng_conditional_activate(GF_Node *node, GF_Route *route)
860 {
861 	if (node) {
862 		GF_SceneEngine *seng = (GF_SceneEngine *) gf_node_get_private(node);
863 		M_Conditional *c = (M_Conditional*)node;
864 		if (c->activate) seng_exec_conditional(c, seng->sg);
865 	}
866 }
867 
seng_conditional_reverse_activate(GF_Node * node,GF_Route * route)868 static void seng_conditional_reverse_activate(GF_Node *node, GF_Route *route)
869 {
870 	if (node) {
871 		GF_SceneEngine *seng = (GF_SceneEngine *) gf_node_get_private(node);
872 		M_Conditional*c = (M_Conditional*)node;
873 		if (!c->reverseActivate) seng_exec_conditional(c, seng->sg);
874 	}
875 }
876 #endif //GPAC_DISABLE_VRML
877 
878 
gf_seng_on_node_modified(void * _seng,GF_SGNodeCbkType type,GF_Node * node,void * ctxdata)879 static void gf_seng_on_node_modified(void *_seng, GF_SGNodeCbkType type, GF_Node *node, void *ctxdata)
880 {
881 	switch (type) {
882 #ifndef GPAC_DISABLE_VRML
883 	case GF_SG_CALLBACK_INIT:
884 		if (gf_node_get_tag(node) == TAG_MPEG4_Conditional) {
885 			M_Conditional*c = (M_Conditional*)node;
886 			c->on_activate = seng_conditional_activate;
887 			c->on_reverseActivate = seng_conditional_reverse_activate;
888 			gf_node_set_private(node, _seng);
889 		}
890 		break;
891 #endif
892 	case GF_SG_CALLBACK_MODIFIED:
893 		gf_node_dirty_parents(node);
894 		break;
895 	default:
896 		break;
897 	}
898 }
899 
900 GF_EXPORT
gf_seng_init(void * calling_object,char * inputContext,u32 load_type,char * dump_path,Bool embed_resources)901 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 	if (!inputContext) return NULL;
907 
908 	GF_SAFEALLOC(seng, GF_SceneEngine)
909 	if (!seng) return NULL;
910 
911 	seng->calling_object = calling_object;
912 
913 	/*Step 1: create context and load input*/
914 	seng->sg = gf_sg_new();
915 	gf_sg_set_node_callback(seng->sg, gf_seng_on_node_modified);
916 	gf_sg_set_private(seng->sg, seng);
917 	seng->dump_path = dump_path;
918 	seng->ctx = gf_sm_new(seng->sg);
919 	seng->owns_context = 1;
920 	memset(&(seng->loader), 0, sizeof(GF_SceneLoader));
921 	seng->loader.ctx = seng->ctx;
922 	seng->loader.type = load_type;
923 	/*since we're encoding in BIFS we must get MPEG-4 nodes only*/
924 	seng->loader.flags = GF_SM_LOAD_MPEG4_STRICT;
925 	if (embed_resources) seng->loader.flags |= GF_SM_LOAD_EMBEDS_RES;
926 
927 #ifdef GPAC_ENABLE_COVERAGE
928 	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 	seng->loader.fileName = inputContext;
937 	e = gf_sm_load_init(&(seng->loader));
938 	if (!e) e = gf_sm_load_run(&(seng->loader));
939 
940 	if (e<0) {
941 		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 	e = gf_sm_live_setup(seng);
945 	if (e!=GF_OK) {
946 		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 exit:
952 	gf_seng_terminate(seng);
953 	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 overriden 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
gf_seng_get_stream_count(GF_SceneEngine * seng)1066 u32 gf_seng_get_stream_count(GF_SceneEngine *seng)
1067 {
1068 	return gf_list_count(seng->ctx->streams);
1069 }
1070 
1071 GF_EXPORT
gf_seng_get_stream_carousel_info(GF_SceneEngine * seng,u16 ESID,u32 * carousel_period,u16 * aggregate_on_es_id)1072 GF_Err gf_seng_get_stream_carousel_info(GF_SceneEngine *seng, u16 ESID, u32 *carousel_period, u16 *aggregate_on_es_id)
1073 {
1074 	u32 i=0;
1075 	GF_StreamContext *sc;
1076 
1077 	if (carousel_period) *carousel_period = (u32) -1;
1078 	if (aggregate_on_es_id) *aggregate_on_es_id = 0;
1079 
1080 	while (NULL != (sc = gf_list_enum(seng->ctx->streams, &i))) {
1081 		if (sc->ESID==ESID) {
1082 			if (carousel_period) *carousel_period = sc->carousel_period;
1083 			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
gf_seng_get_base64_iod(GF_SceneEngine * seng)1091 char *gf_seng_get_base64_iod(GF_SceneEngine *seng)
1092 {
1093 	u32 size, size64;
1094 	u8 *buffer, *buf64;
1095 	u32 i=0;
1096 	GF_StreamContext*sc = NULL;
1097 
1098 	if (!seng->ctx->root_od) return NULL;
1099 
1100 	while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
1101 		if ((sc->streamType == GF_STREAM_SCENE) && (sc->codec_id != GF_CODECID_DIMS))
1102 			break;
1103 	}
1104 	if (!sc) return NULL;
1105 
1106 	size = 0;
1107 	gf_odf_desc_write((GF_Descriptor *) seng->ctx->root_od, &buffer, &size);
1108 	buf64 = gf_malloc(size*2);
1109 	size64 = gf_base64_encode( buffer, size, buf64, size*2);
1110 	buf64[size64] = 0;
1111 	gf_free(buffer);
1112 	return buf64;
1113 }
1114 
1115 GF_EXPORT
gf_seng_get_iod(GF_SceneEngine * seng)1116 GF_Descriptor *gf_seng_get_iod(GF_SceneEngine *seng)
1117 {
1118 	u32 i=0;
1119 	GF_Descriptor *out_iod = NULL;
1120 	GF_StreamContext*sc = NULL;
1121 
1122 	if (!seng->ctx->root_od) return NULL;
1123 	while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
1124 		if ((sc->streamType == GF_STREAM_SCENE) && (sc->codec_id != GF_CODECID_DIMS))
1125 			break;
1126 	}
1127 	if (!sc) return NULL;
1128 	gf_odf_desc_copy((GF_Descriptor *)seng->ctx->root_od, &out_iod);
1129 	return out_iod;
1130 }
1131 
1132 
1133 #endif /*GPAC_DISABLE_SENG*/
1134 
1135