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