1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Jean Le Feuvre
5 * Copyright (c) Telecom ParisTech 2000-2020
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/internal/isomedia_dev.h>
27 #include <gpac/constants.h>
28 #include <gpac/iso639.h>
29
30
31 #if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)
32
CanAccessMovie(GF_ISOFile * movie,GF_ISOOpenMode Mode)33 GF_Err CanAccessMovie(GF_ISOFile *movie, GF_ISOOpenMode Mode)
34 {
35 if (!movie) return GF_BAD_PARAM;
36 if (movie->openMode < Mode) return GF_ISOM_INVALID_MODE;
37
38 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
39 if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_ISOM_INVALID_MODE;
40 #endif
41 return GF_OK;
42 }
43
unpack_track(GF_TrackBox * trak)44 static GF_Err unpack_track(GF_TrackBox *trak)
45 {
46 GF_Err e = GF_OK;
47 if (!trak->is_unpacked) {
48 e = stbl_UnpackOffsets(trak->Media->information->sampleTable);
49 if (e) return e;
50 e = stbl_unpackCTS(trak->Media->information->sampleTable);
51 trak->is_unpacked = GF_TRUE;
52 }
53 return e;
54 }
55
56
FlushCaptureMode(GF_ISOFile * movie)57 GF_Err FlushCaptureMode(GF_ISOFile *movie)
58 {
59 GF_Err e;
60 if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_OK;
61 /*make sure nothing was added*/
62 if (gf_bs_get_position(movie->editFileMap->bs)) return GF_OK;
63
64 if (!strcmp(movie->fileName, "_gpac_isobmff_redirect")) {
65 if (!movie->on_block_out) {
66 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block callback, cannot write\n"));
67 return GF_BAD_PARAM;
68 }
69
70 gf_bs_del(movie->editFileMap->bs);
71 movie->editFileMap->bs = gf_bs_new_cbk(movie->on_block_out, movie->on_block_out_usr_data, movie->on_block_out_block_size);
72 }
73
74 /*add all first boxes*/
75 if (movie->brand) {
76 e = gf_isom_box_size((GF_Box *)movie->brand);
77 if (e) return e;
78 e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs);
79 if (e) return e;
80 }
81 if (movie->pdin) {
82 e = gf_isom_box_size((GF_Box *)movie->pdin);
83 if (e) return e;
84 e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs);
85 if (e) return e;
86 }
87 movie->mdat->bsOffset = gf_bs_get_position(movie->editFileMap->bs);
88
89 /*we have a trick here: the data will be stored on the fly, so the first
90 thing in the file is the MDAT. As we don't know if we have a large file (>4 GB) or not
91 do as if we had one and write 16 bytes: 4 (type) + 4 (size) + 8 (largeSize)...*/
92 gf_bs_write_long_int(movie->editFileMap->bs, 0, 64);
93 gf_bs_write_long_int(movie->editFileMap->bs, 0, 64);
94 return GF_OK;
95 }
96
CheckNoData(GF_ISOFile * movie)97 static GF_Err CheckNoData(GF_ISOFile *movie)
98 {
99 if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_OK;
100 if (gf_bs_get_position(movie->editFileMap->bs)) return GF_BAD_PARAM;
101 return GF_OK;
102 }
103
104 /**************************************************************
105 File Writing / Editing
106 **************************************************************/
107 //quick function to add an IOD/OD to the file if not present (iods is optional)
AddMovieIOD(GF_MovieBox * moov,u8 isIOD)108 GF_Err AddMovieIOD(GF_MovieBox *moov, u8 isIOD)
109 {
110 GF_Descriptor *od;
111 GF_ObjectDescriptorBox *iods;
112
113 //do we have an IOD ?? If not, create one.
114 if (moov->iods) return GF_OK;
115
116 if (isIOD) {
117 od = gf_odf_desc_new(GF_ODF_ISOM_IOD_TAG);
118 } else {
119 od = gf_odf_desc_new(GF_ODF_ISOM_OD_TAG);
120 }
121 if (!od) return GF_OUT_OF_MEM;
122 ((GF_IsomObjectDescriptor *)od)->objectDescriptorID = 1;
123
124 iods = (GF_ObjectDescriptorBox *) gf_isom_box_new_parent(&moov->child_boxes, GF_ISOM_BOX_TYPE_IODS);
125 if (!iods) return GF_OUT_OF_MEM;
126 iods->descriptor = od;
127 return moov_on_child_box((GF_Box*)moov, (GF_Box *)iods);
128 }
129
130 //add a track to the root OD
131 GF_EXPORT
gf_isom_add_track_to_root_od(GF_ISOFile * movie,u32 trackNumber)132 GF_Err gf_isom_add_track_to_root_od(GF_ISOFile *movie, u32 trackNumber)
133 {
134 GF_Err e;
135 GF_ES_ID_Inc *inc;
136
137 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
138 if (e) return e;
139 e = gf_isom_insert_moov(movie);
140 if (e) return e;
141
142 if (!movie->moov->iods) AddMovieIOD(movie->moov, 0);
143
144 if (gf_isom_is_track_in_root_od(movie, trackNumber) == 1) return GF_OK;
145
146 inc = (GF_ES_ID_Inc *) gf_odf_desc_new(GF_ODF_ESD_INC_TAG);
147 inc->trackID = gf_isom_get_track_id(movie, trackNumber);
148 if (!inc->trackID) {
149 gf_odf_desc_del((GF_Descriptor *)inc);
150 return movie->LastError;
151 }
152 if ( (movie->LastError = gf_isom_add_desc_to_root_od(movie, (GF_Descriptor *)inc) ) ) {
153 return movie->LastError;
154 }
155 gf_odf_desc_del((GF_Descriptor *)inc);
156 return GF_OK;
157 }
158
159 //remove the root OD
160 GF_EXPORT
gf_isom_remove_root_od(GF_ISOFile * movie)161 GF_Err gf_isom_remove_root_od(GF_ISOFile *movie)
162 {
163 GF_Err e;
164
165 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
166 if (e) return e;
167 if (!movie->moov || !movie->moov->iods) return GF_OK;
168 gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box *)movie->moov->iods);
169 movie->moov->iods = NULL;
170 return GF_OK;
171 }
172
173 //remove a track to the root OD
174 GF_EXPORT
gf_isom_remove_track_from_root_od(GF_ISOFile * movie,u32 trackNumber)175 GF_Err gf_isom_remove_track_from_root_od(GF_ISOFile *movie, u32 trackNumber)
176 {
177 GF_List *esds;
178 GF_ES_ID_Inc *inc;
179 u32 i;
180 GF_Err e;
181
182 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
183 if (e) return e;
184 if (!movie->moov) return GF_OK;
185
186 if (!gf_isom_is_track_in_root_od(movie, trackNumber)) return GF_OK;
187
188 if (!movie->moov->iods) {
189 e = AddMovieIOD(movie->moov, 0);
190 if (e) return e;
191 }
192 switch (movie->moov->iods->descriptor->tag) {
193 case GF_ODF_ISOM_IOD_TAG:
194 esds = ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors;
195 break;
196 case GF_ODF_ISOM_OD_TAG:
197 esds = ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors;
198 break;
199 default:
200 return GF_ISOM_INVALID_FILE;
201 }
202
203 //get the desc
204 i=0;
205 while ((inc = (GF_ES_ID_Inc*)gf_list_enum(esds, &i))) {
206 if (inc->trackID == (u32) gf_isom_get_track_id(movie, trackNumber)) {
207 gf_odf_desc_del((GF_Descriptor *)inc);
208 gf_list_rem(esds, i-1);
209 break;
210 }
211 }
212 //we don't remove the iod for P&Ls and other potential info
213 return GF_OK;
214 }
215
216 GF_EXPORT
gf_isom_set_creation_time(GF_ISOFile * movie,u64 time)217 GF_Err gf_isom_set_creation_time(GF_ISOFile *movie, u64 time)
218 {
219 if (!movie || !movie->moov) return GF_BAD_PARAM;
220 movie->moov->mvhd->creationTime = time;
221 movie->moov->mvhd->modificationTime = time;
222 return GF_OK;
223 }
224
225 GF_EXPORT
gf_isom_set_track_creation_time(GF_ISOFile * movie,u32 trackNumber,u64 time)226 GF_Err gf_isom_set_track_creation_time(GF_ISOFile *movie,u32 trackNumber, u64 time)
227 {
228 GF_TrackBox *trak;
229 trak = gf_isom_get_track_from_file(movie, trackNumber);
230 if (!trak) return GF_BAD_PARAM;
231
232 trak->Header->creationTime = time;
233 trak->Header->modificationTime = time;
234 return GF_OK;
235 }
236
237
238 //sets the enable flag of a track
239 GF_EXPORT
gf_isom_set_track_enabled(GF_ISOFile * movie,u32 trackNumber,Bool enableTrack)240 GF_Err gf_isom_set_track_enabled(GF_ISOFile *movie, u32 trackNumber, Bool enableTrack)
241 {
242 GF_Err e;
243 GF_TrackBox *trak;
244
245 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
246 if (e) return e;
247
248 trak = gf_isom_get_track_from_file(movie, trackNumber);
249 if (!trak) return GF_BAD_PARAM;
250
251 if (enableTrack) {
252 trak->Header->flags |= 1;
253 } else {
254 trak->Header->flags &= ~1;
255 }
256 return GF_OK;
257 }
258
259 //sets the enable flag of a track
260 GF_EXPORT
gf_isom_set_track_flags(GF_ISOFile * movie,u32 trackNumber,u32 flags,GF_ISOMTrackFlagOp op)261 GF_Err gf_isom_set_track_flags(GF_ISOFile *movie, u32 trackNumber, u32 flags, GF_ISOMTrackFlagOp op)
262 {
263 GF_Err e;
264 GF_TrackBox *trak;
265
266 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
267 if (e) return e;
268
269 trak = gf_isom_get_track_from_file(movie, trackNumber);
270 if (!trak) return GF_BAD_PARAM;
271 if (op==GF_ISOM_TKFLAGS_ADD)
272 trak->Header->flags |= flags;
273 else if (op==GF_ISOM_TKFLAGS_REM)
274 trak->Header->flags &= ~flags;
275 else
276 trak->Header->flags = flags;
277 return GF_OK;
278 }
279
280 GF_EXPORT
gf_isom_set_media_language(GF_ISOFile * movie,u32 trackNumber,char * code)281 GF_Err gf_isom_set_media_language(GF_ISOFile *movie, u32 trackNumber, char *code)
282 {
283 GF_Err e;
284 GF_TrackBox *trak;
285
286 trak = gf_isom_get_track_from_file(movie, trackNumber);
287 if (!trak || !code) return GF_BAD_PARAM;
288 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
289 if (e) return e;
290
291 // Old language-storage processing
292 // if the new code is on 3 chars, we use it
293 // otherwise, we find the associated 3 chars code and use it
294 if (strlen(code) == 3) {
295 memcpy(trak->Media->mediaHeader->packedLanguage, code, sizeof(char)*3);
296 } else {
297 s32 lang_idx;
298 const char *code_3cc;
299 lang_idx = gf_lang_find(code);
300 if (lang_idx == -1) {
301 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("The given code is not a valid one: %s, using 'und' as 3-letter code\n", code));
302 code_3cc = "und";
303 } else {
304 code_3cc = gf_lang_get_3cc(lang_idx);
305 }
306 memcpy(trak->Media->mediaHeader->packedLanguage, code_3cc, sizeof(char)*3);
307 }
308
309 // New language-storage processing
310 // change the code in the extended language box (if any)
311 // otherwise add an extended language box only if the given code is not 3 chars
312 {
313 u32 i, count;
314 GF_ExtendedLanguageBox *elng;
315 elng = NULL;
316 count = gf_list_count(trak->Media->child_boxes);
317 for (i = 0; i < count; i++) {
318 GF_Box *box = (GF_Box *)gf_list_get(trak->Media->child_boxes, i);
319 if (box->type == GF_ISOM_BOX_TYPE_ELNG) {
320 elng = (GF_ExtendedLanguageBox *)box;
321 break;
322 }
323 }
324 if (!elng && (strlen(code) > 3)) {
325 elng = (GF_ExtendedLanguageBox *)gf_isom_box_new_parent(&trak->Media->child_boxes, GF_ISOM_BOX_TYPE_ELNG);
326 if (!elng) return GF_OUT_OF_MEM;
327 }
328 if (elng) {
329 if (elng->extended_language) {
330 gf_free(elng->extended_language);
331 }
332 elng->extended_language = gf_strdup(code);
333 }
334 }
335 if (!movie->keep_utc)
336 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
337 return GF_OK;
338 }
339
gf_isom_set_root_iod(GF_ISOFile * movie)340 static GF_Err gf_isom_set_root_iod(GF_ISOFile *movie)
341 {
342 GF_IsomInitialObjectDescriptor *iod;
343 GF_IsomObjectDescriptor *od;
344 GF_Err e;
345
346 e = gf_isom_insert_moov(movie);
347 if (e) return e;
348 if (!movie->moov->iods) {
349 AddMovieIOD(movie->moov, 1);
350 return GF_OK;
351 }
352 //if OD, switch to IOD
353 if (movie->moov->iods->descriptor->tag == GF_ODF_ISOM_IOD_TAG) return GF_OK;
354 od = (GF_IsomObjectDescriptor *) movie->moov->iods->descriptor;
355 iod = (GF_IsomInitialObjectDescriptor*)gf_malloc(sizeof(GF_IsomInitialObjectDescriptor));
356 if (!iod) return GF_OUT_OF_MEM;
357
358 memset(iod, 0, sizeof(GF_IsomInitialObjectDescriptor));
359
360 iod->ES_ID_IncDescriptors = od->ES_ID_IncDescriptors;
361 od->ES_ID_IncDescriptors = NULL;
362 //not used in root OD
363 iod->ES_ID_RefDescriptors = NULL;
364 iod->extensionDescriptors = od->extensionDescriptors;
365 od->extensionDescriptors = NULL;
366 iod->IPMP_Descriptors = od->IPMP_Descriptors;
367 od->IPMP_Descriptors = NULL;
368 iod->objectDescriptorID = od->objectDescriptorID;
369 iod->OCIDescriptors = od->OCIDescriptors;
370 od->OCIDescriptors = NULL;
371 iod->tag = GF_ODF_ISOM_IOD_TAG;
372 iod->URLString = od->URLString;
373 od->URLString = NULL;
374
375 gf_odf_desc_del((GF_Descriptor *) od);
376 movie->moov->iods->descriptor = (GF_Descriptor *)iod;
377 return GF_OK;
378 }
379
380 GF_EXPORT
gf_isom_add_desc_to_root_od(GF_ISOFile * movie,const GF_Descriptor * theDesc)381 GF_Err gf_isom_add_desc_to_root_od(GF_ISOFile *movie, const GF_Descriptor *theDesc)
382 {
383 GF_Err e;
384 GF_Descriptor *desc, *dupDesc;
385
386 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
387 if (e) return e;
388 e = gf_isom_insert_moov(movie);
389 if (e) return e;
390
391 if (!movie->moov->iods) {
392 e = AddMovieIOD(movie->moov, 0);
393 if (e) return e;
394 }
395 if (theDesc->tag==GF_ODF_IPMP_TL_TAG) gf_isom_set_root_iod(movie);
396
397 desc = movie->moov->iods->descriptor;
398 //the type of desc is handled at the OD/IOD level, we'll be notified
399 //if the desc is not allowed
400 switch (desc->tag) {
401 case GF_ODF_ISOM_IOD_TAG:
402 case GF_ODF_ISOM_OD_TAG:
403 //duplicate the desc
404 e = gf_odf_desc_copy((GF_Descriptor *)theDesc, &dupDesc);
405 if (e) return e;
406 //add it (MUST BE (I)OD level desc)
407 movie->LastError = gf_odf_desc_add_desc(desc, dupDesc);
408 if (movie->LastError) gf_odf_desc_del((GF_Descriptor *)dupDesc);
409 break;
410 default:
411 movie->LastError = GF_ISOM_INVALID_FILE;
412 break;
413 }
414 return movie->LastError;
415 }
416
417
418 GF_EXPORT
gf_isom_set_timescale(GF_ISOFile * movie,u32 timeScale)419 GF_Err gf_isom_set_timescale(GF_ISOFile *movie, u32 timeScale)
420 {
421 GF_TrackBox *trak;
422 u32 i;
423 GF_Err e;
424 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
425 if (e) return e;
426 e = gf_isom_insert_moov(movie);
427 if (e) return e;
428
429 if (movie->moov->mvhd->timeScale == timeScale) return GF_OK;
430
431 /*rewrite all durations and edit lists*/
432 movie->moov->mvhd->duration *= timeScale;
433 movie->moov->mvhd->duration /= movie->moov->mvhd->timeScale;
434 if (movie->moov->mvex && movie->moov->mvex->mehd) {
435 movie->moov->mvex->mehd->fragment_duration *= timeScale;
436 movie->moov->mvex->mehd->fragment_duration /= movie->moov->mvhd->timeScale;
437 }
438
439 i=0;
440 while ((trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) {
441 trak->Header->duration *= timeScale;
442 trak->Header->duration /= movie->moov->mvhd->timeScale;
443
444 if (trak->editBox && trak->editBox->editList) {
445 u32 j, count = gf_list_count(trak->editBox->editList->entryList);
446 for (j=0; j<count; j++) {
447 GF_EdtsEntry *ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, j);
448 ent->segmentDuration *= timeScale;
449 ent->segmentDuration /= movie->moov->mvhd->timeScale;
450 }
451 }
452 }
453 if (movie->moov->mvex && movie->moov->mvex->mehd) {
454 movie->moov->mvex->mehd->fragment_duration *= timeScale;
455 movie->moov->mvex->mehd->fragment_duration /= movie->moov->mvhd->timeScale;
456 }
457 movie->moov->mvhd->timeScale = timeScale;
458 movie->interleavingTime = timeScale;
459 return GF_OK;
460 }
461
462
463 GF_EXPORT
gf_isom_set_pl_indication(GF_ISOFile * movie,u8 PL_Code,GF_ISOProfileLevelType ProfileLevel)464 GF_Err gf_isom_set_pl_indication(GF_ISOFile *movie, u8 PL_Code, GF_ISOProfileLevelType ProfileLevel)
465 {
466 GF_IsomInitialObjectDescriptor *iod;
467 GF_Err e;
468
469 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
470 if (e) return e;
471
472 e = gf_isom_set_root_iod(movie);
473 if (e) return e;
474
475 iod = (GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor;
476
477 switch (PL_Code) {
478 case GF_ISOM_PL_AUDIO:
479 iod->audio_profileAndLevel = ProfileLevel;
480 break;
481 case GF_ISOM_PL_GRAPHICS:
482 iod->graphics_profileAndLevel = ProfileLevel;
483 break;
484 case GF_ISOM_PL_OD:
485 iod->OD_profileAndLevel = ProfileLevel;
486 break;
487 case GF_ISOM_PL_SCENE:
488 iod->scene_profileAndLevel = ProfileLevel;
489 break;
490 case GF_ISOM_PL_VISUAL:
491 iod->visual_profileAndLevel = ProfileLevel;
492 break;
493 case GF_ISOM_PL_INLINE:
494 iod->inlineProfileFlag = ProfileLevel ? 1 : 0;
495 break;
496 }
497 return GF_OK;
498 }
499
500 GF_EXPORT
gf_isom_set_root_od_id(GF_ISOFile * movie,u32 OD_ID)501 GF_Err gf_isom_set_root_od_id(GF_ISOFile *movie, u32 OD_ID)
502 {
503 GF_Err e;
504 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
505 if (e) return e;
506
507 e = gf_isom_insert_moov(movie);
508 if (e) return e;
509 if (!movie->moov->iods) {
510 e = AddMovieIOD(movie->moov, 0);
511 if (e) return e;
512 }
513
514 switch (movie->moov->iods->descriptor->tag) {
515 case GF_ODF_ISOM_OD_TAG:
516 ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID;
517 break;
518 case GF_ODF_ISOM_IOD_TAG:
519 ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID;
520 break;
521 default:
522 return GF_ISOM_INVALID_FILE;
523 }
524 return GF_OK;
525 }
526
527 GF_EXPORT
gf_isom_set_root_od_url(GF_ISOFile * movie,const char * url_string)528 GF_Err gf_isom_set_root_od_url(GF_ISOFile *movie, const char *url_string)
529 {
530 GF_Err e;
531 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
532 if (e) return e;
533 e = gf_isom_insert_moov(movie);
534 if (e) return e;
535
536 if (!movie->moov->iods) {
537 e = AddMovieIOD(movie->moov, 0);
538 if (e) return e;
539 }
540
541 switch (movie->moov->iods->descriptor->tag) {
542 case GF_ODF_ISOM_OD_TAG:
543 if (((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString) gf_free(((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString);
544 ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? gf_strdup(url_string) : NULL;
545 break;
546 case GF_ODF_ISOM_IOD_TAG:
547 if (((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString) gf_free(((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString);
548 ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? gf_strdup(url_string) : NULL;
549 break;
550 default:
551 return GF_ISOM_INVALID_FILE;
552 }
553 return GF_OK;
554 }
555
556 GF_EXPORT
gf_isom_get_last_created_track_id(GF_ISOFile * movie)557 GF_ISOTrackID gf_isom_get_last_created_track_id(GF_ISOFile *movie)
558 {
559 return movie ? movie->last_created_track_id : 0;
560 }
561
562
563 GF_EXPORT
gf_isom_load_extra_boxes(GF_ISOFile * movie,u8 * moov_boxes,u32 moov_boxes_size,Bool udta_only)564 GF_Err gf_isom_load_extra_boxes(GF_ISOFile *movie, u8 *moov_boxes, u32 moov_boxes_size, Bool udta_only)
565 {
566 GF_BitStream *bs;
567
568 GF_Err e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
569 if (e) return e;
570 e = gf_isom_insert_moov(movie);
571 if (e) return e;
572
573 bs = gf_bs_new(moov_boxes, moov_boxes_size, GF_BITSTREAM_READ);
574
575 //we may have terminators in some QT files (4 bytes set to 0 ...)
576 while (gf_bs_available(bs) >= 8) {
577 GF_Box *a_box;
578 e = gf_isom_box_parse_ex((GF_Box**)&a_box, bs, GF_ISOM_BOX_TYPE_MOOV, GF_FALSE);
579 if (e || !a_box) goto exit;
580
581 if (a_box->type == GF_ISOM_BOX_TYPE_UDTA) {
582 if (movie->moov->udta) gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box*)movie->moov->udta);
583 movie->moov->udta = (GF_UserDataBox*) a_box;
584
585 if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new();
586 gf_list_add(movie->moov->child_boxes, a_box);
587
588 } else if (!udta_only && (a_box->type!=GF_ISOM_BOX_TYPE_PSSH) ) {
589 if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new();
590 gf_list_add(movie->moov->child_boxes, a_box);
591 } else {
592 gf_isom_box_del(a_box);
593 }
594 }
595 exit:
596 gf_bs_del(bs);
597 return e;
598 }
599
600 //creates a new Track. If trackID = 0, the trackID is chosen by the API
601 //returns the track number or 0 if error
602 GF_EXPORT
gf_isom_new_track_from_template(GF_ISOFile * movie,GF_ISOTrackID trakID,u32 MediaType,u32 TimeScale,u8 * tk_box,u32 tk_box_size,Bool udta_only)603 u32 gf_isom_new_track_from_template(GF_ISOFile *movie, GF_ISOTrackID trakID, u32 MediaType, u32 TimeScale, u8 *tk_box, u32 tk_box_size, Bool udta_only)
604 {
605 GF_Err e;
606 u64 now;
607 u8 isHint;
608 GF_TrackBox *trak;
609 GF_TrackHeaderBox *tkhd;
610 GF_MediaBox *mdia;
611 GF_UserDataBox *udta = NULL;
612
613 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
614 if (e) {
615 gf_isom_set_last_error(movie, e);
616 return 0;
617 }
618 e = gf_isom_insert_moov(movie);
619 if (e) return e;
620
621
622 isHint = 0;
623 //we're creating a hint track... it's the same, but mode HAS TO BE EDIT
624 if (MediaType == GF_ISOM_MEDIA_HINT) {
625 // if (movie->openMode != GF_ISOM_OPEN_EDIT) return 0;
626 isHint = 1;
627 }
628
629 mdia = NULL;
630 tkhd = NULL;
631 trak = NULL;
632 if (trakID) {
633 //check if we are in ES_ID boundaries
634 if (!isHint && (trakID > 0xFFFF)) {
635 gf_isom_set_last_error(movie, GF_BAD_PARAM);
636 return 0;
637 }
638 //here we should look for available IDs ...
639 if (!RequestTrack(movie->moov, trakID)) return 0;
640 } else {
641 trakID = movie->moov->mvhd->nextTrackID;
642 if (!trakID) trakID = 1;
643 /*ESIDs are on 16 bits*/
644 if (! isHint && (trakID > 0xFFFF)) trakID = 1;
645
646 while (1) {
647 if (RequestTrack(movie->moov, trakID)) break;
648 trakID += 1;
649 if (trakID == 0xFFFFFFFF) break;
650 }
651 if (trakID == 0xFFFFFFFF) {
652 gf_isom_set_last_error(movie, GF_BAD_PARAM);
653 return 0;
654 }
655 if (! isHint && (trakID > 0xFFFF)) {
656 gf_isom_set_last_error(movie, GF_BAD_PARAM);
657 return 0;
658 }
659 }
660
661 if (tk_box) {
662 GF_BitStream *bs = gf_bs_new(tk_box, tk_box_size, GF_BITSTREAM_READ);
663 gf_bs_set_cookie(bs, GF_ISOM_BS_COOKIE_NO_LOGS|GF_ISOM_BS_COOKIE_CLONE_TRACK);
664
665 e = gf_isom_box_parse_ex((GF_Box**)&trak, bs, GF_ISOM_BOX_TYPE_MOOV, GF_FALSE);
666 gf_bs_del(bs);
667 if (e) trak = NULL;
668 else if (udta_only) {
669 udta = trak->udta;
670 trak->udta = NULL;
671 gf_isom_box_del((GF_Box*)trak);
672 } else {
673 Bool tpl_ok = GF_TRUE;
674 if (!trak->Header || !trak->Media || !trak->Media->handler || !trak->Media->mediaHeader || !trak->Media->information) tpl_ok = GF_FALSE;
675
676 else {
677 if (!MediaType) MediaType = trak->Media->handler->handlerType;
678 e = NewMedia(&trak->Media, MediaType, TimeScale);
679 if (e) tpl_ok = GF_FALSE;
680 }
681 if (!tpl_ok) {
682 udta = trak->udta;
683 trak->udta = NULL;
684 gf_isom_box_del((GF_Box*)trak);
685 } else {
686 if (trak->References) gf_isom_box_del_parent(&trak->child_boxes, (GF_Box*)trak->References);
687 trak->References = NULL;
688 }
689 }
690 }
691 now = gf_isom_get_mp4time();
692 if (!trak) {
693 //OK, now create a track...
694 trak = (GF_TrackBox *) gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_TRAK);
695 if (!trak) {
696 gf_isom_set_last_error(movie, GF_OUT_OF_MEM);
697 return 0;
698 }
699 tkhd = (GF_TrackHeaderBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TKHD);
700 if (!tkhd) {
701 gf_isom_set_last_error(movie, GF_OUT_OF_MEM);
702 return 0;
703 }
704
705 //OK, set up the media trak
706 e = NewMedia(&mdia, MediaType, TimeScale);
707 if (e) {
708 gf_isom_box_del((GF_Box *)mdia);
709 return 0;
710 }
711 assert(trak->child_boxes);
712 gf_list_add(trak->child_boxes, mdia);
713
714 //OK, add this media to our track
715 mdia->mediaTrack = trak;
716
717 e = trak_on_child_box((GF_Box*)trak, (GF_Box *) tkhd);
718 if (e) goto err_exit;
719 e = trak_on_child_box((GF_Box*)trak, (GF_Box *) mdia);
720 if (e) goto err_exit;
721 tkhd->trackID = trakID;
722
723 if (gf_sys_is_test_mode() ) {
724 tkhd->creationTime = 0;
725 mdia->mediaHeader->creationTime = 0;
726 } else {
727 tkhd->creationTime = now;
728 mdia->mediaHeader->creationTime = now;
729 }
730
731 } else {
732 tkhd = trak->Header;
733 tkhd->trackID = trakID;
734 mdia = trak->Media;
735 mdia->mediaTrack = trak;
736 mdia->mediaHeader->timeScale = TimeScale;
737 if (mdia->handler->handlerType != MediaType) {
738 mdia->handler->handlerType = MediaType;
739 tkhd->width = 0;
740 tkhd->height = 0;
741 tkhd->volume = 0;
742 } else {
743 MediaType = 0;
744 }
745 trak->Header->duration = 0;
746 mdia->mediaHeader->duration = 0;
747
748 if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new();
749 gf_list_add(movie->moov->child_boxes, trak);
750 }
751 if (MediaType) {
752 //some default properties for Audio, Visual or private tracks
753 switch (MediaType) {
754 case GF_ISOM_MEDIA_VISUAL:
755 case GF_ISOM_MEDIA_AUXV:
756 case GF_ISOM_MEDIA_PICT:
757 case GF_ISOM_MEDIA_SCENE:
758 case GF_ISOM_MEDIA_TEXT:
759 case GF_ISOM_MEDIA_SUBT:
760 /*320-240 pix in 16.16*/
761 tkhd->width = 0x01400000;
762 tkhd->height = 0x00F00000;
763 break;
764 case GF_ISOM_MEDIA_AUDIO:
765 tkhd->volume = 0x0100;
766 break;
767 }
768 }
769 movie->last_created_track_id = tkhd->trackID;
770
771 if (!movie->keep_utc && !gf_sys_is_test_mode() ) {
772 tkhd->modificationTime = now;
773 mdia->mediaHeader->modificationTime = now;
774 }
775
776 //OK, add our trak
777 e = moov_on_child_box((GF_Box*)movie->moov, (GF_Box *)trak);
778 if (e) goto err_exit;
779 //set the new ID available
780 if (trakID+1> movie->moov->mvhd->nextTrackID)
781 movie->moov->mvhd->nextTrackID = trakID+1;
782
783 trak->udta = udta;
784
785 //and return our track number
786 return gf_isom_get_track_by_id(movie, trakID);
787
788 err_exit:
789 //tkhd is registered with track and will be destroyed there
790 if (trak) gf_isom_box_del((GF_Box *)trak);
791 if (mdia) gf_isom_box_del((GF_Box *)mdia);
792 return 0;
793 }
794
795 GF_EXPORT
gf_isom_new_track(GF_ISOFile * movie,GF_ISOTrackID trakID,u32 MediaType,u32 TimeScale)796 u32 gf_isom_new_track(GF_ISOFile *movie, GF_ISOTrackID trakID, u32 MediaType, u32 TimeScale)
797 {
798 return gf_isom_new_track_from_template(movie, trakID, MediaType, TimeScale, NULL, 0, GF_FALSE);
799 }
800
801 GF_EXPORT
gf_isom_remove_stream_description(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex)802 GF_Err gf_isom_remove_stream_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex)
803 {
804 GF_TrackBox *trak;
805 GF_Err e;
806 GF_SampleEntryBox *entry;
807
808 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
809 if (e) return e;
810
811 trak = gf_isom_get_track_from_file(movie, trackNumber);
812 if (!trak || !trak->Media) return GF_BAD_PARAM;
813
814 if (!movie->keep_utc)
815 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
816
817 entry = (GF_SampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1);
818 if (!entry) return GF_BAD_PARAM;
819 gf_list_rem(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1);
820 gf_isom_box_del((GF_Box *)entry);
821 return GF_OK;
822 }
823
824 //Create a new StreamDescription in the file. The URL and URN are used to describe external media
825 GF_EXPORT
gf_isom_new_mpeg4_description(GF_ISOFile * movie,u32 trackNumber,const GF_ESD * esd,const char * URLname,const char * URNname,u32 * outDescriptionIndex)826 GF_Err gf_isom_new_mpeg4_description(GF_ISOFile *movie,
827 u32 trackNumber,
828 const GF_ESD *esd,
829 const char *URLname,
830 const char *URNname,
831 u32 *outDescriptionIndex)
832 {
833 GF_TrackBox *trak;
834 GF_Err e;
835 u32 dataRefIndex;
836 GF_ESD *new_esd;
837
838 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
839 if (e) return e;
840
841 trak = gf_isom_get_track_from_file(movie, trackNumber);
842 if (!trak || !trak->Media ||
843 !esd || !esd->decoderConfig ||
844 !esd->slConfig) return GF_BAD_PARAM;
845
846 //get or create the data ref
847 e = Media_FindDataRef(trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
848 if (e) return e;
849 if (!dataRefIndex) {
850 e = Media_CreateDataRef(movie, trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
851 if (e) return e;
852 }
853 //duplicate our desc
854 e = gf_odf_desc_copy((GF_Descriptor *)esd, (GF_Descriptor **)&new_esd);
855 if (e) return e;
856 if (!movie->keep_utc)
857 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
858 e = Track_SetStreamDescriptor(trak, 0, dataRefIndex, new_esd, outDescriptionIndex);
859 if (e) {
860 gf_odf_desc_del((GF_Descriptor *)new_esd);
861 return e;
862 }
863 return e;
864 }
865
gf_isom_flush_chunk(GF_TrackBox * trak,Bool is_final)866 GF_Err gf_isom_flush_chunk(GF_TrackBox *trak, Bool is_final)
867 {
868 GF_Err e;
869 u64 data_offset;
870 u32 sample_number;
871 u8 *chunk_data;
872 u32 chunk_size, chunk_alloc;
873 if (!trak->chunk_cache) return GF_OK;
874
875 gf_bs_get_content_no_truncate(trak->chunk_cache, &chunk_data, &chunk_size, &chunk_alloc);
876
877 data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler);
878
879 e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, chunk_data, chunk_size);
880 if (e) return e;
881
882 sample_number = 1 + trak->Media->information->sampleTable->SampleSize->sampleCount;
883 sample_number -= trak->nb_samples_in_cache;
884
885 e = stbl_AddChunkOffset(trak->Media, sample_number, trak->chunk_stsd_idx, data_offset, trak->nb_samples_in_cache);
886
887 if (is_final) {
888 gf_free(chunk_data);
889 gf_bs_del(trak->chunk_cache);
890 trak->chunk_cache = NULL;
891 } else {
892 gf_bs_reassign_buffer(trak->chunk_cache, chunk_data, chunk_alloc);
893 }
894 return e;
895 }
896
trak_add_sample(GF_ISOFile * movie,GF_TrackBox * trak,const GF_ISOSample * sample,u32 descIndex,u64 data_offset,u32 syncShadowSampleNum)897 static GF_Err trak_add_sample(GF_ISOFile *movie, GF_TrackBox *trak, const GF_ISOSample *sample, u32 descIndex, u64 data_offset, u32 syncShadowSampleNum)
898 {
899 Bool skip_data = GF_FALSE;
900 GF_Err e;
901
902 //faststart mode with interleaving time, cache data until we have a full chunk
903 if ((movie->storageMode==GF_ISOM_STORE_FASTSTART) && movie->interleavingTime) {
904 Bool flush_chunk = GF_FALSE;
905 u64 stime = sample->DTS;
906 stime *= movie->moov->mvhd->timeScale;
907 stime /= trak->Media->mediaHeader->timeScale;
908
909 if (stime - trak->first_dts_chunk > movie->interleavingTime)
910 flush_chunk = GF_TRUE;
911
912 if (movie->next_flush_chunk_time < stime)
913 flush_chunk = GF_TRUE;
914
915 if (trak->chunk_stsd_idx != descIndex)
916 flush_chunk = GF_TRUE;
917
918 if (trak->Media->information->sampleTable->MaxChunkSize && trak->Media->information->sampleTable->MaxChunkSize < trak->chunk_cache_size + sample->dataLength)
919 flush_chunk = GF_TRUE;
920
921 if (flush_chunk) {
922 movie->next_flush_chunk_time = stime + movie->interleavingTime;
923 if (trak->chunk_cache) {
924 e = gf_isom_flush_chunk(trak, GF_FALSE);
925 if (e) return e;
926 }
927 trak->nb_samples_in_cache = 0;
928 trak->chunk_cache_size = 0;
929 trak->first_dts_chunk = stime;
930 }
931 if (!trak->chunk_cache)
932 trak->chunk_cache = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
933 gf_bs_write_data(trak->chunk_cache, sample->data, sample->dataLength);
934 trak->nb_samples_in_cache += sample->nb_pack ? sample->nb_pack : 1;
935 trak->chunk_cache_size += sample->dataLength;
936 trak->chunk_stsd_idx = descIndex;
937
938 skip_data = GF_TRUE;
939 }
940
941 e = Media_AddSample(trak->Media, data_offset, sample, descIndex, syncShadowSampleNum);
942 if (e) return e;
943
944 if (!skip_data && sample->dataLength) {
945 e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, sample->data, sample->dataLength);
946 if (e) return e;
947 }
948
949 return GF_OK;
950 }
951
952 //Add samples to a track. Use streamDescriptionIndex to specify the desired stream (if several)
953 GF_EXPORT
gf_isom_add_sample(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,const GF_ISOSample * sample)954 GF_Err gf_isom_add_sample(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, const GF_ISOSample *sample)
955 {
956 GF_Err e;
957 GF_TrackBox *trak;
958 GF_SampleEntryBox *entry;
959 u32 dataRefIndex;
960 u64 data_offset;
961 u32 descIndex;
962 GF_DataEntryURLBox *Dentry;
963
964 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
965 if (e) return e;
966
967 trak = gf_isom_get_track_from_file(movie, trackNumber);
968 if (!trak) return GF_BAD_PARAM;
969
970 e = FlushCaptureMode(movie);
971 if (e) return e;
972
973 e = unpack_track(trak);
974 if (e) return e;
975
976 //OK, add the sample
977 //1- Get the streamDescriptionIndex and dataRefIndex
978 //not specified, get the latest used...
979 descIndex = StreamDescriptionIndex;
980 if (!StreamDescriptionIndex) {
981 descIndex = trak->Media->information->sampleTable->currentEntryIndex;
982 }
983 e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex);
984 if (e) return e;
985 if (!entry || !dataRefIndex) return GF_BAD_PARAM;
986 //set the current to this one
987 trak->Media->information->sampleTable->currentEntryIndex = descIndex;
988
989
990 //get this dataRef and return false if not self contained
991 Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1);
992 if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM;
993
994 //Open our data map. We are adding stuff, so use EDIT
995 e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1);
996 if (e) return e;
997
998 //Get the offset...
999 data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler);
1000
1001 /*rewrite OD frame*/
1002 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
1003 GF_ISOSample *od_sample = NULL;
1004
1005 e = Media_ParseODFrame(trak->Media, sample, &od_sample);
1006 if (e) return e;
1007
1008 e = trak_add_sample(movie, trak, od_sample, descIndex, data_offset, 0);
1009
1010 if (od_sample)
1011 gf_isom_sample_del(&od_sample);
1012 } else {
1013 e = trak_add_sample(movie, trak, sample, descIndex, data_offset, 0);
1014 }
1015 if (e) return e;
1016
1017
1018 if (!movie->keep_utc)
1019 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1020 return SetTrackDuration(trak);
1021 }
1022
1023 GF_EXPORT
gf_isom_add_sample_shadow(GF_ISOFile * movie,u32 trackNumber,GF_ISOSample * sample)1024 GF_Err gf_isom_add_sample_shadow(GF_ISOFile *movie, u32 trackNumber, GF_ISOSample *sample)
1025 {
1026 GF_Err e;
1027 GF_TrackBox *trak;
1028 GF_ISOSample *prev;
1029 GF_SampleEntryBox *entry;
1030 u32 dataRefIndex;
1031 u64 data_offset;
1032 u32 descIndex;
1033 u32 sampleNum, prevSampleNum;
1034 GF_DataEntryURLBox *Dentry;
1035 Bool offset_times = GF_FALSE;
1036
1037 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1038 if (e) return e;
1039
1040 trak = gf_isom_get_track_from_file(movie, trackNumber);
1041 if (!trak || !sample) return GF_BAD_PARAM;
1042
1043 e = FlushCaptureMode(movie);
1044 if (e) return e;
1045
1046 e = unpack_track(trak);
1047 if (e) return e;
1048
1049 e = stbl_findEntryForTime(trak->Media->information->sampleTable, sample->DTS, 0, &sampleNum, &prevSampleNum);
1050 if (e) return e;
1051 /*we need the EXACT match*/
1052 if (!sampleNum) return GF_BAD_PARAM;
1053
1054 prev = gf_isom_get_sample_info(movie, trackNumber, sampleNum, &descIndex, NULL);
1055 if (!prev) return gf_isom_last_error(movie);
1056 /*for conformance*/
1057 if (sample->DTS==prev->DTS) offset_times = GF_TRUE;
1058 gf_isom_sample_del(&prev);
1059
1060 e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex);
1061 if (e) return e;
1062 if (!entry || !dataRefIndex) return GF_BAD_PARAM;
1063 trak->Media->information->sampleTable->currentEntryIndex = descIndex;
1064
1065 //get this dataRef and return false if not self contained
1066 Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1);
1067 if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM;
1068
1069 //Open our data map. We are adding stuff, so use EDIT
1070 e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1);
1071 if (e) return e;
1072
1073 data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler);
1074 if (offset_times) sample->DTS += 1;
1075
1076 /*REWRITE ANY OD STUFF*/
1077 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
1078 GF_ISOSample *od_sample = NULL;
1079 e = Media_ParseODFrame(trak->Media, sample, &od_sample);
1080 if (e) return e;
1081
1082 e = trak_add_sample(movie, trak, od_sample, descIndex, data_offset, sampleNum);
1083 if (od_sample)
1084 gf_isom_sample_del(&od_sample);
1085 } else {
1086 e = trak_add_sample(movie, trak, sample, descIndex, data_offset, sampleNum);
1087 }
1088 if (e) return e;
1089 if (offset_times) sample->DTS -= 1;
1090
1091 //OK, update duration
1092 e = Media_SetDuration(trak);
1093 if (e) return e;
1094 if (!movie->keep_utc)
1095 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1096 return SetTrackDuration(trak);
1097 }
1098
1099 GF_EXPORT
gf_isom_append_sample_data(GF_ISOFile * movie,u32 trackNumber,u8 * data,u32 data_size)1100 GF_Err gf_isom_append_sample_data(GF_ISOFile *movie, u32 trackNumber, u8 *data, u32 data_size)
1101 {
1102 GF_Err e;
1103 GF_TrackBox *trak;
1104 GF_SampleEntryBox *entry;
1105 u32 dataRefIndex;
1106 u32 descIndex;
1107 GF_DataEntryURLBox *Dentry;
1108
1109 if (!data_size) return GF_OK;
1110 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1111 if (e) return e;
1112
1113 trak = gf_isom_get_track_from_file(movie, trackNumber);
1114 if (!trak) return GF_BAD_PARAM;
1115
1116 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) return GF_BAD_PARAM;
1117
1118 //OK, add the sample
1119 descIndex = trak->Media->information->sampleTable->currentEntryIndex;
1120
1121 e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex);
1122 if (e) return e;
1123 if (!entry || !dataRefIndex) return GF_BAD_PARAM;
1124
1125 //get this dataRef and return false if not self contained
1126 Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1);
1127 if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM;
1128
1129 //Open our data map. We are adding stuff, so use EDIT
1130 e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1);
1131 if (e) return e;
1132
1133 //add the media data
1134 if (trak->chunk_cache) {
1135 gf_bs_write_data(trak->chunk_cache, data, data_size);
1136 trak->chunk_cache_size += data_size;
1137 } else {
1138 e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, data, data_size);
1139 if (e) return e;
1140 }
1141 //update data size
1142 return stbl_SampleSizeAppend(trak->Media->information->sampleTable->SampleSize, data_size);
1143 }
1144
1145
1146 //Add sample reference to a track. The SampleOffset is the offset of the data in the referenced file
1147 //you must have created a StreamDescription with URL or URN specifying your referenced file
1148 //the data offset specifies the beginning of the chunk
1149 //Use streamDescriptionIndex to specify the desired stream (if several)
1150 GF_EXPORT
gf_isom_add_sample_reference(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,GF_ISOSample * sample,u64 dataOffset)1151 GF_Err gf_isom_add_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_ISOSample *sample, u64 dataOffset)
1152 {
1153 GF_TrackBox *trak;
1154 GF_SampleEntryBox *entry;
1155 u32 dataRefIndex;
1156 u32 descIndex;
1157 GF_DataEntryURLBox *Dentry;
1158 GF_Err e;
1159
1160 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1161 if (e) return e;
1162
1163 trak = gf_isom_get_track_from_file(movie, trackNumber);
1164 if (!trak) return GF_BAD_PARAM;
1165
1166 e = unpack_track(trak);
1167 if (e) return e;
1168
1169 //OD is not allowed as a data ref
1170 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
1171 return GF_BAD_PARAM;
1172 }
1173 //OK, add the sample
1174 //1- Get the streamDescriptionIndex and dataRefIndex
1175 //not specified, get the latest used...
1176 descIndex = StreamDescriptionIndex;
1177 if (!StreamDescriptionIndex) {
1178 descIndex = trak->Media->information->sampleTable->currentEntryIndex;
1179 }
1180 e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex);
1181 if (e) return e;
1182 if (!entry || !dataRefIndex) return GF_BAD_PARAM;
1183 //set the current to this one
1184 trak->Media->information->sampleTable->currentEntryIndex = descIndex;
1185
1186
1187 //get this dataRef and return false if self contained
1188 Dentry =(GF_DataEntryURLBox*) gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1);
1189 if (Dentry->flags == 1) return GF_BAD_PARAM;
1190
1191 //add the meta data
1192 e = Media_AddSample(trak->Media, dataOffset, sample, descIndex, 0);
1193 if (e) return e;
1194
1195 if (!movie->keep_utc)
1196 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1197 //OK, update duration
1198 e = Media_SetDuration(trak);
1199 if (e) return e;
1200 return SetTrackDuration(trak);
1201
1202 }
1203
1204 //set the duration of the last media sample. If not set, the duration of the last sample is the
1205 //duration of the previous one if any, or 1000 (default value).
gf_isom_set_last_sample_duration_internal(GF_ISOFile * movie,u32 trackNumber,u64 dur_num,u32 dur_den,u32 mode)1206 static GF_Err gf_isom_set_last_sample_duration_internal(GF_ISOFile *movie, u32 trackNumber, u64 dur_num, u32 dur_den, u32 mode)
1207 {
1208 GF_TrackBox *trak;
1209 GF_SttsEntry *ent;
1210 GF_TimeToSampleBox *stts;
1211 u64 mdur;
1212 u32 duration;
1213 GF_Err e;
1214 Bool is_patch = GF_FALSE;
1215
1216 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1217 if (e) return e;
1218
1219 trak = gf_isom_get_track_from_file(movie, trackNumber);
1220 if (!trak) return GF_BAD_PARAM;
1221
1222 if (mode==0) {
1223 duration = (u32) dur_num;
1224 } else if (mode==1) {
1225 duration = (u32) dur_num;
1226 if (dur_den) {
1227 duration *= trak->Media->mediaHeader->timeScale;
1228 duration /= dur_den;
1229 }
1230 } else {
1231 is_patch = GF_TRUE;
1232 }
1233 mdur = trak->Media->mediaHeader->duration;
1234 stts = trak->Media->information->sampleTable->TimeToSample;
1235 if (!stts->nb_entries) return GF_BAD_PARAM;
1236
1237 if (is_patch) {
1238 u32 i, avg_dur, nb_samp=0;
1239 u64 cum_dur=0;
1240 for (i=0; i<stts->nb_entries; i++) {
1241 ent = (GF_SttsEntry*) &stts->entries[i];
1242 cum_dur += ent->sampleCount*ent->sampleDelta;
1243 nb_samp += ent->sampleCount;
1244 }
1245 if (cum_dur <= dur_num || !nb_samp) return GF_OK;
1246 avg_dur = (u32) (dur_num / nb_samp);
1247
1248 for (i=0; i<stts->nb_entries; i++) {
1249 ent = (GF_SttsEntry*) &stts->entries[i];
1250 ent->sampleDelta = avg_dur;
1251 }
1252 stts->w_LastDTS = dur_num - avg_dur;
1253 return GF_OK;
1254 }
1255 //get the last entry
1256 ent = (GF_SttsEntry*) &stts->entries[stts->nb_entries-1];
1257 if ((mode==1) && !duration && !dur_den) {
1258 //same as previous, nothing to adjust
1259 if (ent->sampleCount>1) return GF_OK;
1260 if (stts->nb_entries==1) return GF_OK;
1261 duration = stts->entries[stts->nb_entries-2].sampleDelta;
1262 }
1263
1264 mdur -= ent->sampleDelta;
1265 mdur += duration;
1266
1267 //we only have one sample
1268 if (ent->sampleCount == 1) {
1269 ent->sampleDelta = (u32) duration;
1270 if (mode && (stts->nb_entries>1) && (stts->entries[stts->nb_entries-2].sampleDelta==duration)) {
1271 stts->entries[stts->nb_entries-2].sampleCount++;
1272 stts->nb_entries--;
1273 //and update the write cache
1274 stts->w_currentSampleNum = trak->Media->information->sampleTable->SampleSize->sampleCount;
1275 }
1276 } else {
1277 if (ent->sampleDelta == duration) return GF_OK;
1278 ent->sampleCount -= 1;
1279
1280 if (stts->nb_entries==stts->alloc_size) {
1281 stts->alloc_size++;
1282 stts->entries = (GF_SttsEntry*)gf_realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size);
1283 if (!stts->entries) return GF_OUT_OF_MEM;
1284 }
1285 stts->entries[stts->nb_entries].sampleCount = 1;
1286 stts->entries[stts->nb_entries].sampleDelta = (u32) duration;
1287 stts->nb_entries++;
1288 //and update the write cache
1289 stts->w_currentSampleNum = trak->Media->information->sampleTable->SampleSize->sampleCount;
1290 }
1291 if (!movie->keep_utc)
1292 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1293 trak->Media->mediaHeader->duration = mdur;
1294 return SetTrackDuration(trak);
1295 }
1296
1297 GF_EXPORT
gf_isom_set_last_sample_duration(GF_ISOFile * movie,u32 trackNumber,u32 duration)1298 GF_Err gf_isom_set_last_sample_duration(GF_ISOFile *movie, u32 trackNumber, u32 duration)
1299 {
1300 return gf_isom_set_last_sample_duration_internal(movie, trackNumber, duration, 0, 0);
1301 }
1302
1303 GF_EXPORT
gf_isom_patch_last_sample_duration(GF_ISOFile * movie,u32 trackNumber,u64 next_dts)1304 GF_Err gf_isom_patch_last_sample_duration(GF_ISOFile *movie, u32 trackNumber, u64 next_dts)
1305 {
1306 return gf_isom_set_last_sample_duration_internal(movie, trackNumber, next_dts, 0, 2);
1307 }
1308
1309 GF_EXPORT
gf_isom_set_last_sample_duration_ex(GF_ISOFile * movie,u32 trackNumber,u32 dur_num,u32 dur_den)1310 GF_Err gf_isom_set_last_sample_duration_ex(GF_ISOFile *movie, u32 trackNumber, u32 dur_num, u32 dur_den)
1311 {
1312 return gf_isom_set_last_sample_duration_internal(movie, trackNumber, dur_num, dur_den, 1);
1313 }
1314
1315 //update a sample data in the media. Note that the sample MUST exists
1316 GF_EXPORT
gf_isom_update_sample(GF_ISOFile * movie,u32 trackNumber,u32 sampleNumber,GF_ISOSample * sample,Bool data_only)1317 GF_Err gf_isom_update_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, Bool data_only)
1318 {
1319 GF_Err e;
1320 GF_TrackBox *trak;
1321
1322 e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT);
1323 if (e) return e;
1324
1325 trak = gf_isom_get_track_from_file(movie, trackNumber);
1326 if (!trak) return GF_BAD_PARAM;
1327
1328 e = unpack_track(trak);
1329 if (e) return e;
1330
1331 //block for hint tracks
1332 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM;
1333
1334 //REWRITE ANY OD STUFF
1335 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
1336 GF_ISOSample *od_sample = NULL;
1337 e = Media_ParseODFrame(trak->Media, sample, &od_sample);
1338 if (!e) e = Media_UpdateSample(trak->Media, sampleNumber, od_sample, data_only);
1339 if (od_sample) gf_isom_sample_del(&od_sample);
1340 } else {
1341 e = Media_UpdateSample(trak->Media, sampleNumber, sample, data_only);
1342 }
1343 if (e) return e;
1344 if (!movie->keep_utc)
1345 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1346 return GF_OK;
1347 }
1348
1349 //update a sample data in the media. Note that the sample MUST exists,
1350 //that sample->data MUST be NULL and sample->dataLength must be NON NULL;
1351 GF_EXPORT
gf_isom_update_sample_reference(GF_ISOFile * movie,u32 trackNumber,u32 sampleNumber,GF_ISOSample * sample,u64 data_offset)1352 GF_Err gf_isom_update_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset)
1353 {
1354 GF_Err e;
1355 GF_TrackBox *trak;
1356
1357 e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT);
1358 if (e) return e;
1359
1360 trak = gf_isom_get_track_from_file(movie, trackNumber);
1361 if (!trak) return GF_BAD_PARAM;
1362
1363 //block for hint tracks
1364 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM;
1365
1366 if (!sampleNumber || !sample) return GF_BAD_PARAM;
1367
1368 e = unpack_track(trak);
1369 if (e) return e;
1370
1371 //OD is not allowed as a data ref
1372 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
1373 return GF_BAD_PARAM;
1374 }
1375 //OK, update it
1376 e = Media_UpdateSampleReference(trak->Media, sampleNumber, sample, data_offset);
1377 if (e) return e;
1378
1379 if (!movie->keep_utc)
1380 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1381 return GF_OK;
1382 }
1383
1384
1385 //Remove a given sample
1386 GF_EXPORT
gf_isom_remove_sample(GF_ISOFile * movie,u32 trackNumber,u32 sampleNumber)1387 GF_Err gf_isom_remove_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber)
1388 {
1389 GF_Err e;
1390 GF_TrackBox *trak;
1391
1392 e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT);
1393 if (e) return e;
1394
1395 trak = gf_isom_get_track_from_file(movie, trackNumber);
1396 if (!trak || !sampleNumber || (sampleNumber > trak->Media->information->sampleTable->SampleSize->sampleCount) )
1397 return GF_BAD_PARAM;
1398
1399 //block for hint tracks
1400 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM;
1401
1402 e = unpack_track(trak);
1403 if (e) return e;
1404 //do NOT change the order DTS, CTS, size chunk
1405
1406 //remove DTS
1407 e = stbl_RemoveDTS(trak->Media->information->sampleTable, sampleNumber, trak->Media->mediaHeader->timeScale);
1408 if (e) return e;
1409 //remove CTS if any
1410 if (trak->Media->information->sampleTable->CompositionOffset) {
1411 e = stbl_RemoveCTS(trak->Media->information->sampleTable, sampleNumber);
1412 if (e) return e;
1413 }
1414 //remove size
1415 e = stbl_RemoveSize(trak->Media->information->sampleTable, sampleNumber);
1416 if (e) return e;
1417 //remove sampleToChunk and chunk
1418 e = stbl_RemoveChunk(trak->Media->information->sampleTable, sampleNumber);
1419 if (e) return e;
1420 //remove sync
1421 if (trak->Media->information->sampleTable->SyncSample) {
1422 e = stbl_RemoveRAP(trak->Media->information->sampleTable, sampleNumber);
1423 if (e) return e;
1424 }
1425 //remove sample dep
1426 if (trak->Media->information->sampleTable->SampleDep) {
1427 e = stbl_RemoveRedundant(trak->Media->information->sampleTable, sampleNumber);
1428 if (e) return e;
1429 }
1430 //remove shadow
1431 e = stbl_RemoveShadow(trak->Media->information->sampleTable, sampleNumber);
1432 if (e) return e;
1433
1434 //remove padding
1435 e = stbl_RemovePaddingBits(trak->Media->information->sampleTable, sampleNumber);
1436 if (e) return e;
1437
1438 e = stbl_RemoveSubSample(trak->Media->information->sampleTable, sampleNumber);
1439 if (e) return e;
1440
1441 e = stbl_RemoveSampleGroup(trak->Media->information->sampleTable, sampleNumber);
1442 if (e) return e;
1443
1444 return SetTrackDuration(trak);
1445 }
1446
1447
1448 GF_EXPORT
gf_isom_set_final_name(GF_ISOFile * movie,char * filename)1449 GF_Err gf_isom_set_final_name(GF_ISOFile *movie, char *filename)
1450 {
1451 GF_Err e;
1452 if (!movie ) return GF_BAD_PARAM;
1453
1454 //if mode is not OPEN_EDIT file was created under the right name
1455 e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT);
1456 if (e) return e;
1457
1458 if (filename) {
1459 //we don't allow file overwriting
1460 if ( (movie->openMode == GF_ISOM_OPEN_EDIT)
1461 && movie->fileName && !strcmp(filename, movie->fileName))
1462 return GF_BAD_PARAM;
1463 if (movie->finalName) gf_free(movie->finalName);
1464 movie->finalName = gf_strdup(filename);
1465 if (!movie->finalName) return GF_OUT_OF_MEM;
1466 }
1467 return GF_OK;
1468 }
1469
1470 //Add a system descriptor to the ESD of a stream(EDIT or WRITE mode only)
1471 GF_EXPORT
gf_isom_add_desc_to_description(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,const GF_Descriptor * theDesc)1472 GF_Err gf_isom_add_desc_to_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, const GF_Descriptor *theDesc)
1473 {
1474 GF_IPIPtr *ipiD;
1475 GF_Err e;
1476 u16 tmpRef;
1477 GF_TrackBox *trak;
1478 GF_Descriptor *desc;
1479 GF_ESD *esd;
1480 GF_TrackReferenceBox *tref;
1481 GF_TrackReferenceTypeBox *dpnd;
1482 GF_MPEGVisualSampleEntryBox *entry;
1483 u32 msubtype;
1484 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1485 if (e) return e;
1486
1487 trak = gf_isom_get_track_from_file(movie, trackNumber);
1488 if (!trak) return GF_BAD_PARAM;
1489
1490 /*GETS NATIVE DESCRIPTOR ONLY*/
1491 e = Media_GetESD(trak->Media, StreamDescriptionIndex, &esd, GF_TRUE);
1492 if (e) return e;
1493
1494 entry = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex-1);
1495 if (!entry) return GF_BAD_PARAM;
1496 msubtype = entry->type;
1497 if ((msubtype==GF_ISOM_BOX_TYPE_ENCV) || (msubtype==GF_ISOM_BOX_TYPE_ENCA))
1498 gf_isom_get_original_format_type(movie, trackNumber, StreamDescriptionIndex, &msubtype);
1499
1500 //duplicate the desc
1501 e = gf_odf_desc_copy((GF_Descriptor *)theDesc, &desc);
1502 if (e) return e;
1503
1504 //and add it to the ESD EXCEPT IPI PTR (we need to translate from ES_ID to TrackID!!!
1505 if (!movie->keep_utc)
1506 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1507
1508 switch (desc->tag) {
1509 case GF_ODF_IPI_PTR_TAG:
1510 goto insertIPI;
1511 default:
1512 break;
1513 }
1514
1515 if ((msubtype==GF_ISOM_BOX_TYPE_MP4S) || (msubtype==GF_ISOM_BOX_TYPE_MP4V) || (msubtype==GF_ISOM_BOX_TYPE_MP4A)) {
1516 return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc);
1517 }
1518
1519 if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_VISUAL) {
1520 gf_odf_desc_del(desc);
1521 return GF_NOT_SUPPORTED;
1522 }
1523 GF_MPEG4ExtensionDescriptorsBox *mdesc = (GF_MPEG4ExtensionDescriptorsBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_M4DS);
1524 if (!mdesc) {
1525 mdesc = (GF_MPEG4ExtensionDescriptorsBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_M4DS);
1526 }
1527 return gf_list_add(mdesc->descriptors, desc);
1528
1529 insertIPI:
1530 if (esd->ipiPtr) {
1531 gf_odf_desc_del((GF_Descriptor *) esd->ipiPtr);
1532 esd->ipiPtr = NULL;
1533 }
1534
1535 ipiD = (GF_IPIPtr *) desc;
1536 //find a tref
1537 if (!trak->References) {
1538 tref = (GF_TrackReferenceBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TREF);
1539 if (!tref) return GF_OUT_OF_MEM;
1540 e = trak_on_child_box((GF_Box*)trak, (GF_Box *)tref);
1541 if (e) return e;
1542 }
1543 tref = trak->References;
1544
1545 e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd);
1546 if (e) return e;
1547 if (!dpnd) {
1548 tmpRef = 0;
1549 dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT);
1550 if (!dpnd) return GF_OUT_OF_MEM;
1551 dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR;
1552 e = reftype_AddRefTrack(dpnd, ipiD->IPI_ES_Id, &tmpRef);
1553 if (e) return e;
1554 //and replace the tag and value...
1555 ipiD->IPI_ES_Id = tmpRef;
1556 ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1557 } else {
1558 //Watch out! ONLY ONE IPI dependency is allowed per stream
1559 dpnd->trackIDCount = 1;
1560 dpnd->trackIDs[0] = ipiD->IPI_ES_Id;
1561 //and replace the tag and value...
1562 ipiD->IPI_ES_Id = 1;
1563 ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1564 }
1565 //and add the desc to the esd...
1566 return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc);
1567 }
1568
1569
1570 //use carefully. Very useful when you made a lot of changes (IPMP, IPI, OCI, ...)
1571 //THIS WILL REPLACE THE WHOLE DESCRIPTOR ...
1572 GF_EXPORT
gf_isom_change_mpeg4_description(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,const GF_ESD * newESD)1573 GF_Err gf_isom_change_mpeg4_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, const GF_ESD *newESD)
1574 {
1575 GF_Err e;
1576 GF_ESD *esd;
1577 GF_TrackBox *trak;
1578 GF_SampleEntryBox *entry;
1579 GF_SampleDescriptionBox *stsd;
1580
1581 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1582 if (e) return e;
1583
1584 trak = gf_isom_get_track_from_file(movie, trackNumber);
1585 if (!trak) return GF_BAD_PARAM;
1586
1587 stsd = trak->Media->information->sampleTable->SampleDescription;
1588 if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
1589
1590 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
1591 return movie->LastError = GF_BAD_PARAM;
1592 }
1593 entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
1594 //no support for generic sample entries (eg, no MPEG4 descriptor)
1595 if (entry == NULL) return GF_BAD_PARAM;
1596
1597 if (!movie->keep_utc)
1598 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1599 //duplicate our desc
1600 e = gf_odf_desc_copy((GF_Descriptor *)newESD, (GF_Descriptor **)&esd);
1601 if (e) return e;
1602 e = Track_SetStreamDescriptor(trak, StreamDescriptionIndex, entry->dataReferenceIndex, esd, NULL);
1603 if (e != GF_OK) {
1604 gf_odf_desc_del((GF_Descriptor *) esd);
1605 }
1606 return e;
1607 }
1608
1609 GF_EXPORT
gf_isom_set_visual_info(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,u32 Width,u32 Height)1610 GF_Err gf_isom_set_visual_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 Width, u32 Height)
1611 {
1612 GF_Err e;
1613 GF_TrackBox *trak;
1614 GF_SampleEntryBox *entry;
1615 GF_SampleDescriptionBox *stsd;
1616 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1617 if (e) return e;
1618
1619 trak = gf_isom_get_track_from_file(movie, trackNumber);
1620 if (!trak) return GF_BAD_PARAM;
1621
1622 stsd = trak->Media->information->sampleTable->SampleDescription;
1623 if (!stsd) {
1624 return movie->LastError = GF_ISOM_INVALID_FILE;
1625 }
1626 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
1627 return movie->LastError = GF_BAD_PARAM;
1628 }
1629 entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
1630 //no support for generic sample entries (eg, no MPEG4 descriptor)
1631 if (entry == NULL) return GF_BAD_PARAM;
1632 if (!movie->keep_utc)
1633 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1634
1635 //valid for MPEG visual, JPG and 3GPP H263
1636 if (entry->internal_type == GF_ISOM_SAMPLE_ENTRY_VIDEO) {
1637 ((GF_VisualSampleEntryBox*)entry)->Width = Width;
1638 ((GF_VisualSampleEntryBox*)entry)->Height = Height;
1639 trak->Header->width = Width<<16;
1640 trak->Header->height = Height<<16;
1641 return GF_OK;
1642 } else if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_SCENE) {
1643 trak->Header->width = Width<<16;
1644 trak->Header->height = Height<<16;
1645 return GF_OK;
1646 } else {
1647 return GF_BAD_PARAM;
1648 }
1649 }
1650
1651 GF_EXPORT
gf_isom_set_visual_bit_depth(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,u16 bitDepth)1652 GF_Err gf_isom_set_visual_bit_depth(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u16 bitDepth)
1653 {
1654 GF_Err e;
1655 GF_TrackBox *trak;
1656 GF_MPEGVisualSampleEntryBox *entry;
1657 GF_SampleDescriptionBox *stsd;
1658 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1659 if (e) return e;
1660
1661 trak = gf_isom_get_track_from_file(movie, trackNumber);
1662 if (!trak) return GF_BAD_PARAM;
1663
1664 switch (trak->Media->handler->handlerType) {
1665 case GF_ISOM_MEDIA_VISUAL:
1666 case GF_ISOM_MEDIA_PICT:
1667 case GF_ISOM_MEDIA_AUXV:
1668 break;
1669 default:
1670 return GF_OK;
1671 }
1672
1673 stsd = trak->Media->information->sampleTable->SampleDescription;
1674 if (!stsd) {
1675 return movie->LastError = GF_ISOM_INVALID_FILE;
1676 }
1677 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
1678 return movie->LastError = GF_BAD_PARAM;
1679 }
1680 entry = (GF_MPEGVisualSampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
1681 //no support for generic sample entries (eg, no MPEG4 descriptor)
1682 if (entry == NULL) return GF_BAD_PARAM;
1683 entry->bit_depth = bitDepth;
1684 return GF_OK;
1685 }
1686
1687 GF_EXPORT
gf_isom_set_pixel_aspect_ratio(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,s32 hSpacing,s32 vSpacing,Bool force_par)1688 GF_Err gf_isom_set_pixel_aspect_ratio(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, s32 hSpacing, s32 vSpacing, Bool force_par)
1689 {
1690 GF_Err e;
1691 GF_TrackBox *trak;
1692 GF_SampleEntryBox *entry;
1693 GF_SampleDescriptionBox *stsd;
1694 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1695 if (e) return e;
1696
1697 trak = gf_isom_get_track_from_file(movie, trackNumber);
1698 if (!trak) return GF_BAD_PARAM;
1699
1700 stsd = trak->Media->information->sampleTable->SampleDescription;
1701 if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
1702 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
1703 return movie->LastError = GF_BAD_PARAM;
1704 }
1705 entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
1706 //no support for generic sample entries (eg, no MPEG4 descriptor)
1707 if (entry == NULL) return GF_BAD_PARAM;
1708 if (!movie->keep_utc)
1709 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1710
1711 if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
1712
1713 GF_PixelAspectRatioBox *pasp = (GF_PixelAspectRatioBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_PASP);
1714 if (!hSpacing || !vSpacing || ((hSpacing == vSpacing) && !force_par)) {
1715 if (pasp) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *)pasp);
1716 return GF_OK;
1717 }
1718 if (!pasp) {
1719 pasp = (GF_PixelAspectRatioBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_PASP);
1720 if (!pasp) return GF_OUT_OF_MEM;
1721 }
1722 pasp->hSpacing = (u32) hSpacing;
1723 pasp->vSpacing = (u32) vSpacing;
1724 return GF_OK;
1725 }
1726
1727 GF_EXPORT
gf_isom_set_visual_color_info(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,u32 colour_type,u16 colour_primaries,u16 transfer_characteristics,u16 matrix_coefficients,Bool full_range_flag,u8 * icc_data,u32 icc_size)1728 GF_Err gf_isom_set_visual_color_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 colour_type, u16 colour_primaries, u16 transfer_characteristics, u16 matrix_coefficients, Bool full_range_flag, u8 *icc_data, u32 icc_size)
1729 {
1730 GF_Err e;
1731 GF_TrackBox *trak;
1732 GF_SampleEntryBox *entry;
1733 GF_SampleDescriptionBox *stsd;
1734 GF_ColourInformationBox *clr=NULL;
1735 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1736 if (e) return e;
1737
1738 trak = gf_isom_get_track_from_file(movie, trackNumber);
1739 if (!trak) return GF_BAD_PARAM;
1740
1741 stsd = trak->Media->information->sampleTable->SampleDescription;
1742 if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
1743 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
1744 return movie->LastError = GF_BAD_PARAM;
1745 }
1746 entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
1747 //no support for generic sample entries (eg, no MPEG4 descriptor)
1748 if (entry == NULL) return GF_BAD_PARAM;
1749 if (!movie->keep_utc)
1750 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1751
1752 if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_OK;
1753
1754 clr = (GF_ColourInformationBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_COLR);
1755 if (!colour_type) {
1756 if (clr) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *)clr);
1757 return GF_OK;
1758 }
1759 if (!clr) {
1760 clr = (GF_ColourInformationBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_COLR);
1761 if (!clr) return GF_OUT_OF_MEM;
1762 }
1763 clr->colour_type = colour_type;
1764 clr->colour_primaries = colour_primaries;
1765 clr->transfer_characteristics = transfer_characteristics;
1766 clr->matrix_coefficients = matrix_coefficients;
1767 clr->full_range_flag = full_range_flag;
1768 if (clr->opaque) gf_free(clr->opaque);
1769 clr->opaque = NULL;
1770 clr->opaque_size = 0;
1771 if ((colour_type==GF_ISOM_SUBTYPE_RICC) || (colour_type==GF_ISOM_SUBTYPE_PROF)) {
1772 clr->opaque_size = icc_data ? icc_size : 0;
1773 if (clr->opaque_size) {
1774 clr->opaque = gf_malloc(sizeof(char)*clr->opaque_size);
1775 if (!clr->opaque) return GF_OUT_OF_MEM;
1776 memcpy(clr->opaque, icc_data, sizeof(char)*clr->opaque_size);
1777 }
1778 }
1779 return GF_OK;
1780 }
1781
1782 GF_EXPORT
gf_isom_set_dolby_vision_profile(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,u32 dv_profile)1783 GF_Err gf_isom_set_dolby_vision_profile(GF_ISOFile* movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 dv_profile)
1784 {
1785 GF_Err e;
1786 GF_TrackBox* trak;
1787 GF_SampleEntryBox* entry;
1788 GF_SampleDescriptionBox* stsd;
1789 GF_DOVIConfigurationBox* dovi = NULL;
1790 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1791 if (e) return e;
1792
1793 trak = gf_isom_get_track_from_file(movie, trackNumber);
1794 if (!trak) return GF_BAD_PARAM;
1795
1796 stsd = trak->Media->information->sampleTable->SampleDescription;
1797 if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
1798 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
1799 return movie->LastError = GF_BAD_PARAM;
1800 }
1801 entry = (GF_SampleEntryBox*)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
1802 //no support for generic sample entries (eg, no MPEG4 descriptor)
1803 if (entry == NULL) return GF_BAD_PARAM;
1804 if (!movie->keep_utc)
1805 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1806
1807 if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_OK;
1808
1809 dovi = ((GF_MPEGVisualSampleEntryBox*)entry)->dovi_config;
1810 if (!dv_profile) {
1811 if (dovi) gf_isom_box_del((GF_Box*)dovi);
1812 ((GF_MPEGVisualSampleEntryBox*)entry)->dovi_config = NULL;
1813 return GF_OK;
1814 }
1815 if (!dovi) {
1816 dovi = (GF_DOVIConfigurationBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_DVCC);
1817 if (!dovi) return GF_OUT_OF_MEM;
1818 ((GF_MPEGVisualSampleEntryBox*)entry)->dovi_config = dovi;
1819 }
1820 entry->type = GF_ISOM_BOX_TYPE_DVHE;
1821 dovi->DOVIConfig.dv_profile = dv_profile;
1822 return GF_OK;
1823 }
1824
1825 GF_EXPORT
gf_isom_set_high_dynamic_range_info(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,GF_MasteringDisplayColourVolumeInfo * mdcv,GF_ContentLightLevelInfo * clli)1826 GF_Err gf_isom_set_high_dynamic_range_info(GF_ISOFile* movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_MasteringDisplayColourVolumeInfo* mdcv, GF_ContentLightLevelInfo* clli)
1827 {
1828 GF_Err e;
1829 GF_TrackBox* trak;
1830 GF_SampleEntryBox* entry;
1831 GF_SampleDescriptionBox* stsd;
1832
1833 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1834 if (e) return e;
1835
1836 trak = gf_isom_get_track_from_file(movie, trackNumber);
1837 if (!trak) return GF_BAD_PARAM;
1838
1839 stsd = trak->Media->information->sampleTable->SampleDescription;
1840 if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
1841 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
1842 return movie->LastError = GF_BAD_PARAM;
1843 }
1844 entry = (GF_SampleEntryBox*)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
1845 //no support for generic sample entries (eg, no MPEG4 descriptor)
1846 if (entry == NULL) return GF_BAD_PARAM;
1847 if (!movie->keep_utc)
1848 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1849
1850 if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
1851
1852 GF_MasteringDisplayColourVolumeBox *mdcvb = (GF_MasteringDisplayColourVolumeBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_MDCV);
1853 if (!mdcvb) {
1854 mdcvb = (GF_MasteringDisplayColourVolumeBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_MDCV);
1855 if (!mdcvb) return GF_OUT_OF_MEM;
1856 }
1857 mdcvb->mdcv = *mdcv;
1858
1859 /*clli*/
1860 GF_ContentLightLevelBox *cllib = (GF_ContentLightLevelBox *)gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_CLLI);
1861 if (!cllib) {
1862 cllib = (GF_ContentLightLevelBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_CLLI);
1863 if (!cllib) return GF_OUT_OF_MEM;
1864 }
1865 cllib->clli = *clli;
1866 return GF_OK;
1867 }
1868
1869 GF_EXPORT
gf_isom_set_clean_aperture(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,u32 cleanApertureWidthN,u32 cleanApertureWidthD,u32 cleanApertureHeightN,u32 cleanApertureHeightD,u32 horizOffN,u32 horizOffD,u32 vertOffN,u32 vertOffD)1870 GF_Err gf_isom_set_clean_aperture(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 cleanApertureWidthN, u32 cleanApertureWidthD, u32 cleanApertureHeightN, u32 cleanApertureHeightD, u32 horizOffN, u32 horizOffD, u32 vertOffN, u32 vertOffD)
1871 {
1872 GF_Err e;
1873 GF_TrackBox *trak;
1874 GF_SampleEntryBox *entry;
1875 GF_SampleDescriptionBox *stsd;
1876 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1877 if (e) return e;
1878
1879 trak = gf_isom_get_track_from_file(movie, trackNumber);
1880 if (!trak) return GF_BAD_PARAM;
1881
1882 stsd = trak->Media->information->sampleTable->SampleDescription;
1883 if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
1884 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
1885 return movie->LastError = GF_BAD_PARAM;
1886 }
1887 entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
1888 //no support for generic sample entries (eg, no MPEG4 descriptor)
1889 if (entry == NULL) return GF_BAD_PARAM;
1890 if (!movie->keep_utc)
1891 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1892
1893 if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
1894
1895 GF_CleanApertureBox *clap = (GF_CleanApertureBox *)gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_CLAP);
1896 if (!cleanApertureHeightD || !cleanApertureWidthD || !horizOffD || !vertOffD) {
1897 if (clap) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)clap);
1898 return GF_OK;
1899 }
1900 if (!clap) {
1901 clap = (GF_CleanApertureBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_CLAP);
1902 if (!clap) return GF_OUT_OF_MEM;
1903 }
1904
1905 clap->cleanApertureWidthN = cleanApertureWidthN;
1906 clap->cleanApertureWidthD = cleanApertureWidthD;
1907 clap->cleanApertureHeightN = cleanApertureHeightN;
1908 clap->cleanApertureHeightD = cleanApertureHeightD;
1909 clap->horizOffN = horizOffN;
1910 clap->horizOffD = horizOffD;
1911 clap->vertOffN = vertOffN;
1912 clap->vertOffD = vertOffD;
1913 return GF_OK;
1914 }
1915
1916 #include <gpac/maths.h>
gf_isom_update_aperture_info(GF_ISOFile * movie,u32 trackNumber,Bool remove)1917 GF_Err gf_isom_update_aperture_info(GF_ISOFile *movie, u32 trackNumber, Bool remove)
1918 {
1919 GF_Err e;
1920 GF_Box *box, *enof, *prof, *clef;
1921 GF_TrackBox *trak;
1922 GF_VisualSampleEntryBox *ventry;
1923 GF_PixelAspectRatioBox *pasp;
1924 GF_CleanApertureBox *clap;
1925 u32 j, hspacing, vspacing, clap_width_num, clap_width_den, clap_height_num, clap_height_den, high, low;
1926 Double width, height;
1927
1928 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1929 if (e) return e;
1930
1931 trak = gf_isom_get_track_from_file(movie, trackNumber);
1932 if (!trak) return GF_BAD_PARAM;
1933
1934 if (remove) {
1935 if (trak->Aperture) {
1936 gf_isom_box_del(trak->Aperture);
1937 trak->Aperture = NULL;
1938 }
1939 return GF_OK;
1940 }
1941 enof = prof = clef = NULL;
1942 if (!trak->Aperture) {
1943 trak->Aperture = gf_isom_box_new_parent(&trak->child_boxes, GF_QT_BOX_TYPE_TAPT);
1944 if (!trak->Aperture) return GF_OUT_OF_MEM;
1945 }
1946 if (!trak->Aperture->child_boxes) {
1947 trak->Aperture->child_boxes = gf_list_new();
1948 if (!trak->Aperture->child_boxes)
1949 return GF_OUT_OF_MEM;
1950 }
1951
1952 j=0;
1953 while ( (box = gf_list_enum(trak->Aperture->child_boxes, &j))) {
1954 switch (box->type) {
1955 case GF_QT_BOX_TYPE_CLEF: clef = box; break;
1956 case GF_QT_BOX_TYPE_PROF: prof = box; break;
1957 case GF_QT_BOX_TYPE_ENOF: enof = box; break;
1958 }
1959 }
1960 if (!clef) {
1961 clef = gf_isom_box_new(GF_QT_BOX_TYPE_CLEF);
1962 if (!clef) return GF_OUT_OF_MEM;
1963 gf_list_add(trak->Aperture->child_boxes, clef);
1964 }
1965 if (!enof) {
1966 enof = gf_isom_box_new(GF_QT_BOX_TYPE_ENOF);
1967 if (!enof) return GF_OUT_OF_MEM;
1968 gf_list_add(trak->Aperture->child_boxes, enof);
1969 }
1970 if (!prof) {
1971 prof = gf_isom_box_new(GF_QT_BOX_TYPE_PROF);
1972 if (!prof) return GF_OUT_OF_MEM;
1973 gf_list_add(trak->Aperture->child_boxes, prof);
1974 }
1975
1976 ventry = (GF_VisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, 0);
1977 //no support for generic sample entries (eg, no MPEG4 descriptor)
1978 if (ventry == NULL) return GF_BAD_PARAM;
1979 if (!movie->keep_utc)
1980 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1981
1982 if (ventry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
1983
1984 pasp = (GF_PixelAspectRatioBox *) gf_isom_box_find_child(ventry->child_boxes, GF_ISOM_BOX_TYPE_PASP);
1985 hspacing = vspacing = 0;
1986 if (pasp) {
1987 hspacing = pasp->hSpacing;
1988 vspacing = pasp->vSpacing;
1989 }
1990 clap_width_num = ventry->Width;
1991 clap_width_den = 1;
1992 clap_height_num = ventry->Height;
1993 clap_height_den = 1;
1994 clap = (GF_CleanApertureBox *) gf_isom_box_find_child(ventry->child_boxes, GF_ISOM_BOX_TYPE_CLAP);
1995 if (clap) {
1996 clap_width_num = clap->cleanApertureWidthN;
1997 clap_width_den = clap->cleanApertureWidthD;
1998 clap_height_num = clap->cleanApertureHeightN;
1999 clap_height_den = clap->cleanApertureHeightD;
2000 }
2001 //enof: encoded pixels in 16.16
2002 ((GF_ApertureBox *)enof)->width = (ventry->Width)<<16;
2003 ((GF_ApertureBox *)enof)->height = (ventry->Height)<<16;
2004
2005 //prof: encoded pixels + pasp in 16.16
2006 width = (Float) (ventry->Width * hspacing);
2007 width /= vspacing;
2008 high = (u32) floor((Float)width);
2009 low = (u32) ( 0xFFFF * (width - (Double)high) );
2010 ((GF_ApertureBox *)prof)->width = (high)<<16 | low;
2011 ((GF_ApertureBox *)prof)->height = (ventry->Height)<<16;
2012
2013 //clef: encoded pixels + pasp + clap in 16.16
2014 width = (Double) (clap_width_num * hspacing);
2015 width /= clap_width_den * vspacing;
2016 height = (Float) clap_height_num;
2017 height /= clap_height_den;
2018
2019 high = (u32) floor((Float)width);
2020 low = (u32) (0xFFFF * (width - (Double)high));
2021 ((GF_ApertureBox *)clef)->width = (high)<<16 | low;
2022 high = (u32) floor((Float)height);
2023 low = (u32) (0xFFFF * (height - (Double)high));
2024 ((GF_ApertureBox *)clef)->height = (high)<<16 | low;
2025
2026
2027 return GF_OK;
2028 }
2029
2030 GF_EXPORT
gf_isom_set_image_sequence_coding_constraints(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,Bool remove,Bool all_ref_pics_intra,Bool intra_pred_used,u32 max_ref_per_pic)2031 GF_Err gf_isom_set_image_sequence_coding_constraints(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, Bool remove, Bool all_ref_pics_intra, Bool intra_pred_used, u32 max_ref_per_pic)
2032 {
2033 GF_Err e;
2034 GF_TrackBox *trak;
2035 GF_SampleEntryBox *entry;
2036 GF_SampleDescriptionBox *stsd;
2037 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2038 if (e) return e;
2039
2040 trak = gf_isom_get_track_from_file(movie, trackNumber);
2041 if (!trak) return GF_BAD_PARAM;
2042
2043 stsd = trak->Media->information->sampleTable->SampleDescription;
2044 if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
2045 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
2046 return movie->LastError = GF_BAD_PARAM;
2047 }
2048 entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
2049 //no support for generic sample entries (eg, no MPEG4 descriptor)
2050 if (entry == NULL) return GF_BAD_PARAM;
2051 if (!movie->keep_utc)
2052 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
2053
2054 if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
2055
2056 GF_CodingConstraintsBox*ccst = (GF_CodingConstraintsBox*) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_CCST);
2057 if (remove) {
2058 if (ccst) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)ccst);
2059 return GF_OK;
2060 }
2061 if (!ccst) {
2062 ccst = (GF_CodingConstraintsBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_CCST);
2063 if (!ccst) return GF_OUT_OF_MEM;
2064 }
2065 ccst->all_ref_pics_intra = all_ref_pics_intra;
2066 ccst->intra_pred_used = intra_pred_used;
2067 ccst->max_ref_per_pic = max_ref_per_pic;
2068 return GF_OK;
2069 }
2070
2071 GF_EXPORT
gf_isom_set_image_sequence_alpha(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,Bool remove)2072 GF_Err gf_isom_set_image_sequence_alpha(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, Bool remove)
2073 {
2074 GF_Err e;
2075 GF_TrackBox *trak;
2076 GF_SampleEntryBox *entry;
2077 GF_SampleDescriptionBox *stsd;
2078 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2079 if (e) return e;
2080
2081 trak = gf_isom_get_track_from_file(movie, trackNumber);
2082 if (!trak) return GF_BAD_PARAM;
2083
2084 stsd = trak->Media->information->sampleTable->SampleDescription;
2085 if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
2086 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
2087 return movie->LastError = GF_BAD_PARAM;
2088 }
2089 entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
2090 //no support for generic sample entries (eg, no MPEG4 descriptor)
2091 if (entry == NULL) return GF_BAD_PARAM;
2092 if (!movie->keep_utc)
2093 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
2094
2095 if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
2096
2097 GF_AuxiliaryTypeInfoBox *auxi = (GF_AuxiliaryTypeInfoBox *)gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_AUXI);
2098 if (remove) {
2099 if (auxi) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)auxi);
2100 return GF_OK;
2101 }
2102 if (!auxi) {
2103 auxi = (GF_AuxiliaryTypeInfoBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_AUXI);
2104 if (!auxi) return GF_OUT_OF_MEM;
2105 }
2106 auxi->aux_track_type = gf_strdup("urn:mpeg:mpegB:cicp:systems:auxiliary:alpha");
2107 return GF_OK;
2108 }
2109
2110 GF_EXPORT
gf_isom_set_audio_info(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,u32 sampleRate,u32 nbChannels,u8 bitsPerSample,GF_AudioSampleEntryImportMode asemode)2111 GF_Err gf_isom_set_audio_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 sampleRate, u32 nbChannels, u8 bitsPerSample, GF_AudioSampleEntryImportMode asemode)
2112 {
2113 GF_Err e;
2114 u32 i, old_qtff_mode=GF_ISOM_AUDIO_QTFF_NONE;
2115 GF_TrackBox *trak;
2116 GF_SampleEntryBox *entry;
2117 GF_AudioSampleEntryBox*aud_entry;
2118 GF_SampleDescriptionBox *stsd;
2119 GF_Box *wave_box = NULL;
2120 GF_Box *gf_isom_audio_sample_get_audio_codec_cfg_box(GF_AudioSampleEntryBox *ptr);
2121 GF_Box *codec_ext = NULL;
2122 #if 0
2123 GF_ChannelLayoutInfoBox *chan=NULL;
2124 #endif
2125 GF_OriginalFormatBox *frma=NULL;
2126 GF_ChromaInfoBox *enda=NULL;
2127 GF_ESDBox *esds=NULL;
2128 GF_Box *terminator=NULL;
2129 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2130 if (e) return e;
2131
2132 trak = gf_isom_get_track_from_file(movie, trackNumber);
2133 if (!trak) return GF_BAD_PARAM;
2134
2135 stsd = trak->Media->information->sampleTable->SampleDescription;
2136 if (!stsd) {
2137 return movie->LastError = GF_ISOM_INVALID_FILE;
2138 }
2139 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
2140 return movie->LastError = GF_BAD_PARAM;
2141 }
2142 entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
2143 //no support for generic sample entries (eg, no MPEG4 descriptor)
2144 if (entry == NULL) return GF_BAD_PARAM;
2145 if (!movie->keep_utc)
2146 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
2147
2148 if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_AUDIO) return GF_BAD_PARAM;
2149 aud_entry = (GF_AudioSampleEntryBox*) entry;
2150 aud_entry->samplerate_hi = sampleRate;
2151 aud_entry->samplerate_lo = 0;
2152 aud_entry->bitspersample = bitsPerSample;
2153
2154 switch (asemode) {
2155 case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v0_2:
2156 stsd->version = 0;
2157 aud_entry->version = 0;
2158 aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE;
2159 aud_entry->channel_count = 2;
2160 break;
2161 case GF_IMPORT_AUDIO_SAMPLE_ENTRY_NOT_SET:
2162 case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v0_BS:
2163 stsd->version = 0;
2164 aud_entry->version = 0;
2165 aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE;
2166 aud_entry->channel_count = nbChannels;
2167 break;
2168 case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_MPEG:
2169 stsd->version = 1;
2170 aud_entry->version = 1;
2171 aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE;
2172 aud_entry->channel_count = nbChannels;
2173 break;
2174 case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF:
2175 stsd->version = 0;
2176 aud_entry->version = 1;
2177 aud_entry->channel_count = nbChannels;
2178 old_qtff_mode = aud_entry->qtff_mode;
2179 if (aud_entry->qtff_mode != GF_ISOM_AUDIO_QTFF_ON_EXT_VALID)
2180 aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_ON_NOEXT;
2181 break;
2182 }
2183
2184 aud_entry->compression_id = 0;
2185
2186 //check for wave+children and chan for QTFF or remove them for isobmff
2187 for (i=0; i<gf_list_count(aud_entry->child_boxes); i++) {
2188 GF_Box *b = gf_list_get(aud_entry->child_boxes, i);
2189 if ((b->type != GF_QT_BOX_TYPE_WAVE) && (b->type != GF_QT_BOX_TYPE_CHAN) ) continue;
2190 if (asemode==GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF) {
2191 if (b->type == GF_QT_BOX_TYPE_WAVE) wave_box = b;
2192 #if 0
2193 else chan = (GF_ChannelLayoutInfoBox *)b;
2194 #endif
2195
2196 } else {
2197 gf_isom_box_del_parent(&aud_entry->child_boxes, b);
2198 i--;
2199 }
2200 }
2201
2202 //TODO: insert channelLayout for ISOBMFF
2203 if (asemode!=GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF) return GF_OK;
2204
2205 if (aud_entry->type==GF_ISOM_BOX_TYPE_MP4A)
2206 aud_entry->compression_id = -2;
2207
2208 if (!aud_entry->child_boxes) aud_entry->child_boxes = gf_list_new();
2209
2210 #if 0
2211 if (!chan) {
2212 chan = (GF_ChannelLayoutInfoBox *) gf_isom_box_new_parent(&aud_entry->child_boxes, GF_QT_BOX_TYPE_CHAN);
2213 }
2214 //TODO, proper channel mapping
2215 chan->layout_tag = (nbChannels==2) ? 6750210 : 6553601;
2216 #endif
2217
2218 codec_ext = gf_isom_audio_sample_get_audio_codec_cfg_box((GF_AudioSampleEntryBox *)aud_entry);
2219 if (!codec_ext) return GF_OK;
2220
2221 if (!wave_box) {
2222 wave_box = gf_isom_box_new_parent(&aud_entry->child_boxes, GF_QT_BOX_TYPE_WAVE);
2223 }
2224
2225 for (i=0; i<gf_list_count(wave_box->child_boxes); i++) {
2226 GF_Box *b = gf_list_get(wave_box->child_boxes, i);
2227 switch (b->type) {
2228 case GF_QT_BOX_TYPE_ENDA:
2229 enda = (GF_ChromaInfoBox *)b;
2230 break;
2231 case GF_QT_BOX_TYPE_FRMA:
2232 frma = (GF_OriginalFormatBox *)b;
2233 break;
2234 case GF_ISOM_BOX_TYPE_ESDS:
2235 esds = (GF_ESDBox *)b;
2236 break;
2237 case GF_ISOM_BOX_TYPE_UNKNOWN:
2238 if ( ((GF_UnknownBox*)b)->original_4cc == 0)
2239 terminator = b;
2240 break;
2241 case 0:
2242 terminator = b;
2243 break;
2244 }
2245 }
2246 if (!wave_box->child_boxes) wave_box->child_boxes = gf_list_new();
2247
2248 //do not use new_parent, we do this manually to ensure the order
2249 aud_entry->qtff_mode = old_qtff_mode ? old_qtff_mode : GF_ISOM_AUDIO_QTFF_ON_NOEXT;
2250 if (!frma) {
2251 frma = (GF_OriginalFormatBox *)gf_isom_box_new(GF_QT_BOX_TYPE_FRMA);
2252 } else {
2253 gf_list_del_item(wave_box->child_boxes, frma);
2254 }
2255 gf_list_add(wave_box->child_boxes, frma);
2256
2257 if (esds) gf_list_del_item(wave_box->child_boxes, esds);
2258 if (!esds && (aud_entry->type==GF_ISOM_BOX_TYPE_MP4A) && ((GF_MPEGAudioSampleEntryBox*)aud_entry)->esd) {
2259 gf_list_del_item(entry->child_boxes, (GF_Box *) ((GF_MPEGAudioSampleEntryBox*)aud_entry)->esd);
2260 gf_list_add(wave_box->child_boxes, (GF_Box *) ((GF_MPEGAudioSampleEntryBox*)aud_entry)->esd);
2261 }
2262
2263 if (!enda) {
2264 enda = (GF_ChromaInfoBox *)gf_isom_box_new(GF_QT_BOX_TYPE_ENDA);
2265 } else {
2266 gf_list_del_item(wave_box->child_boxes, enda);
2267 }
2268 enda->chroma=1;
2269 gf_list_add(wave_box->child_boxes, enda);
2270
2271 if (!terminator) {
2272 terminator = gf_isom_box_new(0);
2273 } else {
2274 gf_list_del_item(wave_box->child_boxes, terminator);
2275 }
2276 gf_list_add(wave_box->child_boxes, terminator);
2277
2278 if (aud_entry->type==GF_ISOM_BOX_TYPE_GNRA) {
2279 frma->data_format = ((GF_GenericAudioSampleEntryBox*) aud_entry)->EntryType;
2280 } else {
2281 frma->data_format = aud_entry->type;
2282 }
2283
2284 return GF_OK;
2285 }
2286
2287 GF_EXPORT
gf_isom_set_audio_layout(GF_ISOFile * movie,u32 trackNumber,u32 sampleDescriptionIndex,GF_AudioChannelLayout * layout)2288 GF_Err gf_isom_set_audio_layout(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, GF_AudioChannelLayout *layout)
2289 {
2290 GF_Err e;
2291 GF_TrackBox *trak;
2292 GF_SampleEntryBox *entry;
2293 GF_AudioSampleEntryBox*aud_entry;
2294 GF_SampleDescriptionBox *stsd;
2295 GF_ChannelLayoutBox *chnl;
2296 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2297 if (e) return e;
2298
2299 trak = gf_isom_get_track_from_file(movie, trackNumber);
2300 if (!trak) return GF_BAD_PARAM;
2301
2302 stsd = trak->Media->information->sampleTable->SampleDescription;
2303 if (!stsd) {
2304 return movie->LastError = GF_ISOM_INVALID_FILE;
2305 }
2306 if (!sampleDescriptionIndex || sampleDescriptionIndex > gf_list_count(stsd->child_boxes)) {
2307 return movie->LastError = GF_BAD_PARAM;
2308 }
2309 entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, sampleDescriptionIndex - 1);
2310 //no support for generic sample entries (eg, no MPEG4 descriptor)
2311 if (entry == NULL) return GF_BAD_PARAM;
2312 if (!movie->keep_utc)
2313 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
2314
2315 if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_AUDIO) return GF_BAD_PARAM;
2316 aud_entry = (GF_AudioSampleEntryBox*) entry;
2317 if (aud_entry->qtff_mode) {
2318 e = gf_isom_set_audio_info(movie, trackNumber, sampleDescriptionIndex, aud_entry->samplerate_hi, aud_entry->channel_count, (u8) aud_entry->bitspersample, GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_MPEG);
2319 if (e) return e;
2320 }
2321 chnl = (GF_ChannelLayoutBox *) gf_isom_box_find_child(aud_entry->child_boxes, GF_ISOM_BOX_TYPE_CHNL);
2322 if (!chnl) {
2323 chnl = (GF_ChannelLayoutBox *)gf_isom_box_new_parent(&aud_entry->child_boxes, GF_ISOM_BOX_TYPE_CHNL);
2324 if (!chnl) return GF_OUT_OF_MEM;
2325 }
2326 aud_entry->channel_count = layout->channels_count;
2327 memcpy(&chnl->layout, layout, sizeof(GF_AudioChannelLayout));
2328 return GF_OK;
2329 }
2330
2331 //set the storage mode of a file (FLAT, STREAMABLE, INTERLEAVED)
2332 GF_EXPORT
gf_isom_set_storage_mode(GF_ISOFile * movie,GF_ISOStorageMode storageMode)2333 GF_Err gf_isom_set_storage_mode(GF_ISOFile *movie, GF_ISOStorageMode storageMode)
2334 {
2335 GF_Err e;
2336 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2337 if (e) return e;
2338
2339 switch (storageMode) {
2340 case GF_ISOM_STORE_FLAT:
2341 case GF_ISOM_STORE_STREAMABLE:
2342 case GF_ISOM_STORE_INTERLEAVED:
2343 case GF_ISOM_STORE_DRIFT_INTERLEAVED:
2344 case GF_ISOM_STORE_TIGHT:
2345 case GF_ISOM_STORE_FASTSTART:
2346 movie->storageMode = storageMode;
2347 return GF_OK;
2348 default:
2349 return GF_BAD_PARAM;
2350 }
2351 }
2352
2353
2354 GF_EXPORT
gf_isom_enable_compression(GF_ISOFile * file,GF_ISOCompressMode compress_mode,Bool force_compress)2355 GF_Err gf_isom_enable_compression(GF_ISOFile *file, GF_ISOCompressMode compress_mode, Bool force_compress)
2356 {
2357 if (!file) return GF_BAD_PARAM;
2358 file->compress_mode = compress_mode;
2359 file->force_compress = force_compress;
2360 return GF_OK;
2361 }
2362
2363 GF_EXPORT
gf_isom_force_64bit_chunk_offset(GF_ISOFile * file,Bool set_on)2364 GF_Err gf_isom_force_64bit_chunk_offset(GF_ISOFile *file, Bool set_on)
2365 {
2366 if (!file) return GF_BAD_PARAM;
2367 file->force_co64 = set_on;
2368 return GF_OK;
2369 }
2370
2371
2372 //update or insert a new edit segment in the track time line. Edits are used to modify
2373 //the media normal timing. EditTime and EditDuration are expressed in Movie TimeScale
2374 //If a segment with EditTime already exists, IT IS ERASED
2375 GF_EXPORT
gf_isom_set_edit(GF_ISOFile * movie,u32 trackNumber,u64 EditTime,u64 EditDuration,u64 MediaTime,GF_ISOEditType EditMode)2376 GF_Err gf_isom_set_edit(GF_ISOFile *movie, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, GF_ISOEditType EditMode)
2377 {
2378 GF_TrackBox *trak;
2379 GF_EditBox *edts;
2380 GF_EditListBox *elst;
2381 GF_EdtsEntry *ent, *newEnt;
2382 u32 i;
2383 GF_Err e;
2384 u64 startTime;
2385
2386 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2387 if (e) return e;
2388
2389 trak = gf_isom_get_track_from_file(movie, trackNumber);
2390 if (!trak) return GF_BAD_PARAM;
2391
2392 edts = trak->editBox;
2393 if (! edts) {
2394 edts = (GF_EditBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_EDTS);
2395 if (!edts) return GF_OUT_OF_MEM;
2396 trak_on_child_box((GF_Box*)trak, (GF_Box *)edts);
2397 }
2398 elst = edts->editList;
2399 if (!elst) {
2400 elst = (GF_EditListBox *) gf_isom_box_new_parent(&edts->child_boxes, GF_ISOM_BOX_TYPE_ELST);
2401 if (!elst) return GF_OUT_OF_MEM;
2402 edts_on_child_box((GF_Box*)edts, (GF_Box *)elst);
2403 }
2404
2405 startTime = 0;
2406 ent = NULL;
2407 //get the prev entry to this startTime if any
2408 i=0;
2409 while ((ent = (GF_EdtsEntry *)gf_list_enum(elst->entryList, &i))) {
2410 if ( (startTime <= EditTime) && (startTime + ent->segmentDuration > EditTime) )
2411 goto found;
2412 startTime += ent->segmentDuration;
2413 }
2414
2415 //not found, add a new entry and adjust the prev one if any
2416 if (!ent) {
2417 newEnt = CreateEditEntry(EditDuration, MediaTime, EditMode);
2418 if (!newEnt) return GF_OUT_OF_MEM;
2419 gf_list_add(elst->entryList, newEnt);
2420 return SetTrackDuration(trak);
2421 }
2422
2423 startTime -= ent->segmentDuration;
2424
2425 found:
2426
2427 //if same time, we erase the current one...
2428 if (startTime == EditTime) {
2429 ent->segmentDuration = EditDuration;
2430 switch (EditMode) {
2431 case GF_ISOM_EDIT_EMPTY:
2432 ent->mediaRate = 1;
2433 ent->mediaTime = -1;
2434 break;
2435 case GF_ISOM_EDIT_DWELL:
2436 ent->mediaRate = 0;
2437 ent->mediaTime = MediaTime;
2438 break;
2439 default:
2440 ent->mediaRate = 1;
2441 ent->mediaTime = MediaTime;
2442 break;
2443 }
2444 return SetTrackDuration(trak);
2445 }
2446
2447 //adjust so that the prev ent leads to EntryTime
2448 //Note: we don't change the next one as it is unknown to us in
2449 //a lot of case (the author's changes)
2450 ent->segmentDuration = EditTime - startTime;
2451 newEnt = CreateEditEntry(EditDuration, MediaTime, EditMode);
2452 if (!newEnt) return GF_OUT_OF_MEM;
2453 //is it the last entry ???
2454 if (i >= gf_list_count(elst->entryList) - 1) {
2455 //add the new entry at the end
2456 gf_list_add(elst->entryList, newEnt);
2457 return SetTrackDuration(trak);
2458 } else {
2459 //insert after the current entry (which is i)
2460 gf_list_insert(elst->entryList, newEnt, i+1);
2461 return SetTrackDuration(trak);
2462 }
2463 }
2464
2465 //remove the edit segments for the whole track
2466 GF_EXPORT
gf_isom_remove_edits(GF_ISOFile * movie,u32 trackNumber)2467 GF_Err gf_isom_remove_edits(GF_ISOFile *movie, u32 trackNumber)
2468 {
2469 GF_Err e;
2470 GF_TrackBox *trak;
2471 trak = gf_isom_get_track_from_file(movie, trackNumber);
2472 if (!trak) return GF_BAD_PARAM;
2473
2474 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2475 if (e) return e;
2476
2477 if (!trak->editBox || !trak->editBox->editList) return GF_OK;
2478
2479 while (gf_list_count(trak->editBox->editList->entryList)) {
2480 GF_EdtsEntry *ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, 0);
2481 gf_free(ent);
2482 e = gf_list_rem(trak->editBox->editList->entryList, 0);
2483 if (e) return e;
2484 }
2485 //then delete the GF_EditBox...
2486 gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->editBox);
2487 trak->editBox = NULL;
2488 return SetTrackDuration(trak);
2489 }
2490
2491
2492 //remove the edit segments for the whole track
2493 GF_EXPORT
gf_isom_remove_edit(GF_ISOFile * movie,u32 trackNumber,u32 seg_index)2494 GF_Err gf_isom_remove_edit(GF_ISOFile *movie, u32 trackNumber, u32 seg_index)
2495 {
2496 GF_Err e;
2497 GF_TrackBox *trak;
2498 GF_EdtsEntry *ent, *next_ent;
2499 trak = gf_isom_get_track_from_file(movie, trackNumber);
2500 if (!trak || !seg_index) return GF_BAD_PARAM;
2501
2502 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2503 if (e) return e;
2504
2505 if (!trak->editBox || !trak->editBox->editList) return GF_OK;
2506 if (gf_list_count(trak->editBox->editList->entryList)<=1) return gf_isom_remove_edits(movie, trackNumber);
2507
2508 ent = (GF_EdtsEntry*) gf_list_get(trak->editBox->editList->entryList, seg_index-1);
2509 gf_list_rem(trak->editBox->editList->entryList, seg_index-1);
2510 next_ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, seg_index-1);
2511 if (next_ent) next_ent->segmentDuration += ent->segmentDuration;
2512 gf_free(ent);
2513 return SetTrackDuration(trak);
2514 }
2515
2516
2517 GF_EXPORT
gf_isom_append_edit(GF_ISOFile * movie,u32 trackNumber,u64 EditDuration,u64 MediaTime,GF_ISOEditType EditMode)2518 GF_Err gf_isom_append_edit(GF_ISOFile *movie, u32 trackNumber, u64 EditDuration, u64 MediaTime, GF_ISOEditType EditMode)
2519 {
2520 GF_Err e;
2521 GF_TrackBox *trak;
2522 GF_EdtsEntry *ent;
2523 trak = gf_isom_get_track_from_file(movie, trackNumber);
2524 if (!trak) return GF_BAD_PARAM;
2525 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2526 if (e) return e;
2527
2528 if (!trak->editBox) {
2529 GF_EditBox *edts = (GF_EditBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_EDTS);
2530 if (!edts) return GF_OUT_OF_MEM;
2531 trak_on_child_box((GF_Box*)trak, (GF_Box *)edts);
2532 assert(trak->editBox);
2533 }
2534 if (!trak->editBox->editList) {
2535 GF_EditListBox *elst = (GF_EditListBox *) gf_isom_box_new_parent(&trak->editBox->child_boxes, GF_ISOM_BOX_TYPE_ELST);
2536 if (!elst) return GF_OUT_OF_MEM;
2537 edts_on_child_box((GF_Box*)trak->editBox, (GF_Box *)elst);
2538 assert(trak->editBox->editList);
2539 }
2540 ent = (GF_EdtsEntry *)gf_malloc(sizeof(GF_EdtsEntry));
2541 if (!ent) return GF_OUT_OF_MEM;
2542
2543 ent->segmentDuration = EditDuration;
2544 switch (EditMode) {
2545 case GF_ISOM_EDIT_EMPTY:
2546 ent->mediaRate = 1;
2547 ent->mediaTime = -1;
2548 break;
2549 case GF_ISOM_EDIT_DWELL:
2550 ent->mediaRate = 0;
2551 ent->mediaTime = MediaTime;
2552 break;
2553 default:
2554 ent->mediaRate = 1;
2555 ent->mediaTime = MediaTime;
2556 break;
2557 }
2558 gf_list_add(trak->editBox->editList->entryList, ent);
2559 return SetTrackDuration(trak);
2560 }
2561
2562 GF_EXPORT
gf_isom_modify_edit(GF_ISOFile * movie,u32 trackNumber,u32 seg_index,u64 EditDuration,u64 MediaTime,GF_ISOEditType EditMode)2563 GF_Err gf_isom_modify_edit(GF_ISOFile *movie, u32 trackNumber, u32 seg_index, u64 EditDuration, u64 MediaTime, GF_ISOEditType EditMode)
2564 {
2565 GF_Err e;
2566 GF_TrackBox *trak;
2567 GF_EdtsEntry *ent;
2568 trak = gf_isom_get_track_from_file(movie, trackNumber);
2569 if (!trak || !seg_index) return GF_BAD_PARAM;
2570 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2571 if (e) return e;
2572
2573 if (!trak->editBox || !trak->editBox->editList) return GF_OK;
2574 if (gf_list_count(trak->editBox->editList->entryList)<seg_index) return GF_BAD_PARAM;
2575 ent = (GF_EdtsEntry*) gf_list_get(trak->editBox->editList->entryList, seg_index-1);
2576
2577 ent->segmentDuration = EditDuration;
2578 switch (EditMode) {
2579 case GF_ISOM_EDIT_EMPTY:
2580 ent->mediaRate = 1;
2581 ent->mediaTime = -1;
2582 break;
2583 case GF_ISOM_EDIT_DWELL:
2584 ent->mediaRate = 0;
2585 ent->mediaTime = MediaTime;
2586 break;
2587 default:
2588 ent->mediaRate = 1;
2589 ent->mediaTime = MediaTime;
2590 break;
2591 }
2592 return SetTrackDuration(trak);
2593 }
2594
2595 //removes the desired track
2596 GF_EXPORT
gf_isom_remove_track(GF_ISOFile * movie,u32 trackNumber)2597 GF_Err gf_isom_remove_track(GF_ISOFile *movie, u32 trackNumber)
2598 {
2599 GF_Err e;
2600 GF_TrackBox *the_trak, *trak;
2601 GF_TrackReferenceTypeBox *tref;
2602 u32 i, j, k, descIndex;
2603 GF_ISOTrackID *newRefs;
2604 u8 found;
2605 GF_ISOSample *samp;
2606 the_trak = gf_isom_get_track_from_file(movie, trackNumber);
2607 if (!the_trak) return GF_BAD_PARAM;
2608
2609 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2610 if (e) return e;
2611
2612 if (movie->moov->iods && movie->moov->iods->descriptor) {
2613 GF_Descriptor *desc;
2614 GF_ES_ID_Inc *inc;
2615 GF_List *ESDs;
2616 desc = movie->moov->iods->descriptor;
2617 if (desc->tag == GF_ODF_ISOM_IOD_TAG) {
2618 ESDs = ((GF_IsomInitialObjectDescriptor *)desc)->ES_ID_IncDescriptors;
2619 } else if (desc->tag == GF_ODF_ISOM_OD_TAG) {
2620 ESDs = ((GF_IsomObjectDescriptor *)desc)->ES_ID_IncDescriptors;
2621 } else {
2622 return GF_ISOM_INVALID_FILE;
2623 }
2624
2625 //remove the track ref from the root OD if any
2626 i=0;
2627 while ((inc = (GF_ES_ID_Inc *)gf_list_enum(ESDs, &i))) {
2628 if (inc->trackID == the_trak->Header->trackID) {
2629 gf_odf_desc_del((GF_Descriptor *)inc);
2630 i--;
2631 gf_list_rem(ESDs, i);
2632 }
2633 }
2634 }
2635
2636 //remove the track from the movie
2637 gf_list_del_item(movie->moov->trackList, the_trak);
2638
2639 //rewrite any OD tracks
2640 i=0;
2641 while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) {
2642 if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_OD) continue;
2643 //this is an OD track...
2644 j = gf_isom_get_sample_count(movie, i);
2645 for (k=0; k < j; k++) {
2646 //getting the sample will remove the references to the deleted track in the output OD frame
2647 samp = gf_isom_get_sample(movie, i, k+1, &descIndex);
2648 if (!samp) break;
2649 //so let's update with the new OD frame ! If the sample is empty, remove it
2650 if (!samp->dataLength) {
2651 e = gf_isom_remove_sample(movie, i, k+1);
2652 if (e) return e;
2653 } else {
2654 e = gf_isom_update_sample(movie, i, k+1, samp, GF_TRUE);
2655 if (e) return e;
2656 }
2657 //and don't forget to delete the sample
2658 gf_isom_sample_del(&samp);
2659 }
2660 }
2661
2662 //remove the track ref from any "tref" box in all tracks, except the one to delete
2663 //note that we don't touch scal references, as we don't want to rewrite AVC/HEVC samples ...
2664 i=0;
2665 while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) {
2666 if (trak == the_trak) continue;
2667 if (! trak->References || ! gf_list_count(trak->References->child_boxes)) continue;
2668
2669 j=0;
2670 while ((tref = (GF_TrackReferenceTypeBox *)gf_list_enum(trak->References->child_boxes, &j))) {
2671 if (tref->reference_type==GF_ISOM_REF_SCAL)
2672 continue;
2673
2674 found = 0;
2675 for (k=0; k<tref->trackIDCount; k++) {
2676 if (tref->trackIDs[k] == the_trak->Header->trackID) found++;
2677 }
2678 if (!found) continue;
2679 //no more refs, remove this ref_type
2680 if (found == tref->trackIDCount) {
2681 gf_isom_box_del_parent(&trak->References->child_boxes, (GF_Box *)tref);
2682 j--;
2683 } else {
2684 newRefs = (GF_ISOTrackID*)gf_malloc(sizeof(GF_ISOTrackID) * (tref->trackIDCount - found));
2685 if (!newRefs) return GF_OUT_OF_MEM;
2686 found = 0;
2687 for (k = 0; k < tref->trackIDCount; k++) {
2688 if (tref->trackIDs[k] != the_trak->Header->trackID) {
2689 newRefs[k-found] = tref->trackIDs[k];
2690 } else {
2691 found++;
2692 }
2693 }
2694 gf_free(tref->trackIDs);
2695 tref->trackIDs = newRefs;
2696 tref->trackIDCount -= found;
2697 }
2698 }
2699 //a little opt: remove the ref box if empty...
2700 if (! gf_list_count(trak->References->child_boxes)) {
2701 gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->References);
2702 trak->References = NULL;
2703 }
2704 }
2705
2706 //delete the track
2707 gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box *)the_trak);
2708
2709 /*update next track ID*/
2710 movie->moov->mvhd->nextTrackID = 0;
2711 i=0;
2712 while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) {
2713 if (trak->Header->trackID>movie->moov->mvhd->nextTrackID)
2714 movie->moov->mvhd->nextTrackID = trak->Header->trackID;
2715 }
2716
2717 if (!gf_list_count(movie->moov->trackList)) {
2718 gf_list_del_item(movie->TopBoxes, movie->moov);
2719 gf_isom_box_del((GF_Box *)movie->moov);
2720 movie->moov = NULL;
2721 }
2722 return GF_OK;
2723 }
2724
2725
2726 GF_EXPORT
gf_isom_set_copyright(GF_ISOFile * movie,const char * threeCharCode,char * notice)2727 GF_Err gf_isom_set_copyright(GF_ISOFile *movie, const char *threeCharCode, char *notice)
2728 {
2729 GF_Err e;
2730 GF_CopyrightBox *ptr;
2731 GF_UserDataMap *map;
2732 u32 count, i;
2733
2734 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2735 if (e) return e;
2736
2737 if (!notice || !threeCharCode) return GF_BAD_PARAM;
2738
2739 e = gf_isom_insert_moov(movie);
2740 if (e) return e;
2741
2742 if (!movie->moov->udta) {
2743 e = moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA));
2744 if (e) return e;
2745 }
2746 map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL);
2747
2748 if (map) {
2749 //try to find one in our language...
2750 count = gf_list_count(map->boxes);
2751 for (i=0; i<count; i++) {
2752 ptr = (GF_CopyrightBox*)gf_list_get(map->boxes, i);
2753 if (!strcmp(threeCharCode, (const char *) ptr->packedLanguageCode)) {
2754 gf_free(ptr->notice);
2755 ptr->notice = (char*)gf_malloc(sizeof(char) * (strlen(notice) + 1));
2756 if (!ptr->notice) return GF_OUT_OF_MEM;
2757 strcpy(ptr->notice, notice);
2758 return GF_OK;
2759 }
2760 }
2761 }
2762 //nope, create one
2763 ptr = (GF_CopyrightBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CPRT);
2764 if (!ptr) return GF_OUT_OF_MEM;
2765
2766 memcpy(ptr->packedLanguageCode, threeCharCode, 4);
2767 ptr->notice = (char*)gf_malloc(sizeof(char) * (strlen(notice)+1));
2768 if (!ptr->notice) return GF_OUT_OF_MEM;
2769 strcpy(ptr->notice, notice);
2770 return udta_on_child_box((GF_Box *)movie->moov->udta, (GF_Box *) ptr);
2771 }
2772
2773 GF_EXPORT
gf_isom_add_track_kind(GF_ISOFile * movie,u32 trackNumber,const char * schemeURI,const char * value)2774 GF_Err gf_isom_add_track_kind(GF_ISOFile *movie, u32 trackNumber, const char *schemeURI, const char *value)
2775 {
2776 GF_Err e;
2777 GF_KindBox *ptr;
2778 GF_UserDataBox *udta;
2779 GF_UserDataMap *map;
2780 u32 i, count;
2781
2782 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2783 if (e) return e;
2784
2785 e = gf_isom_insert_moov(movie);
2786 if (e) return e;
2787
2788 if (trackNumber) {
2789 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
2790 if (!trak) return GF_BAD_PARAM;
2791 if (!trak->udta) {
2792 e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA));
2793 if (e) return e;
2794 }
2795 udta = trak->udta;
2796 } else {
2797 return GF_BAD_PARAM;
2798 }
2799
2800 map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_KIND, NULL);
2801 if (map) {
2802 count = gf_list_count(map->boxes);
2803 for (i=0; i<count; i++) {
2804 GF_Box *b = (GF_Box *)gf_list_get(map->boxes, i);
2805 if (b->type == GF_ISOM_BOX_TYPE_KIND) {
2806 GF_KindBox *kb = (GF_KindBox *)b;
2807 if (!strcmp(kb->schemeURI, schemeURI) &&
2808 ((value && kb->value && !strcmp(value, kb->value)) || (!value && !kb->value))) {
2809 // Already there
2810 return GF_OK;
2811 }
2812 }
2813 }
2814 }
2815
2816 ptr = (GF_KindBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_KIND);
2817 if (e) return e;
2818
2819 ptr->schemeURI = gf_strdup(schemeURI);
2820 if (value) ptr->value = gf_strdup(value);
2821 return udta_on_child_box((GF_Box *)udta, (GF_Box *) ptr);
2822 }
2823
2824 GF_EXPORT
gf_isom_remove_track_kind(GF_ISOFile * movie,u32 trackNumber,const char * schemeURI,const char * value)2825 GF_Err gf_isom_remove_track_kind(GF_ISOFile *movie, u32 trackNumber, const char *schemeURI, const char *value)
2826 {
2827 GF_Err e;
2828 GF_UserDataBox *udta;
2829 GF_UserDataMap *map;
2830 u32 i;
2831
2832 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2833 if (e) return e;
2834 e = gf_isom_insert_moov(movie);
2835 if (e) return e;
2836
2837 if (trackNumber) {
2838 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
2839 if (!trak) return GF_BAD_PARAM;
2840 if (!trak->udta) {
2841 e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA));
2842 if (e) return e;
2843 }
2844 udta = trak->udta;
2845 } else {
2846 return GF_OK;
2847 }
2848 map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_KIND, NULL);
2849 if (map) {
2850 for (i=0; i<gf_list_count(map->boxes); i++) {
2851 GF_Box *b = (GF_Box *)gf_list_get(map->boxes, i);
2852 if (b->type == GF_ISOM_BOX_TYPE_KIND) {
2853 GF_KindBox *kb = (GF_KindBox *)b;
2854 if (!schemeURI ||
2855 (!strcmp(kb->schemeURI, schemeURI) &&
2856 ((value && kb->value && !strcmp(value, kb->value)) || (!value && !kb->value)))) {
2857 gf_isom_box_del_parent(&map->boxes, b);
2858 i--;
2859 }
2860 }
2861 }
2862 }
2863 return GF_OK;
2864 }
2865
2866 GF_EXPORT
gf_isom_add_chapter(GF_ISOFile * movie,u32 trackNumber,u64 timestamp,char * name)2867 GF_Err gf_isom_add_chapter(GF_ISOFile *movie, u32 trackNumber, u64 timestamp, char *name)
2868 {
2869 GF_Err e;
2870 GF_ChapterListBox *ptr;
2871 u32 i, count;
2872 GF_ChapterEntry *ce;
2873 GF_UserDataBox *udta;
2874 GF_UserDataMap *map;
2875
2876 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2877 if (e) return e;
2878
2879 e = gf_isom_insert_moov(movie);
2880 if (e) return e;
2881
2882 if (trackNumber) {
2883 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
2884 if (!trak) return GF_BAD_PARAM;
2885 if (!trak->udta) {
2886 e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA));
2887 if (e) return e;
2888 }
2889 udta = trak->udta;
2890 } else {
2891 if (!movie->moov->udta) {
2892 e = moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA));
2893 if (e) return e;
2894 }
2895 udta = movie->moov->udta;
2896 }
2897
2898 ptr = NULL;
2899 map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL);
2900 if (!map) {
2901 ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL);
2902 e = udta_on_child_box((GF_Box *)udta, (GF_Box *) ptr);
2903 if (e) return e;
2904 map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL);
2905 } else {
2906 ptr = (GF_ChapterListBox*)gf_list_get(map->boxes, 0);
2907 }
2908 if (!map) return GF_OUT_OF_MEM;
2909
2910 /*this may happen if original MP4 is not properly formatted*/
2911 if (!ptr) {
2912 ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL);
2913 if (!ptr) return GF_OUT_OF_MEM;
2914 gf_list_add(map->boxes, ptr);
2915 }
2916
2917 GF_SAFEALLOC(ce, GF_ChapterEntry);
2918 if (!ce) return GF_OUT_OF_MEM;
2919
2920 ce->start_time = timestamp * 10000L;
2921 ce->name = name ? gf_strdup(name) : NULL;
2922
2923 /*insert in order*/
2924 count = gf_list_count(ptr->list);
2925 for (i=0; i<count; i++) {
2926 GF_ChapterEntry *ace = (GF_ChapterEntry *)gf_list_get(ptr->list, i);
2927 if (ace->start_time == ce->start_time) {
2928 if (ace->name) gf_free(ace->name);
2929 ace->name = ce->name;
2930 gf_free(ce);
2931 return GF_OK;
2932 }
2933 if (ace->start_time >= ce->start_time)
2934 return gf_list_insert(ptr->list, ce, i);
2935 }
2936 return gf_list_add(ptr->list, ce);
2937 }
2938
2939 GF_EXPORT
gf_isom_remove_chapter(GF_ISOFile * movie,u32 trackNumber,u32 index)2940 GF_Err gf_isom_remove_chapter(GF_ISOFile *movie, u32 trackNumber, u32 index)
2941 {
2942 GF_Err e;
2943 GF_ChapterListBox *ptr;
2944 GF_ChapterEntry *ce;
2945 GF_UserDataBox *udta;
2946 GF_UserDataMap *map;
2947
2948 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2949 if (e) return e;
2950 e = gf_isom_insert_moov(movie);
2951 if (e) return e;
2952
2953 if (trackNumber) {
2954 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
2955 if (!trak) return GF_BAD_PARAM;
2956 if (!trak->udta) return GF_OK;
2957 udta = trak->udta;
2958 } else {
2959 if (!movie->moov->udta) return GF_OK;
2960 udta = movie->moov->udta;
2961 }
2962
2963 map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL);
2964 if (!map) return GF_OK;
2965 ptr = (GF_ChapterListBox*)gf_list_get(map->boxes, 0);
2966 if (!ptr) return GF_OK;
2967
2968 if (index) {
2969 ce = (GF_ChapterEntry *)gf_list_get(ptr->list, index-1);
2970 if (!ce) return GF_BAD_PARAM;
2971 if (ce->name) gf_free(ce->name);
2972 gf_free(ce);
2973 gf_list_rem(ptr->list, index-1);
2974 } else {
2975 while (gf_list_count(ptr->list)) {
2976 ce = (GF_ChapterEntry *)gf_list_get(ptr->list, 0);
2977 if (ce->name) gf_free(ce->name);
2978 gf_free(ce);
2979 gf_list_rem(ptr->list, 0);
2980 }
2981 }
2982 if (!gf_list_count(ptr->list)) {
2983 gf_list_del_item(udta->recordList, map);
2984 gf_isom_box_array_del(map->boxes);
2985 gf_free(map);
2986 }
2987 return GF_OK;
2988 }
2989
2990 #if 0 //unused
2991 GF_Err gf_isom_remove_copyright(GF_ISOFile *movie, u32 index)
2992 {
2993 GF_Err e;
2994 GF_CopyrightBox *ptr;
2995 GF_UserDataMap *map;
2996 u32 count;
2997
2998 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2999 if (e) return e;
3000 e = gf_isom_insert_moov(movie);
3001 if (e) return e;
3002
3003 if (!index) return GF_BAD_PARAM;
3004 if (!movie->moov->udta) return GF_OK;
3005
3006 map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL);
3007 if (!map) return GF_OK;
3008
3009 count = gf_list_count(map->boxes);
3010 if (index>count) return GF_BAD_PARAM;
3011
3012 ptr = (GF_CopyrightBox*)gf_list_get(map->boxes, index-1);
3013 if (ptr) {
3014 gf_list_rem(map->boxes, index-1);
3015 if (ptr->notice) gf_free(ptr->notice);
3016 gf_free(ptr);
3017 }
3018 /*last copyright, remove*/
3019 if (!gf_list_count(map->boxes)) {
3020 gf_list_del_item(movie->moov->udta->recordList, map);
3021 gf_list_del(map->boxes);
3022 gf_free(map);
3023 }
3024 return GF_OK;
3025 }
3026
3027 GF_Err gf_isom_set_watermark(GF_ISOFile *movie, bin128 UUID, u8* data, u32 length)
3028 {
3029 GF_Err e;
3030 GF_UnknownUUIDBox *ptr;
3031 GF_UserDataMap *map;
3032
3033 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3034 if (e) return e;
3035
3036 e = gf_isom_insert_moov(movie);
3037 if (e) return e;
3038 if (!movie->moov->udta) {
3039 e = moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA));
3040 if (e) return e;
3041 }
3042
3043 map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_UUID, (bin128 *) & UUID);
3044 if (map) {
3045 ptr = (GF_UnknownUUIDBox *)gf_list_get(map->boxes, 0);
3046 if (ptr) {
3047 gf_free(ptr->data);
3048 ptr->data = (char*)gf_malloc(length);
3049 if (!ptr->data) return GF_OUT_OF_MEM;
3050 memcpy(ptr->data, data, length);
3051 ptr->dataSize = length;
3052 return GF_OK;
3053 }
3054 }
3055 //nope, create one
3056 ptr = (GF_UnknownUUIDBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID);
3057 if (!ptr) return GF_OUT_OF_MEM;
3058
3059 memcpy(ptr->uuid, UUID, 16);
3060 ptr->data = (char*)gf_malloc(length);
3061 if (!ptr->data) return GF_OUT_OF_MEM;
3062 memcpy(ptr->data, data, length);
3063 ptr->dataSize = length;
3064 return udta_on_child_box((GF_Box *)movie->moov->udta, (GF_Box *) ptr);
3065 }
3066 #endif
3067
3068 //set the interleaving time of media data (INTERLEAVED mode only)
3069 //InterleaveTime is in MovieTimeScale
3070 GF_EXPORT
gf_isom_set_interleave_time(GF_ISOFile * movie,u32 InterleaveTime)3071 GF_Err gf_isom_set_interleave_time(GF_ISOFile *movie, u32 InterleaveTime)
3072 {
3073 GF_Err e;
3074 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3075 if (e) return e;
3076
3077 if (!InterleaveTime || !movie->moov) return GF_OK;
3078 movie->interleavingTime = InterleaveTime;
3079 return GF_OK;
3080 }
3081
3082
3083
3084 //use a compact track version for sample size. This is not usually recommended
3085 //except for speech codecs where the track has a lot of small samples
3086 //compaction is done automatically while writing based on the track's sample sizes
3087 GF_EXPORT
gf_isom_use_compact_size(GF_ISOFile * movie,u32 trackNumber,Bool CompactionOn)3088 GF_Err gf_isom_use_compact_size(GF_ISOFile *movie, u32 trackNumber, Bool CompactionOn)
3089 {
3090 GF_TrackBox *trak;
3091 u32 i, size;
3092 GF_SampleSizeBox *stsz;
3093 GF_Err e;
3094
3095 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3096 if (e) return e;
3097
3098 trak = gf_isom_get_track_from_file(movie, trackNumber);
3099 if (!trak) return GF_BAD_PARAM;
3100
3101 if (!trak->Media || !trak->Media->information
3102 || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleSize)
3103 return GF_ISOM_INVALID_FILE;
3104
3105 stsz = trak->Media->information->sampleTable->SampleSize;
3106
3107 //switch to regular table
3108 if (!CompactionOn) {
3109 if (stsz->type == GF_ISOM_BOX_TYPE_STSZ) return GF_OK;
3110 stsz->type = GF_ISOM_BOX_TYPE_STSZ;
3111 //invalidate the sampleSize and recompute it
3112 stsz->sampleSize = 0;
3113 if (!stsz->sampleCount) return GF_OK;
3114 //if the table is empty we can only assume the track is empty (no size indication)
3115 if (!stsz->sizes) return GF_OK;
3116 size = stsz->sizes[0];
3117 //check whether the sizes are all the same or not
3118 for (i=1; i<stsz->sampleCount; i++) {
3119 if (size != stsz->sizes[i]) {
3120 size = 0;
3121 break;
3122 }
3123 }
3124 if (size) {
3125 gf_free(stsz->sizes);
3126 stsz->sizes = NULL;
3127 stsz->sampleSize = size;
3128 }
3129 return GF_OK;
3130 }
3131
3132 //switch to compact table
3133 if (stsz->type == GF_ISOM_BOX_TYPE_STZ2) return GF_OK;
3134 //fill the table. Although it seems weird , this is needed in case of edition
3135 //after the function is called. NOte however than we force regular table
3136 //at write time if all samples are of same size
3137 if (stsz->sampleSize) {
3138 //this is a weird table indeed ;)
3139 if (stsz->sizes) gf_free(stsz->sizes);
3140 stsz->sizes = (u32*) gf_malloc(sizeof(u32)*stsz->sampleCount);
3141 if (!stsz->sizes) return GF_OUT_OF_MEM;
3142 memset(stsz->sizes, stsz->sampleSize, sizeof(u32));
3143 }
3144 //set the SampleSize to 0 while the file is open
3145 stsz->sampleSize = 0;
3146 stsz->type = GF_ISOM_BOX_TYPE_STZ2;
3147 return GF_OK;
3148 }
3149
3150
3151 GF_EXPORT
gf_isom_set_brand_info(GF_ISOFile * movie,u32 MajorBrand,u32 MinorVersion)3152 GF_Err gf_isom_set_brand_info(GF_ISOFile *movie, u32 MajorBrand, u32 MinorVersion)
3153 {
3154 u32 i, *p;
3155
3156 if (!MajorBrand) return GF_BAD_PARAM;
3157
3158 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
3159 if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) {
3160 GF_Err e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3161 if (e) return e;
3162
3163 e = CheckNoData(movie);
3164 if (e) return e;
3165 }
3166 #endif
3167
3168 if (!movie->brand) {
3169 movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP);
3170 if (!movie->brand) return GF_OUT_OF_MEM;
3171 gf_list_add(movie->TopBoxes, movie->brand);
3172 }
3173
3174 movie->brand->majorBrand = MajorBrand;
3175 movie->brand->minorVersion = MinorVersion;
3176
3177 if (!movie->brand->altBrand) {
3178 movie->brand->altBrand = (u32*)gf_malloc(sizeof(u32));
3179 if (!movie->brand->altBrand) return GF_OUT_OF_MEM;
3180 movie->brand->altBrand[0] = MajorBrand;
3181 movie->brand->altCount = 1;
3182 return GF_OK;
3183 }
3184
3185 //if brand already present don't change anything
3186 for (i=0; i<movie->brand->altCount; i++) {
3187 if (movie->brand->altBrand[i] == MajorBrand) return GF_OK;
3188 }
3189 p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount + 1));
3190 if (!p) return GF_OUT_OF_MEM;
3191 memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount);
3192 p[movie->brand->altCount] = MajorBrand;
3193 movie->brand->altCount += 1;
3194 gf_free(movie->brand->altBrand);
3195 movie->brand->altBrand = p;
3196 return GF_OK;
3197 }
3198
3199
3200 GF_EXPORT
gf_isom_modify_alternate_brand(GF_ISOFile * movie,u32 Brand,Bool AddIt)3201 GF_Err gf_isom_modify_alternate_brand(GF_ISOFile *movie, u32 Brand, Bool AddIt)
3202 {
3203 u32 i, k, *p;
3204
3205 if (!Brand) return GF_BAD_PARAM;
3206
3207 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
3208 if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) {
3209 GF_Err e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3210 if (e) return e;
3211
3212 e = CheckNoData(movie);
3213 if (e) return e;
3214 }
3215 #endif
3216
3217 if (!movie->brand && AddIt) {
3218 movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP);
3219 if (!movie->brand) return GF_OUT_OF_MEM;
3220 gf_list_add(movie->TopBoxes, movie->brand);
3221 }
3222 if (!AddIt && !movie->brand) return GF_OK;
3223
3224 //do not mofify major one
3225 if (!AddIt && movie->brand->majorBrand == Brand) return GF_OK;
3226
3227 if (!AddIt && movie->brand->altCount == 1) {
3228 //fixes it in case
3229 movie->brand->altBrand[0] = movie->brand->majorBrand;
3230 return GF_OK;
3231 }
3232 //check for the brand
3233 for (i=0; i<movie->brand->altCount; i++) {
3234 if (movie->brand->altBrand[i] == Brand) goto found;
3235 }
3236 //Not found
3237 if (!AddIt) return GF_OK;
3238 //add it
3239 p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount + 1));
3240 if (!p) return GF_OUT_OF_MEM;
3241 memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount);
3242 p[movie->brand->altCount] = Brand;
3243 movie->brand->altCount += 1;
3244 gf_free(movie->brand->altBrand);
3245 movie->brand->altBrand = p;
3246 return GF_OK;
3247
3248 found:
3249
3250 //found
3251 if (AddIt) return GF_OK;
3252 assert(movie->brand->altCount>1);
3253
3254 //remove it
3255 p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount - 1));
3256 if (!p) return GF_OUT_OF_MEM;
3257 k = 0;
3258 for (i=0; i<movie->brand->altCount; i++) {
3259 if (movie->brand->altBrand[i] == Brand) continue;
3260 else {
3261 p[k] = movie->brand->altBrand[i];
3262 k++;
3263 }
3264 }
3265 movie->brand->altCount -= 1;
3266 gf_free(movie->brand->altBrand);
3267 movie->brand->altBrand = p;
3268 return GF_OK;
3269 }
3270
3271
3272 GF_EXPORT
gf_isom_reset_alt_brands(GF_ISOFile * movie)3273 GF_Err gf_isom_reset_alt_brands(GF_ISOFile *movie)
3274 {
3275 u32 *p;
3276
3277 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
3278 if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) {
3279 GF_Err e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3280 if (e) return e;
3281
3282 e = CheckNoData(movie);
3283 if (e) return e;
3284 }
3285 #endif
3286
3287 if (!movie->brand) {
3288 movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP);
3289 if (!movie->brand) return GF_OUT_OF_MEM;
3290 gf_list_add(movie->TopBoxes, movie->brand);
3291 }
3292 p = (u32*)gf_malloc(sizeof(u32));
3293 if (!p) return GF_OUT_OF_MEM;
3294 p[0] = movie->brand->majorBrand;
3295 movie->brand->altCount = 1;
3296 gf_free(movie->brand->altBrand);
3297 movie->brand->altBrand = p;
3298 return GF_OK;
3299 }
3300
3301 #if 0 //unused
3302 GF_Err gf_isom_set_sample_padding_bits(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u8 NbBits)
3303 {
3304 GF_TrackBox *trak;
3305 GF_Err e;
3306
3307 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3308 if (e) return e;
3309
3310 trak = gf_isom_get_track_from_file(movie, trackNumber);
3311 if (!trak || NbBits > 7) return GF_BAD_PARAM;
3312
3313 //set Padding info
3314 return stbl_SetPaddingBits(trak->Media->information->sampleTable, sampleNumber, NbBits);
3315 }
3316 #endif
3317
3318 GF_EXPORT
gf_isom_remove_user_data_item(GF_ISOFile * movie,u32 trackNumber,u32 UserDataType,bin128 UUID,u32 UserDataIndex)3319 GF_Err gf_isom_remove_user_data_item(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, u32 UserDataIndex)
3320 {
3321 GF_UserDataMap *map;
3322 GF_Box *a;
3323 u32 i;
3324 bin128 t;
3325 GF_Err e;
3326 GF_TrackBox *trak;
3327 GF_UserDataBox *udta;
3328
3329 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3330 if (e) return e;
3331
3332 if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0;
3333 memset(t, 1, 16);
3334
3335 if (trackNumber) {
3336 trak = gf_isom_get_track_from_file(movie, trackNumber);
3337 if (!trak) return GF_BAD_PARAM;
3338 udta = trak->udta;
3339 } else {
3340 udta = movie->moov->udta;
3341 }
3342 if (!udta) return GF_BAD_PARAM;
3343 if (!UserDataIndex) return GF_BAD_PARAM;
3344
3345 i=0;
3346 while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) {
3347 if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found;
3348 else if (map->boxType == UserDataType) goto found;
3349 }
3350 //not found
3351 return GF_OK;
3352
3353 found:
3354
3355 if (UserDataIndex > gf_list_count(map->boxes) ) return GF_BAD_PARAM;
3356 //delete the box
3357 a = (GF_Box*)gf_list_get(map->boxes, UserDataIndex-1);
3358 gf_isom_box_del_parent(&map->boxes, a);
3359
3360 //remove the map if empty
3361 if (!gf_list_count(map->boxes)) {
3362 gf_list_rem(udta->recordList, i-1);
3363 gf_isom_box_array_del(map->boxes);
3364 gf_free(map);
3365 }
3366 //but we keep the UDTA no matter what
3367 return GF_OK;
3368 }
3369
3370 GF_EXPORT
gf_isom_remove_user_data(GF_ISOFile * movie,u32 trackNumber,u32 UserDataType,bin128 UUID)3371 GF_Err gf_isom_remove_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID)
3372 {
3373 GF_UserDataMap *map;
3374 u32 i;
3375 GF_Err e;
3376 bin128 t;
3377 GF_TrackBox *trak;
3378 GF_UserDataBox *udta;
3379
3380 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3381 if (e) return e;
3382
3383 if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0;
3384 memset(t, 1, 16);
3385
3386 if (trackNumber) {
3387 trak = gf_isom_get_track_from_file(movie, trackNumber);
3388 if (!trak) return GF_BAD_PARAM;
3389 udta = trak->udta;
3390 } else {
3391 udta = movie->moov->udta;
3392 }
3393 if (!udta) return GF_BAD_PARAM;
3394
3395 i=0;
3396 while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) {
3397 if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found;
3398 else if (map->boxType == UserDataType) goto found;
3399 }
3400 //not found
3401 return GF_OK;
3402
3403 found:
3404
3405 gf_list_rem(udta->recordList, i-1);
3406 gf_isom_box_array_del(map->boxes);
3407 gf_free(map);
3408
3409 //but we keep the UDTA no matter what
3410 return GF_OK;
3411 }
3412
3413 GF_EXPORT
gf_isom_add_user_data(GF_ISOFile * movie,u32 trackNumber,u32 UserDataType,bin128 UUID,u8 * data,u32 DataLength)3414 GF_Err gf_isom_add_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, u8 *data, u32 DataLength)
3415 {
3416 GF_Err e;
3417 GF_TrackBox *trak;
3418 GF_UserDataBox *udta;
3419
3420 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3421 if (e) return e;
3422
3423 if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0;
3424
3425 if (trackNumber) {
3426 trak = gf_isom_get_track_from_file(movie, trackNumber);
3427 if (!trak) return GF_BAD_PARAM;
3428 if (!trak->udta) trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA));
3429 udta = trak->udta;
3430 } else {
3431 if (!movie->moov->udta) moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA));
3432 udta = movie->moov->udta;
3433 }
3434 if (!udta) return GF_OUT_OF_MEM;
3435
3436 //create a default box
3437 if (UserDataType) {
3438 GF_UnknownBox *a = (GF_UnknownBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UNKNOWN);
3439 if (!a) return GF_OUT_OF_MEM;
3440 a->original_4cc = UserDataType;
3441 if (DataLength) {
3442 a->data = (char*)gf_malloc(sizeof(char)*DataLength);
3443 if (!a->data) return GF_OUT_OF_MEM;
3444 memcpy(a->data, data, DataLength);
3445 a->dataSize = DataLength;
3446 }
3447 return udta_on_child_box((GF_Box *)udta, (GF_Box *) a);
3448 } else {
3449 GF_UnknownUUIDBox *a = (GF_UnknownUUIDBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID);
3450 if (!a) return GF_OUT_OF_MEM;
3451 memcpy(a->uuid, UUID, 16);
3452 if (DataLength) {
3453 a->data = (char*)gf_malloc(sizeof(char)*DataLength);
3454 if (!a->data) return GF_OUT_OF_MEM;
3455 memcpy(a->data, data, DataLength);
3456 a->dataSize = DataLength;
3457 }
3458 return udta_on_child_box((GF_Box *)udta, (GF_Box *) a);
3459 }
3460 return GF_OK;
3461 }
3462
3463 GF_EXPORT
gf_isom_add_user_data_boxes(GF_ISOFile * movie,u32 trackNumber,u8 * data,u32 DataLength)3464 GF_Err gf_isom_add_user_data_boxes(GF_ISOFile *movie, u32 trackNumber, u8 *data, u32 DataLength)
3465 {
3466 GF_Err e;
3467 GF_TrackBox *trak;
3468 GF_UserDataBox *udta;
3469 GF_BitStream *bs;
3470
3471 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3472 if (e) return e;
3473
3474 if (trackNumber) {
3475 trak = gf_isom_get_track_from_file(movie, trackNumber);
3476 if (!trak) return GF_BAD_PARAM;
3477 if (!trak->udta) trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA));
3478 udta = trak->udta;
3479 } else {
3480 if (!movie->moov) return GF_BAD_PARAM;
3481 if (!movie->moov->udta) moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA));
3482 udta = movie->moov->udta;
3483 }
3484 if (!udta) return GF_OUT_OF_MEM;
3485
3486 bs = gf_bs_new(data, DataLength, GF_BITSTREAM_READ);
3487 while (gf_bs_available(bs)) {
3488 GF_Box *a;
3489 e = gf_isom_box_parse(&a, bs);
3490 if (e) break;
3491 e = udta_on_child_box((GF_Box *)udta, a);
3492 if (e) break;
3493 }
3494 gf_bs_del(bs);
3495 return e;
3496 }
3497
3498 GF_EXPORT
gf_isom_clone_pl_indications(GF_ISOFile * orig,GF_ISOFile * dest)3499 GF_Err gf_isom_clone_pl_indications(GF_ISOFile *orig, GF_ISOFile *dest)
3500 {
3501 GF_IsomInitialObjectDescriptor *iod_d;
3502 if (!orig || !dest) return GF_BAD_PARAM;
3503 if (!orig->moov->iods || !orig->moov->iods->descriptor) return GF_OK;
3504 if (orig->moov->iods->descriptor->tag != GF_ODF_ISOM_IOD_TAG) return GF_OK;
3505
3506 AddMovieIOD(dest->moov, 1);
3507 gf_odf_desc_del((GF_Descriptor *)dest->moov->iods->descriptor);
3508 gf_odf_desc_copy((GF_Descriptor *)orig->moov->iods->descriptor, (GF_Descriptor **)&dest->moov->iods->descriptor);
3509 iod_d = (GF_IsomInitialObjectDescriptor *) dest->moov->iods->descriptor;
3510 while (gf_list_count(iod_d->ES_ID_IncDescriptors)) {
3511 GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_IncDescriptors, 0);
3512 gf_list_rem(iod_d->ES_ID_IncDescriptors, 0);
3513 gf_odf_desc_del(d);
3514 }
3515 while (gf_list_count(iod_d->ES_ID_RefDescriptors)) {
3516 GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_RefDescriptors, 0);
3517 gf_list_rem(iod_d->ES_ID_RefDescriptors, 0);
3518 gf_odf_desc_del(d);
3519 }
3520 return GF_OK;
3521 }
3522
3523 GF_EXPORT
gf_isom_clone_box(GF_Box * src,GF_Box ** dst)3524 GF_Err gf_isom_clone_box(GF_Box *src, GF_Box **dst)
3525 {
3526 GF_Err e;
3527 u8 *data;
3528 u32 data_size;
3529 GF_BitStream *bs;
3530
3531 if (*dst) {
3532 gf_isom_box_del(*dst);
3533 *dst=NULL;
3534 }
3535 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
3536 if (!bs) return GF_OUT_OF_MEM;
3537 e = gf_isom_box_size( (GF_Box *) src);
3538 if (!e) e = gf_isom_box_write((GF_Box *) src, bs);
3539 gf_bs_get_content(bs, &data, &data_size);
3540 gf_bs_del(bs);
3541 if (e) return e;
3542 bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
3543 if (!bs) return GF_OUT_OF_MEM;
3544 e = gf_isom_box_parse(dst, bs);
3545 gf_bs_del(bs);
3546 gf_free(data);
3547 return e;
3548 }
3549
3550 #if 0 //unused
3551 /*clones the entire movie file to destination. Tracks can be cloned if clone_tracks is set, in which case hint tracks can be
3552 kept if keep_hint_tracks is set
3553 if keep_pssh, all pssh boxes will be kept
3554 fragment information (mvex) is not kept*/
3555 GF_Err gf_isom_clone_movie(GF_ISOFile *orig_file, GF_ISOFile *dest_file, Bool clone_tracks, Bool keep_hint_tracks, Bool keep_pssh)
3556 {
3557 GF_Err e;
3558 u32 i;
3559 GF_Box *box;
3560
3561 e = CanAccessMovie(dest_file, GF_ISOM_OPEN_WRITE);
3562 if (e) return e;
3563
3564 if (orig_file->brand) {
3565 gf_list_del_item(dest_file->TopBoxes, dest_file->brand);
3566 gf_isom_box_del((GF_Box *)dest_file->brand);
3567 dest_file->brand = NULL;
3568 gf_isom_clone_box((GF_Box *)orig_file->brand, (GF_Box **)&dest_file->brand);
3569 if (dest_file->brand) gf_list_add(dest_file->TopBoxes, dest_file->brand);
3570 }
3571
3572 if (orig_file->meta) {
3573 gf_list_del_item(dest_file->TopBoxes, dest_file->meta);
3574 gf_isom_box_del((GF_Box *)dest_file->meta);
3575 dest_file->meta = NULL;
3576 /*fixme - check imports*/
3577 gf_isom_clone_box((GF_Box *)orig_file->meta, (GF_Box **)&dest_file->meta);
3578 if (dest_file->meta) gf_list_add(dest_file->TopBoxes, dest_file->meta);
3579 }
3580 if (orig_file->moov) {
3581 u32 i, dstTrack;
3582 GF_Box *iods;
3583 GF_List *tracks = gf_list_new();
3584 GF_List *old_tracks = orig_file->moov->trackList;
3585 orig_file->moov->trackList = tracks;
3586 iods = (GF_Box*)orig_file->moov->iods;
3587 orig_file->moov->iods = NULL;
3588 e = gf_isom_clone_box((GF_Box *)orig_file->moov, (GF_Box **)&dest_file->moov);
3589 if (e) {
3590 gf_list_del(tracks);
3591 orig_file->moov->trackList = old_tracks;
3592 return e;
3593 }
3594 orig_file->moov->trackList = old_tracks;
3595 gf_list_del(tracks);
3596 orig_file->moov->iods = (GF_ObjectDescriptorBox*)iods;
3597 gf_list_add(dest_file->TopBoxes, dest_file->moov);
3598
3599 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
3600 if (dest_file->moov->mvex) {
3601 gf_isom_box_del_parent(&dest_file->moov->child_boxes, (GF_Box *)dest_file->moov->mvex);
3602 dest_file->moov->mvex = NULL;
3603 }
3604 #endif
3605
3606 if (clone_tracks) {
3607 for (i=0; i<gf_list_count(orig_file->moov->trackList); i++) {
3608 GF_TrackBox *trak = (GF_TrackBox*)gf_list_get( orig_file->moov->trackList, i);
3609 if (!trak) continue;
3610 if (keep_hint_tracks || (trak->Media->handler->handlerType != GF_ISOM_MEDIA_HINT)) {
3611 e = gf_isom_clone_track(orig_file, i+1, dest_file, 0, &dstTrack);
3612 if (e) return e;
3613 }
3614 }
3615 if (iods)
3616 gf_isom_clone_box((GF_Box *)orig_file->moov->iods, (GF_Box **)dest_file->moov->iods);
3617 } else {
3618 dest_file->moov->mvhd->nextTrackID = 1;
3619 gf_isom_clone_pl_indications(orig_file, dest_file);
3620 }
3621 dest_file->moov->mov = dest_file;
3622 }
3623
3624 if (!keep_pssh) {
3625 i=0;
3626 while ((box = (GF_Box*)gf_list_get(dest_file->moov->child_boxes, i++))) {
3627 if (box->type == GF_ISOM_BOX_TYPE_PSSH) {
3628 i--;
3629 gf_isom_box_del_parent(&dest_file->moov->child_boxes, box);
3630 }
3631 }
3632 }
3633
3634 //duplicate other boxes
3635 i=0;
3636 while ((box = (GF_Box*)gf_list_get(orig_file->TopBoxes, i++))) {
3637 switch(box->type) {
3638 case GF_ISOM_BOX_TYPE_MOOV:
3639 case GF_ISOM_BOX_TYPE_META:
3640 case GF_ISOM_BOX_TYPE_MDAT:
3641 case GF_ISOM_BOX_TYPE_FTYP:
3642 case GF_ISOM_BOX_TYPE_PDIN:
3643 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
3644 case GF_ISOM_BOX_TYPE_STYP:
3645 case GF_ISOM_BOX_TYPE_SIDX:
3646 case GF_ISOM_BOX_TYPE_SSIX:
3647 case GF_ISOM_BOX_TYPE_MOOF:
3648 #endif
3649 case GF_ISOM_BOX_TYPE_JP:
3650 break;
3651
3652 case GF_ISOM_BOX_TYPE_PSSH:
3653 if (!keep_pssh)
3654 break;
3655
3656 default:
3657 {
3658 GF_Box *box2 = NULL;
3659 gf_isom_clone_box(box, &box2);
3660 gf_list_add(dest_file->TopBoxes, box2);
3661 }
3662 break;
3663 }
3664 }
3665
3666 return GF_OK;
3667 }
3668 #endif
3669
3670
3671 GF_EXPORT
gf_isom_get_raw_user_data(GF_ISOFile * file,u8 ** output,u32 * output_size)3672 GF_Err gf_isom_get_raw_user_data(GF_ISOFile *file, u8 **output, u32 *output_size)
3673 {
3674 GF_BitStream *bs;
3675 GF_Err e;
3676 GF_Box *b;
3677 u32 i;
3678
3679 *output = NULL;
3680 *output_size = 0;
3681 if (!file || !file->moov || (!file->moov->udta && !file->moov->child_boxes)) return GF_OK;
3682 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
3683
3684 if (file->moov->udta) {
3685 e = gf_isom_box_size( (GF_Box *) file->moov->udta);
3686 if (e) goto exit;
3687 e = gf_isom_box_write((GF_Box *) file->moov->udta, bs);
3688 if (e) goto exit;
3689 }
3690 e = GF_OK;
3691 i=0;
3692 while ((b = gf_list_enum(file->moov->child_boxes, &i))) {
3693 switch (b->type) {
3694 case GF_ISOM_BOX_TYPE_TRAK:
3695 case GF_ISOM_BOX_TYPE_MVHD:
3696 case GF_ISOM_BOX_TYPE_MVEX:
3697 case GF_ISOM_BOX_TYPE_IODS:
3698 case GF_ISOM_BOX_TYPE_META:
3699 continue;
3700 }
3701 e = gf_isom_box_size( (GF_Box *) b);
3702 if (e) goto exit;
3703 e = gf_isom_box_write((GF_Box *) b, bs);
3704 if (e) goto exit;
3705 }
3706
3707 gf_bs_get_content(bs, output, output_size);
3708
3709 exit:
3710 gf_bs_del(bs);
3711 return e;
3712 }
3713
3714 GF_EXPORT
gf_isom_get_track_template(GF_ISOFile * file,u32 track,u8 ** output,u32 * output_size)3715 GF_Err gf_isom_get_track_template(GF_ISOFile *file, u32 track, u8 **output, u32 *output_size)
3716 {
3717 GF_TrackBox *trak;
3718 GF_BitStream *bs;
3719 GF_DataReferenceBox *dref;
3720 GF_SampleTableBox *stbl, *stbl_temp;
3721 GF_SampleEncryptionBox *senc;
3722 u32 i, count;
3723
3724 *output = NULL;
3725 *output_size = 0;
3726 /*get orig sample desc and clone it*/
3727 trak = gf_isom_get_track_from_file(file, track);
3728 if (!trak || !trak->Media) return GF_BAD_PARAM;
3729
3730 //don't serialize dref
3731 dref = NULL;
3732 if (trak->Media->information->dataInformation) {
3733 dref = trak->Media->information->dataInformation->dref;
3734 trak->Media->information->dataInformation->dref = NULL;
3735 gf_list_del_item(trak->Media->information->dataInformation->child_boxes, dref);
3736 }
3737
3738 //don't serialize stbl but create a temp one
3739 stbl_temp = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL);
3740 if (!stbl_temp->child_boxes) stbl_temp->child_boxes = gf_list_new();
3741 stbl = trak->Media->information->sampleTable;
3742 gf_list_del_item(trak->Media->information->child_boxes, stbl);
3743
3744 trak->Media->information->sampleTable = stbl_temp;
3745 gf_list_add(trak->Media->information->child_boxes, stbl_temp);
3746
3747 /*do not clone sampleDescription table but create an empty one*/
3748 stbl_temp->SampleDescription = (GF_SampleDescriptionBox *) gf_isom_box_new_parent(&stbl_temp->child_boxes, GF_ISOM_BOX_TYPE_STSD);
3749
3750 /*clone sampleGroups description tables if any*/
3751 stbl_temp->sampleGroupsDescription = stbl->sampleGroupsDescription;
3752 count = gf_list_count(stbl->sampleGroupsDescription);
3753 for (i=0;i<count; i++) {
3754 GF_Box *b = gf_list_get(stbl->sampleGroupsDescription, i);
3755 gf_list_add(stbl_temp->child_boxes, b);
3756 }
3757 /*clone CompositionToDecode table, we may remove it later*/
3758 stbl_temp->CompositionToDecode = stbl->CompositionToDecode;
3759 gf_list_add(stbl_temp->child_boxes, stbl->CompositionToDecode);
3760
3761
3762 //don't serialize senc
3763 senc = trak->sample_encryption;
3764 if (senc) {
3765 assert(trak->child_boxes);
3766 gf_list_del_item(trak->child_boxes, senc);
3767 trak->sample_encryption = NULL;
3768 }
3769
3770 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
3771
3772 gf_isom_box_size( (GF_Box *) trak);
3773 gf_isom_box_write((GF_Box *) trak, bs);
3774 gf_bs_get_content(bs, output, output_size);
3775 gf_bs_del(bs);
3776
3777 //restore our pointers
3778 if (dref) {
3779 trak->Media->information->dataInformation->dref = dref;
3780 gf_list_add(trak->Media->information->dataInformation->child_boxes, dref);
3781 }
3782 trak->Media->information->sampleTable = stbl;
3783 gf_list_add(trak->Media->information->child_boxes, stbl);
3784 gf_list_del_item(trak->Media->information->child_boxes, stbl_temp);
3785 if (senc) {
3786 trak->sample_encryption = senc;
3787 gf_list_add(trak->child_boxes, senc);
3788 }
3789
3790 stbl_temp->sampleGroupsDescription = NULL;
3791 count = gf_list_count(stbl->sampleGroupsDescription);
3792 for (i=0;i<count; i++) {
3793 GF_Box *b = gf_list_get(stbl->sampleGroupsDescription, i);
3794 gf_list_del_item(stbl_temp->child_boxes, b);
3795 }
3796
3797 stbl_temp->CompositionToDecode = NULL;
3798 gf_list_del_item(stbl_temp->child_boxes, stbl->CompositionToDecode);
3799 gf_isom_box_del((GF_Box *)stbl_temp);
3800 return GF_OK;
3801
3802 }
3803
3804 GF_EXPORT
gf_isom_get_trex_template(GF_ISOFile * file,u32 track,u8 ** output,u32 * output_size)3805 GF_Err gf_isom_get_trex_template(GF_ISOFile *file, u32 track, u8 **output, u32 *output_size)
3806 {
3807 GF_TrackBox *trak;
3808 GF_BitStream *bs;
3809 u32 i;
3810 GF_TrackExtendsBox *trex = NULL;
3811 GF_TrackExtensionPropertiesBox *trexprop = NULL;
3812
3813 *output = NULL;
3814 *output_size = 0;
3815 /*get orig sample desc and clone it*/
3816 trak = gf_isom_get_track_from_file(file, track);
3817 if (!trak || !trak->Media) return GF_BAD_PARAM;
3818 if (!file->moov->mvex) return GF_NOT_FOUND;
3819 for (i=0; i<gf_list_count(file->moov->mvex->TrackExList); i++) {
3820 trex = gf_list_get(file->moov->mvex->TrackExList, i);
3821 if (trex->trackID == trak->Header->trackID) break;
3822 trex = NULL;
3823 }
3824 if (!trex) return GF_NOT_FOUND;
3825
3826 for (i=0; i<gf_list_count(file->moov->mvex->TrackExPropList); i++) {
3827 trexprop = gf_list_get(file->moov->mvex->TrackExPropList, i);
3828 if (trexprop->trackID== trak->Header->trackID) break;
3829 trexprop = NULL;
3830 }
3831 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
3832 gf_isom_box_size( (GF_Box *) trex);
3833 gf_isom_box_write((GF_Box *) trex, bs);
3834
3835 if (trexprop) {
3836 gf_isom_box_size( (GF_Box *) trexprop);
3837 gf_isom_box_write((GF_Box *) trexprop, bs);
3838 }
3839 gf_bs_get_content(bs, output, output_size);
3840 gf_bs_del(bs);
3841
3842 return GF_OK;
3843
3844 }
3845
3846 GF_EXPORT
gf_isom_get_stsd_template(GF_ISOFile * file,u32 track,u32 stsd_idx,u8 ** output,u32 * output_size)3847 GF_Err gf_isom_get_stsd_template(GF_ISOFile *file, u32 track, u32 stsd_idx, u8 **output, u32 *output_size)
3848 {
3849 GF_TrackBox *trak;
3850 GF_BitStream *bs;
3851 GF_Box *ent;
3852
3853 *output = NULL;
3854 *output_size = 0;
3855 /*get orig sample desc and clone it*/
3856 trak = gf_isom_get_track_from_file(file, track);
3857 if (!trak || !stsd_idx || !trak->Media || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription) return GF_BAD_PARAM;
3858
3859 ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, stsd_idx-1);
3860 if (!ent) return GF_BAD_PARAM;
3861
3862 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
3863
3864 gf_isom_box_size( (GF_Box *) ent);
3865 gf_isom_box_write((GF_Box *) ent, bs);
3866 gf_bs_get_content(bs, output, output_size);
3867 gf_bs_del(bs);
3868 return GF_OK;
3869
3870 }
3871
3872
3873 GF_EXPORT
gf_isom_clone_track(GF_ISOFile * orig_file,u32 orig_track,GF_ISOFile * dest_file,GF_ISOTrackCloneFlags flags,u32 * dest_track)3874 GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *dest_file, GF_ISOTrackCloneFlags flags, u32 *dest_track)
3875 {
3876 GF_TrackBox *trak, *new_tk;
3877 GF_BitStream *bs;
3878 u8 *data;
3879 const u8 *buffer;
3880 u32 data_size;
3881 Double ts_scale;
3882 u32 i, count;
3883 GF_Err e;
3884 GF_SampleTableBox *stbl, *stbl_temp;
3885 GF_SampleEncryptionBox *senc;
3886
3887 e = CanAccessMovie(dest_file, GF_ISOM_OPEN_WRITE);
3888 if (e) return e;
3889 e = gf_isom_insert_moov(dest_file);
3890 if (e) return e;
3891
3892 /*get orig sample desc and clone it*/
3893 trak = gf_isom_get_track_from_file(orig_file, orig_track);
3894 if (!trak || !trak->Media) return GF_BAD_PARAM;
3895
3896 stbl = trak->Media->information->sampleTable;
3897 stbl_temp = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL);
3898 if (!stbl_temp->child_boxes) stbl_temp->child_boxes = gf_list_new();
3899
3900 trak->Media->information->sampleTable = stbl_temp;
3901 gf_list_add(trak->Media->information->child_boxes, stbl_temp);
3902 gf_list_del_item(trak->Media->information->child_boxes, stbl);
3903
3904 if (!stbl_temp->child_boxes) stbl_temp->child_boxes = gf_list_new();
3905
3906 /*clone sampleDescription table*/
3907 stbl_temp->SampleDescription = stbl->SampleDescription;
3908 gf_list_add(stbl_temp->child_boxes, stbl->SampleDescription);
3909 /*also clone sampleGroups description tables if any*/
3910 stbl_temp->sampleGroupsDescription = stbl->sampleGroupsDescription;
3911 count = gf_list_count(stbl->sampleGroupsDescription);
3912 for (i=0; i<count; i++) {
3913 GF_Box *b = gf_list_get(stbl->sampleGroupsDescription, i);
3914 gf_list_add(stbl_temp->child_boxes, b);
3915 }
3916 /*clone CompositionToDecode table, we may remove it later*/
3917 stbl_temp->CompositionToDecode = stbl->CompositionToDecode;
3918 gf_list_add(stbl_temp->child_boxes, stbl->CompositionToDecode);
3919
3920 senc = trak->sample_encryption;
3921 if (senc) {
3922 assert(trak->child_boxes);
3923 gf_list_del_item(trak->child_boxes, senc);
3924 trak->sample_encryption = NULL;
3925 }
3926
3927 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
3928
3929 gf_isom_box_size( (GF_Box *) trak);
3930 gf_isom_box_write((GF_Box *) trak, bs);
3931 gf_bs_get_content(bs, &data, &data_size);
3932 gf_bs_del(bs);
3933 bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
3934 if (flags & GF_ISOM_CLONE_TRACK_NO_QT)
3935 gf_bs_set_cookie(bs, GF_ISOM_BS_COOKIE_QT_CONV | GF_ISOM_BS_COOKIE_CLONE_TRACK);
3936 else
3937 gf_bs_set_cookie(bs, GF_ISOM_BS_COOKIE_CLONE_TRACK);
3938
3939 e = gf_isom_box_parse((GF_Box **) &new_tk, bs);
3940 gf_bs_del(bs);
3941 gf_free(data);
3942
3943 trak->Media->information->sampleTable = stbl;
3944 gf_list_del_item(trak->Media->information->child_boxes, stbl_temp);
3945 gf_list_add(trak->Media->information->child_boxes, stbl);
3946
3947 if (senc) {
3948 trak->sample_encryption = senc;
3949 gf_list_add(trak->child_boxes, senc);
3950 }
3951 gf_list_del_item(stbl_temp->child_boxes, stbl_temp->SampleDescription);
3952 stbl_temp->SampleDescription = NULL;
3953
3954 count = gf_list_count(stbl->sampleGroupsDescription);
3955 for (i=0; i<count; i++) {
3956 GF_Box *b = gf_list_get(stbl->sampleGroupsDescription, i);
3957 gf_list_del_item(stbl_temp->child_boxes, b);
3958 }
3959 stbl_temp->sampleGroupsDescription = NULL;
3960
3961 gf_list_del_item(stbl_temp->child_boxes, stbl_temp->CompositionToDecode);
3962 stbl_temp->CompositionToDecode = NULL;
3963 gf_isom_box_del((GF_Box *)stbl_temp);
3964
3965 if (e) {
3966 if (new_tk) gf_isom_box_del((GF_Box *)new_tk);
3967 return e;
3968 }
3969
3970
3971
3972 /*create default boxes*/
3973 stbl = new_tk->Media->information->sampleTable;
3974 stbl->ChunkOffset = gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STCO);
3975 if (!stbl->ChunkOffset) return GF_OUT_OF_MEM;
3976 stbl->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSZ);
3977 if (!stbl->SampleSize) return GF_OUT_OF_MEM;
3978 stbl->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSC);
3979 if (!stbl->SampleToChunk) return GF_OUT_OF_MEM;
3980 stbl->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STTS);
3981 if (!stbl->TimeToSample) return GF_OUT_OF_MEM;
3982
3983 /*check trackID validity before adding track*/
3984 if (gf_isom_get_track_by_id(dest_file, new_tk->Header->trackID)) {
3985 u32 ID = 1;
3986 while (1) {
3987 if (RequestTrack(dest_file->moov, ID)) break;
3988 ID += 1;
3989 if (ID == 0xFFFFFFFF) break;
3990 }
3991 new_tk->Header->trackID = ID;
3992 }
3993 if (!dest_file->moov->child_boxes) dest_file->moov->child_boxes = gf_list_new();
3994 gf_list_add(dest_file->moov->child_boxes, new_tk);
3995 moov_on_child_box((GF_Box*)dest_file->moov, (GF_Box *)new_tk);
3996
3997 /*set originalID*/
3998 new_tk->originalID = trak->Header->trackID;
3999 /*set originalFile*/
4000 buffer = gf_isom_get_filename(orig_file);
4001 new_tk->originalFile = gf_crc_32(buffer, (u32) strlen(buffer));
4002
4003 /*rewrite edit list segmentDuration to new movie timescale*/
4004 ts_scale = dest_file->moov->mvhd->timeScale;
4005 ts_scale /= orig_file->moov->mvhd->timeScale;
4006 new_tk->Header->duration = (u64) (s64) ((s64) new_tk->Header->duration * ts_scale);
4007 if (new_tk->editBox && new_tk->editBox->editList) {
4008 count = gf_list_count(new_tk->editBox->editList->entryList);
4009 for (i=0; i<count; i++) {
4010 GF_EdtsEntry *ent = (GF_EdtsEntry *)gf_list_get(new_tk->editBox->editList->entryList, i);
4011 ent->segmentDuration = (u64) (s64) ((s64) ent->segmentDuration * ts_scale);
4012 }
4013 }
4014
4015 if (!new_tk->Media->information->dataInformation->dref) return GF_BAD_PARAM;
4016
4017 /*reset data ref*/
4018 if (! (flags & GF_ISOM_CLONE_TRACK_KEEP_DREF) ) {
4019 GF_SampleEntryBox *entry;
4020 Bool use_alis = GF_FALSE;
4021 if (! (flags & GF_ISOM_CLONE_TRACK_NO_QT)) {
4022 GF_Box *b = gf_list_get(new_tk->Media->information->dataInformation->dref->child_boxes, 0);
4023 if (b && b->type==GF_QT_BOX_TYPE_ALIS)
4024 use_alis = GF_TRUE;
4025 }
4026 gf_isom_box_array_del(new_tk->Media->information->dataInformation->dref->child_boxes);
4027 new_tk->Media->information->dataInformation->dref->child_boxes = gf_list_new();
4028 /*update data ref*/
4029 entry = (GF_SampleEntryBox*)gf_list_get(new_tk->Media->information->sampleTable->SampleDescription->child_boxes, 0);
4030 if (entry) {
4031 u32 dref;
4032 Media_CreateDataRef(dest_file, new_tk->Media->information->dataInformation->dref, use_alis ? "alis" : NULL, NULL, &dref);
4033 entry->dataReferenceIndex = dref;
4034 }
4035 } else {
4036 for (i=0; i<gf_list_count(new_tk->Media->information->dataInformation->dref->child_boxes); i++) {
4037 GF_DataEntryBox *dref_entry = (GF_DataEntryBox *)gf_list_get(new_tk->Media->information->dataInformation->dref->child_boxes, i);
4038 if (dref_entry->flags & 1) {
4039 dref_entry->flags &= ~1;
4040 e = Media_SetDrefURL((GF_DataEntryURLBox *)dref_entry, orig_file->fileName, dest_file->finalName);
4041 if (e) return e;
4042 }
4043 }
4044 }
4045
4046 *dest_track = gf_list_count(dest_file->moov->trackList);
4047
4048 if (dest_file->moov->mvhd->nextTrackID<= new_tk->Header->trackID)
4049 dest_file->moov->mvhd->nextTrackID = new_tk->Header->trackID+1;
4050
4051 return GF_OK;
4052 }
4053
4054 #if 0
4055 /*clones all sampleDescription entries in new track, after an optional reset of existing entries*/
4056 GF_Err gf_isom_clone_sample_descriptions(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, Bool reset_existing)
4057 {
4058 u32 i;
4059 GF_TrackBox *dst_trak, *src_trak;
4060 GF_Err e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE);
4061 if (e) return e;
4062
4063 dst_trak = gf_isom_get_track_from_file(the_file, trackNumber);
4064 if (!dst_trak || !dst_trak->Media) return GF_BAD_PARAM;
4065 src_trak = gf_isom_get_track_from_file(orig_file, orig_track);
4066 if (!src_trak || !src_trak->Media) return GF_BAD_PARAM;
4067
4068 if (reset_existing) {
4069 gf_isom_box_array_del(dst_trak->Media->information->sampleTable->SampleDescription->child_boxes);
4070 dst_trak->Media->information->sampleTable->SampleDescription->child_boxes = gf_list_new();
4071 }
4072
4073 for (i=0; i<gf_list_count(src_trak->Media->information->sampleTable->SampleDescription->child_boxes); i++) {
4074 u32 outDesc;
4075 e = gf_isom_clone_sample_description(the_file, trackNumber, orig_file, orig_track, i+1, NULL, NULL, &outDesc);
4076 if (e) break;
4077 }
4078 return e;
4079 }
4080 #endif
4081
4082
4083 GF_EXPORT
gf_isom_clone_sample_description(GF_ISOFile * the_file,u32 trackNumber,GF_ISOFile * orig_file,u32 orig_track,u32 orig_desc_index,const char * URLname,const char * URNname,u32 * outDescriptionIndex)4084 GF_Err gf_isom_clone_sample_description(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, u32 orig_desc_index, const char *URLname, const char *URNname, u32 *outDescriptionIndex)
4085 {
4086 GF_TrackBox *trak;
4087 GF_BitStream *bs;
4088 u8 *data;
4089 u32 data_size;
4090 GF_Box *entry;
4091 GF_Err e;
4092 u32 dataRefIndex;
4093 u32 mtype;
4094
4095 e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE);
4096 if (e) return e;
4097
4098 /*get orig sample desc and clone it*/
4099 trak = gf_isom_get_track_from_file(orig_file, orig_track);
4100 if (!trak || !trak->Media) return GF_BAD_PARAM;
4101
4102 entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, orig_desc_index-1);
4103 if (!entry) return GF_BAD_PARAM;
4104
4105 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
4106
4107 gf_isom_box_size(entry);
4108 gf_isom_box_write(entry, bs);
4109 gf_bs_get_content(bs, &data, &data_size);
4110 gf_bs_del(bs);
4111 bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
4112 e = gf_isom_box_parse(&entry, bs);
4113 gf_bs_del(bs);
4114 gf_free(data);
4115 if (e) return e;
4116
4117 /*get new track and insert clone*/
4118 trak = gf_isom_get_track_from_file(the_file, trackNumber);
4119 if (!trak || !trak->Media) goto exit;
4120
4121 /*get or create the data ref*/
4122 e = Media_FindDataRef(trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
4123 if (e) goto exit;
4124 if (!dataRefIndex) {
4125 e = Media_CreateDataRef(the_file, trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
4126 if (e) goto exit;
4127 }
4128 if (!the_file->keep_utc)
4129 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
4130 /*overwrite dref*/
4131 ((GF_SampleEntryBox *)entry)->dataReferenceIndex = dataRefIndex;
4132 e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, entry);
4133 *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes);
4134
4135 /*also clone track w/h info*/
4136 mtype = gf_isom_get_media_type(the_file, trackNumber);
4137 if (gf_isom_is_video_handler_type(mtype) ) {
4138 gf_isom_set_visual_info(the_file, trackNumber, (*outDescriptionIndex), ((GF_VisualSampleEntryBox*)entry)->Width, ((GF_VisualSampleEntryBox*)entry)->Height);
4139 }
4140 return e;
4141
4142 exit:
4143 gf_isom_box_del(entry);
4144 return e;
4145 }
4146
4147 GF_EXPORT
gf_isom_new_generic_sample_description(GF_ISOFile * movie,u32 trackNumber,const char * URLname,const char * URNname,GF_GenericSampleDescription * udesc,u32 * outDescriptionIndex)4148 GF_Err gf_isom_new_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, const char *URLname, const char *URNname, GF_GenericSampleDescription *udesc, u32 *outDescriptionIndex)
4149 {
4150 GF_TrackBox *trak;
4151 GF_Err e;
4152 u32 dataRefIndex;
4153
4154 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
4155 if (e) return e;
4156
4157 trak = gf_isom_get_track_from_file(movie, trackNumber);
4158 if (!trak || !trak->Media || !udesc) return GF_BAD_PARAM;
4159
4160 //get or create the data ref
4161 e = Media_FindDataRef(trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
4162 if (e) return e;
4163 if (!dataRefIndex) {
4164 e = Media_CreateDataRef(movie, trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
4165 if (e) return e;
4166 }
4167 if (!movie->keep_utc)
4168 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
4169
4170 if (gf_isom_is_video_handler_type(trak->Media->handler->handlerType)) {
4171 GF_GenericVisualSampleEntryBox *entry;
4172 //create a new entry
4173 entry = (GF_GenericVisualSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRV);
4174 if (!entry) return GF_OUT_OF_MEM;
4175
4176 if (!udesc->codec_tag) {
4177 entry->EntryType = GF_ISOM_BOX_TYPE_UUID;
4178 memcpy(entry->uuid, udesc->UUID, sizeof(bin128));
4179 } else {
4180 entry->EntryType = udesc->codec_tag;
4181 }
4182 if (entry->EntryType == 0) {
4183 gf_isom_box_del((GF_Box *)entry);
4184 return GF_NOT_SUPPORTED;
4185 }
4186
4187 entry->dataReferenceIndex = dataRefIndex;
4188 entry->vendor = udesc->vendor_code;
4189 entry->version = udesc->version;
4190 entry->revision = udesc->revision;
4191 entry->temporal_quality = udesc->temporal_quality;
4192 entry->spatial_quality = udesc->spatial_quality;
4193 entry->Width = udesc->width;
4194 entry->Height = udesc->height;
4195 strcpy(entry->compressor_name, udesc->compressor_name);
4196 entry->color_table_index = -1;
4197 entry->frames_per_sample = 1;
4198 entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000;
4199 entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000;
4200 entry->bit_depth = udesc->depth ? udesc->depth : 0x18;
4201 if (udesc->extension_buf && udesc->extension_buf_size) {
4202 entry->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
4203 if (!entry->data) {
4204 gf_isom_box_del((GF_Box *) entry);
4205 return GF_OUT_OF_MEM;
4206 }
4207 memcpy(entry->data, udesc->extension_buf, udesc->extension_buf_size);
4208 entry->data_size = udesc->extension_buf_size;
4209 }
4210 e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, entry);
4211 }
4212 else if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_AUDIO) {
4213 GF_GenericAudioSampleEntryBox *gena;
4214 //create a new entry
4215 gena = (GF_GenericAudioSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRA);
4216 if (!gena) return GF_OUT_OF_MEM;
4217
4218 if (!udesc->codec_tag) {
4219 gena->EntryType = GF_ISOM_BOX_TYPE_UUID;
4220 memcpy(gena->uuid, udesc->UUID, sizeof(bin128));
4221 } else {
4222 gena->EntryType = udesc->codec_tag;
4223 }
4224 if (gena->EntryType == 0) {
4225 gf_isom_box_del((GF_Box *)gena);
4226 return GF_NOT_SUPPORTED;
4227 }
4228
4229 gena->dataReferenceIndex = dataRefIndex;
4230 gena->vendor = udesc->vendor_code;
4231 gena->version = udesc->version;
4232 gena->revision = udesc->revision;
4233 gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16;
4234 gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2;
4235 gena->samplerate_hi = udesc->samplerate;
4236 gena->samplerate_lo = 0;
4237 gena->qtff_mode = udesc->is_qtff ? GF_ISOM_AUDIO_QTFF_ON_NOEXT : GF_ISOM_AUDIO_QTFF_NONE;
4238
4239 if (udesc->extension_buf && udesc->extension_buf_size) {
4240 gena->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
4241 if (!gena->data) {
4242 gf_isom_box_del((GF_Box *) gena);
4243 return GF_OUT_OF_MEM;
4244 }
4245 memcpy(gena->data, udesc->extension_buf, udesc->extension_buf_size);
4246 gena->data_size = udesc->extension_buf_size;
4247 }
4248 e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, gena);
4249 }
4250 else {
4251 GF_GenericSampleEntryBox *genm;
4252 //create a new entry
4253 genm = (GF_GenericSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRM);
4254 if (!genm) return GF_OUT_OF_MEM;
4255
4256 if (!udesc->codec_tag) {
4257 genm->EntryType = GF_ISOM_BOX_TYPE_UUID;
4258 memcpy(genm->uuid, udesc->UUID, sizeof(bin128));
4259 } else {
4260 genm->EntryType = udesc->codec_tag;
4261 }
4262 if (genm->EntryType == 0) {
4263 gf_isom_box_del((GF_Box *)genm);
4264 return GF_NOT_SUPPORTED;
4265 }
4266
4267 genm->dataReferenceIndex = dataRefIndex;
4268 if (udesc->extension_buf && udesc->extension_buf_size) {
4269 genm->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
4270 if (!genm->data) {
4271 gf_isom_box_del((GF_Box *) genm);
4272 return GF_OUT_OF_MEM;
4273 }
4274 memcpy(genm->data, udesc->extension_buf, udesc->extension_buf_size);
4275 genm->data_size = udesc->extension_buf_size;
4276 }
4277 e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, genm);
4278 }
4279 *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes);
4280 return e;
4281 }
4282
4283 //use carefully. Very useful when you made a lot of changes (IPMP, IPI, OCI, ...)
4284 //THIS WILL REPLACE THE WHOLE DESCRIPTOR ...
4285 #if 0 //unused
4286 /*change the data field of an unknown sample description*/
4287 GF_Err gf_isom_change_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_GenericSampleDescription *udesc)
4288 {
4289 GF_TrackBox *trak;
4290 GF_Err e;
4291 GF_GenericVisualSampleEntryBox *entry;
4292
4293 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
4294 if (e) return e;
4295
4296 trak = gf_isom_get_track_from_file(movie, trackNumber);
4297 if (!trak || !trak->Media || !StreamDescriptionIndex) return GF_BAD_PARAM;
4298
4299 entry = (GF_GenericVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex-1);
4300 if (!entry) return GF_BAD_PARAM;
4301 if (entry->type == GF_ISOM_BOX_TYPE_GNRV) {
4302 entry->vendor = udesc->vendor_code;
4303 entry->version = udesc->version;
4304 entry->revision = udesc->revision;
4305 entry->temporal_quality = udesc->temporal_quality;
4306 entry->spatial_quality = udesc->spatial_quality;
4307 entry->Width = udesc->width;
4308 entry->Height = udesc->height;
4309 strcpy(entry->compressor_name, udesc->compressor_name);
4310 entry->color_table_index = -1;
4311 entry->frames_per_sample = 1;
4312 entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000;
4313 entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000;
4314 entry->bit_depth = udesc->depth ? udesc->depth : 0x18;
4315 if (entry->data) gf_free(entry->data);
4316 entry->data = NULL;
4317 entry->data_size = 0;
4318 if (udesc->extension_buf && udesc->extension_buf_size) {
4319 entry->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
4320 if (!entry->data) {
4321 gf_isom_box_del((GF_Box *) entry);
4322 return GF_OUT_OF_MEM;
4323 }
4324 memcpy(entry->data, udesc->extension_buf, udesc->extension_buf_size);
4325 entry->data_size = udesc->extension_buf_size;
4326 }
4327 return GF_OK;
4328 } else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) {
4329 GF_GenericAudioSampleEntryBox *gena = (GF_GenericAudioSampleEntryBox *)entry;
4330 gena->vendor = udesc->vendor_code;
4331 gena->version = udesc->version;
4332 gena->revision = udesc->revision;
4333 gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16;
4334 gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2;
4335 gena->samplerate_hi = udesc->samplerate;
4336 gena->samplerate_lo = 0;
4337 if (gena->data) gf_free(gena->data);
4338 gena->data = NULL;
4339 gena->data_size = 0;
4340
4341 if (udesc->extension_buf && udesc->extension_buf_size) {
4342 gena->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
4343 if (!gena->data) {
4344 gf_isom_box_del((GF_Box *) gena);
4345 return GF_OUT_OF_MEM;
4346 }
4347 memcpy(gena->data, udesc->extension_buf, udesc->extension_buf_size);
4348 gena->data_size = udesc->extension_buf_size;
4349 }
4350 return GF_OK;
4351 } else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) {
4352 GF_GenericSampleEntryBox *genm = (GF_GenericSampleEntryBox *)entry;
4353 if (genm->data) gf_free(genm->data);
4354 genm->data = NULL;
4355 genm->data_size = 0;
4356
4357 if (udesc->extension_buf && udesc->extension_buf_size) {
4358 genm->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
4359 if (!genm->data) {
4360 gf_isom_box_del((GF_Box *) genm);
4361 return GF_OUT_OF_MEM;
4362 }
4363 memcpy(genm->data, udesc->extension_buf, udesc->extension_buf_size);
4364 genm->data_size = udesc->extension_buf_size;
4365 }
4366 return GF_OK;
4367 }
4368 return GF_BAD_PARAM;
4369 }
4370 #endif
4371
4372 #if 0
4373 /*removes given stream description*/
4374 GF_Err gf_isom_remove_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 streamDescIndex)
4375 {
4376 GF_TrackBox *trak;
4377 GF_Err e;
4378 GF_Box *entry;
4379
4380 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
4381 if (e) return e;
4382 trak = gf_isom_get_track_from_file(movie, trackNumber);
4383 if (!trak || !trak->Media || !streamDescIndex) return GF_BAD_PARAM;
4384 entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, streamDescIndex-1);
4385 if (!entry) return GF_BAD_PARAM;
4386 gf_list_rem(trak->Media->information->sampleTable->SampleDescription->child_boxes, streamDescIndex-1);
4387 gf_isom_box_del(entry);
4388 return GF_OK;
4389 }
4390 #endif
4391
4392 //sets a track reference
4393 GF_EXPORT
gf_isom_set_track_reference(GF_ISOFile * the_file,u32 trackNumber,u32 referenceType,GF_ISOTrackID ReferencedTrackID)4394 GF_Err gf_isom_set_track_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, GF_ISOTrackID ReferencedTrackID)
4395 {
4396 GF_Err e;
4397 GF_TrackBox *trak;
4398 GF_TrackReferenceBox *tref;
4399 GF_TrackReferenceTypeBox *dpnd;
4400
4401 trak = gf_isom_get_track_from_file(the_file, trackNumber);
4402 if (!trak) return GF_BAD_PARAM;
4403
4404 //no tref, create one
4405 tref = trak->References;
4406 if (!tref) {
4407 tref = (GF_TrackReferenceBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TREF);
4408 if (!tref) return GF_OUT_OF_MEM;
4409 e = trak_on_child_box((GF_Box*)trak, (GF_Box *) tref);
4410 if (e) return e;
4411 }
4412 //find a ref of the given type
4413 e = Track_FindRef(trak, referenceType, &dpnd);
4414 if (e) return e;
4415
4416 if (!dpnd) {
4417 dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT);
4418 if (!dpnd) return GF_OUT_OF_MEM;
4419 dpnd->reference_type = referenceType;
4420 }
4421 //add the ref
4422 return reftype_AddRefTrack(dpnd, ReferencedTrackID, NULL);
4423 }
4424
4425 //removes a track reference
4426 #if 0 //unused
4427 GF_Err gf_isom_remove_track_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, u32 ReferenceIndex)
4428 {
4429 GF_Err e;
4430 GF_TrackBox *trak;
4431 GF_TrackReferenceBox *tref;
4432 GF_TrackReferenceTypeBox *dpnd;
4433 u32 i, k, *newIDs;
4434 trak = gf_isom_get_track_from_file(the_file, trackNumber);
4435 if (!trak || !ReferenceIndex) return GF_BAD_PARAM;
4436
4437 //no tref, nothing to remove
4438 tref = trak->References;
4439 if (!tref) return GF_OK;
4440 //find a ref of the given type otherwise return
4441 e = Track_FindRef(trak, referenceType, &dpnd);
4442 if (e || !dpnd) return GF_OK;
4443 //remove the ref
4444 if (ReferenceIndex > dpnd->trackIDCount) return GF_BAD_PARAM;
4445 //last one
4446 if (dpnd->trackIDCount==1) {
4447 gf_isom_box_del_parent(&tref->child_boxes, (GF_Box *) dpnd);
4448 return GF_OK;
4449 }
4450 k = 0;
4451 newIDs = (u32*)gf_malloc(sizeof(u32)*(dpnd->trackIDCount-1));
4452 if (!newIDs) return GF_OUT_OF_MEM;
4453
4454 for (i=0; i<dpnd->trackIDCount; i++) {
4455 if (i+1 != ReferenceIndex) {
4456 newIDs[k] = dpnd->trackIDs[i];
4457 k++;
4458 }
4459 }
4460 gf_free(dpnd->trackIDs);
4461 dpnd->trackIDCount -= 1;
4462 dpnd->trackIDs = newIDs;
4463 return GF_OK;
4464 }
4465 #endif
4466
4467 //sets a track reference
4468 GF_EXPORT
gf_isom_remove_track_references(GF_ISOFile * the_file,u32 trackNumber)4469 GF_Err gf_isom_remove_track_references(GF_ISOFile *the_file, u32 trackNumber)
4470 {
4471 GF_TrackBox *trak;
4472
4473 trak = gf_isom_get_track_from_file(the_file, trackNumber);
4474 if (!trak) return GF_BAD_PARAM;
4475
4476 if (trak->References) {
4477 gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->References);
4478 trak->References = NULL;
4479 }
4480 return GF_OK;
4481 }
4482
4483
4484
4485 //changes track ID
4486 GF_EXPORT
gf_isom_set_track_id(GF_ISOFile * movie,u32 trackNumber,GF_ISOTrackID trackID)4487 GF_Err gf_isom_set_track_id(GF_ISOFile *movie, u32 trackNumber, GF_ISOTrackID trackID)
4488 {
4489 GF_TrackReferenceTypeBox *ref;
4490 GF_TrackBox *trak, *a_trak;
4491 u32 i, j, k;
4492
4493 if (!movie) return GF_BAD_PARAM;
4494 trak = gf_isom_get_track_from_file(movie, trackNumber);
4495 if (trak && (trak->Header->trackID==trackID)) return GF_OK;
4496 a_trak = gf_isom_get_track_from_id(movie->moov, trackID);
4497 if (!trak || a_trak) return GF_BAD_PARAM;
4498
4499 if (movie->moov->mvhd->nextTrackID<=trackID)
4500 movie->moov->mvhd->nextTrackID = trackID;
4501
4502 /*rewrite all dependencies*/
4503 i=0;
4504 while ((a_trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) {
4505 if (!a_trak->References) continue;
4506 j=0;
4507 while ((ref = (GF_TrackReferenceTypeBox *)gf_list_enum(a_trak->References->child_boxes, &j))) {
4508 for (k=0; k<ref->trackIDCount; k++) {
4509 if (ref->trackIDs[k]==trak->Header->trackID) {
4510 ref->trackIDs[k] = trackID;
4511 break;
4512 }
4513 }
4514 }
4515 }
4516
4517 /*and update IOD if any*/
4518 if (movie->moov->iods && movie->moov->iods->descriptor) {
4519 GF_ES_ID_Inc *inc;
4520 GF_IsomObjectDescriptor *od = (GF_IsomObjectDescriptor *)movie->moov->iods->descriptor;
4521
4522 i=0;
4523 while ((inc = (GF_ES_ID_Inc*)gf_list_enum(od->ES_ID_IncDescriptors, &i))) {
4524 if (inc->trackID==trak->Header->trackID) inc->trackID = trackID;
4525 }
4526 }
4527 trak->Header->trackID = trackID;
4528 return GF_OK;
4529 }
4530
4531 /*force to rewrite all dependencies when the trackID of referenced track changes*/
4532 GF_EXPORT
gf_isom_rewrite_track_dependencies(GF_ISOFile * movie,u32 trackNumber)4533 GF_Err gf_isom_rewrite_track_dependencies(GF_ISOFile *movie, u32 trackNumber)
4534 {
4535 GF_TrackReferenceTypeBox *ref;
4536 GF_TrackBox *trak, *a_trak;
4537 u32 i, k;
4538
4539 trak = gf_isom_get_track_from_file(movie, trackNumber);
4540 if (!trak)
4541 return GF_BAD_PARAM;
4542 if (!trak->References)
4543 return GF_OK;
4544
4545 i=0;
4546 while ((ref = (GF_TrackReferenceTypeBox *)gf_list_enum(trak->References->child_boxes, &i))) {
4547 for (k=0; k < ref->trackIDCount; k++) {
4548 a_trak = gf_isom_get_track_from_original_id(movie->moov, ref->trackIDs[k], trak->originalFile);
4549 if (a_trak) {
4550 ref->trackIDs[k] = a_trak->Header->trackID;
4551 } else {
4552 a_trak = gf_isom_get_track_from_id(movie->moov, ref->trackIDs[k]);
4553 /*we should have a track with no original ID (not imported) - should we rewrite the dependency ?*/
4554 if (! a_trak || a_trak->originalID) return GF_BAD_PARAM;
4555 }
4556 }
4557 }
4558
4559 return GF_OK;
4560 }
4561
4562 #if 0 //unused
4563
4564 /*! changes the sample description index of a sample
4565 \param isom_file the destination ISO file
4566 \param trackNumber the destination track
4567 \param sampleNum the target sample number
4568 \param fnewSampleDescIndex the new sample description index to assign to the sample
4569 \return error if any
4570 */
4571 GF_EXPORT
4572 GF_Err gf_isom_change_sample_desc_index(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 newSampleDescIndex)
4573 {
4574 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
4575 if (!trak || !sample_number || !newSampleDescIndex) return GF_BAD_PARAM;
4576 if (!trak->is_unpacked) {
4577 unpack_track(trak);
4578 }
4579 if (!trak->Media->information->sampleTable->SampleToChunk) return GF_BAD_PARAM;
4580 if (trak->Media->information->sampleTable->SampleToChunk->nb_entries < sample_number) return GF_BAD_PARAM;
4581 trak->Media->information->sampleTable->SampleToChunk->entries[sample_number-1].sampleDescriptionIndex = newSampleDescIndex;
4582 return GF_OK;
4583 }
4584
4585 /*modify CTS offset of a given sample (used for B-frames) - MUST be called in unpack mode only*/
4586 GF_EXPORT
4587 GF_Err gf_isom_modify_cts_offset(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 offset)
4588 {
4589 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
4590 if (!trak) return GF_BAD_PARAM;
4591 if (!trak->Media->information->sampleTable->CompositionOffset) return GF_BAD_PARAM;
4592 if (!trak->Media->information->sampleTable->CompositionOffset->unpack_mode) return GF_BAD_PARAM;
4593 /*we're in unpack mode: one entry per sample*/
4594 trak->Media->information->sampleTable->CompositionOffset->entries[sample_number - 1].decodingOffset = offset;
4595 return GF_OK;
4596 }
4597 #endif
4598
4599 GF_EXPORT
gf_isom_shift_cts_offset(GF_ISOFile * the_file,u32 trackNumber,s32 offset_shift)4600 GF_Err gf_isom_shift_cts_offset(GF_ISOFile *the_file, u32 trackNumber, s32 offset_shift)
4601 {
4602 u32 i;
4603 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
4604 if (!trak) return GF_BAD_PARAM;
4605 if (!trak->Media->information->sampleTable->CompositionOffset) return GF_BAD_PARAM;
4606 if (!trak->Media->information->sampleTable->CompositionOffset->unpack_mode) return GF_BAD_PARAM;
4607
4608 for (i=0; i<trak->Media->information->sampleTable->CompositionOffset->nb_entries; i++) {
4609 /*we're in unpack mode: one entry per sample*/
4610 trak->Media->information->sampleTable->CompositionOffset->entries[i].decodingOffset -= offset_shift;
4611 }
4612 return GF_OK;
4613 }
4614
4615 #if 0 //unused
4616 GF_Err gf_isom_remove_cts_info(GF_ISOFile *the_file, u32 trackNumber)
4617 {
4618 GF_SampleTableBox *stbl;
4619 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
4620 if (!trak) return GF_BAD_PARAM;
4621
4622 stbl = trak->Media->information->sampleTable;
4623 if (!stbl->CompositionOffset) return GF_OK;
4624
4625 gf_isom_box_del_parent(&stbl->child_boxes, (GF_Box *)stbl->CompositionOffset);
4626 stbl->CompositionOffset = NULL;
4627 return GF_OK;
4628 }
4629 #endif
4630
4631 GF_EXPORT
gf_isom_set_cts_packing(GF_ISOFile * the_file,u32 trackNumber,Bool unpack)4632 GF_Err gf_isom_set_cts_packing(GF_ISOFile *the_file, u32 trackNumber, Bool unpack)
4633 {
4634 GF_Err e;
4635 GF_Err stbl_repackCTS(GF_CompositionOffsetBox *ctts);
4636 GF_Err stbl_unpackCTS(GF_SampleTableBox *stbl);
4637 GF_SampleTableBox *stbl;
4638
4639 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
4640 if (!trak) return GF_BAD_PARAM;
4641
4642 stbl = trak->Media->information->sampleTable;
4643 if (unpack) {
4644 if (!stbl->CompositionOffset) {
4645 stbl->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_CTTS);
4646 if (!stbl->CompositionOffset) return GF_OUT_OF_MEM;
4647 }
4648 e = stbl_unpackCTS(stbl);
4649 } else {
4650 if (!stbl->CompositionOffset) return GF_OK;
4651 e = stbl_repackCTS(stbl->CompositionOffset);
4652 }
4653 if (e) return e;
4654 return SetTrackDuration(trak);
4655 }
4656
4657 GF_EXPORT
gf_isom_set_track_matrix(GF_ISOFile * the_file,u32 trackNumber,s32 matrix[9])4658 GF_Err gf_isom_set_track_matrix(GF_ISOFile *the_file, u32 trackNumber, s32 matrix[9])
4659 {
4660 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
4661 if (!trak || !trak->Header) return GF_BAD_PARAM;
4662 memcpy(trak->Header->matrix, matrix, sizeof(trak->Header->matrix));
4663 return GF_OK;
4664 }
4665
4666 GF_EXPORT
gf_isom_set_track_layout_info(GF_ISOFile * the_file,u32 trackNumber,u32 width,u32 height,s32 translation_x,s32 translation_y,s16 layer)4667 GF_Err gf_isom_set_track_layout_info(GF_ISOFile *the_file, u32 trackNumber, u32 width, u32 height, s32 translation_x, s32 translation_y, s16 layer)
4668 {
4669 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
4670 if (!trak || !trak->Header) return GF_BAD_PARAM;
4671 trak->Header->width = width;
4672 trak->Header->height = height;
4673 trak->Header->matrix[6] = translation_x;
4674 trak->Header->matrix[7] = translation_y;
4675 trak->Header->layer = layer;
4676 return GF_OK;
4677 }
4678
4679 GF_EXPORT
gf_isom_set_media_timescale(GF_ISOFile * the_file,u32 trackNumber,u32 newTS,u32 new_tsinc,Bool force_rescale)4680 GF_Err gf_isom_set_media_timescale(GF_ISOFile *the_file, u32 trackNumber, u32 newTS, u32 new_tsinc, Bool force_rescale)
4681 {
4682 Double scale;
4683 u32 old_ts_inc=0;
4684 u32 old_timescale;
4685 GF_TrackBox *trak;
4686 GF_SampleTableBox *stbl;
4687
4688 trak = gf_isom_get_track_from_file(the_file, trackNumber);
4689 if (!trak || !trak->Media || !trak->Media->mediaHeader) return GF_BAD_PARAM;
4690 if ((trak->Media->mediaHeader->timeScale==newTS) && !new_tsinc)
4691 return GF_EOS;
4692
4693 scale = newTS;
4694 scale /= trak->Media->mediaHeader->timeScale;
4695 old_timescale = trak->Media->mediaHeader->timeScale;
4696 trak->Media->mediaHeader->timeScale = newTS;
4697
4698 stbl = trak->Media->information->sampleTable;
4699 if (new_tsinc) {
4700 u32 i;
4701 if (!stbl->TimeToSample || !stbl->TimeToSample->nb_entries)
4702 return GF_BAD_PARAM;
4703
4704 for (i=0; i<stbl->TimeToSample->nb_entries; i++) {
4705 if (!old_ts_inc)
4706 old_ts_inc = stbl->TimeToSample->entries[i].sampleDelta;
4707 else if (old_ts_inc<stbl->TimeToSample->entries[i].sampleDelta)
4708 old_ts_inc = stbl->TimeToSample->entries[i].sampleDelta;
4709 }
4710 if ((old_timescale==newTS) && (old_ts_inc==new_tsinc))
4711 return GF_EOS;
4712
4713 force_rescale = GF_TRUE;
4714 stbl->TimeToSample->entries[0].sampleDelta = new_tsinc;
4715 if (stbl->CompositionOffset) {
4716 for (i=0; i<stbl->CompositionOffset->nb_entries; i++) {
4717 u32 old_offset = stbl->CompositionOffset->entries[i].decodingOffset;
4718 old_offset *= new_tsinc;
4719 old_offset /= old_ts_inc;
4720 stbl->CompositionOffset->entries[i].decodingOffset = old_offset;
4721 }
4722 }
4723 if (stbl->CompositionToDecode) {
4724 stbl->CompositionToDecode->compositionEndTime *= new_tsinc;
4725 stbl->CompositionToDecode->compositionEndTime /= old_ts_inc;
4726
4727 stbl->CompositionToDecode->compositionStartTime *= new_tsinc;
4728 stbl->CompositionToDecode->compositionStartTime /= old_ts_inc;
4729
4730 stbl->CompositionToDecode->compositionToDTSShift *= new_tsinc;
4731 stbl->CompositionToDecode->compositionToDTSShift /= old_ts_inc;
4732
4733 stbl->CompositionToDecode->greatestDecodeToDisplayDelta *= new_tsinc;
4734 stbl->CompositionToDecode->greatestDecodeToDisplayDelta /= old_ts_inc;
4735
4736 stbl->CompositionToDecode->leastDecodeToDisplayDelta *= new_tsinc;
4737 stbl->CompositionToDecode->leastDecodeToDisplayDelta /= old_ts_inc;
4738 }
4739 if (trak->editBox) {
4740 GF_EdtsEntry *ent;
4741 i=0;
4742 while ((ent = (GF_EdtsEntry*)gf_list_enum(trak->editBox->editList->entryList, &i))) {
4743 ent->mediaTime *= new_tsinc;
4744 ent->mediaTime /= old_ts_inc;
4745 }
4746 }
4747 }
4748
4749 if (!force_rescale) {
4750 u32 i, k, idx, last_delta;
4751 u64 cur_dts;
4752 u64*DTSs = NULL;
4753 s64*CTSs = NULL;
4754
4755 if (trak->editBox) {
4756 GF_EdtsEntry *ent;
4757 i=0;
4758 while ((ent = (GF_EdtsEntry*)gf_list_enum(trak->editBox->editList->entryList, &i))) {
4759 ent->mediaTime = (u32) (scale*ent->mediaTime);
4760 }
4761 }
4762 if (! stbl || !stbl->TimeToSample || !stbl->TimeToSample->nb_entries) {
4763 return SetTrackDuration(trak);
4764 }
4765
4766 idx = 0;
4767 cur_dts = 0;
4768 //unpack the DTSs
4769 DTSs = (u64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount) );
4770 if (!DTSs) return GF_OUT_OF_MEM;
4771
4772 CTSs = NULL;
4773 if (stbl->CompositionOffset) {
4774 CTSs = (s64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount) );
4775 if (!CTSs) return GF_OUT_OF_MEM;
4776 }
4777
4778 for (i=0; i<stbl->TimeToSample->nb_entries; i++) {
4779 for (k=0; k<stbl->TimeToSample->entries[i].sampleCount; k++) {
4780 cur_dts += stbl->TimeToSample->entries[i].sampleDelta;
4781 DTSs[idx] = (u64) (cur_dts * scale);
4782
4783 if (stbl->CompositionOffset) {
4784 s32 cts_o;
4785 stbl_GetSampleCTS(stbl->CompositionOffset, idx+1, &cts_o);
4786 CTSs[idx] = (s64) ( ((s64) cur_dts + cts_o) * scale);
4787 }
4788 idx++;
4789 }
4790 }
4791 last_delta = (u32) (stbl->TimeToSample->entries[stbl->TimeToSample->nb_entries-1].sampleDelta * scale);
4792
4793 //repack DTS
4794 if (stbl->SampleSize->sampleCount) {
4795 stbl->TimeToSample->entries = gf_realloc(stbl->TimeToSample->entries, sizeof(GF_SttsEntry)*stbl->SampleSize->sampleCount);
4796 memset(stbl->TimeToSample->entries, 0, sizeof(GF_SttsEntry)*stbl->SampleSize->sampleCount);
4797 stbl->TimeToSample->entries[0].sampleDelta = (u32) DTSs[0];
4798 stbl->TimeToSample->entries[0].sampleCount = 1;
4799 idx=0;
4800 for (i=1; i< stbl->SampleSize->sampleCount - 1; i++) {
4801 if (DTSs[i+1] - DTSs[i] == stbl->TimeToSample->entries[idx].sampleDelta) {
4802 stbl->TimeToSample->entries[idx].sampleCount++;
4803 } else {
4804 idx++;
4805 stbl->TimeToSample->entries[idx].sampleDelta = (u32) ( DTSs[i+1] - DTSs[i] );
4806 stbl->TimeToSample->entries[idx].sampleCount=1;
4807 }
4808 }
4809 //add the sample delta for the last sample
4810 if (stbl->TimeToSample->entries[idx].sampleDelta == last_delta) {
4811 stbl->TimeToSample->entries[idx].sampleCount++;
4812 } else {
4813 idx++;
4814 stbl->TimeToSample->entries[idx].sampleDelta = last_delta;
4815 stbl->TimeToSample->entries[idx].sampleCount=1;
4816 }
4817
4818 stbl->TimeToSample->nb_entries = idx+1;
4819 stbl->TimeToSample->entries = gf_realloc(stbl->TimeToSample->entries, sizeof(GF_SttsEntry)*stbl->TimeToSample->nb_entries);
4820 }
4821
4822 if (CTSs && stbl->SampleSize->sampleCount>0) {
4823 //repack CTS
4824 stbl->CompositionOffset->entries = gf_realloc(stbl->CompositionOffset->entries, sizeof(GF_DttsEntry)*stbl->SampleSize->sampleCount);
4825 memset(stbl->CompositionOffset->entries, 0, sizeof(GF_DttsEntry)*stbl->SampleSize->sampleCount);
4826 stbl->CompositionOffset->entries[0].decodingOffset = (s32) (CTSs[0] - DTSs[0]);
4827 stbl->CompositionOffset->entries[0].sampleCount = 1;
4828 idx=0;
4829 for (i=1; i< stbl->SampleSize->sampleCount; i++) {
4830 s32 cts_o = (s32) (CTSs[i] - DTSs[i]);
4831 if (cts_o == stbl->CompositionOffset->entries[idx].decodingOffset) {
4832 stbl->CompositionOffset->entries[idx].sampleCount++;
4833 } else {
4834 idx++;
4835 stbl->CompositionOffset->entries[idx].decodingOffset = cts_o;
4836 stbl->CompositionOffset->entries[idx].sampleCount=1;
4837 }
4838 }
4839 stbl->CompositionOffset->nb_entries = idx+1;
4840 stbl->CompositionOffset->entries = gf_realloc(stbl->CompositionOffset->entries, sizeof(GF_DttsEntry)*stbl->CompositionOffset->nb_entries);
4841
4842 gf_free(CTSs);
4843 }
4844 gf_free(DTSs);
4845
4846 if (stbl->CompositionToDecode) {
4847 stbl->CompositionToDecode->compositionEndTime = (s32) (stbl->CompositionToDecode->compositionEndTime * scale);
4848 stbl->CompositionToDecode->compositionStartTime = (s32)(stbl->CompositionToDecode->compositionStartTime * scale);
4849 stbl->CompositionToDecode->compositionToDTSShift = (s32)(stbl->CompositionToDecode->compositionToDTSShift * scale);
4850 stbl->CompositionToDecode->greatestDecodeToDisplayDelta = (s32)(stbl->CompositionToDecode->greatestDecodeToDisplayDelta * scale);
4851 stbl->CompositionToDecode->leastDecodeToDisplayDelta = (s32)(stbl->CompositionToDecode->leastDecodeToDisplayDelta * scale);
4852 }
4853 }
4854 return SetTrackDuration(trak);
4855 }
4856
4857 GF_EXPORT
gf_isom_box_equal(GF_Box * a,GF_Box * b)4858 Bool gf_isom_box_equal(GF_Box *a, GF_Box *b)
4859 {
4860 Bool ret;
4861 u8 *data1, *data2;
4862 u32 data1_size, data2_size;
4863 GF_BitStream *bs;
4864
4865 if (a == b) return GF_TRUE;
4866 if (!a || !b) return GF_FALSE;
4867
4868 data1 = data2 = NULL;
4869
4870 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
4871 gf_isom_box_size(a);
4872 gf_isom_box_write(a, bs);
4873 gf_bs_get_content(bs, &data1, &data1_size);
4874 gf_bs_del(bs);
4875
4876 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
4877 gf_isom_box_size(b);
4878 gf_isom_box_write(b, bs);
4879 gf_bs_get_content(bs, &data2, &data2_size);
4880 gf_bs_del(bs);
4881
4882 ret = GF_FALSE;
4883 if (data1_size == data2_size) {
4884 ret = (memcmp(data1, data2, sizeof(char)*data1_size) == 0) ? GF_TRUE : GF_FALSE;
4885 }
4886 gf_free(data1);
4887 gf_free(data2);
4888 return ret;
4889 }
4890
4891 GF_EXPORT
gf_isom_is_same_sample_description(GF_ISOFile * f1,u32 tk1,u32 sdesc_index1,GF_ISOFile * f2,u32 tk2,u32 sdesc_index2)4892 Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, u32 sdesc_index1, GF_ISOFile *f2, u32 tk2, u32 sdesc_index2)
4893 {
4894 u32 i, count;
4895 GF_TrackBox *trak1, *trak2;
4896 GF_ESD *esd1, *esd2;
4897 Bool need_memcmp, ret;
4898 GF_Box *a, *b;
4899
4900 /*get orig sample desc and clone it*/
4901 trak1 = gf_isom_get_track_from_file(f1, tk1);
4902 if (!trak1 || !trak1->Media) return GF_FALSE;
4903 trak2 = gf_isom_get_track_from_file(f2, tk2);
4904 if (!trak2 || !trak2->Media) return GF_FALSE;
4905
4906 if (trak1->Media->handler->handlerType != trak2->Media->handler->handlerType) return GF_FALSE;
4907 count = gf_list_count(trak1->Media->information->sampleTable->SampleDescription->child_boxes);
4908 if (count != gf_list_count(trak2->Media->information->sampleTable->SampleDescription->child_boxes)) {
4909 if (!sdesc_index1 && !sdesc_index2) return GF_FALSE;
4910 }
4911
4912 need_memcmp = GF_TRUE;
4913 for (i=0; i<count; i++) {
4914 GF_Box *ent1 = (GF_Box *)gf_list_get(trak1->Media->information->sampleTable->SampleDescription->child_boxes, i);
4915 GF_Box *ent2 = (GF_Box *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->child_boxes, i);
4916
4917 if (sdesc_index1) ent1 = (GF_Box *)gf_list_get(trak1->Media->information->sampleTable->SampleDescription->child_boxes, sdesc_index1 - 1);
4918 if (sdesc_index2) ent2 = (GF_Box *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->child_boxes, sdesc_index2 - 1);
4919
4920 if (!ent1 || !ent2) return GF_FALSE;
4921 if (ent1->type != ent2->type) return GF_FALSE;
4922
4923 switch (ent1->type) {
4924 /*for MPEG-4 streams, only compare decSpecInfo (bitrate may not be the same but that's not an issue)*/
4925 case GF_ISOM_BOX_TYPE_MP4S:
4926 case GF_ISOM_BOX_TYPE_MP4A:
4927 case GF_ISOM_BOX_TYPE_MP4V:
4928 case GF_ISOM_BOX_TYPE_ENCA:
4929 case GF_ISOM_BOX_TYPE_ENCV:
4930 case GF_ISOM_BOX_TYPE_RESV:
4931 case GF_ISOM_BOX_TYPE_ENCS:
4932 Media_GetESD(trak1->Media, sdesc_index1 ? sdesc_index1 : i+1, &esd1, GF_TRUE);
4933 Media_GetESD(trak2->Media, sdesc_index2 ? sdesc_index2 : i+1, &esd2, GF_TRUE);
4934 if (!esd1 || !esd2) continue;
4935 need_memcmp = GF_FALSE;
4936 if (esd1->decoderConfig->streamType != esd2->decoderConfig->streamType) return GF_FALSE;
4937 if (esd1->decoderConfig->objectTypeIndication != esd2->decoderConfig->objectTypeIndication) return GF_FALSE;
4938 if (!esd1->decoderConfig->decoderSpecificInfo && esd2->decoderConfig->decoderSpecificInfo) return GF_FALSE;
4939 if (esd1->decoderConfig->decoderSpecificInfo && !esd2->decoderConfig->decoderSpecificInfo) return GF_FALSE;
4940 if (!esd1->decoderConfig->decoderSpecificInfo || !esd2->decoderConfig->decoderSpecificInfo) continue;
4941 if (memcmp(esd1->decoderConfig->decoderSpecificInfo->data, esd2->decoderConfig->decoderSpecificInfo->data, sizeof(char)*esd1->decoderConfig->decoderSpecificInfo->dataLength)!=0) return GF_FALSE;
4942 break;
4943 case GF_ISOM_BOX_TYPE_HVT1:
4944 return GF_TRUE;
4945 case GF_ISOM_BOX_TYPE_AVC1:
4946 case GF_ISOM_BOX_TYPE_AVC2:
4947 case GF_ISOM_BOX_TYPE_AVC3:
4948 case GF_ISOM_BOX_TYPE_AVC4:
4949 case GF_ISOM_BOX_TYPE_SVC1:
4950 case GF_ISOM_BOX_TYPE_MVC1:
4951 case GF_ISOM_BOX_TYPE_HVC1:
4952 case GF_ISOM_BOX_TYPE_HEV1:
4953 case GF_ISOM_BOX_TYPE_HVC2:
4954 case GF_ISOM_BOX_TYPE_HEV2:
4955 case GF_ISOM_BOX_TYPE_LHE1:
4956 case GF_ISOM_BOX_TYPE_LHV1:
4957 case GF_ISOM_BOX_TYPE_AV01:
4958 {
4959 GF_MPEGVisualSampleEntryBox *avc1 = (GF_MPEGVisualSampleEntryBox *)ent1;
4960 GF_MPEGVisualSampleEntryBox *avc2 = (GF_MPEGVisualSampleEntryBox *)ent2;
4961
4962 if (avc1->hevc_config)
4963 a = (GF_Box *) avc1->hevc_config;
4964 else if (avc1->lhvc_config)
4965 a = (GF_Box *) avc1->lhvc_config;
4966 else if (avc1->svc_config)
4967 a = (GF_Box *) avc1->svc_config;
4968 else if (avc1->mvc_config)
4969 a = (GF_Box *) avc1->mvc_config;
4970 else if (avc1->av1_config)
4971 a = (GF_Box *)avc1->av1_config;
4972 else
4973 a = (GF_Box *) avc1->avc_config;
4974
4975 if (avc2->hevc_config)
4976 b = (GF_Box *) avc2->hevc_config;
4977 else if (avc2->lhvc_config)
4978 b = (GF_Box *) avc2->lhvc_config;
4979 else if (avc2->svc_config)
4980 b = (GF_Box *) avc2->svc_config;
4981 else if (avc2->mvc_config)
4982 b = (GF_Box *) avc2->mvc_config;
4983 else if (avc2->av1_config)
4984 b = (GF_Box *)avc2->av1_config;
4985 else
4986 b = (GF_Box *) avc2->avc_config;
4987
4988 return gf_isom_box_equal(a,b);
4989 }
4990 break;
4991 case GF_ISOM_BOX_TYPE_LSR1:
4992 {
4993 GF_LASeRSampleEntryBox *lsr1 = (GF_LASeRSampleEntryBox *)ent1;
4994 GF_LASeRSampleEntryBox *lsr2 = (GF_LASeRSampleEntryBox *)ent2;
4995 if (lsr1->lsr_config && lsr2->lsr_config
4996 && lsr1->lsr_config->hdr && lsr2->lsr_config->hdr
4997 && (lsr1->lsr_config->hdr_size==lsr2->lsr_config->hdr_size)
4998 && !memcmp(lsr1->lsr_config->hdr, lsr2->lsr_config->hdr, lsr2->lsr_config->hdr_size)
4999 ) {
5000 return GF_TRUE;
5001 }
5002 return GF_FALSE;
5003 }
5004 break;
5005 #ifndef GPAC_DISABLE_VTT
5006 case GF_ISOM_BOX_TYPE_WVTT:
5007 {
5008 GF_WebVTTSampleEntryBox *wvtt1 = (GF_WebVTTSampleEntryBox *)ent1;
5009 GF_WebVTTSampleEntryBox *wvtt2 = (GF_WebVTTSampleEntryBox *)ent2;
5010 if (wvtt1->config && wvtt2->config &&
5011 (wvtt1->config->string && wvtt2->config->string && !strcmp(wvtt1->config->string, wvtt2->config->string))) {
5012 return GF_TRUE;
5013 }
5014 return GF_FALSE;
5015 }
5016 break;
5017 #endif
5018 case GF_ISOM_BOX_TYPE_STPP:
5019 {
5020 GF_MetaDataSampleEntryBox *stpp1 = (GF_MetaDataSampleEntryBox *)ent1;
5021 GF_MetaDataSampleEntryBox *stpp2 = (GF_MetaDataSampleEntryBox *)ent2;
5022 if (stpp1->xml_namespace && stpp2->xml_namespace && !strcmp(stpp1->xml_namespace, stpp2->xml_namespace)) {
5023 return GF_TRUE;
5024 }
5025 return GF_FALSE;
5026 }
5027 break;
5028 case GF_ISOM_BOX_TYPE_SBTT:
5029 {
5030 return GF_FALSE;
5031 }
5032 break;
5033 case GF_ISOM_BOX_TYPE_STXT:
5034 {
5035 GF_MetaDataSampleEntryBox *stxt1 = (GF_MetaDataSampleEntryBox *)ent1;
5036 GF_MetaDataSampleEntryBox *stxt2 = (GF_MetaDataSampleEntryBox *)ent2;
5037 if (stxt1->mime_type && stxt2->mime_type &&
5038 ( (!stxt1->config && !stxt2->config) ||
5039 (stxt1->config && stxt2->config && stxt1->config->config && stxt2->config->config &&
5040 !strcmp(stxt1->config->config, stxt2->config->config)))) {
5041 return GF_TRUE;
5042 }
5043 return GF_FALSE;
5044 }
5045 case GF_ISOM_BOX_TYPE_MP3:
5046 case GF_QT_SUBTYPE_RAW_AUD:
5047 case GF_QT_SUBTYPE_TWOS:
5048 case GF_QT_SUBTYPE_SOWT:
5049 case GF_QT_SUBTYPE_FL32:
5050 case GF_QT_SUBTYPE_FL64:
5051 case GF_QT_SUBTYPE_IN24:
5052 case GF_QT_SUBTYPE_IN32:
5053 case GF_QT_SUBTYPE_ULAW:
5054 case GF_QT_SUBTYPE_ALAW:
5055 case GF_QT_SUBTYPE_ADPCM:
5056 case GF_QT_SUBTYPE_IMA_ADPCM:
5057 case GF_QT_SUBTYPE_DVCA:
5058 case GF_QT_SUBTYPE_QDMC:
5059 case GF_QT_SUBTYPE_QDMC2:
5060 case GF_QT_SUBTYPE_QCELP:
5061 case GF_QT_SUBTYPE_kMP3:
5062 return GF_TRUE;
5063 case GF_QT_SUBTYPE_RAW_VID:
5064 case GF_QT_SUBTYPE_APCH:
5065 case GF_QT_SUBTYPE_APCO:
5066 case GF_QT_SUBTYPE_APCN:
5067 case GF_QT_SUBTYPE_APCS:
5068 case GF_QT_SUBTYPE_AP4X:
5069 case GF_QT_SUBTYPE_AP4H:
5070 case GF_QT_SUBTYPE_YUV422:
5071 case GF_QT_SUBTYPE_YUV444:
5072 case GF_QT_SUBTYPE_YUV422_10:
5073 case GF_QT_SUBTYPE_YUV444_10:
5074 return GF_TRUE;
5075 }
5076
5077 if (sdesc_index1 && sdesc_index2) break;
5078 }
5079 if (!need_memcmp) return GF_TRUE;
5080 a = (GF_Box *)trak1->Media->information->sampleTable->SampleDescription;
5081 b = (GF_Box *)trak2->Media->information->sampleTable->SampleDescription;
5082 //we ignore all bitrate boxes when comparing the box, disable their writing
5083 gf_isom_registry_disable(GF_ISOM_BOX_TYPE_BTRT, GF_TRUE);
5084 ret = gf_isom_box_equal(a,b);
5085 //re-enable btrt writing
5086 gf_isom_registry_disable(GF_ISOM_BOX_TYPE_BTRT, GF_FALSE);
5087
5088 return ret;
5089 }
5090
5091 GF_EXPORT
gf_isom_estimate_size(GF_ISOFile * movie)5092 u64 gf_isom_estimate_size(GF_ISOFile *movie)
5093 {
5094 GF_Err e;
5095 GF_Box *a;
5096 u32 i, count;
5097 u64 mdat_size;
5098 if (!movie || !movie->moov) return 0;
5099
5100 mdat_size = 0;
5101 count = gf_list_count(movie->moov->trackList);
5102 for (i=0; i<count; i++) {
5103 mdat_size += gf_isom_get_media_data_size(movie, i+1);
5104 }
5105 if (mdat_size) {
5106 mdat_size += 8;
5107 if (mdat_size > 0xFFFFFFFF) mdat_size += 8;
5108 }
5109
5110 i=0;
5111 while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
5112 e = gf_isom_box_size(a);
5113 if (e == GF_OK)
5114 mdat_size += a->size;
5115 }
5116 return mdat_size;
5117 }
5118
5119
5120 //set shadowing on/off
5121 #if 0 //unused
5122 GF_Err gf_isom_remove_sync_shadows(GF_ISOFile *movie, u32 trackNumber)
5123 {
5124 GF_TrackBox *trak;
5125 GF_SampleTableBox *stbl;
5126
5127 if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE;
5128 trak = gf_isom_get_track_from_file(movie, trackNumber);
5129 if (!trak) return GF_BAD_PARAM;
5130
5131 stbl = trak->Media->information->sampleTable;
5132 if (stbl->ShadowSync) {
5133 gf_isom_box_del_parent(&stbl->child_boxes, (GF_Box *) stbl->ShadowSync);
5134 stbl->ShadowSync = NULL;
5135 }
5136 return GF_OK;
5137 }
5138
5139 /*Use this function to do the shadowing if you use shadowing.
5140 the sample to be shadowed MUST be a non-sync sample (ignored if not)
5141 the sample shadowing must be a Sync sample (error if not)*/
5142 GF_Err gf_isom_set_sync_shadow(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u32 syncSample)
5143 {
5144 GF_TrackBox *trak;
5145 GF_SampleTableBox *stbl;
5146 GF_ISOSAPType isRAP;
5147 GF_Err e;
5148
5149 if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE;
5150 trak = gf_isom_get_track_from_file(movie, trackNumber);
5151 if (!trak || !sampleNumber || !syncSample) return GF_BAD_PARAM;
5152
5153 stbl = trak->Media->information->sampleTable;
5154 if (!stbl->ShadowSync) {
5155 stbl->ShadowSync = (GF_ShadowSyncBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSH);
5156 if (!stbl->ShadowSync) return GF_OUT_OF_MEM;
5157 }
5158
5159 //if no sync, skip
5160 if (!stbl->SyncSample) return GF_OK;
5161 //else set the sync shadow.
5162 //if the sample is sync, ignore
5163 e = stbl_GetSampleRAP(stbl->SyncSample, sampleNumber, &isRAP, NULL, NULL);
5164 if (e) return e;
5165 if (isRAP) return GF_OK;
5166 //if the shadowing sample is not sync, error
5167 e = stbl_GetSampleRAP(stbl->SyncSample, syncSample, &isRAP, NULL, NULL);
5168 if (e) return e;
5169 if (!isRAP) return GF_BAD_PARAM;
5170
5171 return stbl_SetSyncShadow(stbl->ShadowSync, sampleNumber, syncSample);
5172 }
5173 #endif
5174
5175 //set the GroupID of a track (only used for interleaving)
5176 GF_EXPORT
gf_isom_set_track_interleaving_group(GF_ISOFile * movie,u32 trackNumber,u32 GroupID)5177 GF_Err gf_isom_set_track_interleaving_group(GF_ISOFile *movie, u32 trackNumber, u32 GroupID)
5178 {
5179 GF_TrackBox *trak;
5180
5181 if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE;
5182 trak = gf_isom_get_track_from_file(movie, trackNumber);
5183 if (!trak || !GroupID) return GF_BAD_PARAM;
5184
5185 trak->Media->information->sampleTable->groupID = GroupID;
5186 return GF_OK;
5187 }
5188
5189
5190 //set the Priority of a track within a Group (only used for tight interleaving)
5191 //Priority ranges from 1 to 9
5192 GF_EXPORT
gf_isom_set_track_priority_in_group(GF_ISOFile * movie,u32 trackNumber,u32 Priority)5193 GF_Err gf_isom_set_track_priority_in_group(GF_ISOFile *movie, u32 trackNumber, u32 Priority)
5194 {
5195 GF_TrackBox *trak;
5196
5197 if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE;
5198 trak = gf_isom_get_track_from_file(movie, trackNumber);
5199 if (!trak || !Priority) return GF_BAD_PARAM;
5200
5201 trak->Media->information->sampleTable->trackPriority = Priority > 255 ? 255 : Priority;
5202 return GF_OK;
5203 }
5204
5205 //set the max SamplesPerChunk (for file optimization)
5206 GF_EXPORT
gf_isom_hint_max_chunk_size(GF_ISOFile * movie,u32 trackNumber,u32 maxChunkSize)5207 GF_Err gf_isom_hint_max_chunk_size(GF_ISOFile *movie, u32 trackNumber, u32 maxChunkSize)
5208 {
5209 GF_TrackBox *trak;
5210
5211 if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE;
5212 trak = gf_isom_get_track_from_file(movie, trackNumber);
5213 if (!trak || !maxChunkSize) return GF_BAD_PARAM;
5214
5215 trak->Media->information->sampleTable->MaxChunkSize = maxChunkSize;
5216 return GF_OK;
5217 }
5218
5219
5220 GF_EXPORT
gf_isom_set_extraction_slc(GF_ISOFile * the_file,u32 trackNumber,u32 StreamDescriptionIndex,const GF_SLConfig * slConfig)5221 GF_Err gf_isom_set_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, const GF_SLConfig *slConfig)
5222 {
5223 GF_TrackBox *trak;
5224 GF_SampleEntryBox *entry;
5225 GF_Err e;
5226 GF_SLConfig **slc;
5227
5228 trak = gf_isom_get_track_from_file(the_file, trackNumber);
5229 if (!trak) return GF_BAD_PARAM;
5230
5231 e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL);
5232 if (e) return e;
5233
5234 //we must be sure we are not using a remote ESD
5235 switch (entry->type) {
5236 case GF_ISOM_BOX_TYPE_MP4S:
5237 if (((GF_MPEGSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
5238 slc = & ((GF_MPEGSampleEntryBox *)entry)->slc;
5239 break;
5240 case GF_ISOM_BOX_TYPE_MP4A:
5241 if (((GF_MPEGAudioSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
5242 slc = & ((GF_MPEGAudioSampleEntryBox *)entry)->slc;
5243 break;
5244 case GF_ISOM_BOX_TYPE_MP4V:
5245 if (((GF_MPEGVisualSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
5246 slc = & ((GF_MPEGVisualSampleEntryBox *)entry)->slc;
5247 break;
5248 default:
5249 return GF_BAD_PARAM;
5250 }
5251
5252 if (*slc) {
5253 gf_odf_desc_del((GF_Descriptor *)*slc);
5254 *slc = NULL;
5255 }
5256 if (!slConfig) return GF_OK;
5257 //finally duplicate the SL
5258 return gf_odf_desc_copy((GF_Descriptor *) slConfig, (GF_Descriptor **) slc);
5259 }
5260
5261 #if 0 //unused
5262 GF_Err gf_isom_get_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig **slConfig)
5263 {
5264 GF_TrackBox *trak;
5265 GF_SampleEntryBox *entry;
5266 GF_Err e;
5267 GF_SLConfig *slc;
5268
5269 trak = gf_isom_get_track_from_file(the_file, trackNumber);
5270 if (!trak) return GF_BAD_PARAM;
5271
5272 e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL);
5273 if (e) return e;
5274
5275 //we must be sure we are not using a remote ESD
5276 slc = NULL;
5277 *slConfig = NULL;
5278 switch (entry->type) {
5279 case GF_ISOM_BOX_TYPE_MP4S:
5280 if (((GF_MPEGSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
5281 slc = ((GF_MPEGSampleEntryBox *)entry)->slc;
5282 break;
5283 case GF_ISOM_BOX_TYPE_MP4A:
5284 if (((GF_MPEGAudioSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
5285 slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc;
5286 break;
5287 case GF_ISOM_BOX_TYPE_MP4V:
5288 if (((GF_MPEGVisualSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
5289 slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc;
5290 break;
5291 default:
5292 return GF_BAD_PARAM;
5293 }
5294
5295 if (!slc) return GF_OK;
5296 //finally duplicate the SL
5297 return gf_odf_desc_copy((GF_Descriptor *) slc, (GF_Descriptor **) slConfig);
5298 }
5299
5300 u32 gf_isom_get_track_group(GF_ISOFile *the_file, u32 trackNumber)
5301 {
5302 GF_TrackBox *trak;
5303 trak = gf_isom_get_track_from_file(the_file, trackNumber);
5304 if (!trak) return 0;
5305 return trak->Media->information->sampleTable->groupID;
5306 }
5307
5308 u32 gf_isom_get_track_priority_in_group(GF_ISOFile *the_file, u32 trackNumber)
5309 {
5310 GF_TrackBox *trak;
5311 trak = gf_isom_get_track_from_file(the_file, trackNumber);
5312 if (!trak) return 0;
5313 return trak->Media->information->sampleTable->trackPriority;
5314 }
5315 #endif
5316
5317
5318 GF_EXPORT
gf_isom_make_interleave(GF_ISOFile * file,Double TimeInSec)5319 GF_Err gf_isom_make_interleave(GF_ISOFile *file, Double TimeInSec)
5320 {
5321 GF_Err e;
5322 if (!file) return GF_BAD_PARAM;
5323
5324 if (file->storageMode==GF_ISOM_STORE_FASTSTART) {
5325 return gf_isom_set_interleave_time(file, (u32) (TimeInSec * gf_isom_get_timescale(file)));
5326 }
5327 if (gf_isom_get_mode(file) < GF_ISOM_OPEN_EDIT) return GF_BAD_PARAM;
5328 e = gf_isom_set_storage_mode(file, GF_ISOM_STORE_DRIFT_INTERLEAVED);
5329 if (e) return e;
5330 return gf_isom_set_interleave_time(file, (u32) (TimeInSec * gf_isom_get_timescale(file)));
5331 }
5332
5333
5334 GF_EXPORT
gf_isom_set_handler_name(GF_ISOFile * the_file,u32 trackNumber,const char * nameUTF8)5335 GF_Err gf_isom_set_handler_name(GF_ISOFile *the_file, u32 trackNumber, const char *nameUTF8)
5336 {
5337 GF_TrackBox *trak;
5338 trak = gf_isom_get_track_from_file(the_file, trackNumber);
5339 if (!trak) return GF_BAD_PARAM;
5340 if (trak->Media->handler->nameUTF8) gf_free(trak->Media->handler->nameUTF8);
5341 trak->Media->handler->nameUTF8 = NULL;
5342
5343 if (!nameUTF8) return GF_OK;
5344
5345 if (!strnicmp(nameUTF8, "file://", 7)) {
5346 u8 BOM[4];
5347 FILE *f = gf_fopen(nameUTF8+7, "rb");
5348 u64 size;
5349 if (!f) return GF_URL_ERROR;
5350 size = gf_fsize(f);
5351 if (3!=gf_fread(BOM, 3, f)) {
5352 gf_fclose(f);
5353 return GF_CORRUPTED_DATA;
5354 }
5355 /*skip BOM if any*/
5356 if ((BOM[0]==0xEF) && (BOM[1]==0xBB) && (BOM[2]==0xBF)) size -= 3;
5357 else if ((BOM[0]==0xEF) || (BOM[0]==0xFF)) {
5358 gf_fclose(f);
5359 return GF_BAD_PARAM;
5360 }
5361 else gf_fseek(f, 0, SEEK_SET);
5362 trak->Media->handler->nameUTF8 = (char*)gf_malloc(sizeof(char)*(size_t)(size+1));
5363 if (!trak->Media->handler->nameUTF8) {
5364 gf_fclose(f);
5365 return GF_OUT_OF_MEM;
5366 }
5367 size = gf_fread(trak->Media->handler->nameUTF8, (size_t)size, f);
5368 trak->Media->handler->nameUTF8[size] = 0;
5369 gf_fclose(f);
5370 } else {
5371 u32 i, j, len;
5372 char szOrig[1024], szLine[1024];
5373 strcpy(szOrig, nameUTF8);
5374 j=0;
5375 len = (u32) strlen(szOrig);
5376 for (i=0; i<len; i++) {
5377 if (szOrig[i] & 0x80) {
5378 /*non UTF8 (likely some win-CP)*/
5379 if ( (szOrig[i+1] & 0xc0) != 0x80) {
5380 szLine[j] = 0xc0 | ( (szOrig[i] >> 6) & 0x3 );
5381 j++;
5382 szOrig[i] &= 0xbf;
5383 }
5384 /*UTF8 2 bytes char */
5385 else if ( (szOrig[i] & 0xe0) == 0xc0) {
5386 szLine[j] = szOrig[i];
5387 i++;
5388 j++;
5389 }
5390 /*UTF8 3 bytes char */
5391 else if ( (szOrig[i] & 0xf0) == 0xe0) {
5392 szLine[j] = szOrig[i];
5393 i++;
5394 j++;
5395 szLine[j] = szOrig[i];
5396 i++;
5397 j++;
5398 }
5399 /*UTF8 4 bytes char */
5400 else if ( (szOrig[i] & 0xf8) == 0xf0) {
5401 szLine[j] = szOrig[i];
5402 i++;
5403 j++;
5404 szLine[j] = szOrig[i];
5405 i++;
5406 j++;
5407 szLine[j] = szOrig[i];
5408 i++;
5409 j++;
5410 }
5411 }
5412 szLine[j] = szOrig[i];
5413 j++;
5414 }
5415 szLine[j] = 0;
5416 trak->Media->handler->nameUTF8 = gf_strdup(szLine);
5417 }
5418 return GF_OK;
5419 }
5420
5421 #if 0 //unused
5422 /*clones root OD from input to output file, without copying root OD track references*/
5423 GF_Err gf_isom_clone_root_od(GF_ISOFile *input, GF_ISOFile *output)
5424 {
5425 GF_List *esds;
5426 GF_Err e;
5427 u32 i;
5428 GF_Descriptor *desc;
5429
5430 e = gf_isom_remove_root_od(output);
5431 if (e) return e;
5432 if (!input->moov || !input->moov->iods || !input->moov->iods->descriptor) return GF_OK;
5433 e = gf_isom_insert_moov(output);
5434 if (e) return e;
5435 e = AddMovieIOD(output->moov, 0);
5436 if (e) return e;
5437 if (output->moov->iods->descriptor) gf_odf_desc_del(output->moov->iods->descriptor);
5438 output->moov->iods->descriptor = NULL;
5439 gf_odf_desc_copy(input->moov->iods->descriptor, &output->moov->iods->descriptor);
5440
5441 switch (output->moov->iods->descriptor->tag) {
5442 case GF_ODF_ISOM_IOD_TAG:
5443 esds = ((GF_IsomInitialObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors;
5444 break;
5445 case GF_ODF_ISOM_OD_TAG:
5446 esds = ((GF_IsomObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors;
5447 break;
5448 default:
5449 return GF_ISOM_INVALID_FILE;
5450 }
5451
5452 //get the desc
5453 i=0;
5454 while ((desc = (GF_Descriptor*)gf_list_enum(esds, &i))) {
5455 gf_odf_desc_del(desc);
5456 gf_list_rem(esds, i-1);
5457 }
5458 return GF_OK;
5459 }
5460 #endif
5461
5462 GF_EXPORT
gf_isom_set_media_type(GF_ISOFile * movie,u32 trackNumber,u32 new_type)5463 GF_Err gf_isom_set_media_type(GF_ISOFile *movie, u32 trackNumber, u32 new_type)
5464 {
5465 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
5466 if (!trak || !new_type) return GF_BAD_PARAM;
5467 trak->Media->handler->handlerType = new_type;
5468 return GF_OK;
5469 }
5470
5471 GF_EXPORT
gf_isom_set_media_subtype(GF_ISOFile * movie,u32 trackNumber,u32 sampleDescriptionIndex,u32 new_type)5472 GF_Err gf_isom_set_media_subtype(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, u32 new_type)
5473 {
5474 GF_SampleEntryBox*entry;
5475 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
5476 if (!trak || !sampleDescriptionIndex || !new_type) return GF_BAD_PARAM;
5477
5478 entry = (GF_SampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex - 1);
5479 if (!entry) return GF_BAD_PARAM;
5480 entry->type = new_type;
5481 return GF_OK;
5482 }
5483
5484
5485 #if 0 //unused
5486 GF_Err gf_isom_set_JPEG2000(GF_ISOFile *mov, Bool set_on)
5487 {
5488 if (!mov) return GF_BAD_PARAM;
5489 mov->is_jp2 = set_on;
5490 return GF_OK;
5491 }
5492 #endif
5493
gf_isom_remove_uuid(GF_ISOFile * movie,u32 trackNumber,bin128 UUID)5494 GF_Err gf_isom_remove_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID)
5495 {
5496 u32 i, count;
5497 GF_List *list;
5498
5499 if (trackNumber==(u32) -1) {
5500 if (!movie) return GF_BAD_PARAM;
5501 list = movie->TopBoxes;
5502 } else if (trackNumber) {
5503 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
5504 if (!trak) return GF_BAD_PARAM;
5505 list = trak->child_boxes;
5506 } else {
5507 if (!movie) return GF_BAD_PARAM;
5508 list = movie->moov->child_boxes;
5509 }
5510
5511 count = list ? gf_list_count(list) : 0;
5512 for (i=0; i<count; i++) {
5513 GF_UnknownUUIDBox *uuid = (GF_UnknownUUIDBox *)gf_list_get(list, i);
5514 if (uuid->type != GF_ISOM_BOX_TYPE_UUID) continue;
5515 if (memcmp(UUID, uuid->uuid, sizeof(bin128))) continue;
5516 gf_list_rem(list, i);
5517 i--;
5518 count--;
5519 gf_isom_box_del((GF_Box*)uuid);
5520 }
5521 return GF_OK;
5522 }
5523
5524 GF_EXPORT
gf_isom_add_uuid(GF_ISOFile * movie,u32 trackNumber,bin128 UUID,const u8 * data,u32 data_size)5525 GF_Err gf_isom_add_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID, const u8 *data, u32 data_size)
5526 {
5527 GF_List *list;
5528 u32 btype;
5529 GF_Box *box;
5530 GF_UnknownUUIDBox *uuidb;
5531
5532 if (data_size && !data) return GF_BAD_PARAM;
5533 if (trackNumber==(u32) -1) {
5534 if (!movie) return GF_BAD_PARAM;
5535 list = movie->TopBoxes;
5536 } else if (trackNumber) {
5537 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
5538 if (!trak) return GF_BAD_PARAM;
5539 if (!trak->child_boxes) trak->child_boxes = gf_list_new();
5540 list = trak->child_boxes;
5541 } else {
5542 if (!movie) return GF_BAD_PARAM;
5543 if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new();
5544 list = movie->moov->child_boxes;
5545 }
5546 btype = gf_isom_solve_uuid_box((char *) UUID);
5547 if (!btype) btype = GF_ISOM_BOX_TYPE_UUID;
5548 box = gf_isom_box_new(btype);
5549 if (!box) return GF_OUT_OF_MEM;
5550 uuidb = (GF_UnknownUUIDBox*)box;
5551 uuidb->internal_4cc = gf_isom_solve_uuid_box((char *) UUID);
5552 memcpy(uuidb->uuid, UUID, sizeof(bin128));
5553 uuidb->dataSize = data_size;
5554 if (data_size) {
5555 uuidb->data = (char*)gf_malloc(sizeof(char)*data_size);
5556 if (!uuidb->data) return GF_OUT_OF_MEM;
5557 memcpy(uuidb->data, data, sizeof(char)*data_size);
5558 }
5559 gf_list_add(list, uuidb);
5560 return GF_OK;
5561 }
5562
5563 /*Apple extensions*/
5564
5565 GF_EXPORT
gf_isom_apple_set_tag(GF_ISOFile * mov,GF_ISOiTunesTag tag,const u8 * data,u32 data_len)5566 GF_Err gf_isom_apple_set_tag(GF_ISOFile *mov, GF_ISOiTunesTag tag, const u8 *data, u32 data_len)
5567 {
5568 GF_BitStream *bs;
5569 GF_Err e;
5570 GF_ItemListBox *ilst;
5571 GF_MetaBox *meta;
5572 GF_ListItemBox *info;
5573 u32 btype, i;
5574
5575
5576 e = CanAccessMovie(mov, GF_ISOM_OPEN_WRITE);
5577 if (e) return e;
5578
5579 meta = gf_isom_apple_create_meta_extensions(mov);
5580 if (!meta) return GF_BAD_PARAM;
5581
5582 ilst = gf_ismo_locate_box(meta->child_boxes, GF_ISOM_BOX_TYPE_ILST, NULL);
5583 if (!ilst) {
5584 ilst = (GF_ItemListBox *) gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_ILST);
5585 }
5586
5587 if (tag==GF_ISOM_ITUNE_GENRE) {
5588 btype = data ? GF_ISOM_BOX_TYPE_0xA9GEN : GF_ISOM_BOX_TYPE_GNRE;
5589 } else {
5590 btype = tag;
5591 }
5592 /*remove tag*/
5593 i = 0;
5594 while ((info = (GF_ListItemBox*)gf_list_enum(ilst->child_boxes, &i))) {
5595 if (info->type==btype) {
5596 gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *) info);
5597 info = NULL;
5598 break;
5599 }
5600 }
5601
5602 if (data != NULL) {
5603 info = (GF_ListItemBox *)gf_isom_box_new(btype);
5604 if (info == NULL) return GF_OUT_OF_MEM;
5605 switch (btype) {
5606 case GF_ISOM_BOX_TYPE_TRKN:
5607 case GF_ISOM_BOX_TYPE_DISK:
5608 case GF_ISOM_BOX_TYPE_GNRE:
5609 info->data->flags = 0x0;
5610 break;
5611 case GF_ISOM_BOX_TYPE_PGAP:
5612 case GF_ISOM_BOX_TYPE_CPIL:
5613 info->data->flags = 0x15;
5614 break;
5615 default:
5616 info->data->flags = 0x1;
5617 break;
5618 }
5619 if (tag==GF_ISOM_ITUNE_COVER_ART) {
5620 if (data_len & 0x80000000) {
5621 data_len = (data_len & 0x7FFFFFFF);
5622 info->data->flags = 14;
5623 } else {
5624 info->data->flags = 13;
5625 }
5626 }
5627 info->data->dataSize = data_len;
5628 info->data->data = (char*)gf_malloc(sizeof(char)*data_len);
5629 if (!info->data->data) return GF_OUT_OF_MEM;
5630 memcpy(info->data->data , data, sizeof(char)*data_len);
5631 }
5632 else if (data_len && (tag==GF_ISOM_ITUNE_GENRE)) {
5633 info = (GF_ListItemBox *)gf_isom_box_new(btype);
5634 if (info == NULL) return GF_OUT_OF_MEM;
5635 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
5636 gf_bs_write_u16(bs, data_len);
5637 gf_bs_get_content(bs, & info->data->data, &info->data->dataSize);
5638 info->data->flags = 0x0;
5639 gf_bs_del(bs);
5640 } else if (data_len && (tag==GF_ISOM_ITUNE_COMPILATION)) {
5641 info = (GF_ListItemBox *)gf_isom_box_new(btype);
5642 if (info == NULL) return GF_OUT_OF_MEM;
5643 info->data->data = (char*)gf_malloc(sizeof(char));
5644 if (!info->data->data) return GF_OUT_OF_MEM;
5645 info->data->data[0] = 1;
5646 info->data->dataSize = 1;
5647 info->data->flags = 21;
5648 } else if (data_len && (tag==GF_ISOM_ITUNE_TEMPO)) {
5649 info = (GF_ListItemBox *)gf_isom_box_new(btype);
5650 if (info == NULL) return GF_OUT_OF_MEM;
5651 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
5652 gf_bs_write_u16(bs, data_len);
5653 gf_bs_get_content(bs, &info->data->data, &info->data->dataSize);
5654 info->data->flags = 0x15;
5655 gf_bs_del(bs);
5656 }
5657
5658 if (!info || (tag==GF_ISOM_ITUNE_ALL) ) {
5659 if (!gf_list_count(ilst->child_boxes) || (tag==GF_ISOM_ITUNE_ALL) ) {
5660 gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) ilst);
5661 }
5662 return GF_OK;
5663 }
5664
5665 return gf_list_add(ilst->child_boxes, info);
5666 }
5667
5668 GF_EXPORT
gf_isom_set_alternate_group_id(GF_ISOFile * movie,u32 trackNumber,u32 groupId)5669 GF_Err gf_isom_set_alternate_group_id(GF_ISOFile *movie, u32 trackNumber, u32 groupId)
5670 {
5671 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
5672 if (!trak) return GF_BAD_PARAM;
5673 trak->Header->alternate_group = groupId;
5674 return GF_OK;
5675 }
5676
5677
5678 GF_EXPORT
gf_isom_set_track_switch_parameter(GF_ISOFile * movie,u32 trackNumber,u32 trackRefGroup,Bool is_switch_group,u32 * switchGroupID,u32 * criteriaList,u32 criteriaListCount)5679 GF_Err gf_isom_set_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, u32 trackRefGroup, Bool is_switch_group, u32 *switchGroupID, u32 *criteriaList, u32 criteriaListCount)
5680 {
5681 GF_TrackSelectionBox *tsel;
5682 GF_TrackBox *trak;
5683 GF_UserDataMap *map;
5684 GF_Err e;
5685 u32 alternateGroupID = 0;
5686 u32 next_switch_group_id = 0;
5687
5688 trak = gf_isom_get_track_from_file(movie, trackNumber);
5689 if (!trak || !switchGroupID) return GF_BAD_PARAM;
5690
5691
5692 if (trackRefGroup) {
5693 GF_TrackBox *trak_ref = gf_isom_get_track_from_file(movie, trackRefGroup);
5694 if (trak_ref != trak) {
5695 if (!trak_ref || !trak_ref->Header->alternate_group) {
5696 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Track %d has not an alternate group - skipping\n", trak_ref ? trak_ref->Header->trackID : 0));
5697 return GF_BAD_PARAM;
5698 }
5699 alternateGroupID = trak_ref->Header->alternate_group;
5700 } else {
5701 alternateGroupID = trak->Header->alternate_group;
5702 }
5703 }
5704 if (!alternateGroupID) {
5705 /*there is a function for this ....*/
5706 if (trak->Header->alternate_group) {
5707 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Track %d has already an alternate group - skipping\n", trak->Header->trackID));
5708 return GF_BAD_PARAM;
5709 }
5710 alternateGroupID = gf_isom_get_next_alternate_group_id(movie);
5711 }
5712
5713 if (is_switch_group) {
5714 u32 i=0;
5715 while (i< gf_isom_get_track_count(movie) ) {
5716 //locate first available ID
5717 GF_TrackBox *a_trak = gf_isom_get_track_from_file(movie, i+1);
5718
5719 if (a_trak->udta) {
5720 u32 j, count;
5721 map = udta_getEntry(a_trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL);
5722 if (map) {
5723 count = gf_list_count(map->boxes);
5724 for (j=0; j<count; j++) {
5725 tsel = (GF_TrackSelectionBox*)gf_list_get(map->boxes, j);
5726
5727 if (*switchGroupID) {
5728 if (tsel->switchGroup==next_switch_group_id) {
5729 if (a_trak->Header->alternate_group != alternateGroupID) return GF_BAD_PARAM;
5730 }
5731 } else {
5732 if (tsel->switchGroup && (tsel->switchGroup>=next_switch_group_id) )
5733 next_switch_group_id = tsel->switchGroup;
5734 }
5735 }
5736 }
5737
5738 }
5739 i++;
5740 }
5741 if (! *switchGroupID) *switchGroupID = next_switch_group_id+1;
5742 }
5743
5744
5745 trak->Header->alternate_group = alternateGroupID;
5746
5747 tsel = NULL;
5748 if (*switchGroupID) {
5749 if (!trak->udta) {
5750 e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA));
5751 if (e) return e;
5752 }
5753
5754 map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL);
5755
5756 /*locate tsel box with no switch group*/
5757 if (map) {
5758 u32 j, count = gf_list_count(map->boxes);
5759 for (j=0; j<count; j++) {
5760 tsel = (GF_TrackSelectionBox*)gf_list_get(map->boxes, j);
5761 if (tsel->switchGroup == *switchGroupID) break;
5762 tsel = NULL;
5763 }
5764 }
5765 if (!tsel) {
5766 tsel = (GF_TrackSelectionBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TSEL);
5767 if (!tsel) return GF_OUT_OF_MEM;
5768 e = udta_on_child_box((GF_Box *)trak->udta, (GF_Box *) tsel);
5769 if (e) return e;
5770 }
5771
5772 tsel->switchGroup = *switchGroupID;
5773 tsel->attributeListCount = criteriaListCount;
5774 if (tsel->attributeList) gf_free(tsel->attributeList);
5775 tsel->attributeList = (u32*)gf_malloc(sizeof(u32)*criteriaListCount);
5776 if (!tsel->attributeList) return GF_OUT_OF_MEM;
5777 memcpy(tsel->attributeList, criteriaList, sizeof(u32)*criteriaListCount);
5778 }
5779 return GF_OK;
5780 }
5781
reset_tsel_box(GF_TrackBox * trak)5782 void reset_tsel_box(GF_TrackBox *trak)
5783 {
5784 GF_UserDataMap *map;
5785 trak->Header->alternate_group = 0;
5786 map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL);
5787 if (map) {
5788 gf_list_del_item(trak->udta->recordList, map);
5789 gf_isom_box_array_del(map->boxes);
5790 gf_free(map);
5791 }
5792
5793 }
5794
5795 GF_EXPORT
gf_isom_reset_track_switch_parameter(GF_ISOFile * movie,u32 trackNumber,Bool reset_all_group)5796 GF_Err gf_isom_reset_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, Bool reset_all_group)
5797 {
5798 GF_TrackBox *trak;
5799 u32 alternateGroupID = 0;
5800
5801 trak = gf_isom_get_track_from_file(movie, trackNumber);
5802 if (!trak) return GF_BAD_PARAM;
5803 if (!trak->Header->alternate_group) return GF_OK;
5804
5805 alternateGroupID = trak->Header->alternate_group;
5806 if (reset_all_group) {
5807 u32 i=0;
5808 while (i< gf_isom_get_track_count(movie) ) {
5809 //locate first available ID
5810 GF_TrackBox *a_trak = gf_isom_get_track_from_file(movie, i+1);
5811 if (a_trak->Header->alternate_group == alternateGroupID) reset_tsel_box(a_trak);
5812 i++;
5813 }
5814 } else {
5815 reset_tsel_box(trak);
5816 }
5817 return GF_OK;
5818 }
5819
5820
5821 GF_EXPORT
gf_isom_reset_switch_parameters(GF_ISOFile * movie)5822 GF_Err gf_isom_reset_switch_parameters(GF_ISOFile *movie)
5823 {
5824 u32 i=0;
5825 while (i< gf_isom_get_track_count(movie) ) {
5826 //locate first available ID
5827 GF_TrackBox *a_trak = gf_isom_get_track_from_file(movie, i+1);
5828 reset_tsel_box(a_trak);
5829 i++;
5830 }
5831 return GF_OK;
5832 }
5833
5834
gf_isom_add_subsample(GF_ISOFile * movie,u32 track,u32 sampleNumber,u32 flags,u32 subSampleSize,u8 priority,u32 reserved,Bool discardable)5835 GF_Err gf_isom_add_subsample(GF_ISOFile *movie, u32 track, u32 sampleNumber, u32 flags, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable)
5836 {
5837 u32 i, count;
5838 GF_SubSampleInformationBox *sub_samples;
5839 GF_TrackBox *trak;
5840 GF_Err e;
5841
5842 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
5843 if (e) return e;
5844
5845 trak = gf_isom_get_track_from_file(movie, track);
5846 if (!trak || !trak->Media || !trak->Media->information->sampleTable)
5847 return GF_BAD_PARAM;
5848
5849 if (!trak->Media->information->sampleTable->sub_samples) {
5850 trak->Media->information->sampleTable->sub_samples=gf_list_new();
5851 }
5852
5853 sub_samples = NULL;
5854 count = gf_list_count(trak->Media->information->sampleTable->sub_samples);
5855 for (i=0; i<count; i++) {
5856 sub_samples = gf_list_get(trak->Media->information->sampleTable->sub_samples, i);
5857 if (sub_samples->flags==flags) break;
5858 sub_samples = NULL;
5859 }
5860 if (!sub_samples) {
5861 sub_samples = (GF_SubSampleInformationBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_SUBS);
5862 if (!sub_samples) return GF_OUT_OF_MEM;
5863 gf_list_add(trak->Media->information->sampleTable->sub_samples, sub_samples);
5864 sub_samples->version = (subSampleSize>0xFFFF) ? 1 : 0;
5865 sub_samples->flags = flags;
5866 }
5867 return gf_isom_add_subsample_info(sub_samples, sampleNumber, subSampleSize, priority, reserved, discardable);
5868 }
5869
5870
5871 GF_EXPORT
gf_isom_set_rvc_config(GF_ISOFile * movie,u32 track,u32 sampleDescriptionIndex,u16 rvc_predefined,char * mime,u8 * data,u32 size)5872 GF_Err gf_isom_set_rvc_config(GF_ISOFile *movie, u32 track, u32 sampleDescriptionIndex, u16 rvc_predefined, char *mime, u8 *data, u32 size)
5873 {
5874 GF_MPEGVisualSampleEntryBox *entry;
5875 GF_Err e;
5876 GF_TrackBox *trak;
5877
5878 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
5879 if (e) return e;
5880
5881 trak = gf_isom_get_track_from_file(movie, track);
5882 if (!trak) return GF_BAD_PARAM;
5883
5884
5885 entry = (GF_MPEGVisualSampleEntryBox *) gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex-1);
5886 if (!entry ) return GF_BAD_PARAM;
5887 if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
5888
5889 GF_RVCConfigurationBox *rvcc = (GF_RVCConfigurationBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_RVCC);
5890 if (rvcc && rvcc->rvc_meta_idx) {
5891 gf_isom_remove_meta_item(movie, GF_FALSE, track, rvcc->rvc_meta_idx);
5892 rvcc->rvc_meta_idx = 0;
5893 }
5894
5895 if (!rvcc) {
5896 rvcc = (GF_RVCConfigurationBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_RVCC);
5897 if (!rvcc) return GF_OUT_OF_MEM;
5898 }
5899 rvcc->predefined_rvc_config = rvc_predefined;
5900 if (!rvc_predefined) {
5901 e = gf_isom_set_meta_type(movie, GF_FALSE, track, GF_META_TYPE_RVCI);
5902 if (e) return e;
5903 gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_ISO2, GF_TRUE);
5904 e = gf_isom_add_meta_item_memory(movie, GF_FALSE, track, "rvcconfig.xml", 0, GF_META_ITEM_TYPE_MIME, mime, NULL, NULL, data, size, NULL);
5905 if (e) return e;
5906 rvcc->rvc_meta_idx = gf_isom_get_meta_item_count(movie, GF_FALSE, track);
5907 }
5908 return GF_OK;
5909 }
5910
5911 /*for now not exported*/
5912 /*expands sampleGroup table for the given grouping type and sample_number. If sample_number is 0, just appends an entry at the end of the table*/
gf_isom_add_sample_group_entry(GF_List * sampleGroups,u32 sample_number,u32 grouping_type,u32 grouping_type_parameter,u32 sampleGroupDescriptionIndex,GF_List * parent)5913 static GF_Err gf_isom_add_sample_group_entry(GF_List *sampleGroups, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, u32 sampleGroupDescriptionIndex, GF_List *parent)
5914 {
5915 GF_SampleGroupBox *sgroup = NULL;
5916 u32 i, count, last_sample_in_entry;
5917
5918 assert(sampleGroups);
5919 count = gf_list_count(sampleGroups);
5920 for (i=0; i<count; i++) {
5921 sgroup = (GF_SampleGroupBox*)gf_list_get(sampleGroups, i);
5922 if (sgroup->grouping_type==grouping_type) break;
5923 sgroup = NULL;
5924 }
5925 if (!sgroup) {
5926 sgroup = (GF_SampleGroupBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SBGP);
5927 if (!sgroup) return GF_OUT_OF_MEM;
5928 sgroup->grouping_type = grouping_type;
5929 sgroup->grouping_type_parameter = grouping_type_parameter;
5930 // gf_list_add(sampleGroups, sgroup);
5931 //crude patch to align old arch and filters
5932 gf_list_insert(sampleGroups, sgroup, 0);
5933 assert(parent);
5934 gf_list_add(parent, sgroup);
5935 }
5936 /*used in fragments, means we are adding the last sample*/
5937 if (!sample_number) {
5938 sample_number = 1;
5939 if (sgroup->entry_count) {
5940 for (i=0; i<sgroup->entry_count; i++) {
5941 sample_number += sgroup->sample_entries[i].sample_count;
5942 }
5943 }
5944 }
5945
5946 if (!sgroup->entry_count) {
5947 u32 idx = 0;
5948 sgroup->entry_count = (sample_number>1) ? 2 : 1;
5949 sgroup->sample_entries = (GF_SampleGroupEntry*)gf_malloc(sizeof(GF_SampleGroupEntry) * sgroup->entry_count );
5950 if (!sgroup->sample_entries) return GF_OUT_OF_MEM;
5951 if (sample_number>1) {
5952 sgroup->sample_entries[0].sample_count = sample_number-1;
5953 sgroup->sample_entries[0].group_description_index = sampleGroupDescriptionIndex ? 0 : 1;
5954 idx = 1;
5955 }
5956 sgroup->sample_entries[idx].sample_count = 1;
5957 sgroup->sample_entries[idx].group_description_index = sampleGroupDescriptionIndex;
5958 return GF_OK;
5959 }
5960 last_sample_in_entry = 0;
5961 for (i=0; i<sgroup->entry_count; i++) {
5962 /*TODO*/
5963 if (last_sample_in_entry + sgroup->sample_entries[i].sample_count > sample_number) return GF_NOT_SUPPORTED;
5964 last_sample_in_entry += sgroup->sample_entries[i].sample_count;
5965 }
5966
5967 if (last_sample_in_entry == sample_number) {
5968 if (sgroup->sample_entries[sgroup->entry_count-1].group_description_index==sampleGroupDescriptionIndex)
5969 return GF_OK;
5970 else
5971 return GF_NOT_SUPPORTED;
5972 }
5973
5974 if ((sgroup->sample_entries[sgroup->entry_count-1].group_description_index==sampleGroupDescriptionIndex) && (last_sample_in_entry+1==sample_number)) {
5975 sgroup->sample_entries[sgroup->entry_count-1].sample_count++;
5976 return GF_OK;
5977 }
5978 /*last entry was an empty desc (no group associated), just add the number of samples missing until new one, then add new one*/
5979 if (! sgroup->sample_entries[sgroup->entry_count-1].group_description_index) {
5980 sgroup->sample_entries[sgroup->entry_count-1].sample_count += sample_number - 1 - last_sample_in_entry;
5981 sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 1) );
5982 sgroup->sample_entries[sgroup->entry_count].sample_count = 1;
5983 sgroup->sample_entries[sgroup->entry_count].group_description_index = sampleGroupDescriptionIndex;
5984 sgroup->entry_count++;
5985 return GF_OK;
5986 }
5987 /*we are adding a sample with no desc, add entry at the end*/
5988 if (!sampleGroupDescriptionIndex || (sample_number - 1 - last_sample_in_entry==0) ) {
5989 sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 1) );
5990 sgroup->sample_entries[sgroup->entry_count].sample_count = 1;
5991 sgroup->sample_entries[sgroup->entry_count].group_description_index = sampleGroupDescriptionIndex;
5992 sgroup->entry_count++;
5993 return GF_OK;
5994 }
5995 /*need to insert two entries ...*/
5996 sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 2) );
5997
5998 sgroup->sample_entries[sgroup->entry_count].sample_count = sample_number - 1 - last_sample_in_entry;
5999 sgroup->sample_entries[sgroup->entry_count].group_description_index = 0;
6000
6001 sgroup->sample_entries[sgroup->entry_count+1].sample_count = 1;
6002 sgroup->sample_entries[sgroup->entry_count+1].group_description_index = sampleGroupDescriptionIndex;
6003 sgroup->entry_count+=2;
6004 return GF_OK;
6005 }
6006
6007 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
get_sgdp(GF_SampleTableBox * stbl,GF_TrackFragmentBox * traf,u32 grouping_type,Bool * is_traf_sgdp)6008 static GF_SampleGroupDescriptionBox *get_sgdp(GF_SampleTableBox *stbl, GF_TrackFragmentBox *traf, u32 grouping_type, Bool *is_traf_sgdp)
6009 #else
6010 static GF_SampleGroupDescriptionBox *get_sgdp(GF_SampleTableBox *stbl, void *traf, u32 grouping_type, Bool *is_traf_sgdp)
6011 #endif /* GPAC_DISABLE_ISOM_FRAGMENTS */
6012 {
6013 GF_List *groupList=NULL;
6014 GF_List **parent=NULL;
6015 GF_SampleGroupDescriptionBox *sgdesc=NULL;
6016 u32 count, i;
6017
6018 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
6019 if (!stbl && traf && traf->trex->track->Media->information->sampleTable)
6020 stbl = traf->trex->track->Media->information->sampleTable;
6021 #endif
6022 if (stbl) {
6023 if (!stbl->sampleGroupsDescription && !traf)
6024 stbl->sampleGroupsDescription = gf_list_new();
6025
6026 groupList = stbl->sampleGroupsDescription;
6027 if (is_traf_sgdp) *is_traf_sgdp = GF_FALSE;
6028 parent = &stbl->child_boxes;
6029
6030 count = gf_list_count(groupList);
6031 for (i=0; i<count; i++) {
6032 sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(groupList, i);
6033 if (sgdesc->grouping_type==grouping_type) break;
6034 sgdesc = NULL;
6035 }
6036 }
6037
6038 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
6039 /*look in stbl or traf for sample sampleGroupsDescription*/
6040 if (!sgdesc && traf) {
6041 if (!traf->sampleGroupsDescription)
6042 traf->sampleGroupsDescription = gf_list_new();
6043 groupList = traf->sampleGroupsDescription;
6044 parent = &traf->child_boxes;
6045 if (is_traf_sgdp) *is_traf_sgdp = GF_TRUE;
6046
6047 count = gf_list_count(groupList);
6048 for (i=0; i<count; i++) {
6049 sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(groupList, i);
6050 if (sgdesc->grouping_type==grouping_type) break;
6051 sgdesc = NULL;
6052 }
6053 }
6054 #endif
6055
6056 if (!sgdesc) {
6057 sgdesc = (GF_SampleGroupDescriptionBox *) gf_isom_box_new_parent(parent, GF_ISOM_BOX_TYPE_SGPD);
6058 if (!sgdesc) return NULL;
6059 sgdesc->grouping_type = grouping_type;
6060 assert(groupList);
6061 gf_list_add(groupList, sgdesc);
6062 }
6063 return sgdesc;
6064 }
6065
6066 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
gf_isom_set_sample_group_info_ex(GF_SampleTableBox * stbl,GF_TrackFragmentBox * traf,u32 sample_number,u32 grouping_type,u32 grouping_type_parameter,void * udta,void * (* sg_create_entry)(void * udta),Bool (* sg_compare_entry)(void * udta,void * entry))6067 static GF_Err gf_isom_set_sample_group_info_ex(GF_SampleTableBox *stbl, GF_TrackFragmentBox *traf, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *udta, void *(*sg_create_entry)(void *udta), Bool (*sg_compare_entry)(void *udta, void *entry))
6068 #else
6069 static GF_Err gf_isom_set_sample_group_info_ex(GF_SampleTableBox *stbl, void *traf, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *udta, void *(*sg_create_entry)(void *udta), Bool (*sg_compare_entry)(void *udta, void *entry))
6070 #endif /* GPAC_DISABLE_ISOM_FRAGMENTS */
6071 {
6072 GF_List *groupList, *parent;
6073 void *entry;
6074 Bool is_traf_sgpd;
6075 GF_SampleGroupDescriptionBox *sgdesc = NULL;
6076 u32 i, entry_idx;
6077
6078 if (!stbl && !traf) return GF_BAD_PARAM;
6079
6080 sgdesc = get_sgdp(stbl, traf, grouping_type, &is_traf_sgpd);
6081 if (!sgdesc) return GF_OUT_OF_MEM;
6082
6083 entry = NULL;
6084 if (sg_compare_entry) {
6085 for (i=0; i<gf_list_count(sgdesc->group_descriptions); i++) {
6086 entry = gf_list_get(sgdesc->group_descriptions, i);
6087 if (sg_compare_entry(udta, entry)) break;
6088 entry = NULL;
6089 }
6090 }
6091 if (!entry && sg_create_entry) {
6092 entry = sg_create_entry(udta);
6093 if (!entry) return GF_IO_ERR;
6094 if (traf && !is_traf_sgpd) {
6095 sgdesc = get_sgdp(NULL, traf, grouping_type, &is_traf_sgpd);
6096 }
6097 gf_list_add(sgdesc->group_descriptions, entry);
6098 }
6099 if (!entry)
6100 entry_idx = 0;
6101 else
6102 entry_idx = 1 + gf_list_find(sgdesc->group_descriptions, entry);
6103
6104 /*look in stbl or traf for sample sampleGroups*/
6105 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
6106 if (traf) {
6107 if (!traf->sampleGroups)
6108 traf->sampleGroups = gf_list_new();
6109 groupList = traf->sampleGroups;
6110 parent = traf->child_boxes;
6111 if (entry_idx && is_traf_sgpd)
6112 entry_idx |= 0x10000;
6113 } else
6114 #endif
6115 {
6116 if (!stbl->sampleGroups)
6117 stbl->sampleGroups = gf_list_new();
6118 groupList = stbl->sampleGroups;
6119 parent = stbl->child_boxes;
6120 }
6121
6122 return gf_isom_add_sample_group_entry(groupList, sample_number, grouping_type, grouping_type_parameter, entry_idx, parent);
6123 }
6124
6125 /*for now not exported*/
gf_isom_set_sample_group_info(GF_ISOFile * movie,u32 track,u32 trafID,u32 sample_number,u32 grouping_type,u32 grouping_type_parameter,void * udta,void * (* sg_create_entry)(void * udta),Bool (* sg_compare_entry)(void * udta,void * entry))6126 static GF_Err gf_isom_set_sample_group_info(GF_ISOFile *movie, u32 track, u32 trafID, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *udta, void *(*sg_create_entry)(void *udta), Bool (*sg_compare_entry)(void *udta, void *entry))
6127 {
6128 GF_Err e;
6129 GF_TrackBox *trak=NULL;
6130 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
6131 GF_TrackFragmentBox *traf=NULL;
6132 #endif
6133 if (!trafID && (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) {
6134 trak = gf_isom_get_track_from_file(movie, track);
6135 if (!trak) return GF_BAD_PARAM;
6136 trafID = trak->Header->trackID;
6137 }
6138
6139 if (trafID) {
6140 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
6141 if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) )
6142 return GF_BAD_PARAM;
6143
6144 traf = gf_isom_get_traf(movie, trafID);
6145 #else
6146 return GF_NOT_SUPPORTED;
6147 #endif
6148
6149 } else if (track) {
6150 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
6151 if (e) return e;
6152
6153 trak = gf_isom_get_track_from_file(movie, track);
6154 if (!trak) return GF_BAD_PARAM;
6155 }
6156
6157 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
6158 return gf_isom_set_sample_group_info_ex(trak ? trak->Media->information->sampleTable : NULL, traf, sample_number, grouping_type, grouping_type_parameter, udta, sg_create_entry, sg_compare_entry);
6159 #else
6160 return gf_isom_set_sample_group_info_ex(trak->Media->information->sampleTable, sample_number, grouping_type, grouping_type_parameter, udta, sg_create_entry, sg_compare_entry);
6161 #endif
6162
6163 }
6164
6165
6166 GF_EXPORT
gf_isom_add_sample_group_info(GF_ISOFile * movie,u32 track,u32 grouping_type,void * data,u32 data_size,Bool is_default,u32 * sampleGroupDescriptionIndex)6167 GF_Err gf_isom_add_sample_group_info(GF_ISOFile *movie, u32 track, u32 grouping_type, void *data, u32 data_size, Bool is_default, u32 *sampleGroupDescriptionIndex)
6168 {
6169 GF_Err e;
6170 GF_TrackBox *trak;
6171 GF_DefaultSampleGroupDescriptionEntry *entry=NULL;
6172 GF_SampleGroupDescriptionBox *sgdesc = NULL;
6173
6174 if (sampleGroupDescriptionIndex) *sampleGroupDescriptionIndex = 0;
6175 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
6176 if (e) return e;
6177
6178 trak = gf_isom_get_track_from_file(movie, track);
6179 if (!trak) return GF_BAD_PARAM;
6180
6181
6182 sgdesc = get_sgdp(trak->Media->information->sampleTable, NULL, grouping_type, NULL);
6183 if (!sgdesc) return GF_OUT_OF_MEM;
6184
6185 if (grouping_type==GF_ISOM_SAMPLE_GROUP_OINF) {
6186 GF_OperatingPointsInformation *ptr = gf_isom_oinf_new_entry();
6187 GF_BitStream *bs=gf_bs_new(data, data_size, GF_BITSTREAM_READ);
6188 e = gf_isom_oinf_read_entry(ptr, bs);
6189 gf_bs_del(bs);
6190 if (e) {
6191 gf_isom_oinf_del_entry(ptr);
6192 return e;
6193 }
6194 e = gf_list_add(sgdesc->group_descriptions, ptr);
6195 if (e) return e;
6196 entry = (GF_DefaultSampleGroupDescriptionEntry *) ptr;
6197 } else if (grouping_type==GF_ISOM_SAMPLE_GROUP_LINF) {
6198 GF_LHVCLayerInformation *ptr = gf_isom_linf_new_entry();
6199 GF_BitStream *bs=gf_bs_new(data, data_size, GF_BITSTREAM_READ);
6200 e = gf_isom_linf_read_entry(ptr, bs);
6201 gf_bs_del(bs);
6202 if (e) {
6203 gf_isom_linf_del_entry(ptr);
6204 return e;
6205 }
6206 e = gf_list_add(sgdesc->group_descriptions, ptr);
6207 if (e) return e;
6208 entry = (GF_DefaultSampleGroupDescriptionEntry *) ptr;
6209 } else {
6210 u32 i, count=gf_list_count(sgdesc->group_descriptions);
6211 for (i=0; i<count; i++) {
6212 GF_DefaultSampleGroupDescriptionEntry *ent = gf_list_get(sgdesc->group_descriptions, i);
6213 if ((ent->length == data_size) && !memcmp(ent->data, data, data_size)) {
6214 entry = ent;
6215 break;
6216 }
6217 entry=NULL;
6218 }
6219 if (!entry) {
6220 GF_SAFEALLOC(entry, GF_DefaultSampleGroupDescriptionEntry);
6221 if (!entry) return GF_OUT_OF_MEM;
6222 entry->data = (u8*)gf_malloc(sizeof(char) * data_size);
6223 if (!entry->data) {
6224 gf_free(entry);
6225 return GF_OUT_OF_MEM;
6226 }
6227 entry->length = data_size;
6228 memcpy(entry->data, data, sizeof(char) * data_size);
6229 e = gf_list_add(sgdesc->group_descriptions, entry);
6230 if (e) {
6231 gf_free(entry->data);
6232 gf_free(entry);
6233 return e;
6234 }
6235 }
6236 }
6237
6238
6239 if (is_default) {
6240 sgdesc->default_description_index = 1 + gf_list_find(sgdesc->group_descriptions, entry);
6241 sgdesc->version = 2;
6242 }
6243 if (sampleGroupDescriptionIndex) *sampleGroupDescriptionIndex = 1 + gf_list_find(sgdesc->group_descriptions, entry);
6244
6245 return GF_OK;
6246 }
6247
6248 GF_EXPORT
gf_isom_remove_sample_group(GF_ISOFile * movie,u32 track,u32 grouping_type)6249 GF_Err gf_isom_remove_sample_group(GF_ISOFile *movie, u32 track, u32 grouping_type)
6250 {
6251 GF_Err e;
6252 GF_TrackBox *trak;
6253 u32 count, i;
6254
6255 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
6256 if (e) return e;
6257
6258 trak = gf_isom_get_track_from_file(movie, track);
6259 if (!trak) return GF_BAD_PARAM;
6260
6261 if (trak->Media->information->sampleTable->sampleGroupsDescription) {
6262 count = gf_list_count(trak->Media->information->sampleTable->sampleGroupsDescription);
6263 for (i=0; i<count; i++) {
6264 GF_SampleGroupDescriptionBox *sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(trak->Media->information->sampleTable->sampleGroupsDescription, i);
6265 if (sgdesc->grouping_type==grouping_type) {
6266 gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box*)sgdesc);
6267 gf_list_rem(trak->Media->information->sampleTable->sampleGroupsDescription, i);
6268 i--;
6269 count--;
6270 }
6271 }
6272 }
6273 if (trak->Media->information->sampleTable->sampleGroups) {
6274 count = gf_list_count(trak->Media->information->sampleTable->sampleGroups);
6275 for (i=0; i<count; i++) {
6276 GF_SampleGroupBox *sgroup = gf_list_get(trak->Media->information->sampleTable->sampleGroups, i);
6277 if (sgroup->grouping_type==grouping_type) {
6278 gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box*)sgroup);
6279 gf_list_rem(trak->Media->information->sampleTable->sampleGroups, i);
6280 i--;
6281 count--;
6282 }
6283 }
6284 }
6285 return GF_OK;
6286 }
6287
6288 GF_EXPORT
gf_isom_add_sample_info(GF_ISOFile * movie,u32 track,u32 sample_number,u32 grouping_type,u32 sampleGroupDescriptionIndex,u32 grouping_type_parameter)6289 GF_Err gf_isom_add_sample_info(GF_ISOFile *movie, u32 track, u32 sample_number, u32 grouping_type, u32 sampleGroupDescriptionIndex, u32 grouping_type_parameter)
6290 {
6291 GF_Err e;
6292 GF_TrackBox *trak;
6293 GF_List *groupList;
6294 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
6295 if (e) return e;
6296
6297 trak = gf_isom_get_track_from_file(movie, track);
6298 if (!trak) return GF_BAD_PARAM;
6299
6300 if (!trak->Media->information->sampleTable->sampleGroups)
6301 trak->Media->information->sampleTable->sampleGroups = gf_list_new();
6302
6303 groupList = trak->Media->information->sampleTable->sampleGroups;
6304 return gf_isom_add_sample_group_entry(groupList, sample_number, grouping_type, grouping_type_parameter, sampleGroupDescriptionIndex, trak->Media->information->sampleTable->child_boxes);
6305 }
6306
sg_rap_create_entry(void * udta)6307 void *sg_rap_create_entry(void *udta)
6308 {
6309 GF_VisualRandomAccessEntry *entry;
6310 u32 *num_leading_samples = (u32 *) udta;
6311 assert(udta);
6312 GF_SAFEALLOC(entry, GF_VisualRandomAccessEntry);
6313 if (!entry) return NULL;
6314 entry->num_leading_samples = *num_leading_samples;
6315 entry->num_leading_samples_known = entry->num_leading_samples ? 1 : 0;
6316 return entry;
6317 }
6318
sg_rap_compare_entry(void * udta,void * entry)6319 Bool sg_rap_compare_entry(void *udta, void *entry)
6320 {
6321 u32 *num_leading_samples = (u32 *) udta;
6322 if (*num_leading_samples == ((GF_VisualRandomAccessEntry *)entry)->num_leading_samples) return GF_TRUE;
6323 return GF_FALSE;
6324 }
6325
6326 GF_EXPORT
gf_isom_set_sample_rap_group(GF_ISOFile * movie,u32 track,u32 sample_number,Bool is_rap,u32 num_leading_samples)6327 GF_Err gf_isom_set_sample_rap_group(GF_ISOFile *movie, u32 track, u32 sample_number, Bool is_rap, u32 num_leading_samples)
6328 {
6329 return gf_isom_set_sample_group_info(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_RAP, 0, &num_leading_samples, is_rap ? sg_rap_create_entry : NULL, is_rap ? sg_rap_compare_entry : NULL);
6330 }
6331
gf_isom_fragment_set_sample_rap_group(GF_ISOFile * movie,GF_ISOTrackID trackID,u32 sample_number_in_frag,Bool is_rap,u32 num_leading_samples)6332 GF_Err gf_isom_fragment_set_sample_rap_group(GF_ISOFile *movie, GF_ISOTrackID trackID, u32 sample_number_in_frag, Bool is_rap, u32 num_leading_samples)
6333 {
6334 return gf_isom_set_sample_group_info(movie, 0, trackID, sample_number_in_frag, GF_ISOM_SAMPLE_GROUP_RAP, 0, &num_leading_samples, is_rap ? sg_rap_create_entry : NULL, is_rap ? sg_rap_compare_entry : NULL);
6335 }
6336
6337
6338
sg_roll_create_entry(void * udta)6339 void *sg_roll_create_entry(void *udta)
6340 {
6341 GF_RollRecoveryEntry *entry;
6342 s16 *roll_distance = (s16 *) udta;
6343 GF_SAFEALLOC(entry, GF_RollRecoveryEntry);
6344 if (!entry) return NULL;
6345 entry->roll_distance = *roll_distance;
6346 return entry;
6347 }
6348
sg_roll_compare_entry(void * udta,void * entry)6349 Bool sg_roll_compare_entry(void *udta, void *entry)
6350 {
6351 s16 *roll_distance = (s16 *) udta;
6352 if (*roll_distance == ((GF_RollRecoveryEntry *)entry)->roll_distance) return GF_TRUE;
6353 return GF_FALSE;
6354 }
6355
6356 GF_EXPORT
gf_isom_set_sample_roll_group(GF_ISOFile * movie,u32 track,u32 sample_number,Bool is_roll,s16 roll_distance)6357 GF_Err gf_isom_set_sample_roll_group(GF_ISOFile *movie, u32 track, u32 sample_number, Bool is_roll, s16 roll_distance)
6358 {
6359 return gf_isom_set_sample_group_info(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_ROLL, 0, &roll_distance, is_roll ? sg_roll_create_entry : NULL, is_roll ? sg_roll_compare_entry : NULL);
6360 }
6361
6362 GF_EXPORT
gf_isom_fragment_set_sample_roll_group(GF_ISOFile * movie,GF_ISOTrackID trackID,u32 sample_number_in_frag,Bool is_roll,s16 roll_distance)6363 GF_Err gf_isom_fragment_set_sample_roll_group(GF_ISOFile *movie, GF_ISOTrackID trackID, u32 sample_number_in_frag, Bool is_roll, s16 roll_distance)
6364 {
6365 return gf_isom_set_sample_group_info(movie, 0, trackID, sample_number_in_frag, GF_ISOM_SAMPLE_GROUP_ROLL, 0, &roll_distance, is_roll ? sg_roll_create_entry : NULL, is_roll ? sg_roll_compare_entry : NULL);
6366 }
6367
6368
sg_encryption_create_entry(void * udta)6369 void *sg_encryption_create_entry(void *udta)
6370 {
6371 GF_CENCSampleEncryptionGroupEntry *entry, *from_entry;
6372 GF_SAFEALLOC(entry, GF_CENCSampleEncryptionGroupEntry);
6373 if (!entry) return NULL;
6374 from_entry = (GF_CENCSampleEncryptionGroupEntry *)udta;
6375 memcpy(entry, from_entry, sizeof(GF_CENCSampleEncryptionGroupEntry) );
6376 return entry;
6377 }
6378
sg_encryption_compare_entry(void * udta,void * _entry)6379 Bool sg_encryption_compare_entry(void *udta, void *_entry)
6380 {
6381 GF_CENCSampleEncryptionGroupEntry *entry = (GF_CENCSampleEncryptionGroupEntry *)_entry;
6382 GF_CENCSampleEncryptionGroupEntry *with_entry = (GF_CENCSampleEncryptionGroupEntry *)udta;
6383 if (!memcmp(entry, with_entry, sizeof(GF_CENCSampleEncryptionGroupEntry)))
6384 return GF_TRUE;
6385
6386 return GF_FALSE;
6387 }
6388
6389
6390 /*sample encryption information group can be in stbl or traf*/
6391 GF_EXPORT
gf_isom_set_sample_cenc_group(GF_ISOFile * movie,u32 track,u32 sample_number,u8 isEncrypted,u8 IV_size,bin128 KeyID,u8 crypt_byte_block,u8 skip_byte_block,u8 constant_IV_size,bin128 constant_IV)6392 GF_Err gf_isom_set_sample_cenc_group(GF_ISOFile *movie, u32 track, u32 sample_number, u8 isEncrypted, u8 IV_size, bin128 KeyID, u8 crypt_byte_block, u8 skip_byte_block, u8 constant_IV_size, bin128 constant_IV)
6393 {
6394 GF_CENCSampleEncryptionGroupEntry entry;
6395 if ((IV_size!=0) && (IV_size!=8) && (IV_size!=16)) return GF_BAD_PARAM;
6396
6397 memset(&entry, 0, sizeof(GF_CENCSampleEncryptionGroupEntry));
6398 entry.crypt_byte_block = crypt_byte_block;
6399 entry.skip_byte_block = skip_byte_block;
6400 entry.IsProtected = isEncrypted;
6401 entry.Per_Sample_IV_size = IV_size;
6402 if (!IV_size && isEncrypted) {
6403 entry.constant_IV_size = constant_IV_size;
6404 memcpy(entry.constant_IV, constant_IV, constant_IV_size);
6405 }
6406 memcpy(entry.KID, KeyID, 16);
6407 return gf_isom_set_sample_group_info(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_SEIG, 0, &entry, sg_encryption_create_entry, sg_encryption_compare_entry);
6408 }
6409
6410 GF_EXPORT
gf_isom_set_sample_cenc_default_group(GF_ISOFile * movie,u32 track,u32 sample_number)6411 GF_Err gf_isom_set_sample_cenc_default_group(GF_ISOFile *movie, u32 track, u32 sample_number)
6412 {
6413 return gf_isom_set_sample_group_info(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_SEIG, 0, NULL, NULL, NULL);
6414 }
6415
gf_isom_set_ctts_v1(GF_ISOFile * file,u32 track,u32 ctts_shift)6416 GF_Err gf_isom_set_ctts_v1(GF_ISOFile *file, u32 track, u32 ctts_shift)
6417 {
6418 u32 i, shift;
6419 u64 duration;
6420 GF_CompositionOffsetBox *ctts;
6421 GF_CompositionToDecodeBox *cslg;
6422 s32 leastCTTS, greatestCTTS;
6423 GF_TrackBox *trak;
6424 GF_Err e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
6425 if (e) return e;
6426
6427 trak = gf_isom_get_track_from_file(file, track);
6428 if (!trak) return GF_BAD_PARAM;
6429
6430 ctts = trak->Media->information->sampleTable->CompositionOffset;
6431 shift = ctts->version ? ctts_shift : ctts->entries[0].decodingOffset;
6432 leastCTTS = GF_INT_MAX;
6433 greatestCTTS = 0;
6434 for (i=0; i<ctts->nb_entries; i++) {
6435 if (!ctts->version)
6436 ctts->entries[i].decodingOffset -= shift;
6437
6438 if ((s32)ctts->entries[i].decodingOffset < leastCTTS)
6439 leastCTTS = ctts->entries[i].decodingOffset;
6440 if ((s32)ctts->entries[i].decodingOffset > greatestCTTS)
6441 greatestCTTS = ctts->entries[i].decodingOffset;
6442 }
6443 if (!ctts->version) {
6444 ctts->version = 1;
6445 gf_isom_remove_edits(file, track);
6446 }
6447
6448 if (!trak->Media->information->sampleTable->CompositionToDecode) {
6449 trak->Media->information->sampleTable->CompositionToDecode = (GF_CompositionToDecodeBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_CSLG);
6450 if (!trak->Media->information->sampleTable->CompositionToDecode)
6451 return GF_OUT_OF_MEM;
6452 }
6453
6454 cslg = trak->Media->information->sampleTable->CompositionToDecode;
6455 if (cslg) {
6456 cslg->compositionToDTSShift = shift;
6457 cslg->leastDecodeToDisplayDelta = leastCTTS;
6458 cslg->greatestDecodeToDisplayDelta = greatestCTTS;
6459 cslg->compositionStartTime = 0;
6460 /*for our use case (first CTS set to 0), the composition end time is the media duration if it fits on 32 bits*/
6461 duration = gf_isom_get_media_duration(file, track);
6462 cslg->compositionEndTime = (duration<0x7FFFFFFF) ? (s32) duration : 0;
6463 }
6464
6465 gf_isom_modify_alternate_brand(file, GF_ISOM_BRAND_ISO4, GF_TRUE);
6466 return GF_OK;
6467 }
6468
gf_isom_set_ctts_v0(GF_ISOFile * file,GF_TrackBox * trak)6469 static GF_Err gf_isom_set_ctts_v0(GF_ISOFile *file, GF_TrackBox *trak)
6470 {
6471 u32 i;
6472 s32 shift;
6473 GF_CompositionOffsetBox *ctts;
6474 GF_CompositionToDecodeBox *cslg;
6475
6476 ctts = trak->Media->information->sampleTable->CompositionOffset;
6477
6478 if (!trak->Media->information->sampleTable->CompositionToDecode)
6479 {
6480 shift = 0;
6481 for (i=0; i<ctts->nb_entries; i++) {
6482 if (-ctts->entries[i].decodingOffset > shift)
6483 shift = -ctts->entries[i].decodingOffset;
6484 }
6485 if (shift > 0)
6486 {
6487 for (i=0; i<ctts->nb_entries; i++) {
6488 ctts->entries[i].decodingOffset += shift;
6489 }
6490 }
6491 }
6492 else
6493 {
6494 cslg = trak->Media->information->sampleTable->CompositionToDecode;
6495 shift = cslg->compositionToDTSShift;
6496 for (i=0; i<ctts->nb_entries; i++) {
6497 ctts->entries[i].decodingOffset += shift;
6498 }
6499 gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box *)cslg);
6500 trak->Media->information->sampleTable->CompositionToDecode = NULL;
6501 }
6502 if (! trak->editBox && shift>0) {
6503 u64 dur = trak->Media->mediaHeader->duration;
6504 dur *= file->moov->mvhd->timeScale;
6505 dur /= trak->Media->mediaHeader->timeScale;
6506 gf_isom_set_edit(file, gf_list_find(file->moov->trackList, trak)+1, 0, dur, shift, GF_ISOM_EDIT_NORMAL);
6507 }
6508 ctts->version = 0;
6509 gf_isom_modify_alternate_brand(file, GF_ISOM_BRAND_ISO4, GF_FALSE);
6510 return GF_OK;
6511 }
6512
6513 GF_EXPORT
gf_isom_set_composition_offset_mode(GF_ISOFile * file,u32 track,Bool use_negative_offsets)6514 GF_Err gf_isom_set_composition_offset_mode(GF_ISOFile *file, u32 track, Bool use_negative_offsets)
6515 {
6516 GF_Err e;
6517 GF_TrackBox *trak;
6518 GF_CompositionOffsetBox *ctts;
6519
6520 e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
6521 if (e) return e;
6522
6523 trak = gf_isom_get_track_from_file(file, track);
6524 if (!trak) return GF_BAD_PARAM;
6525
6526 ctts = trak->Media->information->sampleTable->CompositionOffset;
6527 if (!ctts) {
6528 if (!use_negative_offsets && trak->Media->information->sampleTable->CompositionToDecode) {
6529 gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box *)trak->Media->information->sampleTable->CompositionToDecode);
6530 trak->Media->information->sampleTable->CompositionToDecode = NULL;
6531 }
6532 return GF_OK;
6533 }
6534
6535 if (use_negative_offsets) {
6536 return gf_isom_set_ctts_v1(file, track, 0);
6537 } else {
6538 if (ctts->version==0) return GF_OK;
6539 return gf_isom_set_ctts_v0(file, trak);
6540 }
6541 }
6542
6543 #if 0 //unused
6544 GF_Err gf_isom_set_sync_table(GF_ISOFile *file, u32 track)
6545 {
6546 GF_Err e;
6547 GF_TrackBox *trak;
6548
6549 e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
6550 if (e) return e;
6551
6552 trak = gf_isom_get_track_from_file(file, track);
6553 if (!trak) return GF_BAD_PARAM;
6554
6555 if (!trak->Media->information->sampleTable->SyncSample) {
6556 trak->Media->information->sampleTable->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STSS);
6557
6558 if (!trak->Media->information->sampleTable->SyncSample)
6559 return GF_OUT_OF_MEM;
6560 }
6561 return GF_OK;
6562 }
6563 #endif
6564
gf_isom_set_sample_flags(GF_ISOFile * file,u32 track,u32 sampleNumber,u32 isLeading,u32 dependsOn,u32 dependedOn,u32 redundant)6565 GF_Err gf_isom_set_sample_flags(GF_ISOFile *file, u32 track, u32 sampleNumber, u32 isLeading, u32 dependsOn, u32 dependedOn, u32 redundant)
6566 {
6567 GF_Err e;
6568 GF_TrackBox *trak;
6569
6570 e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
6571 if (e) return e;
6572
6573 trak = gf_isom_get_track_from_file(file, track);
6574 if (!trak) return GF_BAD_PARAM;
6575 return stbl_SetDependencyType(trak->Media->information->sampleTable, sampleNumber, isLeading, dependsOn, dependedOn, redundant);
6576 }
6577
6578 #if 0 //unused
6579 GF_Err gf_isom_sample_set_dep_info(GF_ISOFile *file, u32 track, u32 sampleNumber, u32 isLeading, u32 dependsOn, u32 dependedOn, u32 redundant)
6580 {
6581 GF_TrackBox *trak;
6582
6583 trak = gf_isom_get_track_from_file(file, track);
6584 if (!trak) return GF_BAD_PARAM;
6585
6586 return stbl_AddDependencyType(trak->Media->information->sampleTable, sampleNumber, isLeading, dependsOn, dependedOn, redundant);
6587 }
6588 #endif
6589
6590 GF_EXPORT
gf_isom_copy_sample_info(GF_ISOFile * dst,u32 dst_track,GF_ISOFile * src,u32 src_track,u32 sampleNumber)6591 GF_Err gf_isom_copy_sample_info(GF_ISOFile *dst, u32 dst_track, GF_ISOFile *src, u32 src_track, u32 sampleNumber)
6592 {
6593 u32 i, count, idx, dst_sample_num, subs_flags;
6594 GF_SubSampleInfoEntry *sub_sample;
6595 GF_Err e;
6596 GF_TrackBox *src_trak, *dst_trak;
6597
6598 src_trak = gf_isom_get_track_from_file(src, src_track);
6599 if (!src_trak) return GF_BAD_PARAM;
6600
6601 dst_trak = gf_isom_get_track_from_file(dst, dst_track);
6602 if (!dst_trak) return GF_BAD_PARAM;
6603
6604 dst_sample_num = dst_trak->Media->information->sampleTable->SampleSize->sampleCount;
6605
6606 /*modify depends flags*/
6607 if (src_trak->Media->information->sampleTable->SampleDep) {
6608 u32 isLeading, dependsOn, dependedOn, redundant;
6609
6610 isLeading = dependsOn = dependedOn = redundant = 0;
6611
6612 e = stbl_GetSampleDepType(src_trak->Media->information->sampleTable->SampleDep, sampleNumber, &isLeading, &dependsOn, &dependedOn, &redundant);
6613 if (e) return e;
6614
6615 e = stbl_AppendDependencyType(dst_trak->Media->information->sampleTable, isLeading, dependsOn, dependedOn, redundant);
6616 if (e) return e;
6617 }
6618
6619 /*copy subsample info if any*/
6620 idx=1;
6621 while (gf_isom_get_subsample_types(src, src_track, idx, &subs_flags)) {
6622 GF_SubSampleInformationBox *dst_subs=NULL;
6623 idx++;
6624
6625 if ( ! gf_isom_sample_get_subsample_entry(src, src_track, sampleNumber, subs_flags, &sub_sample))
6626 continue;
6627
6628 /*create subsample if needed*/
6629 if (!dst_trak->Media->information->sampleTable->sub_samples) {
6630 dst_trak->Media->information->sampleTable->sub_samples = gf_list_new();
6631 }
6632 count = gf_list_count(dst_trak->Media->information->sampleTable->sub_samples);
6633 for (i=0; i<count; i++) {
6634 dst_subs = gf_list_get(dst_trak->Media->information->sampleTable->sub_samples, i);
6635 if (dst_subs->flags==subs_flags) break;
6636 dst_subs=NULL;
6637 }
6638 if (!dst_subs) {
6639 dst_subs = (GF_SubSampleInformationBox *) gf_isom_box_new_parent(&dst_trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_SUBS);
6640 if (!dst_subs) return GF_OUT_OF_MEM;
6641 dst_subs->version=0;
6642 dst_subs->flags = subs_flags;
6643 gf_list_add(dst_trak->Media->information->sampleTable->sub_samples, dst_subs);
6644 }
6645
6646 count = gf_list_count(sub_sample->SubSamples);
6647 for (i=0; i<count; i++) {
6648 GF_SubSampleEntry *entry = (GF_SubSampleEntry*)gf_list_get(sub_sample->SubSamples, i);
6649 e = gf_isom_add_subsample_info(dst_subs, dst_sample_num, entry->subsample_size, entry->subsample_priority, entry->reserved, entry->discardable);
6650 if (e) return e;
6651 }
6652 }
6653
6654 /*copy sampleToGroup info if any*/
6655 if (src_trak->Media->information->sampleTable->sampleGroups) {
6656 count = gf_list_count(src_trak->Media->information->sampleTable->sampleGroups);
6657 for (i=0; i<count; i++) {
6658 GF_SampleGroupBox *sg;
6659 u32 j, k, default_index;
6660 u32 first_sample_in_entry, last_sample_in_entry, group_desc_index_src, group_desc_index_dst;
6661 first_sample_in_entry = 1;
6662
6663 sg = (GF_SampleGroupBox*)gf_list_get(src_trak->Media->information->sampleTable->sampleGroups, i);
6664 for (j=0; j<sg->entry_count; j++) {
6665 last_sample_in_entry = first_sample_in_entry + sg->sample_entries[j].sample_count - 1;
6666 if ((sampleNumber<first_sample_in_entry) || (sampleNumber>last_sample_in_entry)) {
6667 first_sample_in_entry = last_sample_in_entry+1;
6668 continue;
6669 }
6670
6671 if (!dst_trak->Media->information->sampleTable->sampleGroups)
6672 dst_trak->Media->information->sampleTable->sampleGroups = gf_list_new();
6673
6674 group_desc_index_src = group_desc_index_dst = sg->sample_entries[j].group_description_index;
6675
6676 if (group_desc_index_src) {
6677 GF_SampleGroupDescriptionBox *sgd_src, *sgd_dst;
6678 GF_DefaultSampleGroupDescriptionEntry *sgde_src, *sgde_dst;
6679
6680 group_desc_index_dst = 0;
6681 //check that the sample group description exists !!
6682 sgde_src = gf_isom_get_sample_group_info_entry(src, src_trak, sg->grouping_type, sg->sample_entries[j].group_description_index, &default_index, &sgd_src);
6683
6684 if (!sgde_src) break;
6685
6686 if (!dst_trak->Media->information->sampleTable->sampleGroupsDescription)
6687 dst_trak->Media->information->sampleTable->sampleGroupsDescription = gf_list_new();
6688
6689 sgd_dst = NULL;
6690 for (k=0; k< gf_list_count(dst_trak->Media->information->sampleTable->sampleGroupsDescription); k++) {
6691 sgd_dst = gf_list_get(dst_trak->Media->information->sampleTable->sampleGroupsDescription, k);
6692 if (sgd_dst->grouping_type==sgd_src->grouping_type) break;
6693 sgd_dst = NULL;
6694 }
6695 if (!sgd_dst) {
6696 gf_isom_clone_box( (GF_Box *) sgd_src, (GF_Box **) &sgd_dst);
6697 if (!sgd_dst) return GF_OUT_OF_MEM;
6698 gf_list_add(dst_trak->Media->information->sampleTable->sampleGroupsDescription, sgd_dst);
6699 }
6700
6701 //find the same entry
6702 for (k=0; k<gf_list_count(sgd_dst->group_descriptions); k++) {
6703 sgde_dst = gf_list_get(sgd_dst->group_descriptions, i);
6704 if (gf_isom_is_identical_sgpd(sgde_src, sgde_dst, sgd_src->grouping_type)) {
6705 group_desc_index_dst = k+1;
6706 break;
6707 }
6708 }
6709 if (!group_desc_index_dst) {
6710 GF_SampleGroupDescriptionBox *cloned=NULL;
6711 gf_isom_clone_box( (GF_Box *) sgd_src, (GF_Box **) &cloned);
6712 if (!cloned) return GF_OUT_OF_MEM;
6713 sgde_dst = gf_list_get(cloned->group_descriptions, group_desc_index_dst);
6714 gf_list_rem(cloned->group_descriptions, group_desc_index_dst);
6715 gf_isom_box_del( (GF_Box *) cloned);
6716 gf_list_add(sgd_dst->group_descriptions, sgde_dst);
6717 group_desc_index_dst = gf_list_count(sgd_dst->group_descriptions);
6718 }
6719 }
6720
6721
6722 /*found our sample, add it to trak->sampleGroups*/
6723 e = gf_isom_add_sample_group_entry(dst_trak->Media->information->sampleTable->sampleGroups, dst_sample_num, sg->grouping_type, sg->grouping_type_parameter, group_desc_index_dst, dst_trak->Media->information->sampleTable->child_boxes);
6724 if (e) return e;
6725 break;
6726 }
6727 }
6728 }
6729 return GF_OK;
6730 }
6731
6732 GF_EXPORT
gf_isom_text_set_display_flags(GF_ISOFile * file,u32 track,u32 desc_index,u32 flags,GF_TextFlagsMode op_type)6733 GF_Err gf_isom_text_set_display_flags(GF_ISOFile *file, u32 track, u32 desc_index, u32 flags, GF_TextFlagsMode op_type)
6734 {
6735 u32 i;
6736 GF_Err e;
6737 GF_TrackBox *trak;
6738
6739 e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
6740 if (e) return e;
6741
6742 trak = gf_isom_get_track_from_file(file, track);
6743 if (!trak) return GF_BAD_PARAM;
6744
6745 for (i=0; i < gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes); i++) {
6746 GF_Tx3gSampleEntryBox *txt;
6747 if (desc_index && (i+1 != desc_index)) continue;
6748
6749 txt = (GF_Tx3gSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, i);
6750 if (txt->type != GF_ISOM_BOX_TYPE_TX3G) continue;
6751
6752 switch (op_type) {
6753 case GF_ISOM_TEXT_FLAGS_TOGGLE:
6754 txt->displayFlags |= flags;
6755 break;
6756 case GF_ISOM_TEXT_FLAGS_UNTOGGLE:
6757 txt->displayFlags &= ~flags;
6758 break;
6759 default:
6760 txt->displayFlags = flags;
6761 break;
6762 }
6763 }
6764 return GF_OK;
6765
6766 }
6767
6768
6769 GF_EXPORT
gf_isom_update_duration(GF_ISOFile * movie)6770 GF_Err gf_isom_update_duration(GF_ISOFile *movie)
6771 {
6772 u32 i;
6773 u64 maxDur;
6774 GF_TrackBox *trak;
6775
6776 if (!movie || !movie->moov) return GF_BAD_PARAM;
6777
6778 //if file was open in Write or Edit mode, recompute the duration
6779 //the duration of a movie is the MaxDuration of all the tracks...
6780
6781 maxDur = 0;
6782 i=0;
6783 while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) {
6784 if( (movie->LastError = SetTrackDuration(trak)) ) return movie->LastError;
6785 if (trak->Header->duration > maxDur)
6786 maxDur = trak->Header->duration;
6787 }
6788 movie->moov->mvhd->duration = maxDur;
6789
6790 return GF_OK;
6791 }
6792
6793 GF_EXPORT
gf_isom_update_edit_list_duration(GF_ISOFile * file,u32 track)6794 GF_Err gf_isom_update_edit_list_duration(GF_ISOFile *file, u32 track)
6795 {
6796 u32 i;
6797 u64 trackDuration;
6798 GF_EdtsEntry *ent;
6799 GF_Err e;
6800 GF_TrackBox *trak;
6801
6802 e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
6803 if (e) return e;
6804
6805 trak = gf_isom_get_track_from_file(file, track);
6806 if (!trak) return GF_BAD_PARAM;
6807
6808
6809 //the total duration is the media duration: adjust it in case...
6810 e = Media_SetDuration(trak);
6811 if (e) return e;
6812
6813 //assert the timeScales are non-NULL
6814 if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) return GF_ISOM_INVALID_FILE;
6815 trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
6816
6817 //if we have an edit list, the duration is the sum of all the editList
6818 //entries' duration (always expressed in MovieTimeScale)
6819 if (trak->editBox && trak->editBox->editList) {
6820 u64 editListDuration = 0;
6821 GF_EditListBox *elst = trak->editBox->editList;
6822 i=0;
6823 while ((ent = (GF_EdtsEntry*)gf_list_enum(elst->entryList, &i))) {
6824 if (ent->segmentDuration > trackDuration)
6825 ent->segmentDuration = trackDuration;
6826 if (!ent->segmentDuration) {
6827 u64 diff;
6828 ent->segmentDuration = trackDuration;
6829 if (ent->mediaTime>0) {
6830 diff = ent->mediaTime;
6831 diff *= trak->moov->mvhd->timeScale;
6832 diff /= trak->Media->mediaHeader->timeScale;
6833 if (diff < ent->segmentDuration)
6834 ent->segmentDuration -= diff;
6835 /*
6836 else
6837 diff = 0;
6838 */
6839 }
6840 }
6841 if ((ent->mediaTime>=0) && ((u64) ent->mediaTime>=trak->Media->mediaHeader->duration)) {
6842 ent->mediaTime = trak->Media->mediaHeader->duration;
6843 }
6844 editListDuration += ent->segmentDuration;
6845 }
6846 trackDuration = editListDuration;
6847 }
6848 if (!trackDuration) {
6849 trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
6850 }
6851 trak->Header->duration = trackDuration;
6852
6853 return GF_OK;
6854
6855 }
6856
6857
6858 GF_EXPORT
gf_isom_clone_pssh(GF_ISOFile * output,GF_ISOFile * input,Bool in_moof)6859 GF_Err gf_isom_clone_pssh(GF_ISOFile *output, GF_ISOFile *input, Bool in_moof) {
6860 GF_Box *a;
6861 u32 i;
6862 i = 0;
6863
6864 while ((a = (GF_Box *)gf_list_enum(input->moov->child_boxes, &i))) {
6865 if (a->type == GF_ISOM_BOX_TYPE_PSSH) {
6866 GF_List **child_boxes = &output->moov->child_boxes;
6867 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
6868 if (in_moof)
6869 child_boxes = &output->moof->child_boxes;
6870 #endif
6871
6872 GF_ProtectionSystemHeaderBox *pssh = (GF_ProtectionSystemHeaderBox *)gf_isom_box_new_parent(child_boxes, GF_ISOM_BOX_TYPE_PSSH);
6873 if (!pssh) return GF_OUT_OF_MEM;
6874 memmove(pssh->SystemID, ((GF_ProtectionSystemHeaderBox *)a)->SystemID, 16);
6875 if (((GF_ProtectionSystemHeaderBox *)a)->KIDs && ((GF_ProtectionSystemHeaderBox *)a)->KID_count > 0) {
6876 pssh->KID_count = ((GF_ProtectionSystemHeaderBox *)a)->KID_count;
6877 pssh->KIDs = (bin128 *)gf_malloc(pssh->KID_count*sizeof(bin128));
6878 if (!pssh->KIDs) return GF_OUT_OF_MEM;
6879 memmove(pssh->KIDs, ((GF_ProtectionSystemHeaderBox *)a)->KIDs, pssh->KID_count*sizeof(bin128));
6880 }
6881 pssh->private_data_size = ((GF_ProtectionSystemHeaderBox *)a)->private_data_size;
6882 pssh->private_data = (u8 *)gf_malloc(pssh->private_data_size*sizeof(char));
6883 if (!pssh->private_data) return GF_OUT_OF_MEM;
6884 memmove(pssh->private_data, ((GF_ProtectionSystemHeaderBox *)a)->private_data, pssh->private_data_size);
6885 }
6886 }
6887 return GF_OK;
6888 }
6889
6890 GF_EXPORT
gf_isom_set_track_group(GF_ISOFile * file,u32 track_number,u32 track_group_id,u32 group_type,Bool do_add)6891 GF_Err gf_isom_set_track_group(GF_ISOFile *file, u32 track_number, u32 track_group_id, u32 group_type, Bool do_add)
6892 {
6893 u32 i, j;
6894 GF_TrackGroupTypeBox *trgt;
6895 GF_Err e;
6896 GF_TrackBox *trak;
6897
6898 e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
6899 if (e) return e;
6900
6901 trak = gf_isom_get_track_from_file(file, track_number);
6902 if (!trak) return GF_BAD_PARAM;
6903 if (!trak->groups) trak->groups = (GF_TrackGroupBox*) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TRGR);
6904 if (!trak->groups) return GF_OUT_OF_MEM;
6905
6906 for (j=0; j<gf_list_count(file->moov->trackList); j++) {
6907 GF_TrackBox *a_trak = gf_list_get(file->moov->trackList, j);
6908 if (!a_trak->groups) continue;
6909
6910 for (i=0; i<gf_list_count(a_trak->groups->groups); i++) {
6911 trgt = gf_list_get(a_trak->groups->groups, i);
6912
6913 if (trgt->track_group_id==track_group_id) {
6914 if (trgt->group_type != group_type) {
6915 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("A track with same group ID is already defined for different group type %s\n", gf_4cc_to_str(trgt->group_type) ));
6916 return GF_BAD_PARAM;
6917 }
6918 if (a_trak==trak) {
6919 if (!do_add) {
6920 gf_list_rem(trak->groups->groups, i);
6921 gf_isom_box_del_parent(&trak->groups->child_boxes, (GF_Box *)trgt);
6922 }
6923 return GF_OK;
6924 }
6925 }
6926 }
6927 }
6928 //not found, add new group
6929 trgt = (GF_TrackGroupTypeBox*) gf_isom_box_new_parent(&trak->groups->child_boxes, GF_ISOM_BOX_TYPE_TRGT);
6930 if (!trgt) return GF_OUT_OF_MEM;
6931 trgt->track_group_id = track_group_id;
6932 trgt->group_type = group_type;
6933 return gf_list_add(trak->groups->groups, trgt);
6934 }
6935
6936 GF_EXPORT
gf_isom_set_nalu_length_field(GF_ISOFile * file,u32 track,u32 StreamDescriptionIndex,u32 nalu_size_length)6937 GF_Err gf_isom_set_nalu_length_field(GF_ISOFile *file, u32 track, u32 StreamDescriptionIndex, u32 nalu_size_length)
6938 {
6939 GF_Err e;
6940 GF_TrackBox *trak;
6941 GF_SampleEntryBox *entry;
6942 GF_MPEGVisualSampleEntryBox *ve;
6943 GF_SampleDescriptionBox *stsd;
6944
6945 e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
6946 if (e) return e;
6947
6948 trak = gf_isom_get_track_from_file(file, track);
6949 if (!trak) return GF_BAD_PARAM;
6950
6951 stsd = trak->Media->information->sampleTable->SampleDescription;
6952 if (!stsd || !StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
6953 return GF_BAD_PARAM;
6954 }
6955
6956 entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
6957 //no support for generic sample entries (eg, no MPEG4 descriptor)
6958 if (!entry || ! gf_isom_is_nalu_based_entry(trak->Media, entry)) {
6959 return GF_BAD_PARAM;
6960 }
6961
6962 ve = (GF_MPEGVisualSampleEntryBox*)entry;
6963 if (ve->avc_config) ve->avc_config->config->nal_unit_size = nalu_size_length;
6964 if (ve->svc_config) ve->svc_config->config->nal_unit_size = nalu_size_length;
6965 if (ve->hevc_config) ve->hevc_config->config->nal_unit_size = nalu_size_length;
6966 if (ve->lhvc_config) ve->lhvc_config->config->nal_unit_size = nalu_size_length;
6967 return GF_OK;
6968 }
6969
gf_isom_set_sample_group_in_traf(GF_ISOFile * file)6970 GF_Err gf_isom_set_sample_group_in_traf(GF_ISOFile *file)
6971 {
6972 GF_Err e;
6973 e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
6974 if (e) return e;
6975
6976 file->sample_groups_in_traf = GF_TRUE;
6977 return GF_OK;
6978 }
6979
6980 GF_EXPORT
gf_isom_set_progress_callback(GF_ISOFile * file,void (* progress_cbk)(void * udta,u64 nb_done,u64 nb_total),void * progress_cbk_udta)6981 void gf_isom_set_progress_callback(GF_ISOFile *file, void (*progress_cbk)(void *udta, u64 nb_done, u64 nb_total), void *progress_cbk_udta)
6982 {
6983 if (file) {
6984 file->progress_cbk = progress_cbk;
6985 file->progress_cbk_udta = progress_cbk_udta;
6986
6987 }
6988 }
6989
gf_isom_update_video_sample_entry_fields(GF_ISOFile * file,u32 track,u32 stsd_idx,u16 revision,u32 vendor,u32 temporalQ,u32 spatialQ,u32 horiz_res,u32 vert_res,u16 frames_per_sample,const char * compressor_name,s16 color_table_index)6990 GF_Err gf_isom_update_video_sample_entry_fields(GF_ISOFile *file, u32 track, u32 stsd_idx, u16 revision, u32 vendor, u32 temporalQ, u32 spatialQ, u32 horiz_res, u32 vert_res, u16 frames_per_sample, const char *compressor_name, s16 color_table_index)
6991 {
6992
6993 GF_TrackBox *trak;
6994 GF_MPEGVisualSampleEntryBox *vid_ent;
6995
6996 /*get orig sample desc and clone it*/
6997 trak = gf_isom_get_track_from_file(file, track);
6998 if (!trak || !stsd_idx) return GF_BAD_PARAM;
6999
7000 if (!trak->Media || !trak->Media->handler || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription)
7001 return GF_ISOM_INVALID_FILE;
7002
7003 switch (trak->Media->handler->handlerType) {
7004 case GF_ISOM_MEDIA_VISUAL:
7005 case GF_ISOM_MEDIA_AUXV:
7006 case GF_ISOM_MEDIA_PICT:
7007 break;
7008 default:
7009 return GF_BAD_PARAM;
7010 }
7011 vid_ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, stsd_idx-1);
7012 if (!vid_ent)
7013 return GF_BAD_PARAM;
7014
7015 vid_ent->revision = revision;
7016 vid_ent->vendor = vendor;
7017 vid_ent->temporal_quality = temporalQ;
7018 vid_ent->spatial_quality = spatialQ;
7019 vid_ent->horiz_res = horiz_res;
7020 vid_ent->vert_res = vert_res;
7021 vid_ent->frames_per_sample = frames_per_sample;
7022 if (compressor_name)
7023 strncpy(vid_ent->compressor_name, compressor_name, 32);
7024
7025 vid_ent->color_table_index = color_table_index;
7026 return GF_OK;
7027 }
7028
7029
gf_isom_update_sample_description_from_template(GF_ISOFile * file,u32 track,u32 sampleDescriptionIndex,u8 * data,u32 size)7030 GF_Err gf_isom_update_sample_description_from_template(GF_ISOFile *file, u32 track, u32 sampleDescriptionIndex, u8 *data, u32 size)
7031 {
7032 GF_BitStream *bs;
7033 GF_TrackBox *trak;
7034 GF_Box *ent, *tpl_ent;
7035 GF_Err e;
7036 /*get orig sample desc and clone it*/
7037 trak = gf_isom_get_track_from_file(file, track);
7038 if (!trak || !sampleDescriptionIndex) return GF_BAD_PARAM;
7039
7040 if (!trak->Media || !trak->Media->handler || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription)
7041 return GF_ISOM_INVALID_FILE;
7042
7043 ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex-1);
7044 if (!ent) return GF_BAD_PARAM;
7045
7046 bs = gf_bs_new(data, size, GF_BITSTREAM_READ);
7047 // e = gf_isom_box_parse(&tpl_ent, bs);
7048 e = gf_isom_box_parse_ex (&tpl_ent, bs, GF_ISOM_BOX_TYPE_STSD, GF_FALSE);
7049 gf_bs_del(bs);
7050 if (e) return e;
7051
7052 while (gf_list_count(tpl_ent->child_boxes)) {
7053 u32 j=0;
7054 Bool found = GF_FALSE;
7055 GF_Box *abox = gf_list_pop_front(tpl_ent->child_boxes);
7056
7057 switch (abox->type) {
7058 case GF_ISOM_BOX_TYPE_SINF:
7059 case GF_ISOM_BOX_TYPE_RINF:
7060 case GF_ISOM_BOX_TYPE_BTRT:
7061 found = GF_TRUE;
7062 break;
7063 }
7064
7065 if (found) {
7066 gf_isom_box_del(abox);
7067 continue;
7068 }
7069
7070 if (!ent->child_boxes) ent->child_boxes = gf_list_new();
7071 for (j=0; j<gf_list_count(ent->child_boxes); j++) {
7072 GF_Box *b = gf_list_get(ent->child_boxes, j);
7073 if (b->type == abox->type) {
7074 found = GF_TRUE;
7075 break;
7076 }
7077 }
7078 if (!found) {
7079 gf_list_add(ent->child_boxes, abox);
7080 } else {
7081 gf_isom_box_del(abox);
7082 }
7083 }
7084 gf_isom_box_del(tpl_ent);
7085
7086 //patch for old export
7087 GF_Box *abox = gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_SINF);
7088 if (abox) {
7089 gf_list_del_item(ent->child_boxes, abox);
7090 gf_list_add(ent->child_boxes, abox);
7091 }
7092 return GF_OK;
7093 }
7094
7095 #include <gpac/xml.h>
7096 GF_EXPORT
gf_isom_apply_box_patch(GF_ISOFile * file,GF_ISOTrackID globalTrackID,const char * box_patch_filename,Bool for_fragments)7097 GF_Err gf_isom_apply_box_patch(GF_ISOFile *file, GF_ISOTrackID globalTrackID, const char *box_patch_filename, Bool for_fragments)
7098 {
7099 GF_Err e;
7100 GF_DOMParser *dom;
7101 u32 i;
7102 GF_XMLNode *root;
7103 u8 *box_data=NULL;
7104 u32 box_data_size;
7105 if (!file || !box_patch_filename) return GF_BAD_PARAM;
7106 dom = gf_xml_dom_new();
7107 if (strstr(box_patch_filename, "<?xml")) {
7108 e = gf_xml_dom_parse_string(dom, (char *) box_patch_filename);
7109 } else {
7110 e = gf_xml_dom_parse(dom, box_patch_filename, NULL, NULL);
7111 }
7112 if (e) goto err_exit;
7113
7114 root = gf_xml_dom_get_root(dom);
7115 if (!root || strcmp(root->name, "GPACBOXES")) {
7116 e = GF_NON_COMPLIANT_BITSTREAM;
7117 goto err_exit;
7118 }
7119
7120 //compute size of each child boxes to freeze the order
7121 if (for_fragments) {
7122 u32 count = file->moof ? gf_list_count(file->moof->child_boxes) : 0;
7123 for (i=0; i<count; i++) {
7124 GF_Box *box = gf_list_get(file->moof->child_boxes, i);
7125 if (!(box->internal_flags & GF_ISOM_ORDER_FREEZE)) {
7126 gf_isom_box_size(box);
7127 gf_isom_box_freeze_order(box);
7128 }
7129 }
7130 } else {
7131 for (i=0; i<gf_list_count(file->TopBoxes); i++) {
7132 GF_Box *box = gf_list_get(file->TopBoxes, i);
7133 if (!(box->internal_flags & GF_ISOM_ORDER_FREEZE)) {
7134 gf_isom_box_size(box);
7135 gf_isom_box_freeze_order(box);
7136 }
7137 }
7138 }
7139
7140 for (i=0; i<gf_list_count(root->content); i++) {
7141 u32 j;
7142 u32 path_len;
7143 Bool essential_prop=GF_FALSE;
7144 u32 trackID=globalTrackID;
7145 u32 item_id=trackID;
7146 Bool is_frag_box;
7147 char *box_path=NULL;
7148 GF_Box *parent_box = NULL;
7149 GF_XMLNode *box_edit = gf_list_get(root->content, i);
7150 if (!box_edit->name || strcmp(box_edit->name, "Box")) continue;
7151
7152 for (j=0; j<gf_list_count(box_edit->attributes);j++) {
7153 GF_XMLAttribute *att = gf_list_get(box_edit->attributes, j);
7154 if (!strcmp(att->name, "path")) box_path = att->value;
7155 else if (!strcmp(att->name, "essential")) {
7156 if (!strcmp(att->value, "yes") || !strcmp(att->value, "true") || !strcmp(att->value, "1")) {
7157 essential_prop=GF_TRUE;
7158 }
7159 }
7160 else if (!strcmp(att->name, "itemID"))
7161 item_id = atoi(att->value);
7162 else if (!globalTrackID && !strcmp(att->name, "trackID"))
7163 trackID = atoi(att->value);
7164 }
7165
7166 if (!box_path) continue;
7167 path_len = (u32) strlen(box_path);
7168
7169 is_frag_box = !strncmp(box_path, "traf", 4) ? GF_TRUE : GF_FALSE;
7170
7171 if (for_fragments && !is_frag_box) continue;
7172 else if (!for_fragments && is_frag_box) continue;
7173
7174 gf_xml_parse_bit_sequence(box_edit, box_patch_filename, &box_data, &box_data_size);
7175 if (box_data_size && (box_data_size<4) ) {
7176 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Wrong BS specification for box, shall either be empty or at least 4 bytes for box type\n"));
7177 e = GF_NON_COMPLIANT_BITSTREAM;
7178 goto err_exit;
7179 }
7180
7181 while (box_path && (path_len>=4)) {
7182 GF_List **parent_list;
7183 u32 box_type = GF_4CC(box_path[0],box_path[1],box_path[2],box_path[3]);
7184 GF_Box *box=NULL;
7185 GF_BitStream *bs;
7186 s32 insert_pos = -1;
7187 box_path+=4;
7188 path_len-=4;
7189
7190 if (!parent_box) {
7191 box=gf_isom_box_find_child(file->TopBoxes, box_type);
7192 if (!box) {
7193 if (box_type==GF_ISOM_BOX_TYPE_TRAK) {
7194 if (trackID) {
7195 box = (GF_Box *) gf_isom_get_track_from_file(file, gf_isom_get_track_by_id(file, trackID) );
7196 }
7197 if (!box && gf_list_count(file->moov->trackList)==1) {
7198 box = gf_list_get(file->moov->trackList, 0);
7199 }
7200 }
7201 else if (box_type==GF_ISOM_BOX_TYPE_TRAF) {
7202 if (trackID) {
7203 box = (GF_Box *) gf_isom_get_traf(file, trackID);
7204 }
7205 if (!box && file->moof && gf_list_count(file->moof->TrackList)==1) {
7206 box = gf_list_get(file->moof->TrackList, 0);
7207 }
7208 }
7209 }
7210 if (!box) {
7211 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Cannot locate box type %s at root or as track\n", gf_4cc_to_str(box_type) ));
7212 e = GF_BAD_PARAM;
7213 goto err_exit;
7214 }
7215 } else {
7216 box = gf_isom_box_find_child(parent_box->child_boxes, box_type);
7217 if (!box) {
7218 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Cannot locate box type %s at child of %s\n", gf_4cc_to_str(box_type), gf_4cc_to_str(parent_box->type)));
7219 e = GF_BAD_PARAM;
7220 goto err_exit;
7221 }
7222 }
7223 // '.' is child access
7224 if (path_len && (box_path[0]=='.')) {
7225 box_path += 1;
7226 path_len-=1;
7227 parent_box = box;
7228 continue;
7229 }
7230
7231 if (parent_box && !parent_box->child_boxes) parent_box->child_boxes = gf_list_new();
7232 parent_list = parent_box ? &parent_box->child_boxes : &file->TopBoxes;
7233
7234 // '+' is append after, '-' is insert before
7235 if (path_len && ((box_path[0]=='-') || (box_path[0]=='+')) ) {
7236 s32 idx = gf_list_find(*parent_list, box);
7237 assert(idx>=0);
7238 if (box_path[0]=='+') insert_pos = idx+1;
7239 else insert_pos = idx;
7240 }
7241 else if (path_len) {
7242 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Invalid path %s, expecting either '-', '+' or '.' as separators\n", box_path));
7243 e = GF_NON_COMPLIANT_BITSTREAM;
7244 goto err_exit;
7245 }
7246
7247 if (!box_data) {
7248 if (insert_pos>=0) {
7249 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[ISOBMFF] Invalid path %s for box removal, ignoring position\n", box_path));
7250 }
7251 switch (box->type) {
7252 case GF_ISOM_BOX_TYPE_MOOV:
7253 file->moov = NULL;
7254 break;
7255 case GF_ISOM_BOX_TYPE_MDAT:
7256 file->mdat = NULL;
7257 break;
7258 case GF_ISOM_BOX_TYPE_PDIN:
7259 file->pdin = NULL;
7260 break;
7261 case GF_ISOM_BOX_TYPE_FTYP:
7262 file->brand = NULL;
7263 break;
7264 case GF_ISOM_BOX_TYPE_META:
7265 if ((GF_Box *) file->meta == box) file->meta = NULL;
7266 break;
7267 }
7268 gf_isom_box_del_parent(parent_list, box);
7269 } else {
7270 u32 size;
7271
7272 bs = gf_bs_new(box_data, box_data_size, GF_BITSTREAM_READ);
7273 size = gf_bs_read_u32(bs);
7274 if (size != box_data_size) {
7275 GF_UnknownBox *new_box = (GF_UnknownBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UNKNOWN);
7276 new_box->original_4cc = size;
7277 new_box->dataSize = (u32) gf_bs_available(bs);
7278 new_box->data = gf_malloc(sizeof(u8)*new_box->dataSize);
7279 gf_bs_read_data(bs, new_box->data, new_box->dataSize);
7280 if (insert_pos<0) {
7281 gf_list_add(box->child_boxes, new_box);
7282 insert_pos = gf_list_find(box->child_boxes, new_box);
7283 } else {
7284 gf_list_insert(*parent_list, new_box, insert_pos);
7285 }
7286
7287 if (parent_box && (parent_box->type==GF_ISOM_BOX_TYPE_IPRP)) {
7288 GF_ItemPropertyAssociationBox *ipma = (GF_ItemPropertyAssociationBox *) gf_isom_box_find_child(parent_box->child_boxes, GF_ISOM_BOX_TYPE_IPMA);
7289 if (!item_id) {
7290 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[ISOBMFF] Inserting box in ipco without itemID, no association added\n"));
7291 } else if (ipma) {
7292 u32 nb_asso, k;
7293 GF_ItemPropertyAssociationEntry *entry = NULL;
7294 nb_asso = gf_list_count(ipma->entries);
7295 for (k=0; k<nb_asso;k++) {
7296 entry = gf_list_get(ipma->entries, k);
7297 if (entry->item_id==item_id) break;
7298 entry = NULL;
7299 }
7300 if (!entry) {
7301 GF_SAFEALLOC(entry, GF_ItemPropertyAssociationEntry);
7302 if (!entry) return GF_OUT_OF_MEM;
7303 gf_list_add(ipma->entries, entry);
7304 entry->item_id = item_id;
7305 }
7306 entry->associations = gf_realloc(entry->associations, sizeof(GF_ItemPropertyAssociationSlot) * (entry->nb_associations+1));
7307 entry->associations[entry->nb_associations].essential = essential_prop;
7308 entry->associations[entry->nb_associations].index = 1+insert_pos;
7309 entry->nb_associations++;
7310 }
7311 }
7312 } else {
7313 u32 box_idx = 0;
7314
7315 gf_bs_seek(bs, 0);
7316 while (gf_bs_available(bs)) {
7317 GF_Box *new_box;
7318 e = gf_isom_box_parse_ex(&new_box, bs, box->type, parent_box ? GF_FALSE : GF_TRUE);
7319 if (e) {
7320 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] failed to parse box\n", box_path));
7321 gf_bs_del(bs);
7322 goto err_exit;
7323 }
7324 if (insert_pos<0) {
7325 gf_list_add(box->child_boxes, new_box);
7326 } else {
7327 gf_list_insert(*parent_list, new_box, insert_pos+box_idx);
7328 box_idx++;
7329 }
7330 }
7331 }
7332 gf_bs_del(bs);
7333
7334 }
7335 gf_free(box_data);
7336 box_data = NULL;
7337 box_path = NULL;
7338 }
7339 }
7340
7341 err_exit:
7342
7343 gf_xml_dom_del(dom);
7344 if (box_data) gf_free(box_data);
7345 return e;
7346 }
7347
7348 GF_EXPORT
gf_isom_set_track_magic(GF_ISOFile * movie,u32 trackNumber,u64 magic)7349 GF_Err gf_isom_set_track_magic(GF_ISOFile *movie, u32 trackNumber, u64 magic)
7350 {
7351 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
7352 if (!trak) return GF_BAD_PARAM;
7353 trak->magic = magic;
7354 return GF_OK;
7355 }
7356
7357 GF_EXPORT
gf_isom_set_track_index(GF_ISOFile * movie,u32 trackNumber,u32 index,void (* track_num_changed)(void * udta,u32 old_track_num,u32 new_track_num),void * udta)7358 GF_Err gf_isom_set_track_index(GF_ISOFile *movie, u32 trackNumber, u32 index, void (*track_num_changed)(void *udta, u32 old_track_num, u32 new_track_num), void *udta)
7359 {
7360 u32 i, count;
7361 GF_List *tracks;
7362 u32 prev_index=0, prev_pos=0;
7363 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
7364 if (!trak || !index) return GF_BAD_PARAM;
7365 trak->index = index;
7366 tracks = gf_list_new();
7367 count = gf_list_count(movie->moov->trackList);
7368 //sort tracks in new list
7369 for (i=0; i<count; i++) {
7370 GF_TrackBox *a_tk = gf_list_get(movie->moov->trackList, i);
7371 if (!a_tk->index) {
7372 gf_list_insert(tracks, a_tk, 0);
7373 } else if (a_tk->index < prev_index) {
7374 gf_list_insert(tracks, a_tk, prev_pos);
7375 } else {
7376 gf_list_add(tracks, a_tk);
7377 }
7378 prev_pos = gf_list_count(tracks) - 1;
7379 prev_index = a_tk->index;
7380 }
7381 if (gf_list_count(tracks) != count) {
7382 gf_list_del(tracks);
7383 return GF_OUT_OF_MEM;
7384 }
7385 if (track_num_changed) {
7386 for (i=0; i<count; i++) {
7387 GF_TrackBox *a_tk = gf_list_get(tracks, i);
7388 s32 old_pos = gf_list_find(movie->moov->trackList, a_tk);
7389 assert(old_pos>=0);
7390 if (old_pos != i)
7391 track_num_changed(udta, old_pos+1, i+1);
7392 }
7393 }
7394 gf_list_del(movie->moov->trackList);
7395 movie->moov->trackList = tracks;
7396 return GF_OK;
7397 }
7398
7399 #endif /*!defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)*/
7400