1 /*
2 *			GPAC - Multimedia Framework C SDK
3 *
4 *			Authors: Jean Le Feuvre
5 *			Copyright (c) Telecom ParisTech 2000-2012
6 *					All rights reserved
7 *
8 *  This file is part of GPAC / MPEG-4 ObjectDescriptor 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/internal/odf_dev.h>
27 
28 /************************************************************
29 Object GF_Descriptor Codec Functions
30 ************************************************************/
31 
32 GF_EXPORT
gf_odf_codec_new()33 GF_ODCodec *gf_odf_codec_new()
34 {
35 	GF_ODCodec *codec;
36 	GF_List *comList;
37 
38 	comList = gf_list_new();
39 	if (!comList) return NULL;
40 
41 	codec = (GF_ODCodec *)gf_malloc(sizeof(GF_ODCodec));
42 	if (!codec) {
43 		gf_list_del(comList);
44 		return NULL;
45 	}
46 	//the bitstream is always NULL. It is created on the fly for access unit processing only
47 	codec->bs = NULL;
48 	codec->CommandList = comList;
49 	return codec;
50 }
51 
52 GF_EXPORT
gf_odf_codec_del(GF_ODCodec * codec)53 void gf_odf_codec_del(GF_ODCodec *codec)
54 {
55 	if (!codec) return;
56 
57 	while (gf_list_count(codec->CommandList)) {
58 		GF_ODCom *com = (GF_ODCom *)gf_list_get(codec->CommandList, 0);
59 		gf_odf_delete_command(com);
60 		gf_list_rem(codec->CommandList, 0);
61 	}
62 	gf_list_del(codec->CommandList);
63 	if (codec->bs) gf_bs_del(codec->bs);
64 	gf_free(codec);
65 }
66 
67 
68 /************************************************************
69 Codec Encoder Functions
70 ************************************************************/
71 
72 GF_EXPORT
gf_odf_codec_add_com(GF_ODCodec * codec,GF_ODCom * command)73 GF_Err gf_odf_codec_add_com(GF_ODCodec *codec, GF_ODCom *command)
74 {
75 	if (!codec || !command) return GF_BAD_PARAM;
76 	return gf_list_add(codec->CommandList, command);
77 }
78 
79 GF_EXPORT
gf_odf_codec_encode(GF_ODCodec * codec,u32 cleanup_type)80 GF_Err gf_odf_codec_encode(GF_ODCodec *codec, u32 cleanup_type)
81 {
82 	GF_ODCom *com;
83 	GF_Err e = GF_OK;
84 	u32 i;
85 
86 	if (!codec) return GF_BAD_PARAM;
87 
88 	//check our bitstream: if existing, this means the previous encoded AU was not retrieved
89 	//we DON'T allow that
90 	if (codec->bs) return GF_BAD_PARAM;
91 	codec->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
92 	if (!codec->bs) return GF_OUT_OF_MEM;
93 
94 	/*encode each command*/
95 	i = 0;
96 	while ((com = (GF_ODCom *)gf_list_enum(codec->CommandList, &i))) {
97 		e = gf_odf_write_command(codec->bs, com);
98 		if (e) goto err_exit;
99 		//don't forget OD Commands are aligned...
100 		gf_bs_align(codec->bs);
101 	}
102 
103 	//if an error occurs, delete the GF_BitStream and empty the codec
104 err_exit:
105 	if (e) {
106 		gf_bs_del(codec->bs);
107 		codec->bs = NULL;
108 	}
109 	if (cleanup_type == 1) {
110 		while (gf_list_count(codec->CommandList)) {
111 			com = (GF_ODCom *)gf_list_get(codec->CommandList, 0);
112 			gf_odf_delete_command(com);
113 			gf_list_rem(codec->CommandList, 0);
114 		}
115 	}
116 	if (cleanup_type == 0) {
117 		gf_list_reset(codec->CommandList);
118 	}
119 	return e;
120 }
121 
122 GF_EXPORT
gf_odf_codec_get_au(GF_ODCodec * codec,char ** outAU,u32 * au_length)123 GF_Err gf_odf_codec_get_au(GF_ODCodec *codec, char **outAU, u32 *au_length)
124 {
125 	if (!codec || !codec->bs || !outAU || *outAU) return GF_BAD_PARAM;
126 	gf_bs_get_content(codec->bs, outAU, au_length);
127 	gf_bs_del(codec->bs);
128 	codec->bs = NULL;
129 	return GF_OK;
130 }
131 
132 
133 
134 /************************************************************
135 Codec Decoder Functions
136 ************************************************************/
137 
138 GF_EXPORT
gf_odf_codec_set_au(GF_ODCodec * codec,const char * au,u32 au_length)139 GF_Err gf_odf_codec_set_au(GF_ODCodec *codec, const char *au, u32 au_length)
140 {
141 	if (!codec) return GF_BAD_PARAM;
142 	if (!au || !au_length) return GF_OK;
143 
144 	//if the command list is not empty, this is an error
145 	if (gf_list_count(codec->CommandList)) return GF_BAD_PARAM;
146 
147 	//the bitStream should not be here
148 	if (codec->bs) return GF_BAD_PARAM;
149 
150 	codec->bs = gf_bs_new(au, (u64)au_length, (unsigned char)GF_BITSTREAM_READ);
151 	if (!codec->bs) return GF_OUT_OF_MEM;
152 	return GF_OK;
153 }
154 
155 
156 GF_EXPORT
gf_odf_codec_decode(GF_ODCodec * codec)157 GF_Err gf_odf_codec_decode(GF_ODCodec *codec)
158 {
159 	GF_Err e = GF_OK;
160 	u32 size = 0, comSize, bufSize;
161 	GF_ODCom *com;
162 
163 	if (!codec || !codec->bs) return GF_BAD_PARAM;
164 
165 	bufSize = (u32)gf_bs_available(codec->bs);
166 	while (size < bufSize) {
167 		e = gf_odf_parse_command(codec->bs, &com, &comSize);
168 		if (e) goto err_exit;
169 		gf_list_add(codec->CommandList, com);
170 		size += comSize + gf_odf_size_field_size(comSize);
171 		//OD Commands are aligned
172 		gf_bs_align(codec->bs);
173 	}
174 	//then delete our bitstream
175 	gf_bs_del(codec->bs);
176 	codec->bs = NULL;
177 	if (size != bufSize) {
178 		e = GF_ODF_INVALID_COMMAND;
179 		goto err_exit;
180 	}
181 	return e;
182 
183 err_exit:
184 	if (codec->bs) {
185 		gf_bs_del(codec->bs);
186 		codec->bs = NULL;
187 	}
188 	while (gf_list_count(codec->CommandList)) {
189 		com = (GF_ODCom*)gf_list_get(codec->CommandList, 0);
190 		gf_odf_delete_command(com);
191 		gf_list_rem(codec->CommandList, 0);
192 	}
193 	return e;
194 }
195 
196 //get the first command in the codec and remove the entry
197 GF_EXPORT
gf_odf_codec_get_com(GF_ODCodec * codec)198 GF_ODCom *gf_odf_codec_get_com(GF_ODCodec *codec)
199 {
200 	GF_ODCom *com;
201 	if (!codec || codec->bs) return NULL;
202 	com = (GF_ODCom*)gf_list_get(codec->CommandList, 0);
203 	if (com) gf_list_rem(codec->CommandList, 0);
204 	return com;
205 }
206 
207 
208 
209 /************************************************************
210 OD Commands Functions
211 ************************************************************/
212 
213 //some easy way to get an OD GF_ODCom...
214 GF_EXPORT
gf_odf_com_new(u8 tag)215 GF_ODCom *gf_odf_com_new(u8 tag)
216 {
217 	GF_ODCom *newcom;
218 
219 	newcom = gf_odf_create_command(tag);
220 	newcom->tag = tag;
221 	return (GF_ODCom *)newcom;
222 }
223 
224 //	... and to delete it
225 GF_EXPORT
gf_odf_com_del(GF_ODCom ** com)226 GF_Err gf_odf_com_del(GF_ODCom **com)
227 {
228 	GF_Err e;
229 	e = gf_odf_delete_command(*com);
230 	*com = NULL;
231 	return e;
232 }
233 
234 
235 /************************************************************
236 Object Descriptors Functions
237 ************************************************************/
238 
239 //some easy way to get an mpeg4 descriptor ...
240 GF_EXPORT
gf_odf_desc_new(u8 tag)241 GF_Descriptor *gf_odf_desc_new(u8 tag)
242 {
243 	GF_Descriptor *newdesc;
244 	newdesc = gf_odf_create_descriptor(tag);
245 	newdesc->tag = tag;
246 	return (GF_Descriptor *)newdesc;
247 }
248 
249 //	... and to delete it
250 GF_EXPORT
gf_odf_desc_del(GF_Descriptor * desc)251 void gf_odf_desc_del(GF_Descriptor *desc)
252 {
253 	if (desc) gf_odf_delete_descriptor(desc);
254 }
255 
256 //this functions will destroy the descriptors in a list but not the list
257 GF_EXPORT
gf_odf_desc_list_del(GF_List * descList)258 GF_Err gf_odf_desc_list_del(GF_List *descList)
259 {
260 	GF_Err e;
261 	GF_Descriptor *tmp;
262 
263 	if (!descList) return GF_BAD_PARAM;
264 
265 	while (gf_list_count(descList)) {
266 		tmp = (GF_Descriptor*)gf_list_get(descList, 0);
267 		gf_list_rem(descList, 0);
268 		e = gf_odf_delete_descriptor(tmp);
269 		if (e) return e;
270 	}
271 	return GF_OK;
272 }
273 
274 
275 
276 GF_EXPORT
gf_odf_desc_esd_new(u32 sl_predefined)277 GF_ESD *gf_odf_desc_esd_new(u32 sl_predefined)
278 {
279 	GF_ESD *esd;
280 	esd = (GF_ESD *)gf_odf_desc_new(GF_ODF_ESD_TAG);
281 	esd->decoderConfig = (GF_DecoderConfig *)gf_odf_desc_new(GF_ODF_DCD_TAG);
282 	esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *)gf_odf_desc_new(GF_ODF_DSI_TAG);
283 	esd->slConfig = (GF_SLConfig *)gf_odf_new_slc((u8)sl_predefined);
284 	return esd;
285 }
286 
287 
288 //use this function to decode a standalone descriptor
289 //the desc MUST be formatted with tag and size field!!!
290 GF_EXPORT
gf_odf_desc_read(char * raw_desc,u32 descSize,GF_Descriptor ** outDesc)291 GF_Err gf_odf_desc_read(char *raw_desc, u32 descSize, GF_Descriptor **outDesc)
292 {
293 	GF_Err e;
294 	u32 size;
295 	GF_BitStream *bs;
296 
297 	if (!raw_desc || !descSize) return GF_BAD_PARAM;
298 
299 	bs = gf_bs_new(raw_desc, (u64)descSize, GF_BITSTREAM_READ);
300 	if (!bs) return GF_OUT_OF_MEM;
301 
302 	size = 0;
303 	e = gf_odf_parse_descriptor(bs, outDesc, &size);
304 	//the size dosn't have the header in it
305 	size += gf_odf_size_field_size(size);
306 	/*
307 	if (size != descSize) {
308 	if (*outDesc) gf_odf_delete_descriptor(*outDesc);
309 	*outDesc = NULL;
310 	e = GF_ODF_INVALID_DESCRIPTOR;
311 	}
312 	*/
313 
314 	gf_bs_del(bs);
315 	return e;
316 }
317 
318 //use this function to encode a standalone descriptor
319 //the desc will be formatted with tag and size field
320 GF_EXPORT
gf_odf_desc_write_bs(GF_Descriptor * desc,GF_BitStream * bs)321 GF_Err gf_odf_desc_write_bs(GF_Descriptor *desc, GF_BitStream *bs)
322 {
323 	GF_Err e;
324 	if (!desc || !bs) return GF_BAD_PARAM;
325 
326 	//then encode our desc...
327 	e = gf_odf_write_descriptor(bs, desc);
328 	if (e) {
329 		gf_bs_del(bs);
330 		return e;
331 	}
332 	return GF_OK;
333 }
334 
335 GF_EXPORT
gf_odf_desc_write(GF_Descriptor * desc,char ** outEncDesc,u32 * outSize)336 GF_Err gf_odf_desc_write(GF_Descriptor *desc, char **outEncDesc, u32 *outSize)
337 {
338 	GF_Err e;
339 	GF_BitStream *bs;
340 	if (!desc || !outEncDesc || !outSize) return GF_BAD_PARAM;
341 	*outEncDesc = NULL;
342 	*outSize = 0;
343 
344 	bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
345 	if (!bs) return GF_OUT_OF_MEM;
346 
347 	e = gf_odf_desc_write_bs(desc, bs);
348 
349 	//then get the content from our bitstream
350 	gf_bs_get_content(bs, outEncDesc, outSize);
351 	gf_bs_del(bs);
352 	return e;
353 }
354 
355 
356 //use this function to get the size of a standalone descriptor
357 GF_EXPORT
gf_odf_desc_size(GF_Descriptor * desc)358 u32 gf_odf_desc_size(GF_Descriptor *desc)
359 {
360 	u32 descSize;
361 	GF_Err e;
362 
363 	if (!desc) return GF_BAD_PARAM;
364 	//get the descriptor length
365 	e = gf_odf_size_descriptor(desc, &descSize);
366 	if (e) return 0;
367 	//add the header length
368 	descSize += gf_odf_size_field_size(descSize);
369 	return descSize;
370 
371 }
372 
373 //this is useful to duplicate on the fly a descriptor (mainly for authoring purposes)
374 GF_EXPORT
gf_odf_desc_copy(GF_Descriptor * inDesc,GF_Descriptor ** outDesc)375 GF_Err gf_odf_desc_copy(GF_Descriptor *inDesc, GF_Descriptor **outDesc)
376 {
377 	GF_Err e;
378 	char *desc;
379 	u32 size;
380 
381 	//warning: here we get some data allocated
382 	e = gf_odf_desc_write(inDesc, &desc, &size);
383 	if (e) return e;
384 	e = gf_odf_desc_read(desc, size, outDesc);
385 	gf_free(desc);
386 	return e;
387 }
388 
389 /************************************************************
390 Object Descriptors Edit Functions
391 ************************************************************/
392 
393 //This functions handles internally what desc can be added to another desc
394 //and adds it. NO DUPLICATION of the descriptor, so
395 //once a desc is added to its parent, destroying the parent WILL destroy this desc
396 GF_EXPORT
gf_odf_desc_add_desc(GF_Descriptor * parentDesc,GF_Descriptor * newDesc)397 GF_Err gf_odf_desc_add_desc(GF_Descriptor *parentDesc, GF_Descriptor *newDesc)
398 {
399 	GF_DecoderConfig *dcd;
400 
401 	//our ADD definition
402 	GF_Err AddDescriptorToOD(GF_ObjectDescriptor *od, GF_Descriptor *desc);
403 	GF_Err AddDescriptorToIOD(GF_InitialObjectDescriptor *iod, GF_Descriptor *desc);
404 	GF_Err AddDescriptorToESD(GF_ESD *esd, GF_Descriptor *desc);
405 	GF_Err AddDescriptorToIsomIOD(GF_IsomInitialObjectDescriptor *iod, GF_Descriptor *desc);
406 	GF_Err AddDescriptorToIsomOD(GF_IsomObjectDescriptor *od, GF_Descriptor *desc);
407 
408 	if (!parentDesc || !newDesc) return GF_BAD_PARAM;
409 
410 	switch (parentDesc->tag) {
411 		//these are container descriptors
412 	case GF_ODF_OD_TAG:
413 		return AddDescriptorToOD((GF_ObjectDescriptor *)parentDesc, newDesc);
414 	case GF_ODF_IOD_TAG:
415 		return AddDescriptorToIOD((GF_InitialObjectDescriptor *)parentDesc, newDesc);
416 	case GF_ODF_ESD_TAG:
417 		return AddDescriptorToESD((GF_ESD *)parentDesc, newDesc);
418 	case GF_ODF_DCD_TAG:
419 		dcd = (GF_DecoderConfig *)parentDesc;
420 		if ((newDesc->tag == GF_ODF_DSI_TAG)
421 			|| (newDesc->tag == GF_ODF_BIFS_CFG_TAG)
422 			|| (newDesc->tag == GF_ODF_UI_CFG_TAG)
423 			|| (newDesc->tag == GF_ODF_TEXT_CFG_TAG)
424 			) {
425 			if (dcd->decoderSpecificInfo) return GF_ODF_FORBIDDEN_DESCRIPTOR;
426 			dcd->decoderSpecificInfo = (GF_DefaultDescriptor *)newDesc;
427 			return GF_OK;
428 		}
429 		else if (newDesc->tag == GF_ODF_EXT_PL_TAG) {
430 			return gf_list_add(dcd->profileLevelIndicationIndexDescriptor, newDesc);
431 		}
432 		return GF_ODF_FORBIDDEN_DESCRIPTOR;
433 
434 	case GF_ODF_TEXT_CFG_TAG:
435 		if (newDesc->tag != GF_ODF_TX3G_TAG) return GF_ODF_FORBIDDEN_DESCRIPTOR;
436 		return gf_list_add(((GF_TextConfig *)parentDesc)->sample_descriptions, newDesc);
437 
438 	case GF_ODF_QOS_TAG:
439 		return GF_BAD_PARAM;
440 
441 		//MP4 File Format tags
442 	case GF_ODF_ISOM_IOD_TAG:
443 		return AddDescriptorToIsomIOD((GF_IsomInitialObjectDescriptor *)parentDesc, newDesc);
444 	case GF_ODF_ISOM_OD_TAG:
445 		return AddDescriptorToIsomOD((GF_IsomObjectDescriptor *)parentDesc, newDesc);
446 
447 	case GF_ODF_IPMP_TL_TAG:
448 		if (newDesc->tag != GF_ODF_IPMP_TOOL_TAG) return GF_BAD_PARAM;
449 		return gf_list_add(((GF_IPMP_ToolList *)parentDesc)->ipmp_tools, newDesc);
450 
451 	case GF_ODF_BIFS_CFG_TAG:
452 	{
453 		GF_BIFSConfig *cfg = (GF_BIFSConfig *)parentDesc;
454 		if (newDesc->tag != GF_ODF_ELEM_MASK_TAG) return GF_BAD_PARAM;
455 		if (!cfg->elementaryMasks) cfg->elementaryMasks = gf_list_new();
456 		return gf_list_add(cfg->elementaryMasks, newDesc);
457 	}
458 	default:
459 		return GF_ODF_FORBIDDEN_DESCRIPTOR;
460 	}
461 }
462 
463 
464 
465 /*****************************************************************************************
466 Since IPMP V2, we introduce a new set of functions to read / write a list of
467 descriptors that have no containers (a bit like an OD command, but for descriptors)
468 This is useful for IPMPv2 DecoderSpecificInfo which contains a set of
469 IPMP_Declarators.
470 As it could be used for other purposes we keep it generic
471 You must create the list yourself, the functions just encode/decode from/to the list
472 *****************************************************************************************/
473 
474 GF_EXPORT
gf_odf_desc_list_read(char * raw_list,u32 raw_size,GF_List * descList)475 GF_Err gf_odf_desc_list_read(char *raw_list, u32 raw_size, GF_List *descList)
476 {
477 	GF_BitStream *bs;
478 	u32 size, desc_size;
479 	GF_Descriptor *desc;
480 	GF_Err e = GF_OK;
481 
482 	if (!descList || !raw_list || !raw_size) return GF_BAD_PARAM;
483 
484 	bs = gf_bs_new(raw_list, raw_size, GF_BITSTREAM_READ);
485 	if (!bs) return GF_OUT_OF_MEM;
486 
487 	size = 0;
488 	while (size < raw_size) {
489 		e = gf_odf_parse_descriptor(bs, &desc, &desc_size);
490 		if (e) goto exit;
491 		gf_list_add(descList, desc);
492 		size += desc_size + gf_odf_size_field_size(desc_size);
493 	}
494 
495 exit:
496 	//then delete our bitstream
497 	gf_bs_del(bs);
498 	if (size != raw_size) e = GF_ODF_INVALID_DESCRIPTOR;
499 	return e;
500 }
501 
502 
503 GF_EXPORT
gf_odf_desc_list_write(GF_List * descList,char ** outEncList,u32 * outSize)504 GF_Err gf_odf_desc_list_write(GF_List *descList, char **outEncList, u32 *outSize)
505 {
506 	GF_BitStream *bs;
507 	GF_Err e;
508 
509 	if (!descList || !outEncList || *outEncList || !outSize) return GF_BAD_PARAM;
510 
511 	*outSize = 0;
512 
513 	bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
514 	if (!bs) return GF_OUT_OF_MEM;
515 
516 	e = gf_odf_write_descriptor_list(bs, descList);
517 	if (e) {
518 		gf_bs_del(bs);
519 		return e;
520 	}
521 
522 	gf_bs_get_content(bs, outEncList, outSize);
523 	gf_bs_del(bs);
524 	return GF_OK;
525 }
526 
527 GF_EXPORT
gf_odf_desc_list_size(GF_List * descList,u32 * outSize)528 GF_Err gf_odf_desc_list_size(GF_List *descList, u32 *outSize)
529 {
530 	return gf_odf_size_descriptor_list(descList, outSize);
531 }
532 
533 
534 
gf_odf_codec_apply_com(GF_ODCodec * codec,GF_ODCom * command)535 GF_Err gf_odf_codec_apply_com(GF_ODCodec *codec, GF_ODCom *command)
536 {
537 	GF_ODCom *com;
538 	GF_ODUpdate *odU, *odU_o;
539 	u32 i, count;
540 	count = gf_list_count(codec->CommandList);
541 
542 	switch (command->tag) {
543 	case GF_ODF_OD_REMOVE_TAG:
544 		for (i = 0; i<count; i++) {
545 			com = (GF_ODCom *)gf_list_get(codec->CommandList, i);
546 			/*process OD updates*/
547 			if (com->tag == GF_ODF_OD_UPDATE_TAG) {
548 				u32 count, j, k;
549 				GF_ODRemove *odR = (GF_ODRemove *)command;
550 				odU = (GF_ODUpdate *)com;
551 				count = gf_list_count(odU->objectDescriptors);
552 				/*remove all descs*/
553 				for (k = 0; k<count; k++) {
554 					GF_ObjectDescriptor *od = (GF_ObjectDescriptor *)gf_list_get(odU->objectDescriptors, k);
555 					for (j = 0; j<odR->NbODs; j++) {
556 						if (od->objectDescriptorID == odR->OD_ID[j]) {
557 							gf_list_rem(odU->objectDescriptors, k);
558 							k--;
559 							count--;
560 							gf_odf_desc_del((GF_Descriptor *)od);
561 							break;
562 						}
563 					}
564 				}
565 				if (!gf_list_count(odU->objectDescriptors)) {
566 					gf_list_rem(codec->CommandList, i);
567 					i--;
568 					count--;
569 				}
570 			}
571 			/*process ESD updates*/
572 			else if (com->tag == GF_ODF_ESD_UPDATE_TAG) {
573 				u32 j;
574 				GF_ODRemove *odR = (GF_ODRemove *)command;
575 				GF_ESDUpdate *esdU = (GF_ESDUpdate*)com;
576 				for (j = 0; j<odR->NbODs; j++) {
577 					if (esdU->ODID == odR->OD_ID[j]) {
578 						gf_list_rem(codec->CommandList, i);
579 						i--;
580 						count--;
581 						gf_odf_com_del((GF_ODCom**)&esdU);
582 						break;
583 					}
584 				}
585 			}
586 		}
587 		return GF_OK;
588 	case GF_ODF_OD_UPDATE_TAG:
589 		odU_o = NULL;
590 		for (i = 0; i<count; i++) {
591 			odU_o = (GF_ODUpdate*)gf_list_get(codec->CommandList, i);
592 			/*process OD updates*/
593 			if (odU_o->tag == GF_ODF_OD_UPDATE_TAG) break;
594 			odU_o = NULL;
595 		}
596 		if (!odU_o) {
597 			odU_o = (GF_ODUpdate *)gf_odf_com_new(GF_ODF_OD_UPDATE_TAG);
598 			gf_list_add(codec->CommandList, odU_o);
599 		}
600 		odU = (GF_ODUpdate*)command;
601 		count = gf_list_count(odU->objectDescriptors);
602 		for (i = 0; i<count; i++) {
603 			Bool found = GF_FALSE;
604 			GF_ObjectDescriptor *od = (GF_ObjectDescriptor *)gf_list_get(odU->objectDescriptors, i);
605 			u32 j, count2 = gf_list_count(odU_o->objectDescriptors);
606 			for (j = 0; j<count2; j++) {
607 				GF_ObjectDescriptor *od2 = (GF_ObjectDescriptor *)gf_list_get(odU_o->objectDescriptors, j);
608 				if (od2->objectDescriptorID == od->objectDescriptorID) {
609 					found = GF_TRUE;
610 					break;
611 				}
612 			}
613 			if (!found) {
614 				GF_ObjectDescriptor *od_new;
615 				GF_Err e = gf_odf_desc_copy((GF_Descriptor*)od, (GF_Descriptor**)&od_new);
616 				if (e == GF_OK)
617 					gf_list_add(odU_o->objectDescriptors, od_new);
618 			}
619 
620 		}
621 		return GF_OK;
622 	}
623 	return GF_NOT_SUPPORTED;
624 }
625