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