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