1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Jean Le Feuvre
5 * Copyright (c) Telecom ParisTech 2000-2012
6 * All rights reserved
7 *
8 * This file is part of GPAC / 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
28 GF_EXPORT
29 const char *gf_lang_get_3cc(u32 idx);
30 GF_EXPORT
31 s32 gf_lang_find(const char *lang_or_rfc_5646_code);
32
33
34 #if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)
35
CanAccessMovie(GF_ISOFile * movie,u32 Mode)36 GF_Err CanAccessMovie(GF_ISOFile *movie, u32 Mode)
37 {
38 if (!movie) return GF_BAD_PARAM;
39 if (movie->openMode < Mode) return GF_ISOM_INVALID_MODE;
40
41 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
42 if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_ISOM_INVALID_MODE;
43 #endif
44 return GF_OK;
45 }
46
unpack_track(GF_TrackBox * trak)47 static GF_Err unpack_track(GF_TrackBox *trak)
48 {
49 GF_Err e = GF_OK;
50 if (!trak->is_unpacked) {
51 e = stbl_UnpackOffsets(trak->Media->information->sampleTable);
52 if (e) return e;
53 e = stbl_unpackCTS(trak->Media->information->sampleTable);
54 trak->is_unpacked = GF_TRUE;
55 }
56 return e;
57 }
58
59
FlushCaptureMode(GF_ISOFile * movie)60 GF_Err FlushCaptureMode(GF_ISOFile *movie)
61 {
62 GF_Err e;
63 if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_OK;
64 /*make sure nothing was added*/
65 if (gf_bs_get_position(movie->editFileMap->bs)) return GF_OK;
66
67 /*add all first boxes*/
68 if (movie->brand) {
69 e = gf_isom_box_size((GF_Box *)movie->brand);
70 if (e) return e;
71 e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs);
72 if (e) return e;
73 }
74 if (movie->pdin) {
75 e = gf_isom_box_size((GF_Box *)movie->pdin);
76 if (e) return e;
77 e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs);
78 if (e) return e;
79 }
80
81 /*we have a trick here: the data will be stored on the fly, so the first
82 thing in the file is the MDAT. As we don't know if we have a large file (>4 GB) or not
83 do as if we had one and write 16 bytes: 4 (type) + 4 (size) + 8 (largeSize)...*/
84 gf_bs_write_int(movie->editFileMap->bs, 0, 128);
85 return GF_OK;
86 }
87
CheckNoData(GF_ISOFile * movie)88 static GF_Err CheckNoData(GF_ISOFile *movie)
89 {
90 if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_OK;
91 if (gf_bs_get_position(movie->editFileMap->bs)) return GF_BAD_PARAM;
92 return GF_OK;
93 }
94
95 /**************************************************************
96 File Writing / Editing
97 **************************************************************/
98 //quick function to add an IOD/OD to the file if not present (iods is optional)
AddMovieIOD(GF_MovieBox * moov,u8 isIOD)99 GF_Err AddMovieIOD(GF_MovieBox *moov, u8 isIOD)
100 {
101 GF_Descriptor *od;
102 GF_ObjectDescriptorBox *iods;
103
104 //do we have an IOD ?? If not, create one.
105 if (moov->iods) return GF_OK;
106
107 if (isIOD) {
108 od = gf_odf_desc_new(GF_ODF_ISOM_IOD_TAG);
109 }
110 else {
111 od = gf_odf_desc_new(GF_ODF_ISOM_OD_TAG);
112 }
113 if (!od) return GF_OUT_OF_MEM;
114 ((GF_IsomObjectDescriptor *)od)->objectDescriptorID = 1;
115
116 iods = (GF_ObjectDescriptorBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_IODS);
117 iods->descriptor = od;
118 return moov_AddBox((GF_Box*)moov, (GF_Box *)iods);
119 }
120
121 //add a track to the root OD
122 GF_EXPORT
gf_isom_add_track_to_root_od(GF_ISOFile * movie,u32 trackNumber)123 GF_Err gf_isom_add_track_to_root_od(GF_ISOFile *movie, u32 trackNumber)
124 {
125 GF_Err e;
126 GF_ES_ID_Inc *inc;
127
128 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
129 if (e) return e;
130 gf_isom_insert_moov(movie);
131
132 if (!movie->moov->iods) AddMovieIOD(movie->moov, 0);
133
134 if (gf_isom_is_track_in_root_od(movie, trackNumber) == 1) return GF_OK;
135
136 inc = (GF_ES_ID_Inc *)gf_odf_desc_new(GF_ODF_ESD_INC_TAG);
137 inc->trackID = gf_isom_get_track_id(movie, trackNumber);
138 if (!inc->trackID) {
139 gf_odf_desc_del((GF_Descriptor *)inc);
140 return movie->LastError;
141 }
142 if ((movie->LastError = gf_isom_add_desc_to_root_od(movie, (GF_Descriptor *)inc))) {
143 return movie->LastError;
144 }
145 gf_odf_desc_del((GF_Descriptor *)inc);
146 return GF_OK;
147 }
148
149 //remove the root OD
150 GF_EXPORT
gf_isom_remove_root_od(GF_ISOFile * movie)151 GF_Err gf_isom_remove_root_od(GF_ISOFile *movie)
152 {
153 GF_Err e;
154
155 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
156 if (e) return e;
157 if (!movie->moov || !movie->moov->iods) return GF_OK;
158 gf_isom_box_del((GF_Box *)movie->moov->iods);
159 movie->moov->iods = NULL;
160 return GF_OK;
161 }
162
163 //remove a track to the root OD
164 GF_EXPORT
gf_isom_remove_track_from_root_od(GF_ISOFile * movie,u32 trackNumber)165 GF_Err gf_isom_remove_track_from_root_od(GF_ISOFile *movie, u32 trackNumber)
166 {
167 GF_List *esds;
168 GF_ES_ID_Inc *inc;
169 u32 i;
170 GF_Err e;
171
172 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
173 if (e) return e;
174 if (!movie->moov) return GF_OK;
175
176 if (!gf_isom_is_track_in_root_od(movie, trackNumber)) return GF_OK;
177
178 if (!movie->moov->iods) AddMovieIOD(movie->moov, 0);
179 if (!movie->moov->iods) return GF_OUT_OF_MEM;
180
181 switch (movie->moov->iods->descriptor->tag) {
182 case GF_ODF_ISOM_IOD_TAG:
183 esds = ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors;
184 break;
185 case GF_ODF_ISOM_OD_TAG:
186 esds = ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors;
187 break;
188 default:
189 return GF_ISOM_INVALID_FILE;
190 }
191
192 //get the desc
193 i = 0;
194 while ((inc = (GF_ES_ID_Inc*)gf_list_enum(esds, &i))) {
195 if (inc->trackID == gf_isom_get_track_id(movie, trackNumber)) {
196 gf_odf_desc_del((GF_Descriptor *)inc);
197 gf_list_rem(esds, i - 1);
198 break;
199 }
200 }
201 //we don't remove the iod for P&Ls and other potential info
202 return GF_OK;
203 }
204
205 GF_EXPORT
gf_isom_set_creation_time(GF_ISOFile * movie,u64 time)206 GF_Err gf_isom_set_creation_time(GF_ISOFile *movie, u64 time)
207 {
208 if (!movie || !movie->moov) return GF_BAD_PARAM;
209 movie->moov->mvhd->creationTime = time;
210 movie->moov->mvhd->modificationTime = time;
211 return GF_OK;
212 }
213
214 GF_EXPORT
gf_isom_set_track_creation_time(GF_ISOFile * movie,u32 trackNumber,u64 time)215 GF_Err gf_isom_set_track_creation_time(GF_ISOFile *movie, u32 trackNumber, u64 time)
216 {
217 GF_TrackBox *trak;
218 trak = gf_isom_get_track_from_file(movie, trackNumber);
219 if (!trak) return GF_BAD_PARAM;
220
221 trak->Header->creationTime = time;
222 trak->Header->modificationTime = time;
223 return GF_OK;
224 }
225
226
227 //sets the enable flag of a track
228 GF_EXPORT
gf_isom_set_track_enabled(GF_ISOFile * movie,u32 trackNumber,u8 enableTrack)229 GF_Err gf_isom_set_track_enabled(GF_ISOFile *movie, u32 trackNumber, u8 enableTrack)
230 {
231 GF_Err e;
232 GF_TrackBox *trak;
233
234 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
235 if (e) return e;
236
237 trak = gf_isom_get_track_from_file(movie, trackNumber);
238 if (!trak) return GF_BAD_PARAM;
239
240 if (enableTrack) {
241 trak->Header->flags |= 1;
242 }
243 else {
244 trak->Header->flags &= ~1;
245 }
246 return GF_OK;
247 }
248
249 GF_EXPORT
gf_isom_set_media_language(GF_ISOFile * movie,u32 trackNumber,char * code)250 GF_Err gf_isom_set_media_language(GF_ISOFile *movie, u32 trackNumber, char *code)
251 {
252 GF_Err e;
253 GF_TrackBox *trak;
254
255 trak = gf_isom_get_track_from_file(movie, trackNumber);
256 if (!trak) return GF_BAD_PARAM;
257 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
258 if (e) return e;
259
260 // Old language-storage processing
261 // if the new code is on 3 chars, we use it
262 // otherwise, we find the associated 3 chars code and use it
263 if (strlen(code) == 3) {
264 memcpy(trak->Media->mediaHeader->packedLanguage, code, sizeof(char) * 3);
265 }
266 else {
267 s32 lang_idx;
268 const char *code_3cc;
269 lang_idx = gf_lang_find(code);
270 if (lang_idx == -1) {
271 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));
272 code_3cc = "und";
273 }
274 else {
275 code_3cc = gf_lang_get_3cc(lang_idx);
276 }
277 memcpy(trak->Media->mediaHeader->packedLanguage, code_3cc, sizeof(char) * 3);
278 }
279
280 // New language-storage processing
281 // change the code in the extended language box (if any)
282 // otherwise add an extended language box only if the given code is not 3 chars
283 {
284 u32 i, count;
285 GF_ExtendedLanguageBox *elng;
286 elng = NULL;
287 count = gf_list_count(trak->Media->other_boxes);
288 for (i = 0; i < count; i++) {
289 GF_Box *box = (GF_Box *)gf_list_get(trak->Media->other_boxes, i);
290 if (box->type == GF_ISOM_BOX_TYPE_ELNG) {
291 elng = (GF_ExtendedLanguageBox *)box;
292 break;
293 }
294 }
295 if (!elng && (strlen(code) != 3)) {
296 elng = (GF_ExtendedLanguageBox *)elng_New();
297 if (!count) {
298 trak->Media->other_boxes = gf_list_new();
299 }
300 gf_list_add(trak->Media->other_boxes, elng);
301 }
302 if (elng) {
303 if (elng->extended_language) {
304 gf_free(elng->extended_language);
305 }
306 elng->extended_language = gf_strdup(code);
307 }
308 }
309 if (!movie->keep_utc)
310 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
311 return GF_OK;
312 }
313
gf_isom_set_root_iod(GF_ISOFile * movie)314 static void gf_isom_set_root_iod(GF_ISOFile *movie)
315 {
316 GF_IsomInitialObjectDescriptor *iod;
317 GF_IsomObjectDescriptor *od;
318
319 gf_isom_insert_moov(movie);
320 if (!movie->moov->iods) {
321 AddMovieIOD(movie->moov, 1);
322 return;
323 }
324 //if OD, switch to IOD
325 if (movie->moov->iods->descriptor->tag == GF_ODF_ISOM_IOD_TAG) return;
326 od = (GF_IsomObjectDescriptor *)movie->moov->iods->descriptor;
327 iod = (GF_IsomInitialObjectDescriptor*)gf_malloc(sizeof(GF_IsomInitialObjectDescriptor));
328 memset(iod, 0, sizeof(GF_IsomInitialObjectDescriptor));
329
330 iod->ES_ID_IncDescriptors = od->ES_ID_IncDescriptors;
331 od->ES_ID_IncDescriptors = NULL;
332 //not used in root OD
333 iod->ES_ID_RefDescriptors = NULL;
334 iod->extensionDescriptors = od->extensionDescriptors;
335 od->extensionDescriptors = NULL;
336 iod->IPMP_Descriptors = od->IPMP_Descriptors;
337 od->IPMP_Descriptors = NULL;
338 iod->objectDescriptorID = od->objectDescriptorID;
339 iod->OCIDescriptors = od->OCIDescriptors;
340 od->OCIDescriptors = NULL;
341 iod->tag = GF_ODF_ISOM_IOD_TAG;
342 iod->URLString = od->URLString;
343 od->URLString = NULL;
344
345 gf_odf_desc_del((GF_Descriptor *)od);
346 movie->moov->iods->descriptor = (GF_Descriptor *)iod;
347 }
348
gf_isom_add_desc_to_root_od(GF_ISOFile * movie,GF_Descriptor * theDesc)349 GF_Err gf_isom_add_desc_to_root_od(GF_ISOFile *movie, GF_Descriptor *theDesc)
350 {
351 GF_Err e;
352 GF_Descriptor *desc, *dupDesc;
353
354 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
355 if (e) return e;
356 gf_isom_insert_moov(movie);
357
358 if (!movie->moov->iods) AddMovieIOD(movie->moov, 0);
359 if (!movie->moov->iods) return GF_OUT_OF_MEM;
360 if (theDesc->tag == GF_ODF_IPMP_TL_TAG) gf_isom_set_root_iod(movie);
361
362 desc = movie->moov->iods->descriptor;
363 //the type of desc is handled at the OD/IOD level, we'll be notified
364 //if the desc is not allowed
365 switch (desc->tag) {
366 case GF_ODF_ISOM_IOD_TAG:
367 case GF_ODF_ISOM_OD_TAG:
368 //duplicate the desc
369 e = gf_odf_desc_copy(theDesc, &dupDesc);
370 if (e) return e;
371 //add it (MUST BE (I)OD level desc)
372 movie->LastError = gf_odf_desc_add_desc(desc, dupDesc);
373 if (movie->LastError) gf_odf_desc_del((GF_Descriptor *)dupDesc);
374 break;
375 default:
376 movie->LastError = GF_ISOM_INVALID_FILE;
377 break;
378 }
379 return movie->LastError;
380 }
381
382
383 GF_EXPORT
gf_isom_set_timescale(GF_ISOFile * movie,u32 timeScale)384 GF_Err gf_isom_set_timescale(GF_ISOFile *movie, u32 timeScale)
385 {
386 Double ts_scale;
387 GF_TrackBox *trak;
388 u32 i;
389 GF_Err e;
390 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
391 if (e) return e;
392 gf_isom_insert_moov(movie);
393
394 if (movie->moov->mvhd->timeScale == timeScale) return GF_OK;
395
396 /*rewrite all durations and edit lists*/
397 ts_scale = timeScale;
398 ts_scale /= movie->moov->mvhd->timeScale;
399
400 movie->moov->mvhd->timeScale = timeScale;
401 movie->interleavingTime = timeScale;
402
403 movie->moov->mvhd->duration = (u64)(s64)((s64)movie->moov->mvhd->duration * ts_scale);
404
405 i = 0;
406 while ((trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) {
407 trak->Header->duration = (u64)(s64)((s64)trak->Header->duration * ts_scale);
408 if (trak->editBox && trak->editBox->editList) {
409 u32 j, count = gf_list_count(trak->editBox->editList->entryList);
410 for (j = 0; j<count; j++) {
411 GF_EdtsEntry *ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, j);
412 ent->segmentDuration = (u64)(s64)((s64)ent->segmentDuration * ts_scale);
413 }
414 }
415 }
416 return GF_OK;
417 }
418
419
420 GF_EXPORT
gf_isom_set_pl_indication(GF_ISOFile * movie,u8 PL_Code,u8 ProfileLevel)421 GF_Err gf_isom_set_pl_indication(GF_ISOFile *movie, u8 PL_Code, u8 ProfileLevel)
422 {
423 GF_IsomInitialObjectDescriptor *iod;
424 GF_Err e;
425
426 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
427 if (e) return e;
428
429 gf_isom_set_root_iod(movie);
430 iod = (GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor;
431
432 switch (PL_Code) {
433 case GF_ISOM_PL_AUDIO:
434 iod->audio_profileAndLevel = ProfileLevel;
435 break;
436 case GF_ISOM_PL_GRAPHICS:
437 iod->graphics_profileAndLevel = ProfileLevel;
438 break;
439 case GF_ISOM_PL_OD:
440 iod->OD_profileAndLevel = ProfileLevel;
441 break;
442 case GF_ISOM_PL_SCENE:
443 iod->scene_profileAndLevel = ProfileLevel;
444 break;
445 case GF_ISOM_PL_VISUAL:
446 iod->visual_profileAndLevel = ProfileLevel;
447 break;
448 case GF_ISOM_PL_INLINE:
449 iod->inlineProfileFlag = ProfileLevel ? 1 : 0;
450 break;
451 }
452 return GF_OK;
453 }
454
455
gf_isom_set_root_od_id(GF_ISOFile * movie,u32 OD_ID)456 GF_Err gf_isom_set_root_od_id(GF_ISOFile *movie, u32 OD_ID)
457 {
458 GF_Err e;
459 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
460 if (e) return e;
461
462 gf_isom_insert_moov(movie);
463 if (!movie->moov->iods) AddMovieIOD(movie->moov, 0);
464 if (!movie->moov->iods) return GF_OUT_OF_MEM;
465
466 switch (movie->moov->iods->descriptor->tag) {
467 case GF_ODF_ISOM_OD_TAG:
468 ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID;
469 break;
470 case GF_ODF_ISOM_IOD_TAG:
471 ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID;
472 break;
473 default:
474 return GF_ISOM_INVALID_FILE;
475 }
476 return GF_OK;
477 }
478
gf_isom_set_root_od_url(GF_ISOFile * movie,char * url_string)479 GF_Err gf_isom_set_root_od_url(GF_ISOFile *movie, char *url_string)
480 {
481 GF_Err e;
482 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
483 if (e) return e;
484 gf_isom_insert_moov(movie);
485
486 if (!movie->moov->iods) AddMovieIOD(movie->moov, 0);
487 if (!movie->moov->iods) return GF_OUT_OF_MEM;
488
489 switch (movie->moov->iods->descriptor->tag) {
490 case GF_ODF_ISOM_OD_TAG:
491 if (((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString) gf_free(((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString);
492 ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? gf_strdup(url_string) : NULL;
493 break;
494 case GF_ODF_ISOM_IOD_TAG:
495 if (((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString) gf_free(((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString);
496 ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? gf_strdup(url_string) : NULL;
497 break;
498 default:
499 return GF_ISOM_INVALID_FILE;
500 }
501 return GF_OK;
502 }
503
504
505
506 //creates a new Track. If trackID = 0, the trackID is chosen by the API
507 //returns the track number or 0 if error
508 GF_EXPORT
gf_isom_new_track(GF_ISOFile * movie,u32 trakID,u32 MediaType,u32 TimeScale)509 u32 gf_isom_new_track(GF_ISOFile *movie, u32 trakID, u32 MediaType, u32 TimeScale)
510 {
511 GF_Err e;
512 u64 now;
513 u8 isHint;
514 GF_TrackBox *trak;
515 GF_TrackHeaderBox *tkhd;
516 GF_MediaBox *mdia;
517
518 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
519 if (e) {
520 gf_isom_set_last_error(movie, e);
521 return 0;
522 }
523 gf_isom_insert_moov(movie);
524
525
526 isHint = 0;
527 //we're creating a hint track... it's the same, but mode HAS TO BE EDIT
528 if (MediaType == GF_ISOM_MEDIA_HINT) {
529 // if (movie->openMode != GF_ISOM_OPEN_EDIT) return 0;
530 isHint = 1;
531 }
532
533 mdia = NULL;
534 tkhd = NULL;
535 trak = NULL;
536 if (trakID) {
537 //check if we are in ES_ID boundaries
538 if (!isHint && (trakID > 0xFFFF)) {
539 gf_isom_set_last_error(movie, GF_BAD_PARAM);
540 return 0;
541 }
542 //here we should look for available IDs ...
543 if (!RequestTrack(movie->moov, trakID)) return 0;
544 }
545 else {
546 trakID = movie->moov->mvhd->nextTrackID;
547 if (!trakID) trakID = 1;
548 /*ESIDs are on 16 bits*/
549 if (!isHint && (trakID > 0xFFFF)) trakID = 1;
550
551 while (1) {
552 if (RequestTrack(movie->moov, trakID)) break;
553 trakID += 1;
554 if (trakID == 0xFFFFFFFF) break;
555 }
556 if (trakID == 0xFFFFFFFF) {
557 gf_isom_set_last_error(movie, GF_BAD_PARAM);
558 return 0;
559 }
560 if (!isHint && (trakID > 0xFFFF)) {
561 gf_isom_set_last_error(movie, GF_BAD_PARAM);
562 return 0;
563 }
564 }
565
566 //OK, now create a track...
567 trak = (GF_TrackBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TRAK);
568 if (!trak) {
569 gf_isom_set_last_error(movie, GF_OUT_OF_MEM);
570 return 0;
571 }
572 tkhd = (GF_TrackHeaderBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TKHD);
573 if (!tkhd) {
574 gf_isom_set_last_error(movie, GF_OUT_OF_MEM);
575 gf_isom_box_del((GF_Box *)trak);
576 return 0;
577 }
578 now = gf_isom_get_mp4time();
579 tkhd->creationTime = now;
580 if (!movie->keep_utc)
581 tkhd->modificationTime = now;
582 //OK, set up the media trak
583 e = NewMedia(&mdia, MediaType, TimeScale);
584 if (e) {
585 gf_isom_box_del((GF_Box *)mdia);
586 gf_isom_box_del((GF_Box *)trak);
587 gf_isom_box_del((GF_Box *)tkhd);
588 return 0;
589 }
590 //OK, add this media to our track
591 mdia->mediaTrack = trak;
592
593 e = trak_AddBox((GF_Box*)trak, (GF_Box *)tkhd);
594 if (e) goto err_exit;
595 e = trak_AddBox((GF_Box*)trak, (GF_Box *)mdia);
596 if (e) goto err_exit;
597 tkhd->trackID = trakID;
598
599
600 //some default properties for Audio, Visual or private tracks
601 switch (MediaType) {
602 case GF_ISOM_MEDIA_VISUAL:
603 case GF_ISOM_MEDIA_SCENE:
604 case GF_ISOM_MEDIA_TEXT:
605 case GF_ISOM_MEDIA_SUBT:
606 /*320-240 pix in 16.16*/
607 tkhd->width = 0x01400000;
608 tkhd->height = 0x00F00000;
609 break;
610 case GF_ISOM_MEDIA_AUDIO:
611 tkhd->volume = 0x0100;
612 break;
613 }
614
615 mdia->mediaHeader->creationTime = mdia->mediaHeader->modificationTime = now;
616 trak->Header->creationTime = trak->Header->modificationTime = now;
617
618 //OK, add our trak
619 e = moov_AddBox((GF_Box*)movie->moov, (GF_Box *)trak);
620 if (e) goto err_exit;
621 //set the new ID available
622 if (trakID + 1> movie->moov->mvhd->nextTrackID)
623 movie->moov->mvhd->nextTrackID = trakID + 1;
624
625 //and return our track number
626 return gf_isom_get_track_by_id(movie, trakID);
627
628 err_exit:
629 if (tkhd) gf_isom_box_del((GF_Box *)tkhd);
630 if (trak) gf_isom_box_del((GF_Box *)trak);
631 if (mdia) gf_isom_box_del((GF_Box *)mdia);
632 return 0;
633 }
634
635
636 //Create a new StreamDescription in the file. The URL and URN are used to describe external media
637 GF_EXPORT
gf_isom_new_mpeg4_description(GF_ISOFile * movie,u32 trackNumber,GF_ESD * esd,char * URLname,char * URNname,u32 * outDescriptionIndex)638 GF_Err gf_isom_new_mpeg4_description(GF_ISOFile *movie,
639 u32 trackNumber,
640 GF_ESD *esd,
641 char *URLname,
642 char *URNname,
643 u32 *outDescriptionIndex)
644 {
645 GF_TrackBox *trak;
646 GF_Err e;
647 u32 dataRefIndex;
648 GF_ESD *new_esd;
649
650 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
651 if (e) return e;
652
653 trak = gf_isom_get_track_from_file(movie, trackNumber);
654 if (!trak || !trak->Media ||
655 !esd || !esd->decoderConfig ||
656 !esd->slConfig) return GF_BAD_PARAM;
657
658 //get or create the data ref
659 e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex);
660 if (e) return e;
661 if (!dataRefIndex) {
662 e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex);
663 if (e) return e;
664 }
665 //duplicate our desc
666 e = gf_odf_desc_copy((GF_Descriptor *)esd, (GF_Descriptor **)&new_esd);
667 if (e) return e;
668 if (!movie->keep_utc)
669 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
670 e = Track_SetStreamDescriptor(trak, 0, dataRefIndex, new_esd, outDescriptionIndex);
671 if (e) {
672 gf_odf_desc_del((GF_Descriptor *)new_esd);
673 return e;
674 }
675 if (new_esd->URLString) {
676
677 }
678 return e;
679 }
680
681 //Add samples to a track. Use streamDescriptionIndex to specify the desired stream (if several)
682 GF_EXPORT
gf_isom_add_sample(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,const GF_ISOSample * sample)683 GF_Err gf_isom_add_sample(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, const GF_ISOSample *sample)
684 {
685 GF_Err e;
686 GF_TrackBox *trak;
687 GF_SampleEntryBox *entry;
688 u32 dataRefIndex;
689 u64 data_offset;
690 u32 descIndex;
691 GF_DataEntryURLBox *Dentry;
692
693 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
694 if (e) return e;
695
696 trak = gf_isom_get_track_from_file(movie, trackNumber);
697 if (!trak) return GF_BAD_PARAM;
698
699 e = FlushCaptureMode(movie);
700 if (e) return e;
701
702 e = unpack_track(trak);
703 if (e) return e;
704
705 //OK, add the sample
706 //1- Get the streamDescriptionIndex and dataRefIndex
707 //not specified, get the latest used...
708 descIndex = StreamDescriptionIndex;
709 if (!StreamDescriptionIndex) {
710 descIndex = trak->Media->information->sampleTable->currentEntryIndex;
711 }
712 e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex);
713 if (e) return e;
714 if (!entry || !dataRefIndex) return GF_BAD_PARAM;
715 //set the current to this one
716 trak->Media->information->sampleTable->currentEntryIndex = descIndex;
717
718
719 //get this dataRef and return false if not self contained
720 Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->other_boxes, dataRefIndex - 1);
721 if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM;
722
723 //Open our data map. We are adding stuff, so use EDIT
724 e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1);
725 if (e) return e;
726
727 //Get the offset...
728 data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler);
729
730 /*rewrite OD frame*/
731 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
732 GF_ISOSample *od_sample = NULL;
733 e = Media_ParseODFrame(trak->Media, sample, &od_sample);
734 if (e) return e;
735 e = Media_AddSample(trak->Media, data_offset, od_sample, descIndex, 0);
736 if (e) return e;
737 e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, od_sample->data, od_sample->dataLength);
738 if (e) return e;
739 if (od_sample) gf_isom_sample_del(&od_sample);
740 }
741 else {
742 e = Media_AddSample(trak->Media, data_offset, sample, descIndex, 0);
743 if (e) return e;
744 if (sample->dataLength) {
745 e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, sample->data, sample->dataLength);
746 if (e) return e;
747 }
748 }
749
750 if (!movie->keep_utc)
751 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
752 return SetTrackDuration(trak);
753 }
754
gf_isom_add_sample_shadow(GF_ISOFile * movie,u32 trackNumber,GF_ISOSample * sample)755 GF_Err gf_isom_add_sample_shadow(GF_ISOFile *movie, u32 trackNumber, GF_ISOSample *sample)
756 {
757 GF_Err e;
758 GF_TrackBox *trak;
759 GF_ISOSample *prev;
760 GF_SampleEntryBox *entry;
761 u32 dataRefIndex;
762 u64 data_offset;
763 u32 descIndex;
764 u32 sampleNum, prevSampleNum;
765 GF_DataEntryURLBox *Dentry;
766 Bool offset_times = GF_FALSE;
767
768 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
769 if (e) return e;
770
771 trak = gf_isom_get_track_from_file(movie, trackNumber);
772 if (!trak || !sample) return GF_BAD_PARAM;
773
774 e = FlushCaptureMode(movie);
775 if (e) return e;
776
777 e = unpack_track(trak);
778 if (e) return e;
779
780 e = stbl_findEntryForTime(trak->Media->information->sampleTable, sample->DTS, 0, &sampleNum, &prevSampleNum);
781 if (e) return e;
782 /*we need the EXACT match*/
783 if (!sampleNum) return GF_BAD_PARAM;
784
785 prev = gf_isom_get_sample_info(movie, trackNumber, sampleNum, &descIndex, NULL);
786 if (!prev) return gf_isom_last_error(movie);
787 /*for conformance*/
788 if (sample->DTS == prev->DTS) offset_times = GF_TRUE;
789 gf_isom_sample_del(&prev);
790
791 e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex);
792 if (e) return e;
793 if (!entry || !dataRefIndex) return GF_BAD_PARAM;
794 trak->Media->information->sampleTable->currentEntryIndex = descIndex;
795
796 //get this dataRef and return false if not self contained
797 Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->other_boxes, dataRefIndex - 1);
798 if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM;
799
800 //Open our data map. We are adding stuff, so use EDIT
801 e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1);
802 if (e) return e;
803
804 data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler);
805 if (offset_times) sample->DTS += 1;
806
807 /*REWRITE ANY OD STUFF*/
808 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
809 GF_ISOSample *od_sample = NULL;
810 e = Media_ParseODFrame(trak->Media, sample, &od_sample);
811 if (!e) e = Media_AddSample(trak->Media, data_offset, od_sample, descIndex, sampleNum);
812 if (!e) e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, od_sample->data, od_sample->dataLength);
813 if (od_sample) gf_isom_sample_del(&od_sample);
814 }
815 else {
816 e = Media_AddSample(trak->Media, data_offset, sample, descIndex, sampleNum);
817 if (!e) e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, sample->data, sample->dataLength);
818 }
819 if (e) return e;
820 if (offset_times) sample->DTS -= 1;
821
822 //OK, update duration
823 e = Media_SetDuration(trak);
824 if (e) return e;
825 if (!movie->keep_utc)
826 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
827 return SetTrackDuration(trak);
828 }
829
gf_isom_set_sample_rap(GF_ISOFile * movie,u32 trackNumber)830 GF_Err gf_isom_set_sample_rap(GF_ISOFile *movie, u32 trackNumber)
831 {
832 GF_SampleTableBox *stbl;
833 GF_Err e;
834 GF_TrackBox *trak;
835 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
836 if (e) return e;
837
838 trak = gf_isom_get_track_from_file(movie, trackNumber);
839 if (!trak) return GF_BAD_PARAM;
840 stbl = trak->Media->information->sampleTable;
841 if (!stbl->SyncSample) stbl->SyncSample = (GF_SyncSampleBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS);
842 return stbl_AddRAP(stbl->SyncSample, stbl->SampleSize->sampleCount);
843
844 }
845
gf_isom_append_sample_data(GF_ISOFile * movie,u32 trackNumber,char * data,u32 data_size)846 GF_Err gf_isom_append_sample_data(GF_ISOFile *movie, u32 trackNumber, char *data, u32 data_size)
847 {
848 GF_Err e;
849 GF_TrackBox *trak;
850 GF_SampleEntryBox *entry;
851 u32 dataRefIndex;
852 u32 descIndex;
853 GF_DataEntryURLBox *Dentry;
854
855 if (!data_size) return GF_OK;
856 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
857 if (e) return e;
858
859 trak = gf_isom_get_track_from_file(movie, trackNumber);
860 if (!trak) return GF_BAD_PARAM;
861
862 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) return GF_BAD_PARAM;
863
864 //OK, add the sample
865 descIndex = trak->Media->information->sampleTable->currentEntryIndex;
866
867 e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex);
868 if (e) return e;
869 if (!entry || !dataRefIndex) return GF_BAD_PARAM;
870
871 //get this dataRef and return false if not self contained
872 Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->other_boxes, dataRefIndex - 1);
873 if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM;
874
875 //Open our data map. We are adding stuff, so use EDIT
876 e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1);
877 if (e) return e;
878
879 //add the media data
880 e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, data, data_size);
881 if (e) return e;
882 //update data size
883 return stbl_SampleSizeAppend(trak->Media->information->sampleTable->SampleSize, data_size);
884 }
885
886
887 //Add sample reference to a track. The SampleOffset is the offset of the data in the referenced file
888 //you must have created a StreamDescription with URL or URN specifying your referenced file
889 //the data offset specifies the beginning of the chunk
890 //Use streamDescriptionIndex to specify the desired stream (if several)
891 GF_EXPORT
gf_isom_add_sample_reference(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,GF_ISOSample * sample,u64 dataOffset)892 GF_Err gf_isom_add_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_ISOSample *sample, u64 dataOffset)
893 {
894 GF_TrackBox *trak;
895 GF_SampleEntryBox *entry;
896 u32 dataRefIndex;
897 u32 descIndex;
898 GF_DataEntryURLBox *Dentry;
899 GF_Err e;
900
901 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
902 if (e) return e;
903
904 trak = gf_isom_get_track_from_file(movie, trackNumber);
905 if (!trak) return GF_BAD_PARAM;
906
907 e = unpack_track(trak);
908 if (e) return e;
909
910 //OD is not allowed as a data ref
911 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
912 return GF_BAD_PARAM;
913 }
914 //OK, add the sample
915 //1- Get the streamDescriptionIndex and dataRefIndex
916 //not specified, get the latest used...
917 descIndex = StreamDescriptionIndex;
918 if (!StreamDescriptionIndex) {
919 descIndex = trak->Media->information->sampleTable->currentEntryIndex;
920 }
921 e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex);
922 if (e) return e;
923 if (!entry || !dataRefIndex) return GF_BAD_PARAM;
924 //set the current to this one
925 trak->Media->information->sampleTable->currentEntryIndex = descIndex;
926
927
928 //get this dataRef and return false if self contained
929 Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->other_boxes, dataRefIndex - 1);
930 if (Dentry->flags == 1) return GF_BAD_PARAM;
931
932 //add the meta data
933 e = Media_AddSample(trak->Media, dataOffset, sample, descIndex, 0);
934 if (e) return e;
935
936 if (!movie->keep_utc)
937 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
938 //OK, update duration
939 e = Media_SetDuration(trak);
940 if (e) return e;
941 return SetTrackDuration(trak);
942
943 }
944
945 //set the duration of the last media sample. If not set, the duration of the last sample is the
946 //duration of the previous one if any, or 1000 (default value).
947 GF_EXPORT
gf_isom_set_last_sample_duration(GF_ISOFile * movie,u32 trackNumber,u32 duration)948 GF_Err gf_isom_set_last_sample_duration(GF_ISOFile *movie, u32 trackNumber, u32 duration)
949 {
950 GF_TrackBox *trak;
951 GF_SttsEntry *ent;
952 GF_TimeToSampleBox *stts;
953 u64 mdur;
954 GF_Err e;
955
956 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
957 if (e) return e;
958
959 trak = gf_isom_get_track_from_file(movie, trackNumber);
960 if (!trak) return GF_BAD_PARAM;
961
962 mdur = trak->Media->mediaHeader->duration;
963 stts = trak->Media->information->sampleTable->TimeToSample;
964 if (!stts->nb_entries) return GF_BAD_PARAM;
965 //get the last entry
966 ent = (GF_SttsEntry*)&stts->entries[stts->nb_entries - 1];
967
968 mdur -= ent->sampleDelta;
969 mdur += duration;
970 //we only have one sample
971 if (ent->sampleCount == 1) {
972 ent->sampleDelta = duration;
973 }
974 else {
975 if (ent->sampleDelta == duration) return GF_OK;
976 ent->sampleCount -= 1;
977
978 if (stts->nb_entries == stts->alloc_size) {
979 stts->alloc_size++;
980 stts->entries = (GF_SttsEntry*)gf_realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size);
981 if (!stts->entries) return GF_OUT_OF_MEM;
982 }
983 stts->entries[stts->nb_entries].sampleCount = 1;
984 stts->entries[stts->nb_entries].sampleDelta = duration;
985 stts->nb_entries++;
986 //and update the write cache
987 stts->w_currentSampleNum = trak->Media->information->sampleTable->SampleSize->sampleCount;
988 }
989 if (!movie->keep_utc)
990 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
991 trak->Media->mediaHeader->duration = mdur;
992 return SetTrackDuration(trak);
993 }
994
995 //update a sample data in the media. Note that the sample MUST exists
996 GF_EXPORT
gf_isom_update_sample(GF_ISOFile * movie,u32 trackNumber,u32 sampleNumber,GF_ISOSample * sample,Bool data_only)997 GF_Err gf_isom_update_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, Bool data_only)
998 {
999 GF_Err e;
1000 GF_TrackBox *trak;
1001
1002 e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT);
1003 if (e) return e;
1004
1005 trak = gf_isom_get_track_from_file(movie, trackNumber);
1006 if (!trak) return GF_BAD_PARAM;
1007
1008 e = unpack_track(trak);
1009 if (e) return e;
1010
1011 //block for hint tracks
1012 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM;
1013
1014 //REWRITE ANY OD STUFF
1015 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
1016 GF_ISOSample *od_sample = NULL;
1017 e = Media_ParseODFrame(trak->Media, sample, &od_sample);
1018 if (!e) e = Media_UpdateSample(trak->Media, sampleNumber, od_sample, data_only);
1019 if (od_sample) gf_isom_sample_del(&od_sample);
1020 }
1021 else {
1022 e = Media_UpdateSample(trak->Media, sampleNumber, sample, data_only);
1023 }
1024 if (e) return e;
1025 if (!movie->keep_utc)
1026 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1027 return GF_OK;
1028 }
1029
1030 //update a sample data in the media. Note that the sample MUST exists,
1031 //that sample->data MUST be NULL and sample->dataLength must be NON NULL;
1032 GF_EXPORT
gf_isom_update_sample_reference(GF_ISOFile * movie,u32 trackNumber,u32 sampleNumber,GF_ISOSample * sample,u64 data_offset)1033 GF_Err gf_isom_update_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset)
1034 {
1035 GF_Err e;
1036 GF_TrackBox *trak;
1037
1038 e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT);
1039 if (e) return e;
1040
1041 trak = gf_isom_get_track_from_file(movie, trackNumber);
1042 if (!trak) return GF_BAD_PARAM;
1043
1044 //block for hint tracks
1045 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM;
1046
1047 if (!sampleNumber || !sample) return GF_BAD_PARAM;
1048
1049 e = unpack_track(trak);
1050 if (e) return e;
1051
1052 //OD is not allowed as a data ref
1053 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
1054 return GF_BAD_PARAM;
1055 }
1056 //OK, update it
1057 e = Media_UpdateSampleReference(trak->Media, sampleNumber, sample, data_offset);
1058 if (e) return e;
1059
1060 if (!movie->keep_utc)
1061 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1062 return GF_OK;
1063 }
1064
1065
1066 //Remove a given sample
1067 GF_EXPORT
gf_isom_remove_sample(GF_ISOFile * movie,u32 trackNumber,u32 sampleNumber)1068 GF_Err gf_isom_remove_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber)
1069 {
1070 GF_Err e;
1071 GF_TrackBox *trak;
1072
1073 e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT);
1074 if (e) return e;
1075
1076 trak = gf_isom_get_track_from_file(movie, trackNumber);
1077 if (!trak || !sampleNumber || (sampleNumber > trak->Media->information->sampleTable->SampleSize->sampleCount))
1078 return GF_BAD_PARAM;
1079
1080 //block for hint tracks
1081 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM;
1082
1083 e = unpack_track(trak);
1084 if (e) return e;
1085
1086 //remove DTS
1087 e = stbl_RemoveDTS(trak->Media->information->sampleTable, sampleNumber, trak->Media->mediaHeader->timeScale);
1088 if (e) return e;
1089 //remove CTS if any
1090 if (trak->Media->information->sampleTable->CompositionOffset) {
1091 e = stbl_RemoveCTS(trak->Media->information->sampleTable, sampleNumber);
1092 if (e) return e;
1093 }
1094 //remove size
1095 e = stbl_RemoveSize(trak->Media->information->sampleTable->SampleSize, sampleNumber);
1096 if (e) return e;
1097 //remove sampleToChunk and chunk
1098 e = stbl_RemoveChunk(trak->Media->information->sampleTable, sampleNumber);
1099 if (e) return e;
1100 //remove sync
1101 if (trak->Media->information->sampleTable->SyncSample) {
1102 e = stbl_RemoveRAP(trak->Media->information->sampleTable, sampleNumber);
1103 if (e) return e;
1104 }
1105 //remove sample dep
1106 if (trak->Media->information->sampleTable->SampleDep) {
1107 e = stbl_RemoveRedundant(trak->Media->information->sampleTable, sampleNumber);
1108 if (e) return e;
1109 }
1110 //remove shadow
1111 if (trak->Media->information->sampleTable->ShadowSync) {
1112 e = stbl_RemoveShadow(trak->Media->information->sampleTable->ShadowSync, sampleNumber);
1113 if (e) return e;
1114 }
1115 //remove padding
1116 e = stbl_RemovePaddingBits(trak->Media->information->sampleTable, sampleNumber);
1117 if (e) return e;
1118
1119 e = stbl_RemoveSubSample(trak->Media->information->sampleTable, sampleNumber);
1120 if (e) return e;
1121
1122 e = stbl_RemoveSampleGroup(trak->Media->information->sampleTable, sampleNumber);
1123 if (e) return e;
1124
1125 return SetTrackDuration(trak);
1126 }
1127
1128
1129 GF_EXPORT
gf_isom_set_final_name(GF_ISOFile * movie,char * filename)1130 GF_Err gf_isom_set_final_name(GF_ISOFile *movie, char *filename)
1131 {
1132 GF_Err e;
1133 if (!movie) return GF_BAD_PARAM;
1134
1135 //if mode is not OPEN_EDIT file was created under the right name
1136 e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT);
1137 if (e) return e;
1138
1139 if (filename) {
1140 //we don't allow file overwriting
1141 if ((movie->openMode == GF_ISOM_OPEN_EDIT)
1142 && movie->fileName && !strcmp(filename, movie->fileName))
1143 return GF_BAD_PARAM;
1144 if (movie->finalName) gf_free(movie->finalName);
1145 movie->finalName = gf_strdup(filename);
1146 if (!movie->finalName) return GF_OUT_OF_MEM;
1147 }
1148 return GF_OK;
1149 }
1150
1151 //Add a system descriptor to the ESD of a stream(EDIT or WRITE mode only)
gf_isom_add_desc_to_description(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,GF_Descriptor * theDesc)1152 GF_Err gf_isom_add_desc_to_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_Descriptor *theDesc)
1153 {
1154 GF_IPIPtr *ipiD;
1155 GF_Err e;
1156 u16 tmpRef;
1157 GF_TrackBox *trak;
1158 GF_Descriptor *desc;
1159 GF_ESD *esd;
1160 GF_TrackReferenceBox *tref;
1161 GF_TrackReferenceTypeBox *dpnd;
1162
1163 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1164 if (e) return e;
1165
1166 trak = gf_isom_get_track_from_file(movie, trackNumber);
1167 if (!trak) return GF_BAD_PARAM;
1168
1169 /*GETS NATIVE DESCRIPTOR ONLY*/
1170 e = Media_GetESD(trak->Media, StreamDescriptionIndex, &esd, GF_TRUE);
1171 if (e) return e;
1172
1173 //duplicate the desc
1174 e = gf_odf_desc_copy(theDesc, &desc);
1175 if (e) return e;
1176
1177 //and add it to the ESD EXCEPT IPI PTR (we need to translate from ES_ID to TrackID!!!
1178 if (!movie->keep_utc)
1179 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1180 switch (desc->tag) {
1181 case GF_ODF_IPI_PTR_TAG:
1182 goto insertIPI;
1183
1184 default:
1185 return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc);
1186 }
1187
1188
1189 insertIPI:
1190 if (esd->ipiPtr) {
1191 gf_odf_desc_del((GF_Descriptor *)esd->ipiPtr);
1192 esd->ipiPtr = NULL;
1193 }
1194
1195 ipiD = (GF_IPIPtr *)desc;
1196 //find a tref
1197 if (!trak->References) {
1198 tref = (GF_TrackReferenceBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF);
1199 e = trak_AddBox((GF_Box*)trak, (GF_Box *)tref);
1200 if (e) return e;
1201 }
1202 tref = trak->References;
1203
1204 e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd);
1205 if (e) return e;
1206 if (!dpnd) {
1207 tmpRef = 0;
1208 dpnd = (GF_TrackReferenceTypeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT);
1209 dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR;
1210 e = tref_AddBox((GF_Box*)tref, (GF_Box *)dpnd);
1211 if (e) return e;
1212 e = reftype_AddRefTrack(dpnd, ipiD->IPI_ES_Id, &tmpRef);
1213 if (e) return e;
1214 //and replace the tag and value...
1215 ipiD->IPI_ES_Id = tmpRef;
1216 ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1217 }
1218 else {
1219 //Watch out! ONLY ONE IPI dependancy is allowed per stream
1220 dpnd->trackIDCount = 1;
1221 dpnd->trackIDs[0] = ipiD->IPI_ES_Id;
1222 //and replace the tag and value...
1223 ipiD->IPI_ES_Id = 1;
1224 ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1225 }
1226 //and add the desc to the esd...
1227 return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc);
1228 }
1229
1230
1231 //use carefully. Very useful when you made a lot of changes (IPMP, IPI, OCI, ...)
1232 //THIS WILL REPLACE THE WHOLE DESCRIPTOR ...
1233 GF_EXPORT
gf_isom_change_mpeg4_description(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,GF_ESD * newESD)1234 GF_Err gf_isom_change_mpeg4_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_ESD *newESD)
1235 {
1236 GF_Err e;
1237 GF_ESD *esd;
1238 GF_TrackBox *trak;
1239 GF_SampleEntryBox *entry;
1240 GF_SampleDescriptionBox *stsd;
1241
1242 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1243 if (e) return e;
1244
1245 trak = gf_isom_get_track_from_file(movie, trackNumber);
1246 if (!trak) return GF_BAD_PARAM;
1247
1248 stsd = trak->Media->information->sampleTable->SampleDescription;
1249 if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
1250
1251 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->other_boxes)) {
1252 return movie->LastError = GF_BAD_PARAM;
1253 }
1254 entry = (GF_SampleEntryBox *)gf_list_get(stsd->other_boxes, StreamDescriptionIndex - 1);
1255 //no support for generic sample entries (eg, no MPEG4 descriptor)
1256 if (entry == NULL) return GF_BAD_PARAM;
1257
1258 if (!movie->keep_utc)
1259 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1260 //duplicate our desc
1261 e = gf_odf_desc_copy((GF_Descriptor *)newESD, (GF_Descriptor **)&esd);
1262 if (e) return e;
1263 e = Track_SetStreamDescriptor(trak, StreamDescriptionIndex, entry->dataReferenceIndex, esd, NULL);
1264 if (e != GF_OK) {
1265 gf_odf_desc_del((GF_Descriptor *)esd);
1266 }
1267 return e;
1268 }
1269
1270 GF_EXPORT
gf_isom_set_visual_info(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,u32 Width,u32 Height)1271 GF_Err gf_isom_set_visual_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 Width, u32 Height)
1272 {
1273 GF_Err e;
1274 GF_TrackBox *trak;
1275 GF_SampleEntryBox *entry;
1276 GF_SampleDescriptionBox *stsd;
1277 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1278 if (e) return e;
1279
1280 trak = gf_isom_get_track_from_file(movie, trackNumber);
1281 if (!trak) return GF_BAD_PARAM;
1282
1283 stsd = trak->Media->information->sampleTable->SampleDescription;
1284 if (!stsd) {
1285 return movie->LastError = GF_ISOM_INVALID_FILE;
1286 }
1287 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->other_boxes)) {
1288 return movie->LastError = GF_BAD_PARAM;
1289 }
1290 entry = (GF_SampleEntryBox *)gf_list_get(stsd->other_boxes, StreamDescriptionIndex - 1);
1291 //no support for generic sample entries (eg, no MPEG4 descriptor)
1292 if (entry == NULL) return GF_BAD_PARAM;
1293 if (!movie->keep_utc)
1294 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1295
1296 //valid for MPEG visual, JPG and 3GPP H263
1297 switch (entry->type) {
1298 case GF_ISOM_BOX_TYPE_MP4V:
1299 case GF_ISOM_SUBTYPE_3GP_H263:
1300 case GF_ISOM_BOX_TYPE_AVC1:
1301 case GF_ISOM_BOX_TYPE_AVC2:
1302 case GF_ISOM_BOX_TYPE_AVC3:
1303 case GF_ISOM_BOX_TYPE_AVC4:
1304 case GF_ISOM_BOX_TYPE_SVC1:
1305 case GF_ISOM_BOX_TYPE_HVC1:
1306 case GF_ISOM_BOX_TYPE_HEV1:
1307 case GF_ISOM_BOX_TYPE_HVC2:
1308 case GF_ISOM_BOX_TYPE_HEV2:
1309 case GF_ISOM_BOX_TYPE_LHE1:
1310 case GF_ISOM_BOX_TYPE_LHV1:
1311 case GF_ISOM_BOX_TYPE_HVT1:
1312 ((GF_VisualSampleEntryBox*)entry)->Width = Width;
1313 ((GF_VisualSampleEntryBox*)entry)->Height = Height;
1314 trak->Header->width = Width << 16;
1315 trak->Header->height = Height << 16;
1316 return GF_OK;
1317 case GF_ISOM_BOX_TYPE_GNRV:
1318 ((GF_GenericVisualSampleEntryBox*)entry)->Width = Width;
1319 ((GF_GenericVisualSampleEntryBox*)entry)->Height = Height;
1320 trak->Header->width = Width << 16;
1321 trak->Header->height = Height << 16;
1322 return GF_OK;
1323
1324 /*check BIFS*/
1325 default:
1326 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_SCENE) {
1327 trak->Header->width = Width << 16;
1328 trak->Header->height = Height << 16;
1329 return GF_OK;
1330 }
1331 return GF_BAD_PARAM;
1332 }
1333 }
1334
gf_isom_set_pixel_aspect_ratio(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,u32 hSpacing,u32 vSpacing)1335 GF_Err gf_isom_set_pixel_aspect_ratio(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 hSpacing, u32 vSpacing)
1336 {
1337 GF_Err e;
1338 GF_TrackBox *trak;
1339 GF_SampleEntryBox *entry;
1340 GF_VisualSampleEntryBox*vent;
1341 GF_SampleDescriptionBox *stsd;
1342 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1343 if (e) return e;
1344
1345 trak = gf_isom_get_track_from_file(movie, trackNumber);
1346 if (!trak) return GF_BAD_PARAM;
1347
1348 stsd = trak->Media->information->sampleTable->SampleDescription;
1349 if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
1350 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->other_boxes)) {
1351 return movie->LastError = GF_BAD_PARAM;
1352 }
1353 entry = (GF_SampleEntryBox *)gf_list_get(stsd->other_boxes, StreamDescriptionIndex - 1);
1354 //no support for generic sample entries (eg, no MPEG4 descriptor)
1355 if (entry == NULL) return GF_BAD_PARAM;
1356 if (!movie->keep_utc)
1357 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1358 switch (entry->type) {
1359 case GF_ISOM_BOX_TYPE_MP4V:
1360 case GF_ISOM_SUBTYPE_3GP_H263:
1361 case GF_ISOM_BOX_TYPE_AVC1:
1362 case GF_ISOM_BOX_TYPE_AVC2:
1363 case GF_ISOM_BOX_TYPE_AVC3:
1364 case GF_ISOM_BOX_TYPE_AVC4:
1365 case GF_ISOM_BOX_TYPE_SVC1:
1366 case GF_ISOM_BOX_TYPE_HVC1:
1367 case GF_ISOM_BOX_TYPE_HEV1:
1368 case GF_ISOM_BOX_TYPE_HVC2:
1369 case GF_ISOM_BOX_TYPE_HEV2:
1370 case GF_ISOM_BOX_TYPE_LHE1:
1371 case GF_ISOM_BOX_TYPE_LHV1:
1372 case GF_ISOM_BOX_TYPE_HVT1:
1373 break;
1374 default:
1375 return GF_BAD_PARAM;
1376 }
1377
1378 vent = (GF_VisualSampleEntryBox*)entry;
1379 if (!hSpacing || !vSpacing) {
1380 if (vent->pasp) gf_isom_box_del((GF_Box*)vent->pasp);
1381 vent->pasp = NULL;
1382 return GF_OK;
1383 }
1384 if (!vent->pasp) vent->pasp = (GF_PixelAspectRatioBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_PASP);
1385 vent->pasp->hSpacing = hSpacing;
1386 vent->pasp->vSpacing = vSpacing;
1387 return GF_OK;
1388 }
1389
1390 GF_EXPORT
gf_isom_set_audio_info(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,u32 sampleRate,u32 nbChannels,u8 bitsPerSample)1391 GF_Err gf_isom_set_audio_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 sampleRate, u32 nbChannels, u8 bitsPerSample)
1392 {
1393 GF_Err e;
1394 GF_TrackBox *trak;
1395 GF_SampleEntryBox *entry;
1396 GF_SampleDescriptionBox *stsd;
1397 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1398 if (e) return e;
1399
1400 trak = gf_isom_get_track_from_file(movie, trackNumber);
1401 if (!trak) return GF_BAD_PARAM;
1402
1403 stsd = trak->Media->information->sampleTable->SampleDescription;
1404 if (!stsd) {
1405 return movie->LastError = GF_ISOM_INVALID_FILE;
1406 }
1407 if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->other_boxes)) {
1408 return movie->LastError = GF_BAD_PARAM;
1409 }
1410 entry = (GF_SampleEntryBox *)gf_list_get(stsd->other_boxes, StreamDescriptionIndex - 1);
1411 //no support for generic sample entries (eg, no MPEG4 descriptor)
1412 if (entry == NULL) return GF_BAD_PARAM;
1413 if (!movie->keep_utc)
1414 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1415
1416 switch (entry->type) {
1417 case GF_ISOM_BOX_TYPE_MP4A:
1418 case GF_ISOM_BOX_TYPE_AC3:
1419 case GF_ISOM_BOX_TYPE_EC3:
1420 case GF_ISOM_SUBTYPE_3GP_AMR:
1421 case GF_ISOM_SUBTYPE_3GP_AMR_WB:
1422 case GF_ISOM_SUBTYPE_3GP_EVRC:
1423 case GF_ISOM_SUBTYPE_3GP_QCELP:
1424 case GF_ISOM_SUBTYPE_3GP_SMV:
1425 ((GF_AudioSampleEntryBox*)entry)->samplerate_hi = sampleRate;
1426 ((GF_AudioSampleEntryBox*)entry)->samplerate_lo = 0;
1427 ((GF_AudioSampleEntryBox*)entry)->channel_count = nbChannels;
1428 ((GF_AudioSampleEntryBox*)entry)->bitspersample = bitsPerSample;
1429 return GF_OK;
1430 /*check BIFS*/
1431 default:
1432 return GF_BAD_PARAM;
1433 }
1434 }
1435
1436 //set the storage mode of a file (FLAT, STREAMABLE, INTERLEAVED)
1437 GF_EXPORT
gf_isom_set_storage_mode(GF_ISOFile * movie,u8 storageMode)1438 GF_Err gf_isom_set_storage_mode(GF_ISOFile *movie, u8 storageMode)
1439 {
1440 GF_Err e;
1441 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1442 if (e) return e;
1443
1444 switch (storageMode) {
1445 case GF_ISOM_STORE_FLAT:
1446 case GF_ISOM_STORE_STREAMABLE:
1447 case GF_ISOM_STORE_INTERLEAVED:
1448 case GF_ISOM_STORE_DRIFT_INTERLEAVED:
1449 case GF_ISOM_STORE_TIGHT:
1450 movie->storageMode = storageMode;
1451 return GF_OK;
1452 default:
1453 return GF_BAD_PARAM;
1454 }
1455 }
1456
1457 GF_EXPORT
gf_isom_force_64bit_chunk_offset(GF_ISOFile * file,Bool set_on)1458 void gf_isom_force_64bit_chunk_offset(GF_ISOFile *file, Bool set_on)
1459 {
1460 file->force_co64 = set_on;
1461 }
1462
1463
1464 //update or insert a new edit segment in the track time line. Edits are used to modify
1465 //the media normal timing. EditTime and EditDuration are expressed in Movie TimeScale
1466 //If a segment with EditTime already exists, IT IS ERASED
1467 GF_EXPORT
gf_isom_set_edit_segment(GF_ISOFile * movie,u32 trackNumber,u64 EditTime,u64 EditDuration,u64 MediaTime,u8 EditMode)1468 GF_Err gf_isom_set_edit_segment(GF_ISOFile *movie, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, u8 EditMode)
1469 {
1470 GF_TrackBox *trak;
1471 GF_EditBox *edts;
1472 GF_EditListBox *elst;
1473 GF_EdtsEntry *ent, *newEnt;
1474 u32 i;
1475 GF_Err e;
1476 u64 startTime;
1477
1478 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1479 if (e) return e;
1480
1481 trak = gf_isom_get_track_from_file(movie, trackNumber);
1482 if (!trak) return GF_BAD_PARAM;
1483
1484 edts = trak->editBox;
1485 if (!edts) {
1486 edts = (GF_EditBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_EDTS);
1487 if (!edts) return GF_OUT_OF_MEM;
1488 trak_AddBox((GF_Box*)trak, (GF_Box *)edts);
1489 }
1490 elst = edts->editList;
1491 if (!elst) {
1492 elst = (GF_EditListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_ELST);
1493 if (!elst) return GF_OUT_OF_MEM;
1494 edts_AddBox((GF_Box*)edts, (GF_Box *)elst);
1495 }
1496
1497 startTime = 0;
1498 ent = NULL;
1499 //get the prev entry to this startTime if any
1500 i = 0;
1501 while ((ent = (GF_EdtsEntry *)gf_list_enum(elst->entryList, &i))) {
1502 if ((startTime <= EditTime) && (startTime + ent->segmentDuration > EditTime))
1503 goto found;
1504 startTime += ent->segmentDuration;
1505 }
1506
1507 //not found, add a new entry and adjust the prev one if any
1508 if (!ent) {
1509 newEnt = CreateEditEntry(EditDuration, MediaTime, EditMode);
1510 if (!newEnt) return GF_OUT_OF_MEM;
1511 gf_list_add(elst->entryList, newEnt);
1512 return SetTrackDuration(trak);
1513 }
1514
1515 startTime -= ent->segmentDuration;
1516
1517 found:
1518
1519 //if same time, we erase the current one...
1520 if (startTime == EditTime) {
1521 ent->segmentDuration = EditDuration;
1522 switch (EditMode) {
1523 case GF_ISOM_EDIT_EMPTY:
1524 ent->mediaRate = 1;
1525 ent->mediaTime = -1;
1526 break;
1527 case GF_ISOM_EDIT_DWELL:
1528 ent->mediaRate = 0;
1529 ent->mediaTime = MediaTime;
1530 break;
1531 default:
1532 ent->mediaRate = 1;
1533 ent->mediaTime = MediaTime;
1534 break;
1535 }
1536 return SetTrackDuration(trak);
1537 }
1538
1539 //adjust so that the prev ent leads to EntryTime
1540 //Note: we don't change the next one as it is unknown to us in
1541 //a lot of case (the author's changes)
1542 ent->segmentDuration = EditTime - startTime;
1543 newEnt = CreateEditEntry(EditDuration, MediaTime, EditMode);
1544 if (!newEnt) return GF_OUT_OF_MEM;
1545 //is it the last entry ???
1546 if (i >= gf_list_count(elst->entryList) - 1) {
1547 //add the new entry at the end
1548 gf_list_add(elst->entryList, newEnt);
1549 return SetTrackDuration(trak);
1550 }
1551 else {
1552 //insert after the current entry (which is i)
1553 gf_list_insert(elst->entryList, newEnt, i + 1);
1554 return SetTrackDuration(trak);
1555 }
1556 }
1557
1558 //remove the edit segments for the whole track
1559 GF_EXPORT
gf_isom_remove_edit_segments(GF_ISOFile * movie,u32 trackNumber)1560 GF_Err gf_isom_remove_edit_segments(GF_ISOFile *movie, u32 trackNumber)
1561 {
1562 GF_Err e;
1563 GF_TrackBox *trak;
1564 GF_EdtsEntry *ent;
1565 trak = gf_isom_get_track_from_file(movie, trackNumber);
1566 if (!trak) return GF_BAD_PARAM;
1567
1568 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1569 if (e) return e;
1570
1571 if (!trak->editBox || !trak->editBox->editList) return GF_OK;
1572
1573 while (gf_list_count(trak->editBox->editList->entryList)) {
1574 ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, 0);
1575 gf_free(ent);
1576 e = gf_list_rem(trak->editBox->editList->entryList, 0);
1577 if (e) return e;
1578 }
1579 //then delete the GF_EditBox...
1580 gf_isom_box_del((GF_Box *)trak->editBox);
1581 trak->editBox = NULL;
1582 return SetTrackDuration(trak);
1583 }
1584
1585
1586 //remove the edit segments for the whole track
gf_isom_remove_edit_segment(GF_ISOFile * movie,u32 trackNumber,u32 seg_index)1587 GF_Err gf_isom_remove_edit_segment(GF_ISOFile *movie, u32 trackNumber, u32 seg_index)
1588 {
1589 GF_Err e;
1590 GF_TrackBox *trak;
1591 GF_EdtsEntry *ent, *next_ent;
1592 trak = gf_isom_get_track_from_file(movie, trackNumber);
1593 if (!trak || !seg_index) return GF_BAD_PARAM;
1594
1595 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1596 if (e) return e;
1597
1598 if (!trak->editBox || !trak->editBox->editList) return GF_OK;
1599 if (gf_list_count(trak->editBox->editList->entryList) <= 1) return gf_isom_remove_edit_segments(movie, trackNumber);
1600
1601 ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, seg_index - 1);
1602 gf_list_rem(trak->editBox->editList->entryList, seg_index - 1);
1603 next_ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, seg_index - 1);
1604 if (next_ent) next_ent->segmentDuration += ent->segmentDuration;
1605 gf_free(ent);
1606 return SetTrackDuration(trak);
1607 }
1608
1609
1610 GF_EXPORT
gf_isom_append_edit_segment(GF_ISOFile * movie,u32 trackNumber,u64 EditDuration,u64 MediaTime,u8 EditMode)1611 GF_Err gf_isom_append_edit_segment(GF_ISOFile *movie, u32 trackNumber, u64 EditDuration, u64 MediaTime, u8 EditMode)
1612 {
1613 GF_Err e;
1614 GF_TrackBox *trak;
1615 GF_EdtsEntry *ent;
1616 trak = gf_isom_get_track_from_file(movie, trackNumber);
1617 if (!trak) return GF_BAD_PARAM;
1618 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1619 if (e) return e;
1620
1621 if (!trak->editBox) {
1622 GF_EditBox *edts = (GF_EditBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_EDTS);
1623 if (!edts) return GF_OUT_OF_MEM;
1624 trak_AddBox((GF_Box*)trak, (GF_Box *)edts);
1625 }
1626 if (!trak->editBox->editList) {
1627 GF_EditListBox *elst = (GF_EditListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_ELST);
1628 if (!elst) return GF_OUT_OF_MEM;
1629 edts_AddBox((GF_Box*)trak->editBox, (GF_Box *)elst);
1630 }
1631 ent = (GF_EdtsEntry *)gf_malloc(sizeof(GF_EdtsEntry));
1632 if (!ent) return GF_OUT_OF_MEM;
1633
1634 ent->segmentDuration = EditDuration;
1635 switch (EditMode) {
1636 case GF_ISOM_EDIT_EMPTY:
1637 ent->mediaRate = 1;
1638 ent->mediaTime = -1;
1639 break;
1640 case GF_ISOM_EDIT_DWELL:
1641 ent->mediaRate = 0;
1642 ent->mediaTime = MediaTime;
1643 break;
1644 default:
1645 ent->mediaRate = 1;
1646 ent->mediaTime = MediaTime;
1647 break;
1648 }
1649 gf_list_add(trak->editBox->editList->entryList, ent);
1650 return SetTrackDuration(trak);
1651 }
1652
1653 GF_EXPORT
gf_isom_modify_edit_segment(GF_ISOFile * movie,u32 trackNumber,u32 seg_index,u64 EditDuration,u64 MediaTime,u8 EditMode)1654 GF_Err gf_isom_modify_edit_segment(GF_ISOFile *movie, u32 trackNumber, u32 seg_index, u64 EditDuration, u64 MediaTime, u8 EditMode)
1655 {
1656 GF_Err e;
1657 GF_TrackBox *trak;
1658 GF_EdtsEntry *ent;
1659 trak = gf_isom_get_track_from_file(movie, trackNumber);
1660 if (!trak || !seg_index) return GF_BAD_PARAM;
1661 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1662 if (e) return e;
1663
1664 if (!trak->editBox || !trak->editBox->editList) return GF_OK;
1665 if (gf_list_count(trak->editBox->editList->entryList)<seg_index) return GF_BAD_PARAM;
1666 ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, seg_index - 1);
1667
1668 ent->segmentDuration = EditDuration;
1669 switch (EditMode) {
1670 case GF_ISOM_EDIT_EMPTY:
1671 ent->mediaRate = 1;
1672 ent->mediaTime = -1;
1673 break;
1674 case GF_ISOM_EDIT_DWELL:
1675 ent->mediaRate = 0;
1676 ent->mediaTime = MediaTime;
1677 break;
1678 default:
1679 ent->mediaRate = 1;
1680 ent->mediaTime = MediaTime;
1681 break;
1682 }
1683 return SetTrackDuration(trak);
1684 }
1685
1686 //removes the desired track
1687 GF_EXPORT
gf_isom_remove_track(GF_ISOFile * movie,u32 trackNumber)1688 GF_Err gf_isom_remove_track(GF_ISOFile *movie, u32 trackNumber)
1689 {
1690 GF_Err e;
1691 GF_TrackBox *the_trak, *trak;
1692 GF_TrackReferenceTypeBox *tref;
1693 u32 i, j, k, *newRefs, descIndex;
1694 u8 found;
1695 GF_ISOSample *samp;
1696 the_trak = gf_isom_get_track_from_file(movie, trackNumber);
1697 if (!the_trak) return GF_BAD_PARAM;
1698
1699 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1700 if (e) return e;
1701
1702 if (movie->moov->iods && movie->moov->iods->descriptor) {
1703 GF_Descriptor *desc;
1704 GF_ES_ID_Inc *inc;
1705 GF_List *ESDs;
1706 desc = movie->moov->iods->descriptor;
1707 if (desc->tag == GF_ODF_ISOM_IOD_TAG) {
1708 ESDs = ((GF_IsomInitialObjectDescriptor *)desc)->ES_ID_IncDescriptors;
1709 }
1710 else if (desc->tag == GF_ODF_ISOM_OD_TAG) {
1711 ESDs = ((GF_IsomObjectDescriptor *)desc)->ES_ID_IncDescriptors;
1712 }
1713 else {
1714 return GF_ISOM_INVALID_FILE;
1715 }
1716
1717 //remove the track ref from the root OD if any
1718 i = 0;
1719 while ((inc = (GF_ES_ID_Inc *)gf_list_enum(ESDs, &i))) {
1720 if (inc->trackID == the_trak->Header->trackID) {
1721 gf_odf_desc_del((GF_Descriptor *)inc);
1722 i--;
1723 gf_list_rem(ESDs, i);
1724 }
1725 }
1726 }
1727
1728 //remove the track from the movie
1729 gf_list_del_item(movie->moov->trackList, the_trak);
1730
1731 //rewrite any OD tracks
1732 i = 0;
1733 while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) {
1734 if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_OD) continue;
1735 //this is an OD track...
1736 j = gf_isom_get_sample_count(movie, i);
1737 for (k = 0; k < j; k++) {
1738 //getting the sample will remove the references to the deleted track in the output OD frame
1739 samp = gf_isom_get_sample(movie, i, k + 1, &descIndex);
1740 if (!samp) break;
1741 //so let's update with the new OD frame ! If the sample is empty, remove it
1742 if (!samp->dataLength) {
1743 e = gf_isom_remove_sample(movie, i, k + 1);
1744 if (e) return e;
1745 }
1746 else {
1747 e = gf_isom_update_sample(movie, i, k + 1, samp, GF_TRUE);
1748 if (e) return e;
1749 }
1750 //and don't forget to delete the sample
1751 gf_isom_sample_del(&samp);
1752 }
1753 }
1754
1755 //remove the track ref from any "tref" box in all tracks, except the one to delete
1756 //note that we don't touch scal references, as we don't want to rewrite AVC/HEVC samples ...
1757 i = 0;
1758 while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) {
1759 if (trak == the_trak) continue;
1760 if (!trak->References || !gf_list_count(trak->References->other_boxes)) continue;
1761
1762 j = 0;
1763 while ((tref = (GF_TrackReferenceTypeBox *)gf_list_enum(trak->References->other_boxes, &j))) {
1764 if (tref->reference_type == GF_4CC('s', 'c', 'a', 'l'))
1765 continue;
1766
1767 found = 0;
1768 for (k = 0; k<tref->trackIDCount; k++) {
1769 if (tref->trackIDs[k] == the_trak->Header->trackID) found++;
1770 }
1771 if (!found) continue;
1772 //no more refs, remove this ref_type
1773 if (found == tref->trackIDCount) {
1774 gf_isom_box_del((GF_Box *)tref);
1775 j--;
1776 gf_list_rem(trak->References->other_boxes, j);
1777 }
1778 else {
1779 newRefs = (u32*)gf_malloc(sizeof(u32) * (tref->trackIDCount - found));
1780 found = 0;
1781 for (k = 0; k < tref->trackIDCount; k++) {
1782 if (tref->trackIDs[k] != the_trak->Header->trackID) {
1783 newRefs[k - found] = tref->trackIDs[k];
1784 }
1785 else {
1786 found++;
1787 }
1788 }
1789 gf_free(tref->trackIDs);
1790 tref->trackIDs = newRefs;
1791 tref->trackIDCount -= found;
1792 }
1793 }
1794 //a little opt: remove the ref box if empty...
1795 if (!gf_list_count(trak->References->other_boxes)) {
1796 gf_isom_box_del((GF_Box *)trak->References);
1797 trak->References = NULL;
1798 }
1799 }
1800
1801 //delete the track
1802 gf_isom_box_del((GF_Box *)the_trak);
1803
1804 /*update next track ID*/
1805 movie->moov->mvhd->nextTrackID = 0;
1806 i = 0;
1807 while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) {
1808 if (trak->Header->trackID>movie->moov->mvhd->nextTrackID)
1809 movie->moov->mvhd->nextTrackID = trak->Header->trackID;
1810 }
1811
1812 if (!gf_list_count(movie->moov->trackList)) {
1813 gf_list_del_item(movie->TopBoxes, movie->moov);
1814 gf_isom_box_del((GF_Box *)movie->moov);
1815 movie->moov = NULL;
1816 }
1817 return GF_OK;
1818 }
1819
1820
1821 GF_EXPORT
gf_isom_set_copyright(GF_ISOFile * movie,const char * threeCharCode,char * notice)1822 GF_Err gf_isom_set_copyright(GF_ISOFile *movie, const char *threeCharCode, char *notice)
1823 {
1824 GF_Err e;
1825 GF_CopyrightBox *ptr;
1826 GF_UserDataMap *map;
1827 u32 count, i;
1828
1829 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1830 if (e) return e;
1831
1832 if (!notice || !threeCharCode) return GF_BAD_PARAM;
1833
1834 gf_isom_insert_moov(movie);
1835
1836 if (!movie->moov->udta) {
1837 e = moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA));
1838 if (e) return e;
1839 }
1840 map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL);
1841
1842 if (map) {
1843 //try to find one in our language...
1844 count = gf_list_count(map->other_boxes);
1845 for (i = 0; i<count; i++) {
1846 ptr = (GF_CopyrightBox*)gf_list_get(map->other_boxes, i);
1847 if (!strcmp(threeCharCode, (const char *)ptr->packedLanguageCode)) {
1848 gf_free(ptr->notice);
1849 ptr->notice = (char*)gf_malloc(sizeof(char) * (strlen(notice) + 1));
1850 strcpy(ptr->notice, notice);
1851 return GF_OK;
1852 }
1853 }
1854 }
1855 //nope, create one
1856 ptr = (GF_CopyrightBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CPRT);
1857
1858 memcpy(ptr->packedLanguageCode, threeCharCode, 4);
1859 ptr->notice = (char*)gf_malloc(sizeof(char) * (strlen(notice) + 1));
1860 strcpy(ptr->notice, notice);
1861 return udta_AddBox(movie->moov->udta, (GF_Box *)ptr);
1862 }
1863
1864 GF_EXPORT
gf_isom_add_track_kind(GF_ISOFile * movie,u32 trackNumber,const char * schemeURI,const char * value)1865 GF_Err gf_isom_add_track_kind(GF_ISOFile *movie, u32 trackNumber, const char *schemeURI, const char *value)
1866 {
1867 GF_Err e;
1868 GF_KindBox *ptr;
1869 GF_UserDataBox *udta;
1870 GF_UserDataMap *map;
1871 u32 i, count;
1872
1873 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1874 if (e) return e;
1875
1876 gf_isom_insert_moov(movie);
1877
1878 if (trackNumber) {
1879 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
1880 if (!trak) return GF_BAD_PARAM;
1881 if (!trak->udta) {
1882 e = trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA));
1883 if (e) return e;
1884 }
1885 udta = trak->udta;
1886 }
1887 else {
1888 return GF_BAD_PARAM;
1889 }
1890
1891 map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_KIND, NULL);
1892 if (map) {
1893 count = gf_list_count(map->other_boxes);
1894 for (i = 0; i<count; i++) {
1895 GF_Box *b = (GF_Box *)gf_list_get(map->other_boxes, i);
1896 if (b->type == GF_ISOM_BOX_TYPE_KIND) {
1897 GF_KindBox *kb = (GF_KindBox *)b;
1898 if (!strcmp(kb->schemeURI, schemeURI) &&
1899 ((value && kb->value && !strcmp(value, kb->value)) || (!value && !kb->value))) {
1900 // Already there
1901 return GF_OK;
1902 }
1903 }
1904 }
1905 }
1906
1907 ptr = (GF_KindBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_KIND);
1908 if (e) return e;
1909
1910 ptr->schemeURI = gf_strdup(schemeURI);
1911 if (value) ptr->value = gf_strdup(value);
1912 return udta_AddBox(udta, (GF_Box *)ptr);
1913 }
1914
1915 GF_EXPORT
gf_isom_remove_track_kind(GF_ISOFile * movie,u32 trackNumber,const char * schemeURI,const char * value)1916 GF_Err gf_isom_remove_track_kind(GF_ISOFile *movie, u32 trackNumber, const char *schemeURI, const char *value)
1917 {
1918 GF_Err e;
1919 GF_UserDataBox *udta;
1920 GF_UserDataMap *map;
1921 u32 i;
1922
1923 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1924 if (e) return e;
1925 gf_isom_insert_moov(movie);
1926
1927 if (trackNumber) {
1928 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
1929 if (!trak) return GF_BAD_PARAM;
1930 if (!trak->udta) {
1931 e = trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA));
1932 if (e) return e;
1933 }
1934 udta = trak->udta;
1935 }
1936 else {
1937 return GF_OK;
1938 }
1939 map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_KIND, NULL);
1940 if (map) {
1941 for (i = 0; i<gf_list_count(map->other_boxes); i++) {
1942 GF_Box *b = (GF_Box *)gf_list_get(map->other_boxes, i);
1943 if (b->type == GF_ISOM_BOX_TYPE_KIND) {
1944 GF_KindBox *kb = (GF_KindBox *)b;
1945 if (!schemeURI ||
1946 (!strcmp(kb->schemeURI, schemeURI) &&
1947 ((value && kb->value && !strcmp(value, kb->value)) || (!value && !kb->value)))) {
1948 gf_isom_box_del(b);
1949 gf_list_rem(map->other_boxes, i);
1950 i--;
1951 }
1952 }
1953 }
1954 }
1955 return GF_OK;
1956 }
1957
1958 GF_EXPORT
gf_isom_add_chapter(GF_ISOFile * movie,u32 trackNumber,u64 timestamp,char * name)1959 GF_Err gf_isom_add_chapter(GF_ISOFile *movie, u32 trackNumber, u64 timestamp, char *name)
1960 {
1961 GF_Err e;
1962 GF_ChapterListBox *ptr;
1963 u32 i, count;
1964 GF_ChapterEntry *ce;
1965 GF_UserDataBox *udta;
1966 GF_UserDataMap *map;
1967
1968 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
1969 if (e) return e;
1970
1971 gf_isom_insert_moov(movie);
1972
1973 if (trackNumber) {
1974 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
1975 if (!trak) return GF_BAD_PARAM;
1976 if (!trak->udta) {
1977 e = trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA));
1978 if (e) return e;
1979 }
1980 udta = trak->udta;
1981 }
1982 else {
1983 if (!movie->moov->udta) {
1984 e = moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA));
1985 if (e) return e;
1986 }
1987 udta = movie->moov->udta;
1988 }
1989
1990 ptr = NULL;
1991 map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL);
1992 if (!map) {
1993 ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL);
1994 e = udta_AddBox(udta, (GF_Box *)ptr);
1995 if (e) return e;
1996 map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL);
1997 }
1998 else {
1999 ptr = (GF_ChapterListBox*)gf_list_get(map->other_boxes, 0);
2000 }
2001 if (!map) return GF_OUT_OF_MEM;
2002
2003 /*this may happen if original MP4 is not properly formatted*/
2004 if (!ptr) {
2005 ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL);
2006 if (!ptr) return GF_OUT_OF_MEM;
2007 gf_list_add(map->other_boxes, ptr);
2008 }
2009
2010 GF_SAFEALLOC(ce, GF_ChapterEntry);
2011 if (!ce) return GF_OUT_OF_MEM;
2012
2013 ce->start_time = timestamp * 10000L;
2014 ce->name = name ? gf_strdup(name) : NULL;
2015
2016 /*insert in order*/
2017 count = gf_list_count(ptr->list);
2018 for (i = 0; i<count; i++) {
2019 GF_ChapterEntry *ace = (GF_ChapterEntry *)gf_list_get(ptr->list, i);
2020 if (ace->start_time == ce->start_time) {
2021 if (ace->name) gf_free(ace->name);
2022 ace->name = ce->name;
2023 gf_free(ce);
2024 return GF_OK;
2025 }
2026 if (ace->start_time >= ce->start_time)
2027 return gf_list_insert(ptr->list, ce, i);
2028 }
2029 return gf_list_add(ptr->list, ce);
2030 }
2031
2032
gf_isom_remove_chapter(GF_ISOFile * movie,u32 trackNumber,u32 index)2033 GF_Err gf_isom_remove_chapter(GF_ISOFile *movie, u32 trackNumber, u32 index)
2034 {
2035 GF_Err e;
2036 GF_ChapterListBox *ptr;
2037 GF_ChapterEntry *ce;
2038 GF_UserDataBox *udta;
2039 GF_UserDataMap *map;
2040
2041 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2042 if (e) return e;
2043 gf_isom_insert_moov(movie);
2044
2045 if (trackNumber) {
2046 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
2047 if (!trak) return GF_BAD_PARAM;
2048 if (!trak->udta) {
2049 e = trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA));
2050 if (e) return e;
2051 }
2052 udta = trak->udta;
2053 }
2054 else {
2055 if (!movie->moov->udta) {
2056 e = moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA));
2057 if (e) return e;
2058 }
2059 udta = movie->moov->udta;
2060 }
2061
2062 map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL);
2063 if (!map) return GF_OK;
2064 ptr = (GF_ChapterListBox*)gf_list_get(map->other_boxes, 0);
2065 if (!ptr) return GF_OK;
2066
2067 if (index) {
2068 ce = (GF_ChapterEntry *)gf_list_get(ptr->list, index - 1);
2069 if (!ce) return GF_BAD_PARAM;
2070 if (ce->name) gf_free(ce->name);
2071 gf_free(ce);
2072 gf_list_rem(ptr->list, index - 1);
2073 }
2074 else {
2075 while (gf_list_count(ptr->list)) {
2076 ce = (GF_ChapterEntry *)gf_list_get(ptr->list, 0);
2077 if (ce->name) gf_free(ce->name);
2078 gf_free(ce);
2079 gf_list_rem(ptr->list, 0);
2080 }
2081 }
2082 if (!gf_list_count(ptr->list)) {
2083 gf_list_del_item(udta->recordList, map);
2084 gf_isom_box_array_del(map->other_boxes);
2085 gf_free(map);
2086 }
2087 return GF_OK;
2088 }
2089
gf_isom_remove_copyright(GF_ISOFile * movie,u32 index)2090 GF_Err gf_isom_remove_copyright(GF_ISOFile *movie, u32 index)
2091 {
2092 GF_Err e;
2093 GF_CopyrightBox *ptr;
2094 GF_UserDataMap *map;
2095 u32 count;
2096
2097 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2098 if (e) return e;
2099 gf_isom_insert_moov(movie);
2100
2101 if (!index) return GF_BAD_PARAM;
2102 if (!movie->moov->udta) return GF_OK;
2103
2104 map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL);
2105 if (!map) return GF_OK;
2106
2107 count = gf_list_count(map->other_boxes);
2108 if (index>count) return GF_BAD_PARAM;
2109
2110 ptr = (GF_CopyrightBox*)gf_list_get(map->other_boxes, index - 1);
2111 if (ptr) {
2112 gf_list_rem(map->other_boxes, index - 1);
2113 if (ptr->notice) gf_free(ptr->notice);
2114 gf_free(ptr);
2115 }
2116 /*last copyright, remove*/
2117 if (!gf_list_count(map->other_boxes)) {
2118 gf_list_del_item(movie->moov->udta->recordList, map);
2119 gf_list_del(map->other_boxes);
2120 gf_free(map);
2121 }
2122 return GF_OK;
2123 }
2124
2125
2126
gf_isom_set_watermark(GF_ISOFile * movie,bin128 UUID,u8 * data,u32 length)2127 GF_Err gf_isom_set_watermark(GF_ISOFile *movie, bin128 UUID, u8* data, u32 length)
2128 {
2129 GF_Err e;
2130 GF_UnknownUUIDBox *ptr;
2131 GF_UserDataMap *map;
2132
2133 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2134 if (e) return e;
2135
2136 gf_isom_insert_moov(movie);
2137 if (!movie->moov->udta) {
2138 e = moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA));
2139 if (e) return e;
2140 }
2141
2142 map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_UUID, (bin128 *)& UUID);
2143 if (map) {
2144 ptr = (GF_UnknownUUIDBox *)gf_list_get(map->other_boxes, 0);
2145 if (ptr) {
2146 gf_free(ptr->data);
2147 ptr->data = (char*)gf_malloc(length);
2148 memcpy(ptr->data, data, length);
2149 ptr->dataSize = length;
2150 return GF_OK;
2151 }
2152 }
2153 //nope, create one
2154 ptr = (GF_UnknownUUIDBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID);
2155 memcpy(ptr->uuid, UUID, 16);
2156 ptr->data = (char*)gf_malloc(length);
2157 memcpy(ptr->data, data, length);
2158 ptr->dataSize = length;
2159 return udta_AddBox(movie->moov->udta, (GF_Box *)ptr);
2160 }
2161
2162 //set the interleaving time of media data (INTERLEAVED mode only)
2163 //InterleaveTime is in MovieTimeScale
gf_isom_set_interleave_time(GF_ISOFile * movie,u32 InterleaveTime)2164 GF_Err gf_isom_set_interleave_time(GF_ISOFile *movie, u32 InterleaveTime)
2165 {
2166 GF_Err e;
2167 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2168 if (e) return e;
2169
2170 if (!InterleaveTime || !movie->moov) return GF_OK;
2171 movie->interleavingTime = InterleaveTime;
2172 return GF_OK;
2173 }
2174
gf_isom_get_interleave_time(GF_ISOFile * movie)2175 u32 gf_isom_get_interleave_time(GF_ISOFile *movie)
2176 {
2177 return movie ? movie->interleavingTime : 0;
2178 }
2179
2180 //set the storage mode of a file (FLAT, STREAMABLE, INTERLEAVED)
gf_isom_get_storage_mode(GF_ISOFile * movie)2181 u8 gf_isom_get_storage_mode(GF_ISOFile *movie)
2182 {
2183 return movie ? movie->storageMode : 0;
2184 }
2185
2186
2187
2188
2189 //use a compact track version for sample size. This is not usually recommended
2190 //except for speech codecs where the track has a lot of small samples
2191 //compaction is done automatically while writing based on the track's sample sizes
gf_isom_use_compact_size(GF_ISOFile * movie,u32 trackNumber,u8 CompactionOn)2192 GF_Err gf_isom_use_compact_size(GF_ISOFile *movie, u32 trackNumber, u8 CompactionOn)
2193 {
2194 GF_TrackBox *trak;
2195 u32 i, size;
2196 GF_SampleSizeBox *stsz;
2197 GF_Err e;
2198
2199 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2200 if (e) return e;
2201
2202 trak = gf_isom_get_track_from_file(movie, trackNumber);
2203 if (!trak) return GF_BAD_PARAM;
2204
2205 if (!trak->Media || !trak->Media->information
2206 || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleSize)
2207 return GF_ISOM_INVALID_FILE;
2208
2209 stsz = trak->Media->information->sampleTable->SampleSize;
2210
2211 //switch to regular table
2212 if (!CompactionOn) {
2213 if (stsz->type == GF_ISOM_BOX_TYPE_STSZ) return GF_OK;
2214 stsz->type = GF_ISOM_BOX_TYPE_STSZ;
2215 //invalidate the sampleSize and recompute it
2216 stsz->sampleSize = 0;
2217 if (!stsz->sampleCount) return GF_OK;
2218 //if the table is empty we can only assume the track is empty (no size indication)
2219 if (!stsz->sizes) return GF_OK;
2220 size = stsz->sizes[0];
2221 //check whether the sizes are all the same or not
2222 for (i = 1; i<stsz->sampleCount; i++) {
2223 if (size != stsz->sizes[i]) {
2224 size = 0;
2225 break;
2226 }
2227 }
2228 if (size) {
2229 gf_free(stsz->sizes);
2230 stsz->sizes = NULL;
2231 stsz->sampleSize = size;
2232 }
2233 return GF_OK;
2234 }
2235
2236 //switch to compact table
2237 if (stsz->type == GF_ISOM_BOX_TYPE_STZ2) return GF_OK;
2238 //fill the table. Although it seems weird , this is needed in case of edition
2239 //after the function is called. NOte however than we force regular table
2240 //at write time if all samples are of same size
2241 if (stsz->sampleSize) {
2242 //this is a weird table indeed ;)
2243 if (stsz->sizes) gf_free(stsz->sizes);
2244 stsz->sizes = (u32*)gf_malloc(sizeof(u32)*stsz->sampleCount);
2245 memset(stsz->sizes, stsz->sampleSize, sizeof(u32));
2246 }
2247 //set the SampleSize to 0 while the file is open
2248 stsz->sampleSize = 0;
2249 stsz->type = GF_ISOM_BOX_TYPE_STZ2;
2250 return GF_OK;
2251 }
2252
2253
2254 GF_EXPORT
gf_isom_set_brand_info(GF_ISOFile * movie,u32 MajorBrand,u32 MinorVersion)2255 GF_Err gf_isom_set_brand_info(GF_ISOFile *movie, u32 MajorBrand, u32 MinorVersion)
2256 {
2257 u32 i, *p;
2258
2259 if (!MajorBrand) return GF_BAD_PARAM;
2260
2261 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
2262 if (!(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) {
2263 GF_Err e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2264 if (e) return e;
2265
2266 e = CheckNoData(movie);
2267 if (e) return e;
2268 }
2269 #endif
2270
2271 if (!movie->brand) {
2272 movie->brand = (GF_FileTypeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP);
2273 gf_list_add(movie->TopBoxes, movie->brand);
2274 }
2275
2276 movie->brand->majorBrand = MajorBrand;
2277 movie->brand->minorVersion = MinorVersion;
2278
2279 if (!movie->brand->altBrand) {
2280 movie->brand->altBrand = (u32*)gf_malloc(sizeof(u32));
2281 movie->brand->altBrand[0] = MajorBrand;
2282 movie->brand->altCount = 1;
2283 return GF_OK;
2284 }
2285
2286 //if brand already present don't change anything
2287 for (i = 0; i<movie->brand->altCount; i++) {
2288 if (movie->brand->altBrand[i] == MajorBrand) return GF_OK;
2289 }
2290 p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount + 1));
2291 if (!p) return GF_OUT_OF_MEM;
2292 memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount);
2293 p[movie->brand->altCount] = MajorBrand;
2294 movie->brand->altCount += 1;
2295 gf_free(movie->brand->altBrand);
2296 movie->brand->altBrand = p;
2297 return GF_OK;
2298 }
2299
2300
2301 GF_EXPORT
gf_isom_modify_alternate_brand(GF_ISOFile * movie,u32 Brand,u8 AddIt)2302 GF_Err gf_isom_modify_alternate_brand(GF_ISOFile *movie, u32 Brand, u8 AddIt)
2303 {
2304 u32 i, k, *p;
2305
2306 if (!Brand) return GF_BAD_PARAM;
2307
2308 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
2309 if (!(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) {
2310 GF_Err e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2311 if (e) return e;
2312
2313 e = CheckNoData(movie);
2314 if (e) return e;
2315 }
2316 #endif
2317
2318 if (!movie->brand && AddIt) {
2319 movie->brand = (GF_FileTypeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP);
2320 if (!movie->brand) return GF_OUT_OF_MEM;
2321 gf_list_add(movie->TopBoxes, movie->brand);
2322 }
2323 if (!AddIt && !movie->brand) return GF_OK;
2324
2325 //do not mofify major one
2326 if (!AddIt && movie->brand->majorBrand == Brand) return GF_OK;
2327
2328 if (!AddIt && movie->brand->altCount == 1) {
2329 //fixes it in case
2330 movie->brand->altBrand[0] = movie->brand->majorBrand;
2331 return GF_OK;
2332 }
2333 //check for the brand
2334 for (i = 0; i<movie->brand->altCount; i++) {
2335 if (movie->brand->altBrand[i] == Brand) goto found;
2336 }
2337 //Not found
2338 if (!AddIt) return GF_OK;
2339 //add it
2340 p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount + 1));
2341 if (!p) return GF_OUT_OF_MEM;
2342 memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount);
2343 p[movie->brand->altCount] = Brand;
2344 movie->brand->altCount += 1;
2345 gf_free(movie->brand->altBrand);
2346 movie->brand->altBrand = p;
2347 return GF_OK;
2348
2349 found:
2350
2351 //found
2352 if (AddIt) return GF_OK;
2353 assert(movie->brand->altCount>1);
2354
2355 //remove it
2356 p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount - 1));
2357 if (!p) return GF_OUT_OF_MEM;
2358 k = 0;
2359 for (i = 0; i<movie->brand->altCount; i++) {
2360 if (movie->brand->altBrand[i] == Brand) continue;
2361 else {
2362 p[k] = movie->brand->altBrand[i];
2363 k++;
2364 }
2365 }
2366 movie->brand->altCount -= 1;
2367 gf_free(movie->brand->altBrand);
2368 movie->brand->altBrand = p;
2369 return GF_OK;
2370 }
2371
2372
2373 GF_EXPORT
gf_isom_reset_alt_brands(GF_ISOFile * movie)2374 GF_Err gf_isom_reset_alt_brands(GF_ISOFile *movie)
2375 {
2376 u32 *p;
2377
2378 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
2379 if (!(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) {
2380 GF_Err e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2381 if (e) return e;
2382
2383 e = CheckNoData(movie);
2384 if (e) return e;
2385 }
2386 #endif
2387
2388 if (!movie->brand) {
2389 movie->brand = (GF_FileTypeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP);
2390 gf_list_add(movie->TopBoxes, movie->brand);
2391 }
2392 p = (u32*)gf_malloc(sizeof(u32));
2393 if (!p) return GF_OUT_OF_MEM;
2394 p[0] = movie->brand->majorBrand;
2395 movie->brand->altCount = 1;
2396 gf_free(movie->brand->altBrand);
2397 movie->brand->altBrand = p;
2398 return GF_OK;
2399 }
2400
2401 GF_EXPORT
gf_isom_set_sample_padding_bits(GF_ISOFile * movie,u32 trackNumber,u32 sampleNumber,u8 NbBits)2402 GF_Err gf_isom_set_sample_padding_bits(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u8 NbBits)
2403 {
2404 GF_TrackBox *trak;
2405 GF_Err e;
2406
2407 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2408 if (e) return e;
2409
2410 trak = gf_isom_get_track_from_file(movie, trackNumber);
2411 if (!trak || NbBits > 7) return GF_BAD_PARAM;
2412
2413 //set Padding info
2414 return stbl_SetPaddingBits(trak->Media->information->sampleTable, sampleNumber, NbBits);
2415 }
2416
2417
2418 GF_EXPORT
gf_isom_remove_user_data_item(GF_ISOFile * movie,u32 trackNumber,u32 UserDataType,bin128 UUID,u32 UserDataIndex)2419 GF_Err gf_isom_remove_user_data_item(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, u32 UserDataIndex)
2420 {
2421 GF_UserDataMap *map;
2422 GF_Box *a;
2423 u32 i;
2424 bin128 t;
2425 GF_Err e;
2426 GF_TrackBox *trak;
2427 GF_UserDataBox *udta;
2428
2429 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2430 if (e) return e;
2431
2432 if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0;
2433 memset(t, 1, 16);
2434
2435 if (trackNumber) {
2436 trak = gf_isom_get_track_from_file(movie, trackNumber);
2437 if (!trak) return GF_BAD_PARAM;
2438 udta = trak->udta;
2439 }
2440 else {
2441 udta = movie->moov->udta;
2442 }
2443 if (!udta) return GF_BAD_PARAM;
2444 if (!UserDataIndex) return GF_BAD_PARAM;
2445
2446 i = 0;
2447 while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) {
2448 if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found;
2449 else if (map->boxType == UserDataType) goto found;
2450 }
2451 //not found
2452 return GF_OK;
2453
2454 found:
2455
2456 if (UserDataIndex > gf_list_count(map->other_boxes)) return GF_BAD_PARAM;
2457 //delete the box
2458 a = (GF_Box*)gf_list_get(map->other_boxes, UserDataIndex - 1);
2459
2460 gf_list_rem(map->other_boxes, UserDataIndex - 1);
2461 gf_isom_box_del(a);
2462
2463 //remove the map if empty
2464 if (!gf_list_count(map->other_boxes)) {
2465 gf_list_rem(udta->recordList, i - 1);
2466 gf_isom_box_array_del(map->other_boxes);
2467 gf_free(map);
2468 }
2469 //but we keep the UDTA no matter what
2470 return GF_OK;
2471 }
2472
2473 GF_EXPORT
gf_isom_remove_user_data(GF_ISOFile * movie,u32 trackNumber,u32 UserDataType,bin128 UUID)2474 GF_Err gf_isom_remove_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID)
2475 {
2476 GF_UserDataMap *map;
2477 u32 i;
2478 GF_Err e;
2479 bin128 t;
2480 GF_TrackBox *trak;
2481 GF_UserDataBox *udta;
2482
2483 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2484 if (e) return e;
2485
2486 if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0;
2487 memset(t, 1, 16);
2488
2489 if (trackNumber) {
2490 trak = gf_isom_get_track_from_file(movie, trackNumber);
2491 if (!trak) return GF_BAD_PARAM;
2492 udta = trak->udta;
2493 }
2494 else {
2495 udta = movie->moov->udta;
2496 }
2497 if (!udta) return GF_BAD_PARAM;
2498
2499 i = 0;
2500 while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) {
2501 if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found;
2502 else if (map->boxType == UserDataType) goto found;
2503 }
2504 //not found
2505 return GF_OK;
2506
2507 found:
2508
2509 gf_list_rem(udta->recordList, i - 1);
2510 gf_isom_box_array_del(map->other_boxes);
2511 gf_free(map);
2512
2513 //but we keep the UDTA no matter what
2514 return GF_OK;
2515 }
2516
2517 GF_EXPORT
gf_isom_add_user_data(GF_ISOFile * movie,u32 trackNumber,u32 UserDataType,bin128 UUID,char * data,u32 DataLength)2518 GF_Err gf_isom_add_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, char *data, u32 DataLength)
2519 {
2520 GF_UnknownBox *a;
2521 GF_Err e;
2522 GF_TrackBox *trak;
2523 GF_UserDataBox *udta;
2524
2525 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2526 if (e) return e;
2527
2528 if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0;
2529
2530 if (trackNumber) {
2531 trak = gf_isom_get_track_from_file(movie, trackNumber);
2532 if (!trak) return GF_BAD_PARAM;
2533 if (!trak->udta) trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA));
2534 udta = trak->udta;
2535 }
2536 else {
2537 if (!movie->moov->udta) moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA));
2538 udta = movie->moov->udta;
2539 }
2540 if (!udta) return GF_OUT_OF_MEM;
2541
2542 //create a default box
2543 if (UserDataType) {
2544 a = (GF_UnknownBox *)gf_isom_box_new(UserDataType);
2545 }
2546 else {
2547 a = (GF_UnknownBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID);
2548 memcpy(((GF_UUIDBox*)a)->uuid, UUID, 16);
2549 }
2550
2551 if (DataLength) {
2552 a->data = (char*)gf_malloc(sizeof(char)*DataLength);
2553 memcpy(a->data, data, DataLength);
2554 a->dataSize = DataLength;
2555 }
2556 return udta_AddBox(udta, (GF_Box *)a);
2557 }
2558
2559 GF_EXPORT
gf_isom_add_user_data_boxes(GF_ISOFile * movie,u32 trackNumber,char * data,u32 DataLength)2560 GF_Err gf_isom_add_user_data_boxes(GF_ISOFile *movie, u32 trackNumber, char *data, u32 DataLength)
2561 {
2562 GF_Err e;
2563 GF_TrackBox *trak;
2564 GF_UserDataBox *udta;
2565 GF_BitStream *bs;
2566
2567 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2568 if (e) return e;
2569
2570 if (trackNumber) {
2571 trak = gf_isom_get_track_from_file(movie, trackNumber);
2572 if (!trak) return GF_BAD_PARAM;
2573 if (!trak->udta) trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA));
2574 udta = trak->udta;
2575 }
2576 else {
2577 if (!movie->moov->udta) moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA));
2578 udta = movie->moov->udta;
2579 }
2580 if (!udta) return GF_OUT_OF_MEM;
2581
2582 bs = gf_bs_new(data, DataLength, GF_BITSTREAM_READ);
2583 while (gf_bs_available(bs)) {
2584 GF_Box *a;
2585 e = gf_isom_parse_box(&a, bs);
2586 if (e) break;
2587 e = udta_AddBox(udta, a);
2588 if (e) break;
2589 }
2590 gf_bs_del(bs);
2591 return e;
2592 }
2593
2594
gf_isom_add_sample_fragment(GF_ISOFile * movie,u32 trackNumber,u32 sampleNumber,u16 FragmentSize)2595 GF_Err gf_isom_add_sample_fragment(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u16 FragmentSize)
2596 {
2597 GF_Err e;
2598 GF_TrackBox *trak;
2599
2600 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2601 if (e) return e;
2602
2603 trak = gf_isom_get_track_from_file(movie, trackNumber);
2604 if (!trak || !sampleNumber || !FragmentSize) return GF_BAD_PARAM;
2605
2606 //set Padding info
2607 return stbl_AddSampleFragment(trak->Media->information->sampleTable, sampleNumber, FragmentSize);
2608 }
2609
2610
2611 GF_EXPORT
gf_isom_remove_sample_fragment(GF_ISOFile * movie,u32 trackNumber,u32 sampleNumber)2612 GF_Err gf_isom_remove_sample_fragment(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber)
2613 {
2614 GF_TrackBox *trak;
2615 GF_Err e;
2616
2617 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2618 if (e) return e;
2619
2620 trak = gf_isom_get_track_from_file(movie, trackNumber);
2621 if (!trak) return GF_BAD_PARAM;
2622 return stbl_RemoveSampleFragments(trak->Media->information->sampleTable, sampleNumber);
2623 }
2624
gf_isom_remove_sample_fragments(GF_ISOFile * movie,u32 trackNumber)2625 GF_Err gf_isom_remove_sample_fragments(GF_ISOFile *movie, u32 trackNumber)
2626 {
2627 GF_TrackBox *trak;
2628 GF_Err e;
2629
2630 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
2631 if (e) return e;
2632
2633 trak = gf_isom_get_track_from_file(movie, trackNumber);
2634 if (!trak) return GF_BAD_PARAM;
2635
2636 if (trak->Media->information->sampleTable->Fragments) {
2637 gf_isom_box_del((GF_Box *)trak->Media->information->sampleTable->Fragments);
2638 trak->Media->information->sampleTable->Fragments = NULL;
2639 }
2640 return GF_OK;
2641 }
2642
2643 GF_EXPORT
gf_isom_clone_pl_indications(GF_ISOFile * orig,GF_ISOFile * dest)2644 GF_Err gf_isom_clone_pl_indications(GF_ISOFile *orig, GF_ISOFile *dest)
2645 {
2646 GF_IsomInitialObjectDescriptor *iod_d;
2647 if (!orig || !dest) return GF_BAD_PARAM;
2648 if (!orig->moov->iods || !orig->moov->iods->descriptor) return GF_OK;
2649 if (orig->moov->iods->descriptor->tag != GF_ODF_ISOM_IOD_TAG) return GF_OK;
2650
2651 AddMovieIOD(dest->moov, 1);
2652 gf_odf_desc_del((GF_Descriptor *)dest->moov->iods->descriptor);
2653 gf_odf_desc_copy((GF_Descriptor *)orig->moov->iods->descriptor, (GF_Descriptor **)&dest->moov->iods->descriptor);
2654 iod_d = (GF_IsomInitialObjectDescriptor *)dest->moov->iods->descriptor;
2655 while (gf_list_count(iod_d->ES_ID_IncDescriptors)) {
2656 GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_IncDescriptors, 0);
2657 gf_list_rem(iod_d->ES_ID_IncDescriptors, 0);
2658 gf_odf_desc_del(d);
2659 }
2660 while (gf_list_count(iod_d->ES_ID_RefDescriptors)) {
2661 GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_RefDescriptors, 0);
2662 gf_list_rem(iod_d->ES_ID_RefDescriptors, 0);
2663 gf_odf_desc_del(d);
2664 }
2665 return GF_OK;
2666 }
2667
gf_isom_clone_box(GF_Box * src,GF_Box ** dst)2668 GF_Err gf_isom_clone_box(GF_Box *src, GF_Box **dst)
2669 {
2670 GF_Err e;
2671 char *data;
2672 u32 data_size;
2673 GF_BitStream *bs;
2674
2675 if (*dst) {
2676 gf_isom_box_del(*dst);
2677 *dst = NULL;
2678 }
2679 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
2680 if (!bs) return GF_OUT_OF_MEM;
2681 e = gf_isom_box_size((GF_Box *)src);
2682 if (!e) e = gf_isom_box_write((GF_Box *)src, bs);
2683 gf_bs_get_content(bs, &data, &data_size);
2684 gf_bs_del(bs);
2685 if (e) return e;
2686 bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
2687 if (!bs) return GF_OUT_OF_MEM;
2688 e = gf_isom_parse_box(dst, bs);
2689 gf_bs_del(bs);
2690 gf_free(data);
2691 return e;
2692 }
2693
gf_isom_clone_movie(GF_ISOFile * orig_file,GF_ISOFile * dest_file,Bool clone_tracks,Bool keep_hint_tracks,Bool keep_pssh)2694 GF_Err gf_isom_clone_movie(GF_ISOFile *orig_file, GF_ISOFile *dest_file, Bool clone_tracks, Bool keep_hint_tracks, Bool keep_pssh)
2695 {
2696 GF_Err e;
2697 u32 i;
2698 GF_Box *box;
2699
2700 e = CanAccessMovie(dest_file, GF_ISOM_OPEN_WRITE);
2701 if (e) return e;
2702
2703 if (orig_file->brand) {
2704 gf_list_del_item(dest_file->TopBoxes, dest_file->brand);
2705 gf_isom_box_del((GF_Box *)dest_file->brand);
2706 dest_file->brand = NULL;
2707 gf_isom_clone_box((GF_Box *)orig_file->brand, (GF_Box **)&dest_file->brand);
2708 if (dest_file->brand) gf_list_add(dest_file->TopBoxes, dest_file->brand);
2709 }
2710
2711 if (orig_file->meta) {
2712 gf_list_del_item(dest_file->TopBoxes, dest_file->meta);
2713 gf_isom_box_del((GF_Box *)dest_file->meta);
2714 dest_file->meta = NULL;
2715 /*fixme - check imports*/
2716 gf_isom_clone_box((GF_Box *)orig_file->meta, (GF_Box **)&dest_file->meta);
2717 if (dest_file->meta) gf_list_add(dest_file->TopBoxes, dest_file->meta);
2718 }
2719 if (orig_file->moov) {
2720 u32 i, dstTrack;
2721 GF_Box *iods;
2722 GF_List *tracks = gf_list_new();
2723 GF_List *old_tracks = orig_file->moov->trackList;
2724 orig_file->moov->trackList = tracks;
2725 iods = (GF_Box*)orig_file->moov->iods;
2726 orig_file->moov->iods = NULL;
2727 e = gf_isom_clone_box((GF_Box *)orig_file->moov, (GF_Box **)&dest_file->moov);
2728 if (e) {
2729 gf_list_del(tracks);
2730 orig_file->moov->trackList = old_tracks;
2731 return e;
2732 }
2733 orig_file->moov->trackList = old_tracks;
2734 gf_list_del(tracks);
2735 orig_file->moov->iods = (GF_ObjectDescriptorBox*)iods;
2736 gf_list_add(dest_file->TopBoxes, dest_file->moov);
2737
2738 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
2739 if (dest_file->moov->mvex) {
2740 gf_isom_box_del((GF_Box *)dest_file->moov->mvex);
2741 dest_file->moov->mvex = NULL;
2742 }
2743 #endif
2744
2745 if (clone_tracks) {
2746 for (i = 0; i<gf_list_count(orig_file->moov->trackList); i++) {
2747 GF_TrackBox *trak = (GF_TrackBox*)gf_list_get(orig_file->moov->trackList, i);
2748 if (!trak) continue;
2749 if (keep_hint_tracks || (trak->Media->handler->handlerType != GF_ISOM_MEDIA_HINT)) {
2750 e = gf_isom_clone_track(orig_file, i + 1, dest_file, GF_FALSE, &dstTrack);
2751 if (e) return e;
2752 }
2753 }
2754 if (iods)
2755 gf_isom_clone_box((GF_Box *)orig_file->moov->iods, (GF_Box **)dest_file->moov->iods);
2756 }
2757 else {
2758 dest_file->moov->mvhd->nextTrackID = 1;
2759 gf_isom_clone_pl_indications(orig_file, dest_file);
2760 }
2761 dest_file->moov->mov = dest_file;
2762 }
2763
2764 if (!keep_pssh) {
2765 i = 0;
2766 while ((box = (GF_Box*)gf_list_get(dest_file->moov->other_boxes, i++))) {
2767 if (box->type == GF_ISOM_BOX_TYPE_PSSH) {
2768 i--;
2769 gf_list_rem(dest_file->moov->other_boxes, i);
2770 gf_isom_box_del(box);
2771 }
2772 }
2773 }
2774
2775 //duplicate other boxes
2776 i = 0;
2777 while ((box = (GF_Box*)gf_list_get(orig_file->TopBoxes, i++))) {
2778 switch (box->type) {
2779 case GF_ISOM_BOX_TYPE_MOOV:
2780 case GF_ISOM_BOX_TYPE_META:
2781 case GF_ISOM_BOX_TYPE_MDAT:
2782 case GF_ISOM_BOX_TYPE_FTYP:
2783 case GF_ISOM_BOX_TYPE_PDIN:
2784 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
2785 case GF_ISOM_BOX_TYPE_STYP:
2786 case GF_ISOM_BOX_TYPE_SIDX:
2787 case GF_ISOM_BOX_TYPE_MOOF:
2788 #endif
2789 case GF_4CC('j', 'P', ' ', ' '):
2790 break;
2791
2792 case GF_ISOM_BOX_TYPE_PSSH:
2793 if (!keep_pssh)
2794 break;
2795
2796 default:
2797 {
2798 GF_Box *box2 = NULL;
2799 gf_isom_clone_box(box, &box2);
2800 gf_list_add(dest_file->TopBoxes, box2);
2801 }
2802 break;
2803 }
2804 }
2805
2806 return GF_OK;
2807 }
2808
2809
2810 GF_EXPORT
gf_isom_clone_track(GF_ISOFile * orig_file,u32 orig_track,GF_ISOFile * dest_file,Bool keep_data_ref,u32 * dest_track)2811 GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *dest_file, Bool keep_data_ref, u32 *dest_track)
2812 {
2813 GF_TrackBox *trak, *new_tk;
2814 GF_BitStream *bs;
2815 char *data;
2816 const char *buffer;
2817 u32 data_size;
2818 Double ts_scale;
2819 GF_Err e;
2820 GF_SampleEntryBox *entry;
2821 GF_SampleTableBox *stbl, *stbl_temp;
2822
2823 e = CanAccessMovie(dest_file, GF_ISOM_OPEN_WRITE);
2824 if (e) return e;
2825 gf_isom_insert_moov(dest_file);
2826
2827 /*get orig sample desc and clone it*/
2828 trak = gf_isom_get_track_from_file(orig_file, orig_track);
2829 if (!trak || !trak->Media) return GF_BAD_PARAM;
2830
2831 stbl = trak->Media->information->sampleTable;
2832 stbl_temp = (GF_SampleTableBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL);
2833 /*clone sampleDescription table*/
2834 stbl_temp->SampleDescription = stbl->SampleDescription;
2835 /*also clone sampleGroups description tables if any*/
2836 stbl_temp->sampleGroupsDescription = stbl->sampleGroupsDescription;
2837 trak->Media->information->sampleTable = stbl_temp;
2838 /*clone CompositionToDecode table, we may remove it later*/
2839 stbl_temp->CompositionToDecode = stbl->CompositionToDecode;
2840
2841 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
2842
2843 gf_isom_box_size((GF_Box *)trak);
2844 gf_isom_box_write((GF_Box *)trak, bs);
2845 gf_bs_get_content(bs, &data, &data_size);
2846 gf_bs_del(bs);
2847 bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
2848 e = gf_isom_parse_box((GF_Box **)&new_tk, bs);
2849 gf_bs_del(bs);
2850 gf_free(data);
2851 trak->Media->information->sampleTable = stbl;
2852
2853 stbl_temp->SampleDescription = NULL;
2854 stbl_temp->sampleGroupsDescription = NULL;
2855 stbl_temp->CompositionToDecode = NULL;
2856 gf_isom_box_del((GF_Box *)stbl_temp);
2857 if (e) return e;
2858
2859 /*create default boxes*/
2860 stbl = new_tk->Media->information->sampleTable;
2861 stbl->ChunkOffset = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO);
2862 stbl->SampleSize = (GF_SampleSizeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSZ);
2863 stbl->SampleToChunk = (GF_SampleToChunkBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
2864 stbl->TimeToSample = (GF_TimeToSampleBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STTS);
2865
2866 /*check trackID validity before adding track*/
2867 if (gf_isom_get_track_by_id(dest_file, new_tk->Header->trackID)) {
2868 u32 ID = 1;
2869 while (1) {
2870 if (RequestTrack(dest_file->moov, ID)) break;
2871 ID += 1;
2872 if (ID == 0xFFFFFFFF) break;
2873 }
2874 new_tk->Header->trackID = ID;
2875 }
2876
2877 moov_AddBox((GF_Box*)dest_file->moov, (GF_Box *)new_tk);
2878
2879 /*set originalID*/
2880 new_tk->originalID = trak->Header->trackID;
2881 /*set originalFile*/
2882 buffer = gf_isom_get_filename(orig_file);
2883 new_tk->originalFile = gf_crc_32(buffer, sizeof(buffer));
2884
2885 /*rewrite edit list segmentDuration to new movie timescale*/
2886 ts_scale = dest_file->moov->mvhd->timeScale;
2887 ts_scale /= orig_file->moov->mvhd->timeScale;
2888 new_tk->Header->duration = (u64)(s64)((s64)new_tk->Header->duration * ts_scale);
2889 if (new_tk->editBox && new_tk->editBox->editList) {
2890 u32 i, count = gf_list_count(new_tk->editBox->editList->entryList);
2891 for (i = 0; i<count; i++) {
2892 GF_EdtsEntry *ent = (GF_EdtsEntry *)gf_list_get(new_tk->editBox->editList->entryList, i);
2893 ent->segmentDuration = (u64)(s64)((s64)ent->segmentDuration * ts_scale);
2894 }
2895 }
2896
2897 /*reset data ref*/
2898 if (!keep_data_ref) {
2899 gf_isom_box_array_del(new_tk->Media->information->dataInformation->dref->other_boxes);
2900 new_tk->Media->information->dataInformation->dref->other_boxes = gf_list_new();
2901 /*update data ref*/
2902 entry = (GF_SampleEntryBox*)gf_list_get(new_tk->Media->information->sampleTable->SampleDescription->other_boxes, 0);
2903 if (entry) {
2904 u32 dref;
2905 Media_CreateDataRef(new_tk->Media->information->dataInformation->dref, NULL, NULL, &dref);
2906 entry->dataReferenceIndex = dref;
2907 }
2908 }
2909 else {
2910 u32 i;
2911 for (i = 0; i<gf_list_count(new_tk->Media->information->dataInformation->dref->other_boxes); i++) {
2912 GF_DataEntryBox *dref_entry = (GF_DataEntryBox *)gf_list_get(new_tk->Media->information->dataInformation->dref->other_boxes, i);
2913 if (dref_entry->flags & 1) {
2914 dref_entry->flags &= ~1;
2915 dref_entry->location = gf_strdup(orig_file->fileName);
2916 }
2917 }
2918 }
2919
2920 *dest_track = gf_list_count(dest_file->moov->trackList);
2921
2922 if (dest_file->moov->mvhd->nextTrackID <= new_tk->Header->trackID)
2923 dest_file->moov->mvhd->nextTrackID = new_tk->Header->trackID + 1;
2924
2925 return GF_OK;
2926 }
2927
gf_isom_clone_sample_descriptions(GF_ISOFile * the_file,u32 trackNumber,GF_ISOFile * orig_file,u32 orig_track,Bool reset_existing)2928 GF_Err gf_isom_clone_sample_descriptions(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, Bool reset_existing)
2929 {
2930 u32 i;
2931 GF_TrackBox *dst_trak, *src_trak;
2932 GF_Err e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE);
2933 if (e) return e;
2934
2935 dst_trak = gf_isom_get_track_from_file(the_file, trackNumber);
2936 if (!dst_trak || !dst_trak->Media) return GF_BAD_PARAM;
2937 src_trak = gf_isom_get_track_from_file(orig_file, orig_track);
2938 if (!src_trak || !src_trak->Media) return GF_BAD_PARAM;
2939
2940 if (reset_existing) {
2941 gf_isom_box_array_del(dst_trak->Media->information->sampleTable->SampleDescription->other_boxes);
2942 dst_trak->Media->information->sampleTable->SampleDescription->other_boxes = gf_list_new();
2943 }
2944
2945 for (i = 0; i<gf_list_count(src_trak->Media->information->sampleTable->SampleDescription->other_boxes); i++) {
2946 u32 outDesc;
2947 e = gf_isom_clone_sample_description(the_file, trackNumber, orig_file, orig_track, i + 1, NULL, NULL, &outDesc);
2948 if (e) break;
2949 }
2950 return e;
2951 }
2952
2953
2954 GF_EXPORT
gf_isom_clone_sample_description(GF_ISOFile * the_file,u32 trackNumber,GF_ISOFile * orig_file,u32 orig_track,u32 orig_desc_index,char * URLname,char * URNname,u32 * outDescriptionIndex)2955 GF_Err gf_isom_clone_sample_description(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, u32 orig_desc_index, char *URLname, char *URNname, u32 *outDescriptionIndex)
2956 {
2957 GF_TrackBox *trak;
2958 GF_BitStream *bs;
2959 char *data;
2960 u32 data_size;
2961 GF_Box *entry;
2962 GF_Err e;
2963 u32 dataRefIndex;
2964
2965 e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE);
2966 if (e) return e;
2967
2968 /*get orig sample desc and clone it*/
2969 trak = gf_isom_get_track_from_file(orig_file, orig_track);
2970 if (!trak || !trak->Media) return GF_BAD_PARAM;
2971
2972 entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, orig_desc_index - 1);
2973 if (!entry) return GF_BAD_PARAM;
2974
2975 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
2976
2977 gf_isom_box_size(entry);
2978 gf_isom_box_write(entry, bs);
2979 gf_bs_get_content(bs, &data, &data_size);
2980 gf_bs_del(bs);
2981 bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
2982 e = gf_isom_parse_box(&entry, bs);
2983 gf_bs_del(bs);
2984 gf_free(data);
2985 if (e) return e;
2986
2987 /*get new track and insert clone*/
2988 trak = gf_isom_get_track_from_file(the_file, trackNumber);
2989 if (!trak || !trak->Media) goto exit;
2990
2991 /*get or create the data ref*/
2992 e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex);
2993 if (e) goto exit;
2994 if (!dataRefIndex) {
2995 e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex);
2996 if (e) goto exit;
2997 }
2998 if (!the_file->keep_utc)
2999 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
3000 /*overwrite dref*/
3001 ((GF_SampleEntryBox *)entry)->dataReferenceIndex = dataRefIndex;
3002 e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->other_boxes, entry);
3003 *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes);
3004
3005 /*also clone track w/h info*/
3006 if (gf_isom_get_media_type(the_file, trackNumber) == GF_ISOM_MEDIA_VISUAL) {
3007 gf_isom_set_visual_info(the_file, trackNumber, (*outDescriptionIndex), ((GF_VisualSampleEntryBox*)entry)->Width, ((GF_VisualSampleEntryBox*)entry)->Height);
3008 }
3009 return e;
3010
3011 exit:
3012 gf_isom_box_del(entry);
3013 return e;
3014 }
3015
3016
gf_isom_new_generic_sample_description(GF_ISOFile * movie,u32 trackNumber,char * URLname,char * URNname,GF_GenericSampleDescription * udesc,u32 * outDescriptionIndex)3017 GF_Err gf_isom_new_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, char *URLname, char *URNname, GF_GenericSampleDescription *udesc, u32 *outDescriptionIndex)
3018 {
3019 GF_TrackBox *trak;
3020 GF_Err e;
3021 u32 dataRefIndex;
3022
3023 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3024 if (e) return e;
3025
3026 trak = gf_isom_get_track_from_file(movie, trackNumber);
3027 if (!trak || !trak->Media || !udesc) return GF_BAD_PARAM;
3028
3029 //get or create the data ref
3030 e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex);
3031 if (e) return e;
3032 if (!dataRefIndex) {
3033 e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex);
3034 if (e) return e;
3035 }
3036 if (!movie->keep_utc)
3037 trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
3038
3039 if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_VISUAL) {
3040 GF_GenericVisualSampleEntryBox *entry;
3041 //create a new entry
3042 entry = (GF_GenericVisualSampleEntryBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRV);
3043 if (!entry) return GF_OUT_OF_MEM;
3044
3045 if (!udesc->codec_tag) {
3046 entry->EntryType = GF_ISOM_BOX_TYPE_UUID;
3047 memcpy(entry->uuid, udesc->UUID, sizeof(bin128));
3048 }
3049 else {
3050 entry->EntryType = udesc->codec_tag;
3051 }
3052 entry->dataReferenceIndex = dataRefIndex;
3053 entry->vendor = udesc->vendor_code;
3054 entry->version = udesc->version;
3055 entry->revision = udesc->revision;
3056 entry->temporal_quality = udesc->temporal_quality;
3057 entry->spatial_quality = udesc->spatial_quality;
3058 entry->Width = udesc->width;
3059 entry->Height = udesc->height;
3060 strcpy(entry->compressor_name, udesc->compressor_name);
3061 entry->color_table_index = -1;
3062 entry->frames_per_sample = 1;
3063 entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000;
3064 entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000;
3065 entry->bit_depth = udesc->depth ? udesc->depth : 0x18;
3066 if (udesc->extension_buf && udesc->extension_buf_size) {
3067 entry->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
3068 if (!entry->data) {
3069 gf_isom_box_del((GF_Box *)entry);
3070 return GF_OUT_OF_MEM;
3071 }
3072 memcpy(entry->data, udesc->extension_buf, udesc->extension_buf_size);
3073 entry->data_size = udesc->extension_buf_size;
3074 }
3075 e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->other_boxes, entry);
3076 }
3077 else if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_AUDIO) {
3078 GF_GenericAudioSampleEntryBox *gena;
3079 //create a new entry
3080 gena = (GF_GenericAudioSampleEntryBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRA);
3081 if (!gena) return GF_OUT_OF_MEM;
3082
3083 if (!udesc->codec_tag) {
3084 gena->EntryType = GF_ISOM_BOX_TYPE_UUID;
3085 memcpy(gena->uuid, udesc->UUID, sizeof(bin128));
3086 }
3087 else {
3088 gena->EntryType = udesc->codec_tag;
3089 }
3090 gena->dataReferenceIndex = dataRefIndex;
3091 gena->vendor = udesc->vendor_code;
3092 gena->version = udesc->version;
3093 gena->revision = udesc->revision;
3094 gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16;
3095 gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2;
3096 gena->samplerate_hi = udesc->samplerate;
3097 gena->samplerate_lo = 0;
3098
3099 if (udesc->extension_buf && udesc->extension_buf_size) {
3100 gena->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
3101 if (!gena->data) {
3102 gf_isom_box_del((GF_Box *)gena);
3103 return GF_OUT_OF_MEM;
3104 }
3105 memcpy(gena->data, udesc->extension_buf, udesc->extension_buf_size);
3106 gena->data_size = udesc->extension_buf_size;
3107 }
3108 e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->other_boxes, gena);
3109 }
3110 else {
3111 GF_GenericSampleEntryBox *genm;
3112 //create a new entry
3113 genm = (GF_GenericSampleEntryBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRM);
3114 if (!genm) return GF_OUT_OF_MEM;
3115
3116 if (!udesc->codec_tag) {
3117 genm->EntryType = GF_ISOM_BOX_TYPE_UUID;
3118 memcpy(genm->uuid, udesc->UUID, sizeof(bin128));
3119 }
3120 else {
3121 genm->EntryType = udesc->codec_tag;
3122 }
3123 genm->dataReferenceIndex = dataRefIndex;
3124 if (udesc->extension_buf && udesc->extension_buf_size) {
3125 genm->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
3126 if (!genm->data) {
3127 gf_isom_box_del((GF_Box *)genm);
3128 return GF_OUT_OF_MEM;
3129 }
3130 memcpy(genm->data, udesc->extension_buf, udesc->extension_buf_size);
3131 genm->data_size = udesc->extension_buf_size;
3132 }
3133 e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->other_boxes, genm);
3134 }
3135 *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes);
3136 return e;
3137 }
3138
3139 //use carefully. Very useful when you made a lot of changes (IPMP, IPI, OCI, ...)
3140 //THIS WILL REPLACE THE WHOLE DESCRIPTOR ...
gf_isom_change_generic_sample_description(GF_ISOFile * movie,u32 trackNumber,u32 StreamDescriptionIndex,GF_GenericSampleDescription * udesc)3141 GF_Err gf_isom_change_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_GenericSampleDescription *udesc)
3142 {
3143 GF_TrackBox *trak;
3144 GF_Err e;
3145 GF_GenericVisualSampleEntryBox *entry;
3146
3147 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3148 if (e) return e;
3149
3150 trak = gf_isom_get_track_from_file(movie, trackNumber);
3151 if (!trak || !trak->Media || !StreamDescriptionIndex) return GF_BAD_PARAM;
3152
3153 entry = (GF_GenericVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, StreamDescriptionIndex - 1);
3154 if (!entry) return GF_BAD_PARAM;
3155 if (entry->type == GF_ISOM_BOX_TYPE_GNRV) {
3156 entry->vendor = udesc->vendor_code;
3157 entry->version = udesc->version;
3158 entry->revision = udesc->revision;
3159 entry->temporal_quality = udesc->temporal_quality;
3160 entry->spatial_quality = udesc->spatial_quality;
3161 entry->Width = udesc->width;
3162 entry->Height = udesc->height;
3163 strcpy(entry->compressor_name, udesc->compressor_name);
3164 entry->color_table_index = -1;
3165 entry->frames_per_sample = 1;
3166 entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000;
3167 entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000;
3168 entry->bit_depth = udesc->depth ? udesc->depth : 0x18;
3169 if (entry->data) gf_free(entry->data);
3170 entry->data = NULL;
3171 entry->data_size = 0;
3172 if (udesc->extension_buf && udesc->extension_buf_size) {
3173 entry->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
3174 if (!entry->data) {
3175 gf_isom_box_del((GF_Box *)entry);
3176 return GF_OUT_OF_MEM;
3177 }
3178 memcpy(entry->data, udesc->extension_buf, udesc->extension_buf_size);
3179 entry->data_size = udesc->extension_buf_size;
3180 }
3181 return GF_OK;
3182 }
3183 else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) {
3184 GF_GenericAudioSampleEntryBox *gena = (GF_GenericAudioSampleEntryBox *)entry;
3185 gena->vendor = udesc->vendor_code;
3186 gena->version = udesc->version;
3187 gena->revision = udesc->revision;
3188 gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16;
3189 gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2;
3190 gena->samplerate_hi = udesc->samplerate;
3191 gena->samplerate_lo = 0;
3192 if (gena->data) gf_free(gena->data);
3193 gena->data = NULL;
3194 gena->data_size = 0;
3195
3196 if (udesc->extension_buf && udesc->extension_buf_size) {
3197 gena->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
3198 if (!gena->data) {
3199 gf_isom_box_del((GF_Box *)gena);
3200 return GF_OUT_OF_MEM;
3201 }
3202 memcpy(gena->data, udesc->extension_buf, udesc->extension_buf_size);
3203 gena->data_size = udesc->extension_buf_size;
3204 }
3205 return GF_OK;
3206 }
3207 else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) {
3208 GF_GenericSampleEntryBox *genm = (GF_GenericSampleEntryBox *)entry;
3209 if (genm->data) gf_free(genm->data);
3210 genm->data = NULL;
3211 genm->data_size = 0;
3212
3213 if (udesc->extension_buf && udesc->extension_buf_size) {
3214 genm->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
3215 if (!genm->data) {
3216 gf_isom_box_del((GF_Box *)genm);
3217 return GF_OUT_OF_MEM;
3218 }
3219 memcpy(genm->data, udesc->extension_buf, udesc->extension_buf_size);
3220 genm->data_size = udesc->extension_buf_size;
3221 }
3222 return GF_OK;
3223 }
3224 return GF_BAD_PARAM;
3225 }
3226
3227 /*removes given stream description*/
gf_isom_remove_sample_description(GF_ISOFile * movie,u32 trackNumber,u32 streamDescIndex)3228 GF_Err gf_isom_remove_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 streamDescIndex)
3229 {
3230 GF_TrackBox *trak;
3231 GF_Err e;
3232 GF_Box *entry;
3233
3234 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
3235 if (e) return e;
3236 trak = gf_isom_get_track_from_file(movie, trackNumber);
3237 if (!trak || !trak->Media || !streamDescIndex) return GF_BAD_PARAM;
3238 entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, streamDescIndex - 1);
3239 if (!entry) return GF_BAD_PARAM;
3240 gf_list_rem(trak->Media->information->sampleTable->SampleDescription->other_boxes, streamDescIndex - 1);
3241 gf_isom_box_del(entry);
3242 return GF_OK;
3243 }
3244
3245
3246 //sets a track reference
3247 GF_EXPORT
gf_isom_set_track_reference(GF_ISOFile * the_file,u32 trackNumber,u32 referenceType,u32 ReferencedTrackID)3248 GF_Err gf_isom_set_track_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, u32 ReferencedTrackID)
3249 {
3250 GF_Err e;
3251 GF_TrackBox *trak;
3252 GF_TrackReferenceBox *tref;
3253 GF_TrackReferenceTypeBox *dpnd;
3254
3255 trak = gf_isom_get_track_from_file(the_file, trackNumber);
3256 if (!trak) return GF_BAD_PARAM;
3257
3258 //no tref, create one
3259 tref = trak->References;
3260 if (!tref) {
3261 tref = (GF_TrackReferenceBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF);
3262 e = trak_AddBox((GF_Box*)trak, (GF_Box *)tref);
3263 if (e) return e;
3264 }
3265 //find a ref of the given type
3266 e = Track_FindRef(trak, referenceType, &dpnd);
3267 if (e) return e;
3268
3269 if (!dpnd) {
3270 dpnd = (GF_TrackReferenceTypeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT);
3271 dpnd->reference_type = referenceType;
3272 e = tref_AddBox((GF_Box*)tref, (GF_Box *)dpnd);
3273 if (e) return e;
3274 }
3275 //add the ref
3276 return reftype_AddRefTrack(dpnd, ReferencedTrackID, NULL);
3277 }
3278
3279 //removes a track reference
3280 GF_EXPORT
gf_isom_remove_track_reference(GF_ISOFile * the_file,u32 trackNumber,u32 referenceType,u32 ReferenceIndex)3281 GF_Err gf_isom_remove_track_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, u32 ReferenceIndex)
3282 {
3283 GF_Err e;
3284 GF_TrackBox *trak;
3285 GF_TrackReferenceBox *tref;
3286 GF_TrackReferenceTypeBox *dpnd, *tmp;
3287 u32 i, k, *newIDs;
3288 trak = gf_isom_get_track_from_file(the_file, trackNumber);
3289 if (!trak || !ReferenceIndex) return GF_BAD_PARAM;
3290
3291 //no tref, nothing to remove
3292 tref = trak->References;
3293 if (!tref) return GF_OK;
3294 //find a ref of the given type otherwise return
3295 e = Track_FindRef(trak, referenceType, &dpnd);
3296 if (e || !dpnd) return GF_OK;
3297 //remove the ref
3298 if (ReferenceIndex > dpnd->trackIDCount) return GF_BAD_PARAM;
3299 //last one
3300 if (dpnd->trackIDCount == 1) {
3301 i = 0;
3302 while ((tmp = (GF_TrackReferenceTypeBox *)gf_list_enum(tref->other_boxes, &i))) {
3303 if (tmp == dpnd) {
3304 gf_list_rem(tref->other_boxes, i - 1);
3305 gf_isom_box_del((GF_Box *)dpnd);
3306 return GF_OK;
3307 }
3308 }
3309 }
3310 k = 0;
3311 newIDs = (u32*)gf_malloc(sizeof(u32)*(dpnd->trackIDCount - 1));
3312 for (i = 0; i<dpnd->trackIDCount; i++) {
3313 if (i + 1 != ReferenceIndex) {
3314 newIDs[k] = dpnd->trackIDs[i];
3315 k++;
3316 }
3317 }
3318 gf_free(dpnd->trackIDs);
3319 dpnd->trackIDCount -= 1;
3320 dpnd->trackIDs = newIDs;
3321 return GF_OK;
3322 }
3323
3324
3325 //changes track ID
3326 GF_EXPORT
gf_isom_set_track_id(GF_ISOFile * movie,u32 trackNumber,u32 trackID)3327 GF_Err gf_isom_set_track_id(GF_ISOFile *movie, u32 trackNumber, u32 trackID)
3328 {
3329 GF_TrackReferenceTypeBox *ref;
3330 GF_TrackBox *trak, *a_trak;
3331 u32 i, j, k;
3332
3333 trak = gf_isom_get_track_from_file(movie, trackNumber);
3334 if (trak && (trak->Header->trackID == trackID)) return GF_OK;
3335 a_trak = gf_isom_get_track_from_id(movie->moov, trackID);
3336 if (!movie || !trak || a_trak) return GF_BAD_PARAM;
3337
3338 if (movie->moov->mvhd->nextTrackID <= trackID)
3339 movie->moov->mvhd->nextTrackID = trackID;
3340
3341 /*rewrite all dependencies*/
3342 i = 0;
3343 while ((a_trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) {
3344 if (!a_trak->References) continue;
3345 j = 0;
3346 while ((ref = (GF_TrackReferenceTypeBox *)gf_list_enum(a_trak->References->other_boxes, &j))) {
3347 for (k = 0; k<ref->trackIDCount; k++) {
3348 if (ref->trackIDs[k] == trak->Header->trackID) {
3349 ref->trackIDs[k] = trackID;
3350 break;
3351 }
3352 }
3353 }
3354 }
3355
3356 /*and update IOD if any*/
3357 if (movie->moov->iods && movie->moov->iods->descriptor) {
3358 GF_ES_ID_Inc *inc;
3359 GF_IsomObjectDescriptor *od = (GF_IsomObjectDescriptor *)movie->moov->iods->descriptor;
3360 u32 i = 0;
3361 while ((inc = (GF_ES_ID_Inc*)gf_list_enum(od->ES_ID_IncDescriptors, &i))) {
3362 if (inc->trackID == trak->Header->trackID) inc->trackID = trackID;
3363 }
3364 }
3365 trak->Header->trackID = trackID;
3366 return GF_OK;
3367 }
3368
3369 /*force to rewrite all dependencies when the trackID of referenced track changes*/
3370 GF_EXPORT
gf_isom_rewrite_track_dependencies(GF_ISOFile * movie,u32 trackNumber)3371 GF_Err gf_isom_rewrite_track_dependencies(GF_ISOFile *movie, u32 trackNumber)
3372 {
3373 GF_TrackReferenceTypeBox *ref;
3374 GF_TrackBox *trak, *a_trak;
3375 u32 i, k;
3376
3377 trak = gf_isom_get_track_from_file(movie, trackNumber);
3378 if (!trak)
3379 return GF_BAD_PARAM;
3380 if (!trak->References)
3381 return GF_OK;
3382
3383 i = 0;
3384 while ((ref = (GF_TrackReferenceTypeBox *)gf_list_enum(trak->References->other_boxes, &i))) {
3385 for (k = 0; k < ref->trackIDCount; k++) {
3386 a_trak = gf_isom_get_track_from_original_id(movie->moov, ref->trackIDs[k], trak->originalFile);
3387 if (a_trak) {
3388 ref->trackIDs[k] = a_trak->Header->trackID;
3389 }
3390 else {
3391 a_trak = gf_isom_get_track_from_id(movie->moov, ref->trackIDs[k]);
3392 /*we should have a track with no original ID (not imported) - should we rewrite the dependency ?*/
3393 if (!a_trak || a_trak->originalID) return GF_BAD_PARAM;
3394 }
3395 }
3396 }
3397
3398 return GF_OK;
3399 }
3400
3401 GF_EXPORT
gf_isom_modify_cts_offset(GF_ISOFile * the_file,u32 trackNumber,u32 sample_number,u32 offset)3402 GF_Err gf_isom_modify_cts_offset(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 offset)
3403 {
3404 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
3405 if (!trak) return GF_BAD_PARAM;
3406 if (!trak->Media->information->sampleTable->CompositionOffset) return GF_BAD_PARAM;
3407 if (!trak->Media->information->sampleTable->CompositionOffset->unpack_mode) return GF_BAD_PARAM;
3408 /*we're in unpack mode: one entry per sample*/
3409 trak->Media->information->sampleTable->CompositionOffset->entries[sample_number - 1].decodingOffset = offset;
3410 return GF_OK;
3411 }
3412
gf_isom_remove_cts_info(GF_ISOFile * the_file,u32 trackNumber)3413 GF_Err gf_isom_remove_cts_info(GF_ISOFile *the_file, u32 trackNumber)
3414 {
3415 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
3416 if (!trak) return GF_BAD_PARAM;
3417 if (!trak->Media->information->sampleTable->CompositionOffset) return GF_OK;
3418
3419 gf_isom_box_del((GF_Box *)trak->Media->information->sampleTable->CompositionOffset);
3420 trak->Media->information->sampleTable->CompositionOffset = NULL;
3421 return GF_OK;
3422 }
3423
3424 GF_EXPORT
gf_isom_set_cts_packing(GF_ISOFile * the_file,u32 trackNumber,Bool unpack)3425 GF_Err gf_isom_set_cts_packing(GF_ISOFile *the_file, u32 trackNumber, Bool unpack)
3426 {
3427 GF_Err e;
3428 GF_Err stbl_repackCTS(GF_CompositionOffsetBox *ctts);
3429 GF_Err stbl_unpackCTS(GF_SampleTableBox *stbl);
3430
3431 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
3432 if (!trak) return GF_BAD_PARAM;
3433 if (unpack) {
3434 if (!trak->Media->information->sampleTable->CompositionOffset) trak->Media->information->sampleTable->CompositionOffset = (GF_CompositionOffsetBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS);
3435 e = stbl_unpackCTS(trak->Media->information->sampleTable);
3436 }
3437 else {
3438 if (!trak->Media->information->sampleTable->CompositionOffset) return GF_OK;
3439 e = stbl_repackCTS(trak->Media->information->sampleTable->CompositionOffset);
3440 }
3441 if (e) return e;
3442 return SetTrackDuration(trak);
3443 }
3444
3445 GF_EXPORT
gf_isom_set_track_matrix(GF_ISOFile * the_file,u32 trackNumber,u32 matrix[9])3446 GF_Err gf_isom_set_track_matrix(GF_ISOFile *the_file, u32 trackNumber, u32 matrix[9])
3447 {
3448 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
3449 if (!trak || !trak->Header) return GF_BAD_PARAM;
3450 memcpy(trak->Header->matrix, matrix, sizeof(trak->Header->matrix));
3451 return GF_OK;
3452 }
3453
3454 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)3455 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)
3456 {
3457 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
3458 if (!trak || !trak->Header) return GF_BAD_PARAM;
3459 trak->Header->width = width;
3460 trak->Header->height = height;
3461 trak->Header->matrix[6] = translation_x;
3462 trak->Header->matrix[7] = translation_y;
3463 trak->Header->layer = layer;
3464 return GF_OK;
3465 }
3466
gf_isom_set_track_name(GF_ISOFile * the_file,u32 trackNumber,char * name)3467 GF_Err gf_isom_set_track_name(GF_ISOFile *the_file, u32 trackNumber, char *name)
3468 {
3469 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
3470 if (!trak) return GF_BAD_PARAM;
3471 if (trak->name) gf_free(trak->name);
3472 trak->name = NULL;
3473 if (name) trak->name = gf_strdup(name);
3474 return GF_OK;
3475 }
gf_isom_get_track_name(GF_ISOFile * the_file,u32 trackNumber)3476 const char *gf_isom_get_track_name(GF_ISOFile *the_file, u32 trackNumber)
3477 {
3478 GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber);
3479 if (!trak) return NULL;
3480 return trak->name;
3481 }
3482
3483
gf_isom_store_movie_config(GF_ISOFile * movie,Bool remove_all)3484 GF_Err gf_isom_store_movie_config(GF_ISOFile *movie, Bool remove_all)
3485 {
3486 u32 i, count, len;
3487 char *data;
3488 GF_BitStream *bs;
3489 bin128 binID;
3490 if (movie == NULL) return GF_BAD_PARAM;
3491
3492 gf_isom_remove_user_data(movie, 0, GF_4CC('G', 'P', 'A', 'C'), binID);
3493 count = gf_isom_get_track_count(movie);
3494 for (i = 0; i<count; i++) gf_isom_remove_user_data(movie, i + 1, GF_4CC('G', 'P', 'A', 'C'), binID);
3495
3496 if (remove_all) return GF_OK;
3497
3498 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
3499 /*update movie: storage mode and interleaving type*/
3500 gf_bs_write_u8(bs, 0xFE); /*marker*/
3501 gf_bs_write_u8(bs, movie->storageMode);
3502 gf_bs_write_u32(bs, movie->interleavingTime);
3503 gf_bs_get_content(bs, &data, &len);
3504 gf_bs_del(bs);
3505 gf_isom_add_user_data(movie, 0, GF_4CC('G', 'P', 'A', 'C'), binID, data, len);
3506 gf_free(data);
3507 /*update tracks: interleaving group/priority and track edit name*/
3508 for (i = 0; i<count; i++) {
3509 u32 j;
3510 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, i + 1);
3511 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
3512 gf_bs_write_u8(bs, 0xFE); /*marker*/
3513 gf_bs_write_u32(bs, trak->Media->information->sampleTable->groupID);
3514 gf_bs_write_u32(bs, trak->Media->information->sampleTable->trackPriority);
3515 len = trak->name ? (u32)strlen(trak->name) : 0;
3516 gf_bs_write_u32(bs, len);
3517 for (j = 0; j<len; j++) gf_bs_write_u8(bs, trak->name[j]);
3518 gf_bs_get_content(bs, &data, &len);
3519 gf_bs_del(bs);
3520 gf_isom_add_user_data(movie, i + 1, GF_4CC('G', 'P', 'A', 'C'), binID, data, len);
3521 gf_free(data);
3522 }
3523 return GF_OK;
3524 }
3525
3526
gf_isom_load_movie_config(GF_ISOFile * movie)3527 GF_Err gf_isom_load_movie_config(GF_ISOFile *movie)
3528 {
3529 u32 i, count, len;
3530 char *data;
3531 GF_BitStream *bs;
3532 Bool found_cfg;
3533 bin128 binID;
3534 if (movie == NULL) return GF_BAD_PARAM;
3535
3536 found_cfg = GF_FALSE;
3537 /*restore movie*/
3538 count = gf_isom_get_user_data_count(movie, 0, GF_4CC('G', 'P', 'A', 'C'), binID);
3539 for (i = 0; i<count; i++) {
3540 data = NULL;
3541 gf_isom_get_user_data(movie, 0, GF_4CC('G', 'P', 'A', 'C'), binID, i + 1, &data, &len);
3542 if (!data) continue;
3543 /*check marker*/
3544 if ((unsigned char)data[0] != 0xFE) {
3545 gf_free(data);
3546 continue;
3547 }
3548 bs = gf_bs_new(data, len, GF_BITSTREAM_READ);
3549 gf_bs_read_u8(bs); /*marker*/
3550 movie->storageMode = gf_bs_read_u8(bs);
3551 movie->interleavingTime = gf_bs_read_u32(bs);
3552 gf_bs_del(bs);
3553 gf_free(data);
3554 found_cfg = GF_TRUE;
3555 break;
3556 }
3557
3558 for (i = 0; i<gf_isom_get_track_count(movie); i++) {
3559 u32 j;
3560 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, i + 1);
3561 count = gf_isom_get_user_data_count(movie, i + 1, GF_4CC('G', 'P', 'A', 'C'), binID);
3562 for (j = 0; j<count; j++) {
3563 data = NULL;
3564 gf_isom_get_user_data(movie, i + 1, GF_4CC('G', 'P', 'A', 'C'), binID, j + 1, &data, &len);
3565 if (!data) continue;
3566 /*check marker*/
3567 if ((unsigned char)data[0] != 0xFE) {
3568 gf_free(data);
3569 continue;
3570 }
3571 bs = gf_bs_new(data, len, GF_BITSTREAM_READ);
3572 gf_bs_read_u8(bs); /*marker*/
3573 trak->Media->information->sampleTable->groupID = gf_bs_read_u32(bs);
3574 trak->Media->information->sampleTable->trackPriority = gf_bs_read_u32(bs);
3575 len = gf_bs_read_u32(bs);
3576 if (len) {
3577 u32 k;
3578 trak->name = (char*)gf_malloc(sizeof(char)*(len + 1));
3579 for (k = 0; k<len; k++) trak->name[k] = gf_bs_read_u8(bs);
3580 trak->name[k] = 0;
3581 }
3582 gf_bs_del(bs);
3583 gf_free(data);
3584 found_cfg = GF_TRUE;
3585 break;
3586 }
3587 }
3588 return found_cfg ? GF_OK : GF_NOT_SUPPORTED;
3589 }
3590
3591 GF_EXPORT
gf_isom_set_media_timescale(GF_ISOFile * the_file,u32 trackNumber,u32 newTS,Bool force_rescale)3592 GF_Err gf_isom_set_media_timescale(GF_ISOFile *the_file, u32 trackNumber, u32 newTS, Bool force_rescale)
3593 {
3594 Double scale;
3595 GF_TrackBox *trak;
3596 trak = gf_isom_get_track_from_file(the_file, trackNumber);
3597 if (!trak || !trak->Media | !trak->Media->mediaHeader) return GF_BAD_PARAM;
3598 if (trak->Media->mediaHeader->timeScale == newTS) return GF_OK;
3599
3600 scale = newTS;
3601 scale /= trak->Media->mediaHeader->timeScale;
3602 trak->Media->mediaHeader->timeScale = newTS;
3603 if (!force_rescale) {
3604 u32 i, k, idx;
3605 GF_SampleTableBox *stbl = trak->Media->information->sampleTable;
3606 u64 cur_dts;
3607 u64*DTSs = NULL;
3608 s64*CTSs = NULL;
3609
3610 if (trak->editBox) {
3611 GF_EdtsEntry *ent;
3612 u32 i = 0;
3613 while ((ent = (GF_EdtsEntry*)gf_list_enum(trak->editBox->editList->entryList, &i))) {
3614 ent->mediaTime = (u32)(scale*ent->mediaTime);
3615 }
3616 }
3617 if (!stbl || !stbl->TimeToSample) {
3618 return SetTrackDuration(trak);
3619 }
3620
3621 idx = 0;
3622 cur_dts = 0;
3623 //unpack the DTSs
3624 DTSs = (u64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount));
3625 CTSs = NULL;
3626
3627 if (!DTSs) return GF_OUT_OF_MEM;
3628 if (stbl->CompositionOffset) {
3629 CTSs = (s64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount));
3630 }
3631
3632 for (i = 0; i<stbl->TimeToSample->nb_entries; i++) {
3633 for (k = 0; k<stbl->TimeToSample->entries[i].sampleCount; k++) {
3634 cur_dts += stbl->TimeToSample->entries[i].sampleDelta;
3635 DTSs[idx] = (u64)(cur_dts * scale);
3636
3637 if (stbl->CompositionOffset) {
3638 s32 cts_o;
3639 stbl_GetSampleCTS(stbl->CompositionOffset, idx + 1, &cts_o);
3640 CTSs[idx] = (s64)(((s64)cur_dts + cts_o) * scale);
3641 }
3642 idx++;
3643 }
3644 }
3645 //repack DTS
3646 if (stbl->SampleSize->sampleCount) {
3647 stbl->TimeToSample->entries = gf_realloc(stbl->TimeToSample->entries, sizeof(GF_SttsEntry)*stbl->SampleSize->sampleCount);
3648 memset(stbl->TimeToSample->entries, 0, sizeof(GF_SttsEntry)*stbl->SampleSize->sampleCount);
3649 stbl->TimeToSample->nb_entries = 1;
3650 stbl->TimeToSample->entries[0].sampleDelta = (u32)DTSs[0];
3651 stbl->TimeToSample->entries[0].sampleCount = 1;
3652 idx = 0;
3653 for (i = 1; i< stbl->SampleSize->sampleCount - 1; i++) {
3654 if (DTSs[i + 1] - DTSs[i] == stbl->TimeToSample->entries[idx].sampleDelta) {
3655 stbl->TimeToSample->entries[idx].sampleCount++;
3656 }
3657 else {
3658 idx++;
3659 stbl->TimeToSample->entries[idx].sampleDelta = (u32)(DTSs[i + 1] - DTSs[i]);
3660 stbl->TimeToSample->entries[idx].sampleCount = 1;
3661 }
3662 }
3663 stbl->TimeToSample->nb_entries = idx + 1;
3664 stbl->TimeToSample->entries = gf_realloc(stbl->TimeToSample->entries, sizeof(GF_SttsEntry)*stbl->TimeToSample->nb_entries);
3665 }
3666
3667 if (CTSs && stbl->SampleSize->sampleCount>0) {
3668 //repack CTS
3669 stbl->CompositionOffset->entries = gf_realloc(stbl->CompositionOffset->entries, sizeof(GF_DttsEntry)*stbl->SampleSize->sampleCount);
3670 memset(stbl->CompositionOffset->entries, 0, sizeof(GF_DttsEntry)*stbl->SampleSize->sampleCount);
3671 stbl->CompositionOffset->nb_entries = 1;
3672 stbl->CompositionOffset->entries[0].decodingOffset = (s32)(CTSs[0] - DTSs[0]);
3673 stbl->CompositionOffset->entries[0].sampleCount = 1;
3674 idx = 0;
3675 for (i = 1; i< stbl->SampleSize->sampleCount; i++) {
3676 s32 cts_o = (s32)(CTSs[i] - DTSs[i]);
3677 if (cts_o == stbl->CompositionOffset->entries[idx].decodingOffset) {
3678 stbl->CompositionOffset->entries[idx].sampleCount++;
3679 }
3680 else {
3681 idx++;
3682 stbl->CompositionOffset->entries[idx].decodingOffset = cts_o;
3683 stbl->CompositionOffset->entries[idx].sampleCount = 1;
3684 }
3685 }
3686 stbl->CompositionOffset->nb_entries = idx + 1;
3687 stbl->CompositionOffset->entries = gf_realloc(stbl->CompositionOffset->entries, sizeof(GF_DttsEntry)*stbl->CompositionOffset->nb_entries);
3688
3689 gf_free(CTSs);
3690 }
3691 gf_free(DTSs);
3692
3693 if (stbl->CompositionToDecode) {
3694 stbl->CompositionToDecode->compositionEndTime = (s32)(stbl->CompositionToDecode->compositionEndTime * scale);
3695 stbl->CompositionToDecode->compositionStartTime = (s32)(stbl->CompositionToDecode->compositionStartTime * scale);
3696 stbl->CompositionToDecode->compositionToDTSShift = (s32)(stbl->CompositionToDecode->compositionToDTSShift * scale);
3697 stbl->CompositionToDecode->greatestDecodeToDisplayDelta = (s32)(stbl->CompositionToDecode->greatestDecodeToDisplayDelta * scale);
3698 stbl->CompositionToDecode->leastDecodeToDisplayDelta = (s32)(stbl->CompositionToDecode->leastDecodeToDisplayDelta * scale);
3699 }
3700 }
3701 return SetTrackDuration(trak);
3702 }
3703
3704 GF_EXPORT
gf_isom_box_equal(GF_Box * a,GF_Box * b)3705 Bool gf_isom_box_equal(GF_Box *a, GF_Box *b)
3706 {
3707 Bool ret;
3708 char *data1, *data2;
3709 u32 data1_size, data2_size;
3710 GF_BitStream *bs;
3711
3712 if (a == b) return GF_TRUE;
3713 if (!a || !b) return GF_FALSE;
3714
3715 data1 = data2 = NULL;
3716
3717 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
3718 gf_isom_box_size(a);
3719 gf_isom_box_write(a, bs);
3720 gf_bs_get_content(bs, &data1, &data1_size);
3721 gf_bs_del(bs);
3722
3723 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
3724 gf_isom_box_size(b);
3725 gf_isom_box_write(b, bs);
3726 gf_bs_get_content(bs, &data2, &data2_size);
3727 gf_bs_del(bs);
3728
3729 ret = GF_FALSE;
3730 if (data1_size == data2_size) {
3731 ret = (memcmp(data1, data2, sizeof(char)*data1_size) == 0) ? GF_TRUE : GF_FALSE;
3732 }
3733 gf_free(data1);
3734 gf_free(data2);
3735 return ret;
3736 }
3737
3738 GF_EXPORT
gf_isom_is_same_sample_description(GF_ISOFile * f1,u32 tk1,u32 sdesc_index1,GF_ISOFile * f2,u32 tk2,u32 sdesc_index2)3739 Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, u32 sdesc_index1, GF_ISOFile *f2, u32 tk2, u32 sdesc_index2)
3740 {
3741 u32 i, count;
3742 GF_TrackBox *trak1, *trak2;
3743 GF_ESD *esd1, *esd2;
3744 Bool need_memcmp;
3745 GF_Box *a, *b;
3746
3747 /*get orig sample desc and clone it*/
3748 trak1 = gf_isom_get_track_from_file(f1, tk1);
3749 if (!trak1 || !trak1->Media) return GF_FALSE;
3750 trak2 = gf_isom_get_track_from_file(f2, tk2);
3751 if (!trak2 || !trak2->Media) return GF_FALSE;
3752
3753 if (trak1->Media->handler->handlerType != trak2->Media->handler->handlerType) return GF_FALSE;
3754 count = gf_list_count(trak1->Media->information->sampleTable->SampleDescription->other_boxes);
3755 if (count != gf_list_count(trak2->Media->information->sampleTable->SampleDescription->other_boxes)) {
3756 if (!sdesc_index1 && !sdesc_index2) return GF_FALSE;
3757 }
3758
3759 need_memcmp = GF_TRUE;
3760 for (i = 0; i<count; i++) {
3761 GF_Box *ent1 = (GF_Box *)gf_list_get(trak1->Media->information->sampleTable->SampleDescription->other_boxes, i);
3762 GF_Box *ent2 = (GF_Box *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->other_boxes, i);
3763
3764 if (sdesc_index1) ent1 = (GF_Box *)gf_list_get(trak1->Media->information->sampleTable->SampleDescription->other_boxes, sdesc_index1 - 1);
3765 if (sdesc_index2) ent2 = (GF_Box *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->other_boxes, sdesc_index2 - 1);
3766
3767 if (!ent1 || !ent2) return GF_FALSE;
3768 if (ent1->type != ent2->type) return GF_FALSE;
3769
3770 switch (ent1->type) {
3771 /*for MPEG-4 streams, only compare decSpecInfo (bitrate may not be the same but that's not an issue)*/
3772 case GF_ISOM_BOX_TYPE_MP4S:
3773 case GF_ISOM_BOX_TYPE_MP4A:
3774 case GF_ISOM_BOX_TYPE_MP4V:
3775 case GF_ISOM_BOX_TYPE_ENCA:
3776 case GF_ISOM_BOX_TYPE_ENCV:
3777 case GF_ISOM_BOX_TYPE_ENCS:
3778 Media_GetESD(trak1->Media, sdesc_index1 ? sdesc_index1 : i + 1, &esd1, GF_TRUE);
3779 Media_GetESD(trak2->Media, sdesc_index2 ? sdesc_index2 : i + 1, &esd2, GF_TRUE);
3780 if (!esd1 || !esd2) continue;
3781 need_memcmp = GF_FALSE;
3782 if (esd1->decoderConfig->streamType != esd2->decoderConfig->streamType) return GF_FALSE;
3783 if (esd1->decoderConfig->objectTypeIndication != esd2->decoderConfig->objectTypeIndication) return GF_FALSE;
3784 if (!esd1->decoderConfig->decoderSpecificInfo && esd2->decoderConfig->decoderSpecificInfo) return GF_FALSE;
3785 if (esd1->decoderConfig->decoderSpecificInfo && !esd2->decoderConfig->decoderSpecificInfo) return GF_FALSE;
3786 if (!esd1->decoderConfig->decoderSpecificInfo || !esd2->decoderConfig->decoderSpecificInfo) continue;
3787 if (memcmp(esd1->decoderConfig->decoderSpecificInfo->data, esd2->decoderConfig->decoderSpecificInfo->data, sizeof(char)*esd1->decoderConfig->decoderSpecificInfo->dataLength) != 0) return GF_FALSE;
3788 break;
3789 case GF_ISOM_BOX_TYPE_HVT1:
3790 return GF_TRUE;
3791 case GF_ISOM_BOX_TYPE_AVC1:
3792 case GF_ISOM_BOX_TYPE_AVC2:
3793 case GF_ISOM_BOX_TYPE_AVC3:
3794 case GF_ISOM_BOX_TYPE_AVC4:
3795 case GF_ISOM_BOX_TYPE_SVC1:
3796 case GF_ISOM_BOX_TYPE_HVC1:
3797 case GF_ISOM_BOX_TYPE_HEV1:
3798 case GF_ISOM_BOX_TYPE_HVC2:
3799 case GF_ISOM_BOX_TYPE_HEV2:
3800 case GF_ISOM_BOX_TYPE_LHE1:
3801 case GF_ISOM_BOX_TYPE_LHV1:
3802 {
3803 GF_MPEGVisualSampleEntryBox *avc1 = (GF_MPEGVisualSampleEntryBox *)ent1;
3804 GF_MPEGVisualSampleEntryBox *avc2 = (GF_MPEGVisualSampleEntryBox *)ent2;
3805
3806 if (avc1->hevc_config)
3807 a = (GF_Box *)avc1->hevc_config;
3808 else if (avc1->lhvc_config)
3809 a = (GF_Box *)avc1->lhvc_config;
3810 else if (avc1->svc_config)
3811 a = (GF_Box *)avc1->svc_config;
3812 else
3813 a = (GF_Box *)avc1->avc_config;
3814
3815 if (avc2->hevc_config)
3816 b = (GF_Box *)avc2->hevc_config;
3817 else if (avc2->lhvc_config)
3818 b = (GF_Box *)avc2->lhvc_config;
3819 else if (avc2->svc_config)
3820 b = (GF_Box *)avc2->svc_config;
3821 else
3822 b = (GF_Box *)avc2->avc_config;
3823
3824 return gf_isom_box_equal(a, b);
3825 }
3826 break;
3827 case GF_ISOM_BOX_TYPE_LSR1:
3828 {
3829 GF_LASeRSampleEntryBox *lsr1 = (GF_LASeRSampleEntryBox *)ent1;
3830 GF_LASeRSampleEntryBox *lsr2 = (GF_LASeRSampleEntryBox *)ent2;
3831 if (lsr1->lsr_config && lsr2->lsr_config
3832 && lsr1->lsr_config->hdr && lsr2->lsr_config->hdr
3833 && (lsr1->lsr_config->hdr_size == lsr2->lsr_config->hdr_size)
3834 && !memcmp(lsr1->lsr_config->hdr, lsr2->lsr_config->hdr, lsr2->lsr_config->hdr_size)
3835 ) {
3836 return GF_TRUE;
3837 }
3838 return GF_FALSE;
3839 }
3840 break;
3841 case GF_ISOM_BOX_TYPE_STPP:
3842 {
3843 GF_MetaDataSampleEntryBox *stpp1 = (GF_MetaDataSampleEntryBox *)ent1;
3844 GF_MetaDataSampleEntryBox *stpp2 = (GF_MetaDataSampleEntryBox *)ent2;
3845 if (stpp1->xml_namespace && stpp2->xml_namespace && !strcmp(stpp1->xml_namespace, stpp2->xml_namespace)) {
3846 return GF_TRUE;
3847 }
3848 return GF_FALSE;
3849 }
3850 break;
3851 case GF_ISOM_BOX_TYPE_SBTT:
3852 {
3853 return GF_FALSE;
3854 }
3855 break;
3856 case GF_ISOM_BOX_TYPE_STXT:
3857 {
3858 GF_MetaDataSampleEntryBox *stxt1 = (GF_MetaDataSampleEntryBox *)ent1;
3859 GF_MetaDataSampleEntryBox *stxt2 = (GF_MetaDataSampleEntryBox *)ent2;
3860 if (stxt1->mime_type && stxt2->mime_type &&
3861 ((!stxt1->config && !stxt2->config) ||
3862 (stxt1->config && stxt2->config && stxt1->config->config && stxt2->config->config &&
3863 !strcmp(stxt1->config->config, stxt2->config->config)))) {
3864 return GF_TRUE;
3865 }
3866 return GF_FALSE;
3867 }
3868 break;
3869 }
3870
3871 if (sdesc_index1 && sdesc_index2) break;
3872 }
3873 if (!need_memcmp) return GF_TRUE;
3874 a = (GF_Box *)trak1->Media->information->sampleTable->SampleDescription;
3875 b = (GF_Box *)trak2->Media->information->sampleTable->SampleDescription;
3876 return gf_isom_box_equal(a, b);
3877 }
3878
3879 GF_EXPORT
gf_isom_estimate_size(GF_ISOFile * movie)3880 u64 gf_isom_estimate_size(GF_ISOFile *movie)
3881 {
3882 GF_Err e;
3883 GF_Box *a;
3884 u32 i, count;
3885 u64 mdat_size;
3886 if (!movie || !movie->moov) return 0;
3887
3888 mdat_size = 0;
3889 count = gf_list_count(movie->moov->trackList);
3890 for (i = 0; i<count; i++) {
3891 mdat_size += gf_isom_get_media_data_size(movie, i + 1);
3892 }
3893 if (mdat_size) {
3894 mdat_size += 8;
3895 if (mdat_size > 0xFFFFFFFF) mdat_size += 8;
3896 }
3897
3898 i = 0;
3899 while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
3900 e = gf_isom_box_size(a);
3901 if (e == GF_OK)
3902 mdat_size += a->size;
3903 }
3904 return mdat_size;
3905 }
3906
3907
3908 //set shadowing on/off
gf_isom_remove_sync_shadows(GF_ISOFile * movie,u32 trackNumber)3909 GF_Err gf_isom_remove_sync_shadows(GF_ISOFile *movie, u32 trackNumber)
3910 {
3911 GF_TrackBox *trak;
3912 GF_SampleTableBox *stbl;
3913
3914 if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE;
3915 trak = gf_isom_get_track_from_file(movie, trackNumber);
3916 if (!trak) return GF_BAD_PARAM;
3917
3918 stbl = trak->Media->information->sampleTable;
3919 if (stbl->ShadowSync) {
3920 gf_isom_box_del((GF_Box *)stbl->ShadowSync);
3921 stbl->ShadowSync = NULL;
3922 }
3923 return GF_OK;
3924 }
3925
3926 //fill the sync shadow table
gf_isom_set_sync_shadow(GF_ISOFile * movie,u32 trackNumber,u32 sampleNumber,u32 syncSample)3927 GF_Err gf_isom_set_sync_shadow(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u32 syncSample)
3928 {
3929 GF_TrackBox *trak;
3930 GF_SampleTableBox *stbl;
3931 SAPType isRAP;
3932 GF_Err e;
3933
3934 if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE;
3935 trak = gf_isom_get_track_from_file(movie, trackNumber);
3936 if (!trak || !sampleNumber || !syncSample) return GF_BAD_PARAM;
3937
3938 stbl = trak->Media->information->sampleTable;
3939 if (!stbl->ShadowSync) stbl->ShadowSync = (GF_ShadowSyncBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSH);
3940
3941 //if no sync, skip
3942 if (!stbl->SyncSample) return GF_OK;
3943 //else set the sync shadow.
3944 //if the sample is sync, ignore
3945 e = stbl_GetSampleRAP(stbl->SyncSample, sampleNumber, &isRAP, NULL, NULL);
3946 if (e) return e;
3947 if (isRAP) return GF_OK;
3948 //if the shadowing sample is not sync, error
3949 e = stbl_GetSampleRAP(stbl->SyncSample, syncSample, &isRAP, NULL, NULL);
3950 if (e) return e;
3951 if (!isRAP) return GF_BAD_PARAM;
3952
3953 return stbl_SetSyncShadow(stbl->ShadowSync, sampleNumber, syncSample);
3954 }
3955
3956 //set the GroupID of a track (only used for interleaving)
gf_isom_set_track_interleaving_group(GF_ISOFile * movie,u32 trackNumber,u32 GroupID)3957 GF_Err gf_isom_set_track_interleaving_group(GF_ISOFile *movie, u32 trackNumber, u32 GroupID)
3958 {
3959 GF_TrackBox *trak;
3960
3961 if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE;
3962 trak = gf_isom_get_track_from_file(movie, trackNumber);
3963 if (!trak || !GroupID) return GF_BAD_PARAM;
3964
3965 trak->Media->information->sampleTable->groupID = GroupID;
3966 return GF_OK;
3967 }
3968
3969
3970 //set the Priority of a track within a Group (only used for tight interleaving)
3971 //Priority ranges from 1 to 9
gf_isom_set_track_priority_in_group(GF_ISOFile * movie,u32 trackNumber,u32 Priority)3972 GF_Err gf_isom_set_track_priority_in_group(GF_ISOFile *movie, u32 trackNumber, u32 Priority)
3973 {
3974 GF_TrackBox *trak;
3975
3976 if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE;
3977 trak = gf_isom_get_track_from_file(movie, trackNumber);
3978 if (!trak || !Priority) return GF_BAD_PARAM;
3979
3980 trak->Media->information->sampleTable->trackPriority = Priority > 255 ? 255 : Priority;
3981 return GF_OK;
3982 }
3983
3984 //set the max SamplesPerChunk (for file optimization)
gf_isom_set_max_samples_per_chunk(GF_ISOFile * movie,u32 trackNumber,u32 maxSamplesPerChunk)3985 GF_Err gf_isom_set_max_samples_per_chunk(GF_ISOFile *movie, u32 trackNumber, u32 maxSamplesPerChunk)
3986 {
3987 GF_TrackBox *trak;
3988
3989 if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE;
3990 trak = gf_isom_get_track_from_file(movie, trackNumber);
3991 if (!trak || !maxSamplesPerChunk) return GF_BAD_PARAM;
3992
3993 trak->Media->information->sampleTable->MaxSamplePerChunk = maxSamplesPerChunk;
3994 return GF_OK;
3995 }
3996
3997
3998 GF_EXPORT
gf_isom_set_extraction_slc(GF_ISOFile * the_file,u32 trackNumber,u32 StreamDescriptionIndex,GF_SLConfig * slConfig)3999 GF_Err gf_isom_set_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig *slConfig)
4000 {
4001 GF_TrackBox *trak;
4002 GF_SampleEntryBox *entry;
4003 GF_Err e;
4004 GF_SLConfig **slc;
4005
4006 trak = gf_isom_get_track_from_file(the_file, trackNumber);
4007 if (!trak) return GF_BAD_PARAM;
4008
4009 e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL);
4010 if (e) return e;
4011
4012 //we must be sure we are not using a remote ESD
4013 switch (entry->type) {
4014 case GF_ISOM_BOX_TYPE_MP4S:
4015 if (((GF_MPEGSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
4016 slc = &((GF_MPEGSampleEntryBox *)entry)->slc;
4017 break;
4018 case GF_ISOM_BOX_TYPE_MP4A:
4019 if (((GF_MPEGAudioSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
4020 slc = &((GF_MPEGAudioSampleEntryBox *)entry)->slc;
4021 break;
4022 case GF_ISOM_BOX_TYPE_MP4V:
4023 if (((GF_MPEGVisualSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
4024 slc = &((GF_MPEGVisualSampleEntryBox *)entry)->slc;
4025 break;
4026 default:
4027 return GF_BAD_PARAM;
4028 }
4029
4030 if (*slc) {
4031 gf_odf_desc_del((GF_Descriptor *)*slc);
4032 *slc = NULL;
4033 }
4034 if (!slConfig) return GF_OK;
4035 //finally duplicate the SL
4036 return gf_odf_desc_copy((GF_Descriptor *)slConfig, (GF_Descriptor **)slc);
4037 }
4038
4039
gf_isom_get_extraction_slc(GF_ISOFile * the_file,u32 trackNumber,u32 StreamDescriptionIndex,GF_SLConfig ** slConfig)4040 GF_Err gf_isom_get_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig **slConfig)
4041 {
4042 GF_TrackBox *trak;
4043 GF_SampleEntryBox *entry;
4044 GF_Err e;
4045 GF_SLConfig *slc;
4046
4047 trak = gf_isom_get_track_from_file(the_file, trackNumber);
4048 if (!trak) return GF_BAD_PARAM;
4049
4050 e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL);
4051 if (e) return e;
4052
4053 //we must be sure we are not using a remote ESD
4054 slc = NULL;
4055 *slConfig = NULL;
4056 switch (entry->type) {
4057 case GF_ISOM_BOX_TYPE_MP4S:
4058 if (((GF_MPEGSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
4059 slc = ((GF_MPEGSampleEntryBox *)entry)->slc;
4060 break;
4061 case GF_ISOM_BOX_TYPE_MP4A:
4062 if (((GF_MPEGAudioSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
4063 slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc;
4064 break;
4065 case GF_ISOM_BOX_TYPE_MP4V:
4066 if (((GF_MPEGVisualSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
4067 slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc;
4068 break;
4069 default:
4070 return GF_BAD_PARAM;
4071 }
4072
4073 if (!slc) return GF_OK;
4074 //finally duplicate the SL
4075 return gf_odf_desc_copy((GF_Descriptor *)slc, (GF_Descriptor **)slConfig);
4076 }
4077
4078
gf_isom_get_track_group(GF_ISOFile * the_file,u32 trackNumber)4079 u32 gf_isom_get_track_group(GF_ISOFile *the_file, u32 trackNumber)
4080 {
4081 GF_TrackBox *trak;
4082 trak = gf_isom_get_track_from_file(the_file, trackNumber);
4083 if (!trak) return 0;
4084 return trak->Media->information->sampleTable->groupID;
4085 }
4086
4087
gf_isom_get_track_priority_in_group(GF_ISOFile * the_file,u32 trackNumber)4088 u32 gf_isom_get_track_priority_in_group(GF_ISOFile *the_file, u32 trackNumber)
4089 {
4090 GF_TrackBox *trak;
4091 trak = gf_isom_get_track_from_file(the_file, trackNumber);
4092 if (!trak) return 0;
4093 return trak->Media->information->sampleTable->trackPriority;
4094 }
4095
4096
4097 GF_EXPORT
gf_isom_make_interleave(GF_ISOFile * file,Double TimeInSec)4098 GF_Err gf_isom_make_interleave(GF_ISOFile *file, Double TimeInSec)
4099 {
4100 GF_Err e;
4101 if (gf_isom_get_mode(file) < GF_ISOM_OPEN_EDIT) return GF_BAD_PARAM;
4102 e = gf_isom_set_storage_mode(file, GF_ISOM_STORE_DRIFT_INTERLEAVED);
4103 if (e) return e;
4104 return gf_isom_set_interleave_time(file, (u32)(TimeInSec * gf_isom_get_timescale(file)));
4105 }
4106
4107
4108 GF_EXPORT
gf_isom_set_handler_name(GF_ISOFile * the_file,u32 trackNumber,const char * nameUTF8)4109 GF_Err gf_isom_set_handler_name(GF_ISOFile *the_file, u32 trackNumber, const char *nameUTF8)
4110 {
4111 GF_TrackBox *trak;
4112 trak = gf_isom_get_track_from_file(the_file, trackNumber);
4113 if (!trak) return GF_BAD_PARAM;
4114 if (trak->Media->handler->nameUTF8) gf_free(trak->Media->handler->nameUTF8);
4115 trak->Media->handler->nameUTF8 = NULL;
4116
4117 if (!nameUTF8) return GF_OK;
4118
4119 if (!strnicmp(nameUTF8, "file://", 7)) {
4120 u8 BOM[4];
4121 FILE *f = gf_fopen(nameUTF8 + 7, "rb");
4122 u64 size;
4123 if (!f) return GF_URL_ERROR;
4124 gf_fseek(f, 0, SEEK_END);
4125 size = gf_ftell(f);
4126 gf_fseek(f, 0, SEEK_SET);
4127 if (3 != fread(BOM, sizeof(char), 3, f)) {
4128 gf_fclose(f);
4129 return GF_CORRUPTED_DATA;
4130 }
4131 /*skip BOM if any*/
4132 if ((BOM[0] == 0xEF) && (BOM[1] == 0xBB) && (BOM[2] == 0xBF)) size -= 3;
4133 else if ((BOM[0] == 0xEF) || (BOM[0] == 0xFF)) {
4134 gf_fclose(f);
4135 return GF_BAD_PARAM;
4136 }
4137 else gf_fseek(f, 0, SEEK_SET);
4138 trak->Media->handler->nameUTF8 = (char*)gf_malloc(sizeof(char)*(size_t)(size + 1));
4139 size = fread(trak->Media->handler->nameUTF8, sizeof(char), (size_t)size, f);
4140 trak->Media->handler->nameUTF8[size] = 0;
4141 gf_fclose(f);
4142 }
4143 else {
4144 u32 i, j, len;
4145 char szOrig[1024], szLine[1024];
4146 strcpy(szOrig, nameUTF8);
4147 j = 0;
4148 len = (u32)strlen(szOrig);
4149 for (i = 0; i<len; i++) {
4150 if (szOrig[i] & 0x80) {
4151 /*non UTF8 (likely some win-CP)*/
4152 if ((szOrig[i + 1] & 0xc0) != 0x80) {
4153 szLine[j] = 0xc0 | ((szOrig[i] >> 6) & 0x3);
4154 j++;
4155 szOrig[i] &= 0xbf;
4156 }
4157 /*UTF8 2 bytes char */
4158 else if ((szOrig[i] & 0xe0) == 0xc0) {
4159 szLine[j] = szOrig[i];
4160 i++;
4161 j++;
4162 }
4163 /*UTF8 3 bytes char */
4164 else if ((szOrig[i] & 0xf0) == 0xe0) {
4165 szLine[j] = szOrig[i];
4166 i++;
4167 j++;
4168 szLine[j] = szOrig[i];
4169 i++;
4170 j++;
4171 }
4172 /*UTF8 4 bytes char */
4173 else if ((szOrig[i] & 0xf8) == 0xf0) {
4174 szLine[j] = szOrig[i];
4175 i++;
4176 j++;
4177 szLine[j] = szOrig[i];
4178 i++;
4179 j++;
4180 szLine[j] = szOrig[i];
4181 i++;
4182 j++;
4183 }
4184 }
4185 szLine[j] = szOrig[i];
4186 j++;
4187 }
4188 szLine[j] = 0;
4189 trak->Media->handler->nameUTF8 = gf_strdup(szLine);
4190 }
4191 return GF_OK;
4192 }
4193
gf_isom_clone_root_od(GF_ISOFile * input,GF_ISOFile * output)4194 GF_Err gf_isom_clone_root_od(GF_ISOFile *input, GF_ISOFile *output)
4195 {
4196 GF_List *esds;
4197 GF_Err e;
4198 u32 i;
4199 GF_Descriptor *desc;
4200
4201 e = gf_isom_remove_root_od(output);
4202 if (e) return e;
4203 if (!input->moov || !input->moov->iods || !input->moov->iods->descriptor) return GF_OK;
4204 gf_isom_insert_moov(output);
4205 e = AddMovieIOD(output->moov, 0);
4206 if (e) return e;
4207 if (output->moov->iods->descriptor) gf_odf_desc_del(output->moov->iods->descriptor);
4208 output->moov->iods->descriptor = NULL;
4209 gf_odf_desc_copy(input->moov->iods->descriptor, &output->moov->iods->descriptor);
4210
4211 switch (output->moov->iods->descriptor->tag) {
4212 case GF_ODF_ISOM_IOD_TAG:
4213 esds = ((GF_IsomInitialObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors;
4214 break;
4215 case GF_ODF_ISOM_OD_TAG:
4216 esds = ((GF_IsomObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors;
4217 break;
4218 default:
4219 return GF_ISOM_INVALID_FILE;
4220 }
4221
4222 //get the desc
4223 i = 0;
4224 while ((desc = (GF_Descriptor*)gf_list_enum(esds, &i))) {
4225 gf_odf_desc_del(desc);
4226 gf_list_rem(esds, i - 1);
4227 }
4228 return GF_OK;
4229 }
4230
4231 GF_EXPORT
gf_isom_set_media_type(GF_ISOFile * movie,u32 trackNumber,u32 new_type)4232 GF_Err gf_isom_set_media_type(GF_ISOFile *movie, u32 trackNumber, u32 new_type)
4233 {
4234 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
4235 if (!trak || !new_type) return GF_BAD_PARAM;
4236 trak->Media->handler->handlerType = new_type;
4237 return GF_OK;
4238 }
4239
4240 GF_EXPORT
gf_isom_set_media_subtype(GF_ISOFile * movie,u32 trackNumber,u32 sampleDescriptionIndex,u32 new_type)4241 GF_Err gf_isom_set_media_subtype(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, u32 new_type)
4242 {
4243 GF_SampleEntryBox*entry;
4244 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
4245 if (!trak || !sampleDescriptionIndex || !new_type) return GF_BAD_PARAM;
4246
4247 entry = (GF_SampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, sampleDescriptionIndex - 1);
4248 if (!entry) return GF_BAD_PARAM;
4249 entry->type = new_type;
4250 return GF_OK;
4251 }
4252
4253
4254 GF_EXPORT
gf_isom_set_JPEG2000(GF_ISOFile * mov,Bool set_on)4255 GF_Err gf_isom_set_JPEG2000(GF_ISOFile *mov, Bool set_on)
4256 {
4257 if (!mov) return GF_BAD_PARAM;
4258 mov->is_jp2 = set_on;
4259 return GF_OK;
4260 }
4261
gf_isom_remove_uuid(GF_ISOFile * movie,u32 trackNumber,bin128 UUID)4262 GF_Err gf_isom_remove_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID)
4263 {
4264 u32 i, count;
4265 GF_List *list;
4266
4267 if (trackNumber == (u32)-1) {
4268 if (!movie) return GF_BAD_PARAM;
4269 list = movie->TopBoxes;
4270 }
4271 else if (trackNumber) {
4272 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
4273 if (!trak) return GF_BAD_PARAM;
4274 list = trak->other_boxes;
4275 }
4276 else {
4277 if (!movie) return GF_BAD_PARAM;
4278 list = movie->moov->other_boxes;
4279 }
4280
4281 count = list ? gf_list_count(list) : 0;
4282 for (i = 0; i<count; i++) {
4283 GF_UnknownUUIDBox *uuid = (GF_UnknownUUIDBox *)gf_list_get(list, i);
4284 if (uuid->type != GF_ISOM_BOX_TYPE_UUID) continue;
4285 if (memcmp(UUID, uuid->uuid, sizeof(bin128))) continue;
4286 gf_list_rem(list, i);
4287 i--;
4288 count--;
4289 gf_isom_box_del((GF_Box*)uuid);
4290 }
4291 return GF_OK;
4292 }
4293
4294 GF_EXPORT
gf_isom_add_uuid(GF_ISOFile * movie,u32 trackNumber,bin128 UUID,const char * data,u32 data_size)4295 GF_Err gf_isom_add_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID, const char *data, u32 data_size)
4296 {
4297 GF_List *list;
4298 GF_UnknownUUIDBox *uuid;
4299
4300 if (!data_size || !data) return GF_OK;
4301
4302 if (trackNumber == (u32)-1) {
4303 if (!movie) return GF_BAD_PARAM;
4304 list = movie->TopBoxes;
4305 }
4306 else if (trackNumber) {
4307 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
4308 if (!trak) return GF_BAD_PARAM;
4309 if (!trak->other_boxes) trak->other_boxes = gf_list_new();
4310 list = trak->other_boxes;
4311 }
4312 else {
4313 if (!movie) return GF_BAD_PARAM;
4314 if (!movie->moov->other_boxes) movie->moov->other_boxes = gf_list_new();
4315 list = movie->moov->other_boxes;
4316 }
4317
4318 GF_SAFEALLOC(uuid, GF_UnknownUUIDBox);
4319 if (!uuid) return GF_OUT_OF_MEM;
4320 uuid->type = GF_ISOM_BOX_TYPE_UUID;
4321 memcpy(uuid->uuid, UUID, sizeof(bin128));
4322 uuid->dataSize = data_size;
4323 uuid->data = (char*)gf_malloc(sizeof(char)*data_size);
4324 memcpy(uuid->data, data, sizeof(char)*data_size);
4325 gf_list_add(list, uuid);
4326 return GF_OK;
4327 }
4328
4329 /*Apple extensions*/
4330
4331 GF_EXPORT
gf_isom_apple_set_tag(GF_ISOFile * mov,u32 tag,const char * data,u32 data_len)4332 GF_Err gf_isom_apple_set_tag(GF_ISOFile *mov, u32 tag, const char *data, u32 data_len)
4333 {
4334 GF_BitStream *bs;
4335 GF_Err e;
4336 GF_ItemListBox *ilst;
4337 GF_MetaBox *meta;
4338 GF_ListItemBox *info;
4339 u32 btype, i;
4340
4341
4342 e = CanAccessMovie(mov, GF_ISOM_OPEN_WRITE);
4343 if (e) return e;
4344
4345 meta = gf_isom_apple_create_meta_extensions(mov);
4346 if (!meta) return GF_BAD_PARAM;
4347
4348 ilst = gf_ismo_locate_box(meta->other_boxes, GF_ISOM_BOX_TYPE_ILST, NULL);
4349 if (!ilst) {
4350 ilst = (GF_ItemListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_ILST);
4351 if (!meta->other_boxes) meta->other_boxes = gf_list_new();
4352 gf_list_add(meta->other_boxes, ilst);
4353 }
4354
4355 if (tag == GF_ISOM_ITUNE_GENRE) {
4356 btype = data ? GF_ISOM_BOX_TYPE_0xA9GEN : GF_ISOM_BOX_TYPE_GNRE;
4357 }
4358 else {
4359 btype = tag;
4360 }
4361 /*remove tag*/
4362 i = 0;
4363 while ((info = (GF_ListItemBox*)gf_list_enum(ilst->other_boxes, &i))) {
4364 if (info->type == btype) {
4365 gf_list_rem(ilst->other_boxes, i - 1);
4366 gf_isom_box_del((GF_Box *)info);
4367 info = NULL;
4368 break;
4369 }
4370 }
4371
4372 if (data != NULL) {
4373 info = (GF_ListItemBox *)gf_isom_box_new(btype);
4374 if (info == NULL) return GF_OUT_OF_MEM;
4375 switch (btype) {
4376 case GF_ISOM_BOX_TYPE_TRKN:
4377 case GF_ISOM_BOX_TYPE_DISK:
4378 case GF_ISOM_BOX_TYPE_GNRE:
4379 info->data->flags = 0x0;
4380 break;
4381 case GF_ISOM_BOX_TYPE_PGAP:
4382 case GF_ISOM_BOX_TYPE_CPIL:
4383 info->data->flags = 0x15;
4384 break;
4385 default:
4386 info->data->flags = 0x1;
4387 break;
4388 }
4389 if (tag == GF_ISOM_ITUNE_COVER_ART) {
4390 if (data_len & 0x80000000) {
4391 data_len = (data_len & 0x7FFFFFFF);
4392 info->data->flags = 14;
4393 }
4394 else {
4395 info->data->flags = 13;
4396 }
4397 }
4398 info->data->dataSize = data_len;
4399 info->data->data = (char*)gf_malloc(sizeof(char)*data_len);
4400 memcpy(info->data->data, data, sizeof(char)*data_len);
4401 }
4402 else if (data_len && (tag == GF_ISOM_ITUNE_GENRE)) {
4403 info = (GF_ListItemBox *)gf_isom_box_new(btype);
4404 if (info == NULL) return GF_OUT_OF_MEM;
4405 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
4406 gf_bs_write_u16(bs, data_len);
4407 gf_bs_get_content(bs, &info->data->data, &info->data->dataSize);
4408 info->data->flags = 0x0;
4409 gf_bs_del(bs);
4410 }
4411 else if (data_len && (tag == GF_ISOM_ITUNE_COMPILATION)) {
4412 info = (GF_ListItemBox *)gf_isom_box_new(btype);
4413 if (info == NULL) return GF_OUT_OF_MEM;
4414 info->data->data = (char*)gf_malloc(sizeof(char));
4415 info->data->data[0] = 1;
4416 info->data->dataSize = 1;
4417 info->data->flags = 21;
4418 }
4419 else if (data_len && (tag == GF_ISOM_ITUNE_TEMPO)) {
4420 info = (GF_ListItemBox *)gf_isom_box_new(btype);
4421 if (info == NULL) return GF_OUT_OF_MEM;
4422 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
4423 gf_bs_write_u16(bs, data_len);
4424 gf_bs_get_content(bs, &info->data->data, &info->data->dataSize);
4425 info->data->flags = 0x15;
4426 gf_bs_del(bs);
4427 }
4428
4429 if (!info || (tag == GF_ISOM_ITUNE_ALL)) {
4430 if (!gf_list_count(ilst->other_boxes) || (tag == GF_ISOM_ITUNE_ALL)) {
4431 gf_list_del_item(meta->other_boxes, ilst);
4432 gf_isom_box_del((GF_Box *)ilst);
4433 }
4434 return GF_OK;
4435 }
4436
4437 return gf_list_add(ilst->other_boxes, info);
4438 }
4439
4440 GF_EXPORT
gf_isom_set_alternate_group_id(GF_ISOFile * movie,u32 trackNumber,u32 groupId)4441 GF_Err gf_isom_set_alternate_group_id(GF_ISOFile *movie, u32 trackNumber, u32 groupId)
4442 {
4443 GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber);
4444 if (!trak) return GF_BAD_PARAM;
4445 trak->Header->alternate_group = groupId;
4446 return GF_OK;
4447 }
4448
4449
4450 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)4451 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)
4452 {
4453 GF_TrackSelectionBox *tsel;
4454 GF_TrackBox *trak;
4455 GF_UserDataMap *map;
4456 GF_Err e;
4457 u32 alternateGroupID = 0;
4458 u32 next_switch_group_id = 0;
4459
4460 trak = gf_isom_get_track_from_file(movie, trackNumber);
4461 if (!trak || !switchGroupID) return GF_BAD_PARAM;
4462
4463
4464 if (trackRefGroup) {
4465 GF_TrackBox *trak_ref = gf_isom_get_track_from_file(movie, trackRefGroup);
4466 if (trak_ref != trak) {
4467 if (!trak_ref || !trak_ref->Header->alternate_group) {
4468 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Track %d has not an alternate group - skipping\n", trak_ref->Header->trackID));
4469 return GF_BAD_PARAM;
4470 }
4471 alternateGroupID = trak_ref->Header->alternate_group;
4472 }
4473 else {
4474 alternateGroupID = trak->Header->alternate_group;
4475 }
4476 }
4477 if (!alternateGroupID) {
4478 /*there is a function for this ....*/
4479 if (trak->Header->alternate_group) {
4480 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Track %d has already an alternate group - skipping\n", trak->Header->trackID));
4481 return GF_BAD_PARAM;
4482 }
4483 alternateGroupID = gf_isom_get_next_alternate_group_id(movie);
4484 }
4485
4486 if (is_switch_group) {
4487 u32 i = 0;
4488 while (i< gf_isom_get_track_count(movie)) {
4489 //locate first available ID
4490 GF_TrackBox *a_trak = gf_isom_get_track_from_file(movie, i + 1);
4491
4492 if (a_trak->udta) {
4493 u32 j, count;
4494 map = udta_getEntry(a_trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL);
4495 if (map) {
4496 count = gf_list_count(map->other_boxes);
4497 for (j = 0; j<count; j++) {
4498 tsel = (GF_TrackSelectionBox*)gf_list_get(map->other_boxes, j);
4499
4500 if (*switchGroupID) {
4501 if (tsel->switchGroup == next_switch_group_id) {
4502 if (a_trak->Header->alternate_group != alternateGroupID) return GF_BAD_PARAM;
4503 }
4504 }
4505 else {
4506 if (tsel->switchGroup && (tsel->switchGroup >= next_switch_group_id))
4507 next_switch_group_id = tsel->switchGroup;
4508 }
4509 }
4510 }
4511
4512 }
4513 i++;
4514 }
4515 if (!*switchGroupID) *switchGroupID = next_switch_group_id + 1;
4516 }
4517
4518
4519 trak->Header->alternate_group = alternateGroupID;
4520
4521 tsel = NULL;
4522 if (*switchGroupID) {
4523 if (!trak->udta) {
4524 e = trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA));
4525 if (e) return e;
4526 }
4527
4528 map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL);
4529
4530 /*locate tsel box with no switch group*/
4531 if (map) {
4532 u32 j, count = gf_list_count(map->other_boxes);
4533 for (j = 0; j<count; j++) {
4534 tsel = (GF_TrackSelectionBox*)gf_list_get(map->other_boxes, j);
4535 if (tsel->switchGroup == *switchGroupID) break;
4536 tsel = NULL;
4537 }
4538 }
4539 if (!tsel) {
4540 tsel = (GF_TrackSelectionBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TSEL);
4541 e = udta_AddBox(trak->udta, (GF_Box *)tsel);
4542 if (e) return e;
4543 }
4544
4545 tsel->switchGroup = *switchGroupID;
4546 tsel->attributeListCount = criteriaListCount;
4547 if (tsel->attributeList) gf_free(tsel->attributeList);
4548 tsel->attributeList = (u32*)gf_malloc(sizeof(u32)*criteriaListCount);
4549 memcpy(tsel->attributeList, criteriaList, sizeof(u32)*criteriaListCount);
4550 }
4551 return GF_OK;
4552 }
4553
reset_tsel_box(GF_TrackBox * trak)4554 void reset_tsel_box(GF_TrackBox *trak)
4555 {
4556 GF_UserDataMap *map;
4557 trak->Header->alternate_group = 0;
4558 map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL);
4559 if (map) {
4560 gf_list_del_item(trak->udta->recordList, map);
4561 gf_isom_box_array_del(map->other_boxes);
4562 gf_free(map);
4563 }
4564
4565 }
4566
4567 GF_EXPORT
gf_isom_reset_track_switch_parameter(GF_ISOFile * movie,u32 trackNumber,Bool reset_all_group)4568 GF_Err gf_isom_reset_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, Bool reset_all_group)
4569 {
4570 GF_TrackBox *trak;
4571 u32 alternateGroupID = 0;
4572
4573 trak = gf_isom_get_track_from_file(movie, trackNumber);
4574 if (!trak) return GF_BAD_PARAM;
4575 if (!trak->Header->alternate_group) return GF_OK;
4576
4577 alternateGroupID = trak->Header->alternate_group;
4578 if (reset_all_group) {
4579 u32 i = 0;
4580 while (i< gf_isom_get_track_count(movie)) {
4581 //locate first available ID
4582 GF_TrackBox *a_trak = gf_isom_get_track_from_file(movie, i + 1);
4583 if (a_trak->Header->alternate_group == alternateGroupID) reset_tsel_box(a_trak);
4584 i++;
4585 }
4586 }
4587 else {
4588 reset_tsel_box(trak);
4589 }
4590 return GF_OK;
4591 }
4592
4593
4594 GF_EXPORT
gf_isom_reset_switch_parameters(GF_ISOFile * movie)4595 GF_Err gf_isom_reset_switch_parameters(GF_ISOFile *movie)
4596 {
4597 u32 i = 0;
4598 while (i< gf_isom_get_track_count(movie)) {
4599 //locate first available ID
4600 GF_TrackBox *a_trak = gf_isom_get_track_from_file(movie, i + 1);
4601 reset_tsel_box(a_trak);
4602 i++;
4603 }
4604 return GF_OK;
4605 }
4606
4607
gf_isom_add_subsample(GF_ISOFile * movie,u32 track,u32 sampleNumber,u32 flags,u32 subSampleSize,u8 priority,u32 reserved,Bool discardable)4608 GF_Err gf_isom_add_subsample(GF_ISOFile *movie, u32 track, u32 sampleNumber, u32 flags, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable)
4609 {
4610 u32 i, count;
4611 GF_SubSampleInformationBox *sub_samples;
4612 GF_TrackBox *trak;
4613 GF_Err e;
4614
4615 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
4616 if (e) return e;
4617
4618 trak = gf_isom_get_track_from_file(movie, track);
4619 if (!trak || !trak->Media || !trak->Media->information->sampleTable)
4620 return GF_BAD_PARAM;
4621
4622 if (!trak->Media->information->sampleTable->sub_samples) {
4623 trak->Media->information->sampleTable->sub_samples = gf_list_new();
4624 }
4625
4626 sub_samples = NULL;
4627 count = gf_list_count(trak->Media->information->sampleTable->sub_samples);
4628 for (i = 0; i<count; i++) {
4629 sub_samples = gf_list_get(trak->Media->information->sampleTable->sub_samples, i);
4630 if (sub_samples->flags == flags) break;
4631 sub_samples = NULL;
4632 }
4633 if (!sub_samples) {
4634 sub_samples = (GF_SubSampleInformationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SUBS);
4635 gf_list_add(trak->Media->information->sampleTable->sub_samples, sub_samples);
4636 sub_samples->version = (subSampleSize>0xFFFF) ? 1 : 0;
4637 sub_samples->flags = flags;
4638 }
4639 return gf_isom_add_subsample_info(sub_samples, sampleNumber, subSampleSize, priority, reserved, discardable);
4640 }
4641
4642
4643 GF_EXPORT
gf_isom_set_rvc_config(GF_ISOFile * movie,u32 track,u32 sampleDescriptionIndex,u16 rvc_predefined,char * mime,char * data,u32 size)4644 GF_Err gf_isom_set_rvc_config(GF_ISOFile *movie, u32 track, u32 sampleDescriptionIndex, u16 rvc_predefined, char *mime, char *data, u32 size)
4645 {
4646 GF_MPEGVisualSampleEntryBox *entry;
4647 GF_Err e;
4648 GF_TrackBox *trak;
4649
4650 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
4651 if (e) return e;
4652
4653 trak = gf_isom_get_track_from_file(movie, track);
4654 if (!trak) return GF_BAD_PARAM;
4655
4656
4657 entry = (GF_MPEGVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, sampleDescriptionIndex - 1);
4658 if (!entry) return GF_BAD_PARAM;
4659 switch (entry->type) {
4660 case GF_ISOM_BOX_TYPE_MP4V:
4661 case GF_ISOM_BOX_TYPE_AVC1:
4662 case GF_ISOM_BOX_TYPE_AVC2:
4663 case GF_ISOM_BOX_TYPE_AVC3:
4664 case GF_ISOM_BOX_TYPE_AVC4:
4665 case GF_ISOM_BOX_TYPE_SVC1:
4666 case GF_ISOM_BOX_TYPE_ENCV:
4667 case GF_ISOM_BOX_TYPE_HVC1:
4668 case GF_ISOM_BOX_TYPE_HEV1:
4669 case GF_ISOM_BOX_TYPE_HVC2:
4670 case GF_ISOM_BOX_TYPE_HEV2:
4671 case GF_ISOM_BOX_TYPE_LHE1:
4672 case GF_ISOM_BOX_TYPE_LHV1:
4673 case GF_ISOM_BOX_TYPE_HVT1:
4674 break;
4675 default:
4676 return GF_BAD_PARAM;
4677 }
4678
4679 if (entry->rvcc && entry->rvcc->rvc_meta_idx) {
4680 gf_isom_remove_meta_item(movie, GF_FALSE, track, entry->rvcc->rvc_meta_idx);
4681 entry->rvcc->rvc_meta_idx = 0;
4682 }
4683
4684 if (!entry->rvcc) {
4685 entry->rvcc = (GF_RVCConfigurationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_RVCC);
4686 }
4687 entry->rvcc->predefined_rvc_config = rvc_predefined;
4688 if (!rvc_predefined) {
4689 e = gf_isom_set_meta_type(movie, GF_FALSE, track, GF_4CC('r', 'v', 'c', 'i'));
4690 if (e) return e;
4691 gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_ISO2, 1);
4692 e = gf_isom_add_meta_item_memory(movie, GF_FALSE, track, "rvcconfig.xml", 0, GF_4CC('m', 'i', 'm', 'e'), mime, NULL, NULL, data, size, NULL);
4693 if (e) return e;
4694 entry->rvcc->rvc_meta_idx = gf_isom_get_meta_item_count(movie, GF_FALSE, track);
4695 }
4696 return GF_OK;
4697 }
4698
4699 /*for now not exported*/
4700 /*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)4701 static GF_Err gf_isom_add_sample_group_entry(GF_List *sampleGroups, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, u32 sampleGroupDescriptionIndex)
4702 {
4703 GF_SampleGroupBox *sgroup = NULL;
4704 u32 i, count, last_sample_in_entry;
4705
4706 count = gf_list_count(sampleGroups);
4707 for (i = 0; i<count; i++) {
4708 sgroup = (GF_SampleGroupBox*)gf_list_get(sampleGroups, i);
4709 if (sgroup->grouping_type == grouping_type) break;
4710 sgroup = NULL;
4711 }
4712 if (!sgroup) {
4713 sgroup = (GF_SampleGroupBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SBGP);
4714 sgroup->grouping_type = grouping_type;
4715 sgroup->grouping_type_parameter = grouping_type_parameter;
4716 gf_list_add(sampleGroups, sgroup);
4717 }
4718 /*used in fragments, means we are adding the last sample*/
4719 if (!sample_number) {
4720 sample_number = 1;
4721 if (sgroup->entry_count) {
4722 for (i = 0; i<sgroup->entry_count; i++) {
4723 sample_number += sgroup->sample_entries[i].sample_count;
4724 }
4725 }
4726 }
4727
4728 if (!sgroup->entry_count) {
4729 u32 idx = 0;
4730 sgroup->entry_count = (sample_number>1) ? 2 : 1;
4731 sgroup->sample_entries = (GF_SampleGroupEntry*)gf_malloc(sizeof(GF_SampleGroupEntry) * sgroup->entry_count);
4732 if (sample_number>1) {
4733 sgroup->sample_entries[0].sample_count = sample_number - 1;
4734 sgroup->sample_entries[0].group_description_index = 0;
4735 idx = 1;
4736 }
4737 sgroup->sample_entries[idx].sample_count = 1;
4738 sgroup->sample_entries[idx].group_description_index = sampleGroupDescriptionIndex;
4739 return GF_OK;
4740 }
4741 last_sample_in_entry = 0;
4742 for (i = 0; i<sgroup->entry_count; i++) {
4743 /*TODO*/
4744 if (last_sample_in_entry + sgroup->sample_entries[i].sample_count > sample_number) return GF_NOT_SUPPORTED;
4745 last_sample_in_entry += sgroup->sample_entries[i].sample_count;
4746 }
4747
4748 if (last_sample_in_entry == sample_number) {
4749 if (sgroup->sample_entries[sgroup->entry_count - 1].group_description_index == sampleGroupDescriptionIndex)
4750 return GF_OK;
4751 else
4752 return GF_NOT_SUPPORTED;
4753 }
4754
4755 if ((sgroup->sample_entries[sgroup->entry_count - 1].group_description_index == sampleGroupDescriptionIndex) && (last_sample_in_entry + 1 == sample_number)) {
4756 sgroup->sample_entries[sgroup->entry_count - 1].sample_count++;
4757 return GF_OK;
4758 }
4759 /*last entry was an empty desc (no group associated), just add the number of samples missing until new one, then add new one*/
4760 if (!sgroup->sample_entries[sgroup->entry_count - 1].group_description_index) {
4761 sgroup->sample_entries[sgroup->entry_count - 1].sample_count += sample_number - 1 - last_sample_in_entry;
4762 sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 1));
4763 sgroup->sample_entries[sgroup->entry_count].sample_count = 1;
4764 sgroup->sample_entries[sgroup->entry_count].group_description_index = sampleGroupDescriptionIndex;
4765 sgroup->entry_count++;
4766 return GF_OK;
4767 }
4768 /*we are adding a sample with no desc, add entry at the end*/
4769 if (!sampleGroupDescriptionIndex || (sample_number - 1 - last_sample_in_entry == 0)) {
4770 sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 1));
4771 sgroup->sample_entries[sgroup->entry_count].sample_count = 1;
4772 sgroup->sample_entries[sgroup->entry_count].group_description_index = sampleGroupDescriptionIndex;
4773 sgroup->entry_count++;
4774 return GF_OK;
4775 }
4776 /*need to insert two entries ...*/
4777 sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 2));
4778
4779 sgroup->sample_entries[sgroup->entry_count].sample_count = sample_number - 1 - last_sample_in_entry;
4780 sgroup->sample_entries[sgroup->entry_count].group_description_index = 0;
4781
4782 sgroup->sample_entries[sgroup->entry_count + 1].sample_count = 1;
4783 sgroup->sample_entries[sgroup->entry_count + 1].group_description_index = sampleGroupDescriptionIndex;
4784 sgroup->entry_count += 2;
4785 return GF_OK;
4786 }
4787
4788 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
get_sgdp(GF_SampleTableBox * stbl,GF_TrackFragmentBox * traf,u32 grouping_type)4789 static GF_SampleGroupDescriptionBox *get_sgdp(GF_SampleTableBox *stbl, GF_TrackFragmentBox *traf, u32 grouping_type)
4790 #else
4791 static GF_SampleGroupDescriptionBox *get_sgdp(GF_SampleTableBox *stbl, void *traf, u32 grouping_type)
4792 #endif /* GPAC_DISABLE_ISOM_FRAGMENTS */
4793 {
4794 GF_List *groupList;
4795 GF_SampleGroupDescriptionBox *sgdesc = NULL;
4796 u32 count, i;
4797 /*look in stbl or traf for sample sampleGroupsDescription*/
4798 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
4799 if (traf) {
4800 if (!traf->sampleGroupsDescription)
4801 traf->sampleGroupsDescription = gf_list_new();
4802 groupList = traf->sampleGroupsDescription;
4803 }
4804 else
4805 #endif
4806 {
4807 if (!stbl->sampleGroupsDescription)
4808 stbl->sampleGroupsDescription = gf_list_new();
4809 groupList = stbl->sampleGroupsDescription;
4810 }
4811
4812 count = gf_list_count(groupList);
4813 for (i = 0; i<count; i++) {
4814 sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(groupList, i);
4815 if (sgdesc->grouping_type == grouping_type) break;
4816 sgdesc = NULL;
4817 }
4818 if (!sgdesc) {
4819 sgdesc = (GF_SampleGroupDescriptionBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SGPD);
4820 sgdesc->grouping_type = grouping_type;
4821 gf_list_add(groupList, sgdesc);
4822 }
4823 return sgdesc;
4824 }
4825
4826 #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))4827 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))
4828 #else
4829 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))
4830 #endif /* GPAC_DISABLE_ISOM_FRAGMENTS */
4831 {
4832 GF_List *groupList;
4833 void *entry;
4834 GF_SampleGroupDescriptionBox *sgdesc = NULL;
4835 u32 i, entry_idx;
4836
4837 if (!stbl && !traf) return GF_BAD_PARAM;
4838
4839 sgdesc = get_sgdp(stbl, traf, grouping_type);
4840 if (!sgdesc) return GF_OUT_OF_MEM;
4841
4842 entry = NULL;
4843 for (i = 0; i<gf_list_count(sgdesc->group_descriptions); i++) {
4844 entry = gf_list_get(sgdesc->group_descriptions, i);
4845 if (sg_compare_entry(udta, entry)) break;
4846 entry = NULL;
4847 }
4848 if (!entry) {
4849 entry = sg_create_entry(udta);
4850 if (!entry) return GF_IO_ERR;
4851 gf_list_add(sgdesc->group_descriptions, entry);
4852 }
4853
4854 entry_idx = 1 + gf_list_find(sgdesc->group_descriptions, entry);
4855
4856 /*look in stbl or traf for sample sampleGroups*/
4857 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
4858 if (traf) {
4859 if (!traf->sampleGroups)
4860 traf->sampleGroups = gf_list_new();
4861 groupList = traf->sampleGroups;
4862 entry_idx |= 0x10000;
4863 }
4864 else
4865 #endif
4866 {
4867 if (!stbl->sampleGroups)
4868 stbl->sampleGroups = gf_list_new();
4869 groupList = stbl->sampleGroups;
4870 }
4871
4872 return gf_isom_add_sample_group_entry(groupList, sample_number, grouping_type, grouping_type_parameter, entry_idx);
4873 }
4874
4875 /*for now not exported*/
gf_isom_set_sample_group_info(GF_ISOFile * movie,u32 track,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))4876 static GF_Err gf_isom_set_sample_group_info(GF_ISOFile *movie, u32 track, 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))
4877 {
4878 GF_Err e;
4879 GF_TrackBox *trak;
4880
4881 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
4882 if (e) return e;
4883
4884 trak = gf_isom_get_track_from_file(movie, track);
4885 if (!trak) return GF_BAD_PARAM;
4886
4887 return gf_isom_set_sample_group_info_ex(trak->Media->information->sampleTable, NULL, sample_number, grouping_type, grouping_type_parameter, udta, sg_create_entry, sg_compare_entry);
4888 }
4889
4890
4891 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)4892 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)
4893 {
4894 GF_Err e;
4895 GF_TrackBox *trak;
4896 GF_DefaultSampleGroupDescriptionEntry *entry = NULL;
4897 GF_SampleGroupDescriptionBox *sgdesc = NULL;
4898
4899 if (sampleGroupDescriptionIndex) *sampleGroupDescriptionIndex = 0;
4900 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
4901 if (e) return e;
4902
4903 trak = gf_isom_get_track_from_file(movie, track);
4904 if (!trak) return GF_BAD_PARAM;
4905
4906
4907 sgdesc = get_sgdp(trak->Media->information->sampleTable, NULL, grouping_type);
4908 if (!sgdesc) return GF_OUT_OF_MEM;
4909
4910 if (grouping_type == GF_4CC('o', 'i', 'n', 'f')) {
4911 GF_OperatingPointsInformation *ptr = gf_isom_oinf_new_entry();
4912 GF_BitStream *bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
4913 e = gf_isom_oinf_read_entry(ptr, bs);
4914 gf_bs_del(bs);
4915 if (e) {
4916 gf_isom_oinf_del_entry(ptr);
4917 return e;
4918 }
4919 e = gf_list_add(sgdesc->group_descriptions, ptr);
4920 if (e) return e;
4921 entry = (GF_DefaultSampleGroupDescriptionEntry *)ptr;
4922 }
4923 else if (grouping_type == GF_4CC('l', 'i', 'n', 'f')) {
4924 GF_LHVCLayerInformation *ptr = gf_isom_linf_new_entry();
4925 GF_BitStream *bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
4926 e = gf_isom_linf_read_entry(ptr, bs);
4927 gf_bs_del(bs);
4928 if (e) {
4929 gf_isom_linf_del_entry(ptr);
4930 return e;
4931 }
4932 e = gf_list_add(sgdesc->group_descriptions, ptr);
4933 if (e) return e;
4934 entry = (GF_DefaultSampleGroupDescriptionEntry *)ptr;
4935 }
4936 else {
4937 u32 i, count = gf_list_count(sgdesc->group_descriptions);
4938 for (i = 0; i<count; i++) {
4939 GF_DefaultSampleGroupDescriptionEntry *ent = gf_list_get(sgdesc->group_descriptions, i);
4940 if ((ent->length == data_size) && !memcmp(ent->data, data, data_size)) {
4941 entry = ent;
4942 break;
4943 }
4944 entry = NULL;
4945 }
4946 if (!entry) {
4947 GF_SAFEALLOC(entry, GF_DefaultSampleGroupDescriptionEntry);
4948 if (!entry) return GF_OUT_OF_MEM;
4949 entry->data = (u8*)gf_malloc(sizeof(char) * data_size);
4950 if (!entry->data) {
4951 gf_free(entry);
4952 return GF_OUT_OF_MEM;
4953 }
4954 entry->length = data_size;
4955 memcpy(entry->data, data, sizeof(char) * data_size);
4956 e = gf_list_add(sgdesc->group_descriptions, entry);
4957 if (e) {
4958 gf_free(entry->data);
4959 gf_free(entry);
4960 return e;
4961 }
4962 }
4963 }
4964
4965
4966 if (is_default) {
4967 sgdesc->default_description_index = 1 + gf_list_find(sgdesc->group_descriptions, entry);
4968 sgdesc->version = 2;
4969 }
4970 if (sampleGroupDescriptionIndex) *sampleGroupDescriptionIndex = 1 + gf_list_find(sgdesc->group_descriptions, entry);
4971
4972 return GF_OK;
4973 }
4974
4975 GF_EXPORT
gf_isom_remove_sample_group(GF_ISOFile * movie,u32 track,u32 grouping_type)4976 GF_Err gf_isom_remove_sample_group(GF_ISOFile *movie, u32 track, u32 grouping_type)
4977 {
4978 GF_Err e;
4979 GF_TrackBox *trak;
4980 GF_SampleGroupDescriptionBox *sgdesc = NULL;
4981 u32 count, i;
4982
4983 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
4984 if (e) return e;
4985
4986 trak = gf_isom_get_track_from_file(movie, track);
4987 if (!trak) return GF_BAD_PARAM;
4988
4989 if (!trak->Media->information->sampleTable->sampleGroupsDescription)
4990 return GF_OK;
4991
4992 count = gf_list_count(trak->Media->information->sampleTable->sampleGroupsDescription);
4993 for (i = 0; i<count; i++) {
4994 sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(trak->Media->information->sampleTable->sampleGroupsDescription, i);
4995 if (sgdesc->grouping_type == grouping_type) {
4996 gf_isom_box_del((GF_Box*)sgdesc);
4997 gf_list_rem(trak->Media->information->sampleTable->sampleGroupsDescription, i);
4998 i--;
4999 count--;
5000 }
5001 sgdesc = NULL;
5002 }
5003
5004 return GF_OK;
5005 }
5006
gf_isom_add_sample_info(GF_ISOFile * movie,u32 track,u32 sample_number,u32 grouping_type,u32 sampleGroupDescriptionIndex,u32 grouping_type_parameter)5007 GF_Err gf_isom_add_sample_info(GF_ISOFile *movie, u32 track, u32 sample_number, u32 grouping_type, u32 sampleGroupDescriptionIndex, u32 grouping_type_parameter)
5008 {
5009 GF_Err e;
5010 GF_TrackBox *trak;
5011 GF_List *groupList;
5012 e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
5013 if (e) return e;
5014
5015 trak = gf_isom_get_track_from_file(movie, track);
5016 if (!trak) return GF_BAD_PARAM;
5017
5018 if (!trak->Media->information->sampleTable->sampleGroups)
5019 trak->Media->information->sampleTable->sampleGroups = gf_list_new();
5020
5021 groupList = trak->Media->information->sampleTable->sampleGroups;
5022 return gf_isom_add_sample_group_entry(groupList, sample_number, grouping_type, grouping_type_parameter, sampleGroupDescriptionIndex);
5023 }
5024
sg_rap_create_entry(void * udta)5025 void *sg_rap_create_entry(void *udta)
5026 {
5027 GF_VisualRandomAccessEntry *entry;
5028 u32 *num_leading_samples = (u32 *)udta;
5029 assert(udta);
5030 GF_SAFEALLOC(entry, GF_VisualRandomAccessEntry);
5031 if (!entry) return NULL;
5032 entry->num_leading_samples = *num_leading_samples;
5033 entry->num_leading_samples_known = entry->num_leading_samples ? 1 : 0;
5034 return entry;
5035 }
5036
sg_rap_compare_entry(void * udta,void * entry)5037 Bool sg_rap_compare_entry(void *udta, void *entry)
5038 {
5039 u32 *num_leading_samples = (u32 *)udta;
5040 if (*num_leading_samples == ((GF_VisualRandomAccessEntry *)entry)->num_leading_samples) return GF_TRUE;
5041 return GF_FALSE;
5042 }
5043
gf_isom_set_sample_rap_group(GF_ISOFile * movie,u32 track,u32 sample_number,u32 num_leading_samples)5044 GF_Err gf_isom_set_sample_rap_group(GF_ISOFile *movie, u32 track, u32 sample_number, u32 num_leading_samples)
5045 {
5046 return gf_isom_set_sample_group_info(movie, track, sample_number, GF_4CC('r', 'a', 'p', ' '), 0, &num_leading_samples, sg_rap_create_entry, sg_rap_compare_entry);
5047 }
5048
5049
5050
sg_roll_create_entry(void * udta)5051 void *sg_roll_create_entry(void *udta)
5052 {
5053 GF_RollRecoveryEntry *entry;
5054 s16 *roll_distance = (s16 *)udta;
5055 GF_SAFEALLOC(entry, GF_RollRecoveryEntry);
5056 if (!entry) return NULL;
5057 entry->roll_distance = *roll_distance;
5058 return entry;
5059 }
5060
sg_roll_compare_entry(void * udta,void * entry)5061 Bool sg_roll_compare_entry(void *udta, void *entry)
5062 {
5063 s16 *roll_distance = (s16 *)udta;
5064 if (*roll_distance == ((GF_RollRecoveryEntry *)entry)->roll_distance) return GF_TRUE;
5065 return GF_FALSE;
5066 }
5067
gf_isom_set_sample_roll_group(GF_ISOFile * movie,u32 track,u32 sample_number,s16 roll_distance)5068 GF_Err gf_isom_set_sample_roll_group(GF_ISOFile *movie, u32 track, u32 sample_number, s16 roll_distance)
5069 {
5070 return gf_isom_set_sample_group_info(movie, track, sample_number, GF_4CC('r', 'o', 'l', 'l'), 0, &roll_distance, sg_roll_create_entry, sg_roll_compare_entry);
5071 }
5072
sg_encryption_create_entry(void * udta)5073 void *sg_encryption_create_entry(void *udta)
5074 {
5075 GF_CENCSampleEncryptionGroupEntry *entry;
5076 GF_BitStream *bs;
5077 GF_SAFEALLOC(entry, GF_CENCSampleEncryptionGroupEntry);
5078 if (!entry) return NULL;
5079 bs = gf_bs_new((char *)udta, sizeof(GF_CENCSampleEncryptionGroupEntry), GF_BITSTREAM_READ);
5080 gf_bs_read_u8(bs); //reserved
5081 entry->crypt_byte_block = gf_bs_read_int(bs, 4);
5082 entry->skip_byte_block = gf_bs_read_int(bs, 4);
5083 entry->IsProtected = gf_bs_read_u8(bs);
5084 entry->Per_Sample_IV_size = gf_bs_read_u8(bs);
5085 gf_bs_read_data(bs, (char *)entry->KID, 16);
5086 if ((entry->IsProtected == 1) && !entry->Per_Sample_IV_size) {
5087 entry->constant_IV_size = gf_bs_read_u8(bs);
5088 assert((entry->constant_IV_size == 8) || (entry->constant_IV_size == 16));
5089 gf_bs_read_data(bs, (char *)entry->constant_IV, entry->constant_IV_size);
5090 }
5091 gf_bs_del(bs);
5092 return entry;
5093 }
5094
sg_encryption_compare_entry(void * udta,void * entry)5095 Bool sg_encryption_compare_entry(void *udta, void *entry)
5096 {
5097 u8 isEncrypted;
5098 u8 IV_size;
5099 bin128 KID;
5100 u8 constant_IV_size = 0;
5101 bin128 constant_IV;
5102 u8 crypt_byte_block, skip_byte_block;
5103 GF_BitStream *bs;
5104 GF_CENCSampleEncryptionGroupEntry *seig = (GF_CENCSampleEncryptionGroupEntry *)entry;
5105 Bool is_identical;
5106 bs = gf_bs_new((char *)udta, sizeof(GF_CENCSampleEncryptionGroupEntry), GF_BITSTREAM_READ);
5107 gf_bs_read_u8(bs); //reserved
5108 crypt_byte_block = gf_bs_read_int(bs, 4);
5109 skip_byte_block = gf_bs_read_int(bs, 4);
5110 isEncrypted = gf_bs_read_u8(bs);
5111 IV_size = gf_bs_read_u8(bs);
5112 gf_bs_read_data(bs, (char *)KID, 16);
5113 if (isEncrypted && !IV_size) {
5114 constant_IV_size = gf_bs_read_u8(bs);
5115 gf_bs_read_data(bs, (char *)constant_IV, 16);
5116 }
5117 gf_bs_del(bs);
5118
5119 is_identical = GF_TRUE;
5120 if ((isEncrypted != seig->IsProtected) || (IV_size != seig->Per_Sample_IV_size) || (strncmp((const char *)KID, (const char *)seig->KID, 16)))
5121 is_identical = GF_FALSE;
5122 if ((crypt_byte_block != seig->crypt_byte_block) || (skip_byte_block != seig->skip_byte_block))
5123 is_identical = GF_FALSE;
5124 if ((isEncrypted == 1) && !IV_size) {
5125 if ((constant_IV_size != seig->constant_IV_size) || (strncmp((const char *)constant_IV, (const char *)seig->constant_IV, constant_IV_size)))
5126 is_identical = GF_FALSE;
5127 }
5128 return is_identical;
5129 }
5130
5131 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
gf_isom_copy_sample_group_entry_to_traf(GF_TrackFragmentBox * traf,GF_SampleTableBox * stbl,u32 grouping_type,u32 grouping_type_parameter,u32 sampleGroupDescriptionIndex,Bool sgpd_in_traf)5132 GF_Err gf_isom_copy_sample_group_entry_to_traf(GF_TrackFragmentBox *traf, GF_SampleTableBox *stbl, u32 grouping_type, u32 grouping_type_parameter, u32 sampleGroupDescriptionIndex, Bool sgpd_in_traf)
5133 {
5134 if (sgpd_in_traf) {
5135 void *entry = NULL;
5136 u32 i, count;
5137 GF_SampleGroupDescriptionBox *sgdesc = NULL;
5138 GF_BitStream *bs;
5139
5140 count = gf_list_count(stbl->sampleGroupsDescription);
5141 for (i = 0; i < count; i++) {
5142 sgdesc = (GF_SampleGroupDescriptionBox *)gf_list_get(stbl->sampleGroupsDescription, i);
5143 if (sgdesc->grouping_type == grouping_type)
5144 break;
5145 sgdesc = NULL;
5146 }
5147 if (!sgdesc)
5148 return GF_BAD_PARAM;
5149
5150 entry = gf_list_get(sgdesc->group_descriptions, sampleGroupDescriptionIndex - 1);
5151 if (!entry)
5152 return GF_BAD_PARAM;
5153
5154 switch (grouping_type) {
5155 case GF_4CC('r', 'a', 'p', ' '):
5156 {
5157 char udta[2];
5158 bs = gf_bs_new(udta, 2 * sizeof(char), GF_BITSTREAM_WRITE);
5159 gf_bs_write_u8(bs, ((GF_VisualRandomAccessEntry *)entry)->num_leading_samples_known);
5160 gf_bs_write_u8(bs, ((GF_VisualRandomAccessEntry *)entry)->num_leading_samples);
5161 gf_bs_del(bs);
5162 return gf_isom_set_sample_group_info_ex(NULL, traf, 0, grouping_type, 0, udta, sg_rap_create_entry, sg_rap_compare_entry);
5163 }
5164 case GF_4CC('r', 'o', 'l', 'l'):
5165 {
5166 char udta[2];
5167 bs = gf_bs_new(udta, 2 * sizeof(char), GF_BITSTREAM_WRITE);
5168 gf_bs_write_u16(bs, ((GF_RollRecoveryEntry *)entry)->roll_distance);
5169 return gf_isom_set_sample_group_info_ex(NULL, traf, 0, grouping_type, 0, udta, sg_roll_create_entry, sg_roll_compare_entry);
5170 }
5171 case GF_4CC('s', 'e', 'i', 'g'):
5172 {
5173 char *udta;
5174 u32 size;
5175 GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
5176 GF_Err e = GF_OK;
5177 gf_bs_write_u8(bs, 0x0);
5178 gf_bs_write_int(bs, ((GF_CENCSampleEncryptionGroupEntry *)entry)->crypt_byte_block, 4);
5179 gf_bs_write_int(bs, ((GF_CENCSampleEncryptionGroupEntry *)entry)->skip_byte_block, 4);
5180 gf_bs_write_u8(bs, ((GF_CENCSampleEncryptionGroupEntry *)entry)->IsProtected);
5181 gf_bs_write_u8(bs, ((GF_CENCSampleEncryptionGroupEntry *)entry)->Per_Sample_IV_size);
5182 gf_bs_write_data(bs, (char *)((GF_CENCSampleEncryptionGroupEntry *)entry)->KID, 16);
5183 if ((((GF_CENCSampleEncryptionGroupEntry *)entry)->IsProtected == 1) && !((GF_CENCSampleEncryptionGroupEntry *)entry)->Per_Sample_IV_size) {
5184 gf_bs_write_u8(bs, ((GF_CENCSampleEncryptionGroupEntry *)entry)->constant_IV_size);
5185 gf_bs_write_data(bs, (char *)((GF_CENCSampleEncryptionGroupEntry *)entry)->constant_IV, ((GF_CENCSampleEncryptionGroupEntry *)entry)->constant_IV_size);
5186 }
5187 gf_bs_get_content(bs, &udta, &size);
5188 gf_bs_del(bs);
5189
5190 e = gf_isom_set_sample_group_info_ex(NULL, traf, 0, grouping_type, 0, udta, sg_encryption_create_entry, sg_encryption_compare_entry);
5191 gf_free(udta);
5192 return e;
5193 }
5194 default:
5195 return GF_BAD_PARAM;
5196 }
5197 }
5198
5199 return gf_isom_add_sample_group_entry(traf->sampleGroups, 0, grouping_type, grouping_type_parameter, sampleGroupDescriptionIndex);
5200 }
5201 #endif /* GPAC_DISABLE_ISOM_FRAGMENTS */
5202
5203 /*sample encryption information group can be in stbl or traf*/
5204 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)5205 GF_Err gf_isom_set_sample_cenc_group(GF_ISOFile *movie, u32 track, u32 sample_number, u8 isEncrypted, u8 IV_size, bin128 KeyID,
5206 u8 crypt_byte_block, u8 skip_byte_block, u8 constant_IV_size, bin128 constant_IV)
5207 {
5208 char *udta;
5209 u32 size;
5210 GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
5211 GF_Err e = GF_OK;
5212 if ((IV_size != 0) && (IV_size != 8) && (IV_size != 16)) return GF_BAD_PARAM;
5213 gf_bs_write_u8(bs, 0x0);
5214 gf_bs_write_int(bs, crypt_byte_block, 4);
5215 gf_bs_write_int(bs, skip_byte_block, 4);
5216 gf_bs_write_u8(bs, isEncrypted);
5217 gf_bs_write_u8(bs, IV_size);
5218 gf_bs_write_data(bs, (char *)KeyID, 16);
5219 if ((isEncrypted == 1) && !IV_size) {
5220 gf_bs_write_u8(bs, constant_IV_size);
5221 gf_bs_write_data(bs, (char *)constant_IV, constant_IV_size);
5222 }
5223 gf_bs_get_content(bs, &udta, &size);
5224 gf_bs_del(bs);
5225
5226 e = gf_isom_set_sample_group_info(movie, track, sample_number, GF_4CC('s', 'e', 'i', 'g'), 0, udta, sg_encryption_create_entry, sg_encryption_compare_entry);
5227 gf_free(udta);
5228 return e;
5229 }
5230
gf_isom_set_ctts_v1(GF_ISOFile * file,u32 track,GF_TrackBox * trak)5231 static GF_Err gf_isom_set_ctts_v1(GF_ISOFile *file, u32 track, GF_TrackBox *trak)
5232 {
5233 u32 i, shift;
5234 u64 duration;
5235 GF_CompositionOffsetBox *ctts;
5236 GF_CompositionToDecodeBox *cslg;
5237 s32 leastCTTS, greatestCTTS;
5238
5239 ctts = trak->Media->information->sampleTable->CompositionOffset;
5240 shift = ctts->entries[0].decodingOffset;
5241 leastCTTS = GF_INT_MAX;
5242 greatestCTTS = 0;
5243 for (i = 0; i<ctts->nb_entries; i++) {
5244 ctts->entries[i].decodingOffset -= shift;
5245 if ((s32)ctts->entries[i].decodingOffset < leastCTTS)
5246 leastCTTS = ctts->entries[i].decodingOffset;
5247 if ((s32)ctts->entries[i].decodingOffset > greatestCTTS)
5248 greatestCTTS = ctts->entries[i].decodingOffset;
5249 }
5250 ctts->version = 1;
5251 gf_isom_remove_edit_segments(file, track);
5252
5253 if (!trak->Media->information->sampleTable->CompositionToDecode)
5254 trak->Media->information->sampleTable->CompositionToDecode = (GF_CompositionToDecodeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CSLG);
5255
5256 cslg = trak->Media->information->sampleTable->CompositionToDecode;
5257
5258 cslg->compositionToDTSShift = shift;
5259 cslg->leastDecodeToDisplayDelta = leastCTTS;
5260 cslg->greatestDecodeToDisplayDelta = greatestCTTS;
5261 cslg->compositionStartTime = 0;
5262 /*for our use case (first CTS set to 0), the composition end time is the media duration if it fits on 32 bits*/
5263 duration = gf_isom_get_media_duration(file, track);
5264 cslg->compositionEndTime = (duration<0x7FFFFFFF) ? (s32)duration : 0;
5265
5266 gf_isom_modify_alternate_brand(file, GF_ISOM_BRAND_ISO4, GF_TRUE);
5267 return GF_OK;
5268 }
5269
gf_isom_set_ctts_v0(GF_ISOFile * file,GF_TrackBox * trak)5270 static GF_Err gf_isom_set_ctts_v0(GF_ISOFile *file, GF_TrackBox *trak)
5271 {
5272 u32 i;
5273 s32 shift;
5274 GF_CompositionOffsetBox *ctts;
5275 GF_CompositionToDecodeBox *cslg;
5276
5277 ctts = trak->Media->information->sampleTable->CompositionOffset;
5278
5279 if (!trak->Media->information->sampleTable->CompositionToDecode)
5280 {
5281 shift = 0;
5282 for (i = 0; i<ctts->nb_entries; i++) {
5283 if (-ctts->entries[i].decodingOffset > shift)
5284 shift = -ctts->entries[i].decodingOffset;
5285 }
5286 if (shift > 0)
5287 {
5288 for (i = 0; i<ctts->nb_entries; i++) {
5289 ctts->entries[i].decodingOffset += shift;
5290 }
5291 }
5292 }
5293 else
5294 {
5295 cslg = trak->Media->information->sampleTable->CompositionToDecode;
5296 shift = cslg->compositionToDTSShift;
5297 for (i = 0; i<ctts->nb_entries; i++) {
5298 ctts->entries[i].decodingOffset += shift;
5299 }
5300 gf_isom_box_del((GF_Box *)cslg);
5301 trak->Media->information->sampleTable->CompositionToDecode = NULL;
5302 }
5303 if (!trak->editBox && shift>0) {
5304 u64 dur = trak->Media->mediaHeader->duration;
5305 dur *= file->moov->mvhd->timeScale;
5306 dur /= trak->Media->mediaHeader->timeScale;
5307 gf_isom_set_edit_segment(file, gf_list_find(file->moov->trackList, trak) + 1, 0, dur, shift, GF_ISOM_EDIT_NORMAL);
5308 }
5309 ctts->version = 0;
5310 gf_isom_modify_alternate_brand(file, GF_ISOM_BRAND_ISO4, GF_FALSE);
5311 return GF_OK;
5312 }
5313
5314 GF_EXPORT
gf_isom_set_composition_offset_mode(GF_ISOFile * file,u32 track,Bool use_negative_offsets)5315 GF_Err gf_isom_set_composition_offset_mode(GF_ISOFile *file, u32 track, Bool use_negative_offsets)
5316 {
5317 GF_Err e;
5318 GF_TrackBox *trak;
5319 GF_CompositionOffsetBox *ctts;
5320
5321 e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
5322 if (e) return e;
5323
5324 trak = gf_isom_get_track_from_file(file, track);
5325 if (!trak) return GF_BAD_PARAM;
5326
5327 ctts = trak->Media->information->sampleTable->CompositionOffset;
5328 if (!ctts) return GF_OK;
5329
5330 if (use_negative_offsets) {
5331 if (ctts->version == 1) return GF_OK;
5332 return gf_isom_set_ctts_v1(file, track, trak);
5333 }
5334 else {
5335 if (ctts->version == 0) return GF_OK;
5336 return gf_isom_set_ctts_v0(file, trak);
5337 }
5338 }
5339
5340 GF_EXPORT
gf_isom_set_sync_table(GF_ISOFile * file,u32 track)5341 GF_Err gf_isom_set_sync_table(GF_ISOFile *file, u32 track)
5342 {
5343 GF_Err e;
5344 GF_TrackBox *trak;
5345
5346 e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
5347 if (e) return e;
5348
5349 trak = gf_isom_get_track_from_file(file, track);
5350 if (!trak) return GF_BAD_PARAM;
5351
5352 trak->Media->information->sampleTable->SyncSample = (GF_SyncSampleBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS);
5353 return GF_OK;
5354 }
5355
gf_isom_is_identical_sgpd(void * ptr1,void * ptr2,u32 grouping_type)5356 Bool gf_isom_is_identical_sgpd(void *ptr1, void *ptr2, u32 grouping_type)
5357 {
5358 Bool res = GF_FALSE;
5359 #ifndef GPAC_DISABLE_ISOM_WRITE
5360 GF_BitStream *bs1, *bs2;
5361 char *buf1, *buf2;
5362 u32 len1, len2;
5363
5364 if (!ptr1 || !ptr2)
5365 return GF_FALSE;
5366
5367 bs1 = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
5368 if (grouping_type) {
5369 sgpd_write_entry(grouping_type, ptr1, bs1);
5370 }
5371 else {
5372 sgpd_Write((GF_Box *)ptr1, bs1);
5373 }
5374 gf_bs_get_content(bs1, &buf1, &len1);
5375 gf_bs_del(bs1);
5376
5377 bs2 = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
5378 if (grouping_type) {
5379 sgpd_write_entry(grouping_type, ptr2, bs2);
5380 }
5381 else {
5382 sgpd_Write((GF_Box *)ptr2, bs2);
5383 }
5384 gf_bs_get_content(bs2, &buf2, &len2);
5385 gf_bs_del(bs2);
5386
5387
5388 if ((len1 == len2) && !memcmp(buf1, buf2, len1))
5389 res = GF_TRUE;
5390
5391 gf_free(buf1);
5392 gf_free(buf2);
5393 #endif
5394 return res;
5395 }
5396
5397 GF_EXPORT
gf_isom_copy_sample_info(GF_ISOFile * dst,u32 dst_track,GF_ISOFile * src,u32 src_track,u32 sampleNumber)5398 GF_Err gf_isom_copy_sample_info(GF_ISOFile *dst, u32 dst_track, GF_ISOFile *src, u32 src_track, u32 sampleNumber)
5399 {
5400 u32 i, count, idx, dst_sample_num, subs_flags;
5401 GF_SubSampleInfoEntry *sub_sample;
5402 GF_Err e;
5403 GF_TrackBox *src_trak, *dst_trak;
5404
5405 src_trak = gf_isom_get_track_from_file(src, src_track);
5406 if (!src_trak) return GF_BAD_PARAM;
5407
5408 dst_trak = gf_isom_get_track_from_file(dst, dst_track);
5409 if (!dst_trak) return GF_BAD_PARAM;
5410
5411 dst_sample_num = dst_trak->Media->information->sampleTable->SampleSize->sampleCount;
5412
5413 /*modify depends flags*/
5414 if (src_trak->Media->information->sampleTable->SampleDep) {
5415 u32 isLeading, dependsOn, dependedOn, redundant;
5416
5417 isLeading = dependsOn = dependedOn = redundant = 0;
5418
5419 e = stbl_GetSampleDepType(src_trak->Media->information->sampleTable->SampleDep, sampleNumber, &isLeading, &dependsOn, &dependedOn, &redundant);
5420 if (e) return e;
5421
5422 e = stbl_AppendDependencyType(dst_trak->Media->information->sampleTable, isLeading, dependsOn, dependedOn, redundant);
5423 if (e) return e;
5424 }
5425
5426 /*copy subsample info if any*/
5427 idx = 1;
5428 while (gf_isom_get_subsample_types(src, src_track, idx, &subs_flags)) {
5429 GF_SubSampleInformationBox *dst_subs = NULL;
5430 idx++;
5431
5432 if (!gf_isom_sample_get_subsample_entry(src, src_track, sampleNumber, subs_flags, &sub_sample))
5433 continue;
5434
5435 /*create subsample if needed*/
5436 if (!dst_trak->Media->information->sampleTable->sub_samples) {
5437 dst_trak->Media->information->sampleTable->sub_samples = gf_list_new();
5438 }
5439 count = gf_list_count(dst_trak->Media->information->sampleTable->sub_samples);
5440 for (i = 0; i<count; i++) {
5441 dst_subs = gf_list_get(dst_trak->Media->information->sampleTable->sub_samples, i);
5442 if (dst_subs->flags == subs_flags) break;
5443 dst_subs = NULL;
5444 }
5445 if (!dst_subs) {
5446 dst_subs = (GF_SubSampleInformationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SUBS);
5447 dst_subs->version = 0;
5448 dst_subs->flags = subs_flags;
5449 gf_list_add(dst_trak->Media->information->sampleTable->sub_samples, dst_subs);
5450 }
5451
5452 count = gf_list_count(sub_sample->SubSamples);
5453 for (i = 0; i<count; i++) {
5454 GF_SubSampleEntry *entry = (GF_SubSampleEntry*)gf_list_get(sub_sample->SubSamples, i);
5455 e = gf_isom_add_subsample_info(dst_subs, dst_sample_num, entry->subsample_size, entry->subsample_priority, entry->reserved, entry->discardable);
5456 if (e) return e;
5457 }
5458 }
5459
5460 /*copy sampleToGroup info if any*/
5461 if (src_trak->Media->information->sampleTable->sampleGroups) {
5462 count = gf_list_count(src_trak->Media->information->sampleTable->sampleGroups);
5463 for (i = 0; i<count; i++) {
5464 GF_SampleGroupBox *sg;
5465 u32 j, k, default_index;
5466 u32 first_sample_in_entry, last_sample_in_entry, group_desc_index_src, group_desc_index_dst;
5467 first_sample_in_entry = 1;
5468
5469 sg = (GF_SampleGroupBox*)gf_list_get(src_trak->Media->information->sampleTable->sampleGroups, i);
5470 for (j = 0; j<sg->entry_count; j++) {
5471 last_sample_in_entry = first_sample_in_entry + sg->sample_entries[j].sample_count - 1;
5472 if ((sampleNumber<first_sample_in_entry) || (sampleNumber>last_sample_in_entry)) {
5473 first_sample_in_entry = last_sample_in_entry + 1;
5474 continue;
5475 }
5476
5477 if (!dst_trak->Media->information->sampleTable->sampleGroups)
5478 dst_trak->Media->information->sampleTable->sampleGroups = gf_list_new();
5479
5480 group_desc_index_src = group_desc_index_dst = sg->sample_entries[j].group_description_index;
5481
5482 if (group_desc_index_src) {
5483 GF_SampleGroupDescriptionBox *sgd_src, *sgd_dst;
5484 GF_DefaultSampleGroupDescriptionEntry *sgde_src, *sgde_dst;
5485
5486 group_desc_index_dst = 0;
5487 //check that the sample group description exists !!
5488 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);
5489
5490 if (!sgde_src) break;
5491
5492 if (!dst_trak->Media->information->sampleTable->sampleGroupsDescription)
5493 dst_trak->Media->information->sampleTable->sampleGroupsDescription = gf_list_new();
5494
5495 sgd_dst = NULL;
5496 for (k = 0; k< gf_list_count(dst_trak->Media->information->sampleTable->sampleGroupsDescription); k++) {
5497 sgd_dst = gf_list_get(dst_trak->Media->information->sampleTable->sampleGroupsDescription, k);
5498 if (sgd_dst->grouping_type == sgd_src->grouping_type) break;
5499 sgd_dst = NULL;
5500 }
5501 if (!sgd_dst) {
5502 gf_isom_clone_box((GF_Box *)sgd_src, (GF_Box **)&sgd_dst);
5503 if (!sgd_dst) return GF_OUT_OF_MEM;
5504 gf_list_add(dst_trak->Media->information->sampleTable->sampleGroupsDescription, sgd_dst);
5505 }
5506
5507 //find the same entry
5508 for (k = 0; k<gf_list_count(sgd_dst->group_descriptions); k++) {
5509 sgde_dst = gf_list_get(sgd_dst->group_descriptions, i);
5510 if (gf_isom_is_identical_sgpd(sgde_src, sgde_dst, sgd_src->grouping_type)) {
5511 group_desc_index_dst = k + 1;
5512 break;
5513 }
5514 }
5515 if (!group_desc_index_dst) {
5516 GF_SampleGroupDescriptionBox *cloned = NULL;
5517 gf_isom_clone_box((GF_Box *)sgd_src, (GF_Box **)&cloned);
5518 if (!cloned) return GF_OUT_OF_MEM;
5519 sgde_dst = gf_list_get(cloned->group_descriptions, group_desc_index_dst);
5520 gf_list_rem(cloned->group_descriptions, group_desc_index_dst);
5521 gf_isom_box_del((GF_Box *)cloned);
5522 gf_list_add(sgd_dst->group_descriptions, sgde_dst);
5523 group_desc_index_dst = gf_list_count(sgd_dst->group_descriptions);
5524 }
5525 }
5526
5527
5528 /*found our sample, add it to trak->sampleGroups*/
5529 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);
5530 if (e) return e;
5531 break;
5532 }
5533 }
5534 }
5535 return GF_OK;
5536 }
5537
5538 GF_EXPORT
gf_isom_text_set_display_flags(GF_ISOFile * file,u32 track,u32 desc_index,u32 flags,GF_TextFlagsMode op_type)5539 GF_Err gf_isom_text_set_display_flags(GF_ISOFile *file, u32 track, u32 desc_index, u32 flags, GF_TextFlagsMode op_type)
5540 {
5541 u32 i;
5542 GF_Err e;
5543 GF_TrackBox *trak;
5544
5545 e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
5546 if (e) return e;
5547
5548 trak = gf_isom_get_track_from_file(file, track);
5549 if (!trak) return GF_BAD_PARAM;
5550
5551 for (i = 0; i < gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes); i++) {
5552 GF_Tx3gSampleEntryBox *txt;
5553 if (desc_index && (i + 1 != desc_index)) continue;
5554
5555 txt = (GF_Tx3gSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, i);
5556 if (txt->type != GF_ISOM_BOX_TYPE_TX3G) continue;
5557
5558 switch (op_type) {
5559 case GF_ISOM_TEXT_FLAGS_TOGGLE:
5560 txt->displayFlags |= flags;
5561 break;
5562 case GF_ISOM_TEXT_FLAGS_UNTOGGLE:
5563 txt->displayFlags &= ~flags;
5564 break;
5565 default:
5566 txt->displayFlags = flags;
5567 break;
5568 }
5569 }
5570 return GF_OK;
5571
5572 }
5573
5574
5575 GF_EXPORT
gf_isom_update_duration(GF_ISOFile * movie)5576 GF_Err gf_isom_update_duration(GF_ISOFile *movie)
5577 {
5578 u32 i;
5579 u64 maxDur;
5580 GF_TrackBox *trak;
5581
5582 if (!movie || !movie->moov) return GF_BAD_PARAM;
5583
5584 //if file was open in Write or Edit mode, recompute the duration
5585 //the duration of a movie is the MaxDuration of all the tracks...
5586
5587 maxDur = 0;
5588 i = 0;
5589 while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) {
5590 if ((movie->LastError = SetTrackDuration(trak))) return movie->LastError;
5591 if (trak->Header->duration > maxDur)
5592 maxDur = trak->Header->duration;
5593 }
5594 movie->moov->mvhd->duration = maxDur;
5595
5596 return GF_OK;
5597 }
5598
5599 GF_EXPORT
gf_isom_update_edit_list_duration(GF_ISOFile * file,u32 track)5600 GF_Err gf_isom_update_edit_list_duration(GF_ISOFile *file, u32 track)
5601 {
5602 u32 i;
5603 u64 trackDuration;
5604 GF_EdtsEntry *ent;
5605 GF_EditListBox *elst;
5606 GF_Err e;
5607 GF_TrackBox *trak;
5608
5609 e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
5610 if (e) return e;
5611
5612 trak = gf_isom_get_track_from_file(file, track);
5613 if (!trak) return GF_BAD_PARAM;
5614
5615
5616 //the total duration is the media duration: adjust it in case...
5617 e = Media_SetDuration(trak);
5618 if (e) return e;
5619
5620 //assert the timeScales are non-NULL
5621 if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) return GF_ISOM_INVALID_FILE;
5622 trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
5623
5624 //if we have an edit list, the duration is the sum of all the editList
5625 //entries' duration (always expressed in MovieTimeScale)
5626 if (trak->editBox && trak->editBox->editList) {
5627 u64 editListDuration = 0;
5628 elst = trak->editBox->editList;
5629 i = 0;
5630 while ((ent = (GF_EdtsEntry*)gf_list_enum(elst->entryList, &i))) {
5631 if (ent->segmentDuration > trackDuration)
5632 ent->segmentDuration = trackDuration;
5633 if ((ent->mediaTime >= 0) && ((u64)ent->mediaTime >= trak->Media->mediaHeader->duration)) {
5634 ent->mediaTime = trak->Media->mediaHeader->duration;
5635 }
5636 editListDuration += ent->segmentDuration;
5637 }
5638 trackDuration = editListDuration;
5639 }
5640 if (!trackDuration) {
5641 trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
5642 }
5643 trak->Header->duration = trackDuration;
5644
5645 return GF_OK;
5646
5647 }
5648
5649
5650 GF_EXPORT
gf_isom_clone_pssh(GF_ISOFile * output,GF_ISOFile * input,Bool in_moof)5651 GF_Err gf_isom_clone_pssh(GF_ISOFile *output, GF_ISOFile *input, Bool in_moof) {
5652 GF_Box *a;
5653 u32 i;
5654 i = 0;
5655
5656 while ((a = (GF_Box *)gf_list_enum(input->moov->other_boxes, &i))) {
5657 if (a->type == GF_ISOM_BOX_TYPE_PSSH) {
5658 GF_ProtectionSystemHeaderBox *pssh = (GF_ProtectionSystemHeaderBox *)pssh_New();
5659 memmove(pssh->SystemID, ((GF_ProtectionSystemHeaderBox *)a)->SystemID, 16);
5660 pssh->KID_count = ((GF_ProtectionSystemHeaderBox *)a)->KID_count;
5661 pssh->KIDs = (bin128 *)gf_malloc(pssh->KID_count * sizeof(bin128));
5662 memmove(pssh->KIDs, ((GF_ProtectionSystemHeaderBox *)a)->KIDs, pssh->KID_count * sizeof(bin128));
5663 pssh->private_data_size = ((GF_ProtectionSystemHeaderBox *)a)->private_data_size;
5664 pssh->private_data = (u8 *)gf_malloc(pssh->private_data_size * sizeof(char));
5665 memmove(pssh->private_data, ((GF_ProtectionSystemHeaderBox *)a)->private_data, pssh->private_data_size);
5666
5667 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
5668 gf_isom_box_add_default(in_moof ? (GF_Box*)output->moof : (GF_Box*)output->moov, (GF_Box*)pssh);
5669 #else
5670 gf_isom_box_add_default((GF_Box*)output->moov, (GF_Box*)pssh);
5671 #endif
5672 }
5673 }
5674 return GF_OK;
5675 }
5676
5677 GF_EXPORT
gf_isom_set_track_group(GF_ISOFile * file,u32 track_number,u32 track_group_id,u32 group_type,Bool do_add)5678 GF_Err gf_isom_set_track_group(GF_ISOFile *file, u32 track_number, u32 track_group_id, u32 group_type, Bool do_add)
5679 {
5680 u32 i, j;
5681 GF_TrackGroupTypeBox *trgt;
5682 GF_Err e;
5683 GF_TrackBox *trak;
5684
5685 e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE);
5686 if (e) return e;
5687
5688 trak = gf_isom_get_track_from_file(file, track_number);
5689 if (!trak) return GF_BAD_PARAM;
5690 if (!trak->groups) trak->groups = (GF_TrackGroupBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_TRGR);
5691
5692 for (j = 0; j<gf_list_count(file->moov->trackList); j++) {
5693 GF_TrackBox *a_trak = gf_list_get(file->moov->trackList, j);
5694 if (!a_trak->groups) continue;
5695
5696 for (i = 0; i<gf_list_count(a_trak->groups->groups); i++) {
5697 trgt = gf_list_get(a_trak->groups->groups, i);
5698
5699 if (trgt->track_group_id == track_group_id) {
5700 if (trgt->group_type != group_type) {
5701 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)));
5702 return GF_BAD_PARAM;
5703 }
5704 if (a_trak == trak) {
5705 if (!do_add) {
5706 gf_list_rem(trak->groups->groups, i);
5707 gf_isom_box_del((GF_Box *)trgt);
5708 }
5709 return GF_OK;
5710 }
5711 }
5712 }
5713 }
5714 //not found, add new group
5715 trgt = (GF_TrackGroupTypeBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_TRGT);
5716 trgt->track_group_id = track_group_id;
5717 trgt->group_type = group_type;
5718 return gf_list_add(trak->groups->groups, trgt);
5719 }
5720
5721 #endif /*!defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)*/
5722
5723
5724