1 /*
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Jean Le Feuvre
5  *			Copyright (c) Telecom ParisTech 2000-2019
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / ISO Media File Format sub-project
9  *
10  *  GPAC is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU Lesser General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  GPAC is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; see the file COPYING.  If not, write to
22  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25 
26 #include <gpac/internal/isomedia_dev.h>
27 #include <gpac/constants.h>
28 
29 #ifndef GPAC_DISABLE_ISOM
30 
GetTrackbyID(GF_MovieBox * moov,GF_ISOTrackID TrackID)31 GF_TrackBox *GetTrackbyID(GF_MovieBox *moov, GF_ISOTrackID TrackID)
32 {
33 	GF_TrackBox *trak;
34 	u32 i;
35 	if (!moov) return NULL;
36 	i=0;
37 	while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) {
38 		if (trak->Header->trackID == TrackID) return trak;
39 	}
40 	return NULL;
41 }
42 
gf_isom_get_track(GF_MovieBox * moov,u32 trackNumber)43 GF_TrackBox *gf_isom_get_track(GF_MovieBox *moov, u32 trackNumber)
44 {
45 	GF_TrackBox *trak;
46 	if (!moov || !trackNumber || (trackNumber > gf_list_count(moov->trackList))) return NULL;
47 	trak = (GF_TrackBox*)gf_list_get(moov->trackList, trackNumber - 1);
48 	return trak;
49 
50 }
51 
52 //get the number of a track given its ID
53 //return 0 if not found error
gf_isom_get_tracknum_from_id(GF_MovieBox * moov,GF_ISOTrackID trackID)54 u32 gf_isom_get_tracknum_from_id(GF_MovieBox *moov, GF_ISOTrackID trackID)
55 {
56 	u32 i;
57 	GF_TrackBox *trak;
58 	i=0;
59 	while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) {
60 		if (trak->Header->trackID == trackID) return i;
61 	}
62 	return 0;
63 }
64 
65 //extraction of the ESD from the track
GetESD(GF_MovieBox * moov,GF_ISOTrackID trackID,u32 StreamDescIndex,GF_ESD ** outESD)66 GF_Err GetESD(GF_MovieBox *moov, GF_ISOTrackID trackID, u32 StreamDescIndex, GF_ESD **outESD)
67 {
68 	GF_Err e;
69 	GF_ESD *esd;
70 	u32 track_num = 0;
71 	u32 k;
72 	GF_SampleTableBox *stbl;
73 	GF_TrackBox *trak, *OCRTrack;
74 	GF_TrackReferenceTypeBox *dpnd;
75 	GF_SLConfig *slc;
76 	GF_MPEGSampleEntryBox *entry;
77 
78 	if (!moov) return GF_ISOM_INVALID_FILE;
79 
80 	track_num = gf_isom_get_tracknum_from_id(moov, trackID);
81 	dpnd = NULL;
82 	*outESD = NULL;
83 
84 	trak = gf_isom_get_track(moov, track_num);
85 	if (!trak) return GF_ISOM_INVALID_FILE;
86 
87 	e = Media_GetESD(trak->Media, StreamDescIndex, &esd, 0);
88 	if (e) return e;
89 	if (!esd) return GF_NON_COMPLIANT_BITSTREAM;
90 
91 	e = Media_GetSampleDesc(trak->Media, StreamDescIndex, (GF_SampleEntryBox **) &entry, NULL);
92 	if (e) return e;
93 	//set the ID
94 	esd->ESID = trackID;
95 
96 	//find stream dependencies: dpnd, sbas and scal
97 	for (k=0; k<3; k++) {
98 		u32 ref = GF_ISOM_BOX_TYPE_DPND;
99 		if (k==1) ref = GF_ISOM_REF_BASE;
100 		else if (k==2) ref = GF_ISOM_REF_SCAL;
101 
102 		e = Track_FindRef(trak, ref , &dpnd);
103 		if (e) return e;
104 		if (dpnd) {
105 			//ONLY ONE STREAM DEPENDENCY IS ALLOWED
106 			if (!k && (dpnd->trackIDCount != 1)) return GF_ISOM_INVALID_MEDIA;
107 			//fix the spec: where is the index located ??
108 			esd->dependsOnESID = dpnd->trackIDs[0];
109 			break;
110 		} else {
111 			esd->dependsOnESID = 0;
112 		}
113 	}
114 
115 	if (trak->udta) {
116 		GF_UserDataMap *map;
117 		u32 i = 0;
118 		while ((map = (GF_UserDataMap*)gf_list_enum(trak->udta->recordList, &i))) {
119 			if (map->boxType == GF_ISOM_BOX_TYPE_AUXV) {
120 				GF_Descriptor *d = gf_odf_desc_new(GF_ODF_AUX_VIDEO_DATA);
121 				gf_list_add(esd->extensionDescriptors, d);
122 				break;
123 			}
124 		}
125 	}
126 
127 	//OK, get the OCR (in a REAL MP4File, OCR is 0 in ESD and is specified through track reference
128 	dpnd = NULL;
129 	OCRTrack = NULL;
130 	//find OCR dependencies
131 	e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_SYNC, &dpnd);
132 	if (e) return e;
133 	if (dpnd) {
134 		if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA;
135 		esd->OCRESID = dpnd->trackIDs[0];
136 		OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]);
137 
138 		while (OCRTrack) {
139 			/*if we have a dependency on a track that doesn't have OCR dep, remove that dependency*/
140 			e = Track_FindRef(OCRTrack, GF_ISOM_BOX_TYPE_SYNC, &dpnd);
141 			if (e || !dpnd || !dpnd->trackIDCount) {
142 				OCRTrack = NULL;
143 				goto default_sync;
144 			}
145 			/*this is explicit desync*/
146 			if ((dpnd->trackIDs[0]==0) || (dpnd->trackIDs[0]==OCRTrack->Header->trackID))
147 				break;
148 			/*loop in OCRs, break it*/
149 			if (esd->ESID == (u16) OCRTrack->Header->trackID) {
150 				OCRTrack = NULL;
151 				goto default_sync;
152 			}
153 			/*check next*/
154 			OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]);
155 		}
156 		if (!OCRTrack) goto default_sync;
157 	} else {
158 default_sync:
159 		/*all tracks are sync'ed by default*/
160 		if (trak->moov->mov->es_id_default_sync<0) {
161 			if (esd->OCRESID)
162 				trak->moov->mov->es_id_default_sync = esd->OCRESID;
163 			else
164 				trak->moov->mov->es_id_default_sync = esd->ESID;
165 		}
166 		if (trak->moov->mov->es_id_default_sync) esd->OCRESID = (u16) trak->moov->mov->es_id_default_sync;
167 		/*cf ESD writer*/
168 		if (esd->OCRESID == esd->ESID) esd->OCRESID = 0;
169 	}
170 
171 
172 
173 	//update the IPI stuff if needed
174 	if (esd->ipiPtr != NULL) {
175 		dpnd = NULL;
176 		e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_IPIR, &dpnd);
177 		if (e) return e;
178 		if (dpnd) {
179 			if (esd->ipiPtr->tag != GF_ODF_ISOM_IPI_PTR_TAG) return GF_ISOM_INVALID_FILE;
180 			//OK, retrieve the ID: the IPI_ES_Id is currently the ref track
181 			esd->ipiPtr->IPI_ES_Id = dpnd->trackIDs[esd->ipiPtr->IPI_ES_Id - 1];
182 			//and change the tag
183 			esd->ipiPtr->tag = GF_ODF_IPI_PTR_TAG;
184 		} else {
185 			return GF_ISOM_INVALID_FILE;
186 		}
187 	}
188 
189 	if ((trak->Media->mediaHeader->packedLanguage[0] != 'u')
190 	        || (trak->Media->mediaHeader->packedLanguage[1] != 'n')
191 	        || (trak->Media->mediaHeader->packedLanguage[2] != 'd') ) {
192 		if (!esd->langDesc) esd->langDesc = (GF_Language *) gf_odf_desc_new(GF_ODF_LANG_TAG);
193 
194 		esd->langDesc->langCode = trak->Media->mediaHeader->packedLanguage[0];
195 		esd->langDesc->langCode <<= 8;
196 		esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[1];
197 		esd->langDesc->langCode <<= 8;
198 		esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[2];
199 	}
200 
201 
202 	{
203 		u16 rvc_predefined;
204 		u8 *rvc_cfg_data;
205 		const char *mime_type;
206 		u32 rvc_cfg_size;
207 		e = gf_isom_get_rvc_config(moov->mov, track_num, 1, &rvc_predefined, &rvc_cfg_data, &rvc_cfg_size, &mime_type);
208 		if (e==GF_OK) {
209 			if (rvc_predefined) {
210 				esd->decoderConfig->predefined_rvc_config = rvc_predefined;
211 			} else {
212 				esd->decoderConfig->rvc_config = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);
213 				if (mime_type && !strcmp(mime_type, "application/rvc-config+xml+gz") ) {
214 #if !defined(GPAC_DISABLE_CORE_TOOLS) && !defined(GPAC_DISABLE_ZLIB)
215 					gf_gz_decompress_payload(rvc_cfg_data, rvc_cfg_size, &esd->decoderConfig->rvc_config->data, &esd->decoderConfig->rvc_config->dataLength);
216 					gf_free(rvc_cfg_data);
217 #endif
218 				} else {
219 					esd->decoderConfig->rvc_config->data = rvc_cfg_data;
220 					esd->decoderConfig->rvc_config->dataLength = rvc_cfg_size;
221 				}
222 			}
223 		}
224 	}
225 
226 
227 	/*normally all files shall be stored with predefined=SLPredef_MP4, but of course some are broken (philips)
228 	so we just check the ESD_URL. If set, use the given cfg, otherwise always rewrite it*/
229 	if (esd->URLString != NULL) {
230 		*outESD = esd;
231 		return GF_OK;
232 	}
233 
234 	//if we are in publishing mode and we have an SLConfig specified, use it as is
235 	switch (entry->type) {
236 	case GF_ISOM_BOX_TYPE_MP4V:
237 		slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc;
238 		break;
239 	case GF_ISOM_BOX_TYPE_MP4A:
240 		slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc;
241 		break;
242 	case GF_ISOM_BOX_TYPE_MP4S:
243 		slc = entry->slc;
244 		break;
245 	default:
246 		slc = NULL;
247 		break;
248 	}
249 	if (slc) {
250 		gf_odf_desc_del((GF_Descriptor *)esd->slConfig);
251 		gf_odf_desc_copy((GF_Descriptor *)slc, (GF_Descriptor **)&esd->slConfig);
252 		*outESD = esd;
253 		return GF_OK;
254 	}
255 	//otherwise use the regular mapping
256 
257 	if (!esd->slConfig)
258 		esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG);
259 
260 	//this is a desc for a media in the file, let's rewrite some param
261 	esd->slConfig->timestampLength = 32;
262 	esd->slConfig->timestampResolution = trak->Media->mediaHeader->timeScale;
263 	//NO OCR from MP4File streams (eg, constant OC Res one)
264 	esd->slConfig->OCRLength = 0;
265 	esd->slConfig->OCRResolution = 0;
266 //	if (OCRTrack) esd->slConfig->OCRResolution = OCRTrack->Media->mediaHeader->timeScale;
267 
268 	stbl = trak->Media->information->sampleTable;
269 	// a little optimization here: if all our samples are sync,
270 	//set the RAPOnly to true... for external users...
271 	if (! stbl->SyncSample) {
272 		if (
273 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
274 		    moov->mvex &&
275 #endif
276 		    esd->decoderConfig && esd->decoderConfig->streamType &&
277 		    (esd->decoderConfig->streamType==GF_STREAM_VISUAL)
278 		) {
279 			esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0;
280 			esd->slConfig->useRandomAccessPointFlag = 1;
281 			if (trak->moov->mov->openMode!=GF_ISOM_OPEN_READ) {
282 				stbl->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSS);
283 				if (!stbl->SyncSample) return GF_OUT_OF_MEM;
284 			}
285 		} else {
286 			esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1;
287 			esd->slConfig->useRandomAccessPointFlag = 0;
288 		}
289 	} else {
290 		esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0;
291 		//signal we are NOT using sync points if no info is present in the table
292 		esd->slConfig->useRandomAccessPointFlag = stbl->SyncSample->nb_entries ? 1 : 0;
293 	}
294 	//change to support reflecting OD streams
295 	esd->slConfig->useAccessUnitEndFlag = 1;
296 	esd->slConfig->useAccessUnitStartFlag = 1;
297 
298 	//signal we do have padding flag (since we only use logical SL packet
299 	//the user can decide whether to use the info or not
300 	esd->slConfig->usePaddingFlag = stbl->PaddingBits ? 1 : 0;
301 
302 	//same with degradation priority
303 	esd->slConfig->degradationPriorityLength = stbl->DegradationPriority ? 32 : 0;
304 
305 	//this new SL will be OUT OF THE FILE. Let's set its predefined to 0
306 	esd->slConfig->predefined = 0;
307 
308 
309 	*outESD = esd;
310 	return GF_OK;
311 }
312 
313 
314 //extraction of the ESD from the track for the given time
GetESDForTime(GF_MovieBox * moov,GF_ISOTrackID trackID,u64 CTS,GF_ESD ** outESD)315 GF_Err GetESDForTime(GF_MovieBox *moov, GF_ISOTrackID trackID, u64 CTS, GF_ESD **outESD)
316 {
317 	GF_Err e;
318 	u32 sampleDescIndex;
319 	GF_TrackBox *trak;
320 
321 	trak = gf_isom_get_track(moov, gf_isom_get_tracknum_from_id(moov, trackID));
322 	if (!trak) return GF_ISOM_INVALID_FILE;
323 
324 	e = Media_GetSampleDescIndex(trak->Media, CTS, &sampleDescIndex );
325 	if (e) return e;
326 	return GetESD(moov, trackID, sampleDescIndex, outESD);
327 }
328 
329 
Track_FindRef(GF_TrackBox * trak,u32 ReferenceType,GF_TrackReferenceTypeBox ** dpnd)330 GF_Err Track_FindRef(GF_TrackBox *trak, u32 ReferenceType, GF_TrackReferenceTypeBox **dpnd)
331 {
332 	GF_TrackReferenceBox *ref;
333 	GF_TrackReferenceTypeBox *a;
334 	u32 i;
335 	if (! trak) return GF_BAD_PARAM;
336 	if (! trak->References) {
337 		*dpnd = NULL;
338 		return GF_OK;
339 	}
340 	ref = trak->References;
341 	i=0;
342 	while ((a = (GF_TrackReferenceTypeBox *)gf_list_enum(ref->child_boxes, &i))) {
343 		if (a->reference_type == ReferenceType) {
344 			*dpnd = a;
345 			return GF_OK;
346 		}
347 	}
348 	*dpnd = NULL;
349 	return GF_OK;
350 }
351 
Track_IsMPEG4Stream(u32 HandlerType)352 Bool Track_IsMPEG4Stream(u32 HandlerType)
353 {
354 	switch (HandlerType) {
355 	case GF_ISOM_MEDIA_VISUAL:
356     case GF_ISOM_MEDIA_AUXV:
357     case GF_ISOM_MEDIA_PICT:
358 	case GF_ISOM_MEDIA_AUDIO:
359 	case GF_ISOM_MEDIA_SUBPIC:
360 	case GF_ISOM_MEDIA_OD:
361 	case GF_ISOM_MEDIA_OCR:
362 	case GF_ISOM_MEDIA_SCENE:
363 	case GF_ISOM_MEDIA_MPEG7:
364 	case GF_ISOM_MEDIA_OCI:
365 	case GF_ISOM_MEDIA_IPMP:
366 	case GF_ISOM_MEDIA_MPEGJ:
367 	case GF_ISOM_MEDIA_ESM:
368 		return 1;
369 	/*Timedtext is NOT an MPEG-4 stream*/
370 	default:
371 		/*consider xxsm as MPEG-4 handlers*/
372 		if ( (((HandlerType>>8) & 0xFF)== 's') && ((HandlerType& 0xFF)== 'm'))
373 			return 1;
374 		return 0;
375 	}
376 }
377 
378 
SetTrackDuration(GF_TrackBox * trak)379 GF_Err SetTrackDuration(GF_TrackBox *trak)
380 {
381 	u64 trackDuration;
382 	u32 i;
383 	GF_Err e;
384 
385 	//the total duration is the media duration: adjust it in case...
386 	e = Media_SetDuration(trak);
387 	if (e) return e;
388 
389 	//assert the timeScales are non-NULL
390 	if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) return GF_ISOM_INVALID_FILE;
391 	trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
392 
393 	//if we have an edit list, the duration is the sum of all the editList
394 	//entries' duration (always expressed in MovieTimeScale)
395 	if (trak->editBox && trak->editBox->editList) {
396 		GF_EdtsEntry *ent;
397 		GF_EditListBox *elst = trak->editBox->editList;
398 		trackDuration = 0;
399 		i=0;
400 		while ((ent = (GF_EdtsEntry*)gf_list_enum(elst->entryList, &i))) {
401 			trackDuration += ent->segmentDuration;
402 		}
403 	}
404 	if (!trackDuration) {
405 		trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
406 	}
407 	trak->Header->duration = trackDuration;
408 	if (!trak->moov->mov->keep_utc && !gf_sys_is_test_mode() )
409 		trak->Header->modificationTime = gf_isom_get_mp4time();
410 	return GF_OK;
411 }
412 
413 
414 #ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
415 
traf_get_sample_entry(GF_TrackFragmentBox * traf,u32 sample_index)416 GF_TrunEntry *traf_get_sample_entry(GF_TrackFragmentBox *traf, u32 sample_index)
417 {
418 	u32 i, idx;
419 	GF_TrackFragmentRunBox *trun;
420 	idx=0;
421 	i=0;
422 	while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
423 		u32 j;
424 		for (j=0; j<trun->sample_count; j++) {
425 			GF_TrunEntry *ent = gf_list_get(trun->entries, j);
426 			if (idx==sample_index) return ent;
427 			if (ent->nb_pack>1) {
428 				if (idx < sample_index + ent->nb_pack)
429 					return ent;
430 				idx += ent->nb_pack;
431 			} else {
432 				idx++;
433 			}
434 		}
435 	}
436 	return NULL;
437 }
438 
MergeTrack(GF_TrackBox * trak,GF_TrackFragmentBox * traf,GF_MovieFragmentBox * moof_box,u64 moof_offset,s32 compressed_diff,u64 * cumulated_offset,Bool is_first_merge)439 GF_Err MergeTrack(GF_TrackBox *trak, GF_TrackFragmentBox *traf, GF_MovieFragmentBox *moof_box, u64 moof_offset, s32 compressed_diff, u64 *cumulated_offset, Bool is_first_merge)
440 {
441 	u32 i, j, chunk_size, track_num;
442 	u64 base_offset, data_offset, traf_duration;
443 	u32 def_duration, DescIndex, def_size, def_flags;
444 	u32 duration, size, flags, prev_trun_data_offset, sample_index;
445 	u8 pad, sync;
446 	u16 degr;
447 	Bool first_samp_in_traf=GF_TRUE;
448 	Bool store_traf_map=GF_FALSE;
449 	u8 *moof_template=NULL;
450 	u32 moof_template_size=0;
451 	Bool is_seg_start = GF_FALSE;
452 	u64 seg_start=0, sidx_start=0, sidx_end=0, frag_start=0;
453 	GF_TrackFragmentRunBox *trun;
454 	GF_TrunEntry *ent;
455 #ifdef GF_ENABLE_CTRN
456 	GF_TrackFragmentBox *traf_ref = NULL;
457 #endif
458 
459 	GF_Err stbl_AppendTime(GF_SampleTableBox *stbl, u32 duration, u32 nb_pack);
460 	GF_Err stbl_AppendSize(GF_SampleTableBox *stbl, u32 size, u32 nb_pack);
461 	GF_Err stbl_AppendChunk(GF_SampleTableBox *stbl, u64 offset);
462 	GF_Err stbl_AppendSampleToChunk(GF_SampleTableBox *stbl, u32 DescIndex, u32 samplesInChunk);
463 	GF_Err stbl_AppendCTSOffset(GF_SampleTableBox *stbl, s32 CTSOffset);
464 	GF_Err stbl_AppendRAP(GF_SampleTableBox *stbl, u8 isRap);
465 	GF_Err stbl_AppendPadding(GF_SampleTableBox *stbl, u8 padding);
466 	GF_Err stbl_AppendDegradation(GF_SampleTableBox *stbl, u16 DegradationPriority);
467 
468 	if (trak->Header->trackID != traf->tfhd->trackID) return GF_OK;
469 	if (!trak->Media->information->sampleTable
470 		|| !trak->Media->information->sampleTable->SampleSize
471 		|| !trak->Media->information->sampleTable->TimeToSample
472 		|| !trak->Media->information->sampleTable->SampleToChunk
473 		|| !trak->Media->information->sampleTable->ChunkOffset
474 	) {
475 		return GF_ISOM_INVALID_FILE;
476 	}
477 
478 	if (!traf->trex->track)
479 		traf->trex->track = trak;
480 
481 	//setup all our defaults
482 	DescIndex = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DESC) ? traf->tfhd->sample_desc_index : traf->trex->def_sample_desc_index;
483 	if (!DescIndex) {
484 		GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] default sample description set to 0, likely broken ! Fixing to 1\n" ));
485 		DescIndex = 1;
486 	} else if (DescIndex > gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes)) {
487 		GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] default sample description set to %d but only %d sample description(s), likely broken ! Fixing to 1\n", DescIndex, gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes)));
488 		DescIndex = 1;
489 	}
490 #ifdef GF_ENABLE_CTRN
491 	if (traf->trex->inherit_from_traf_id) {
492 		u32 traf_count = gf_list_count(moof_box->TrackList);
493 		for (i=0; i<traf_count; i++) {
494 			GF_TrackFragmentBox *atraf = gf_list_get(moof_box->TrackList, i);
495 			if (atraf->tfhd && atraf->tfhd->trackID==traf->trex->inherit_from_traf_id) {
496 				traf_ref = atraf;
497 				break;
498 			}
499 		}
500 	}
501 #endif
502 
503 	def_duration = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DUR) ? traf->tfhd->def_sample_duration : traf->trex->def_sample_duration;
504 	def_size = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_SIZE) ? traf->tfhd->def_sample_size : traf->trex->def_sample_size;
505 	def_flags = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) ? traf->tfhd->def_sample_flags : traf->trex->def_sample_flags;
506 
507 	//locate base offset, by default use moof (dash-like)
508 	base_offset = moof_offset;
509 	//explicit base offset, use it
510 	if (traf->tfhd->flags & GF_ISOM_TRAF_BASE_OFFSET)
511 		base_offset = traf->tfhd->base_data_offset;
512 	//no moof offset and no explicit offset, the offset is the end of the last written chunk of
513 	//the previous traf. For the first traf, *cumulated_offset is actually moof offset
514 	else if (!(traf->tfhd->flags & GF_ISOM_MOOF_BASE_OFFSET))
515 		base_offset = *cumulated_offset;
516 
517 	chunk_size = 0;
518 	prev_trun_data_offset = 0;
519 	data_offset = 0;
520 	traf_duration = 0;
521 
522 	/*in playback mode*/
523 	if (traf->tfdt && is_first_merge) {
524 #ifndef GPAC_DISABLE_LOG
525 		if (trak->moov->mov->NextMoofNumber && trak->present_in_scalable_segment && trak->sample_count_at_seg_start && (trak->dts_at_seg_start != traf->tfdt->baseMediaDecodeTime)) {
526 			s32 drift = (s32) ((s64) traf->tfdt->baseMediaDecodeTime - (s64)trak->dts_at_seg_start);
527 			if (drift<0)  {
528 				GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Warning: TFDT timing "LLD" less than cumulated timing "LLD" - using tfdt\n", traf->tfdt->baseMediaDecodeTime, trak->dts_at_seg_start ));
529 			} else {
530 				GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[iso file] TFDT timing "LLD" higher than cumulated timing "LLD" (last sample got extended in duration)\n", traf->tfdt->baseMediaDecodeTime, trak->dts_at_seg_start ));
531 			}
532 		}
533 #endif
534 		trak->dts_at_seg_start = traf->tfdt->baseMediaDecodeTime;
535 	}
536 
537 	if (trak->moov->mov->signal_frag_bounds) {
538 		store_traf_map = GF_TRUE;
539 		if (is_first_merge) {
540 			GF_MovieFragmentBox *moof_clone = NULL;
541 			gf_isom_box_freeze_order((GF_Box *)moof_box);
542 			gf_isom_clone_box((GF_Box *)moof_box, (GF_Box **)&moof_clone);
543 
544 			if (moof_clone) {
545 				GF_BitStream *bs;
546 				for (i=0; i<gf_list_count(moof_clone->TrackList); i++) {
547 					GF_TrackFragmentBox *traf_clone = gf_list_get(moof_clone->TrackList, i);
548 					gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->TrackRuns);
549 					gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sampleGroups);
550 					gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sampleGroupsDescription);
551 					gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sub_samples);
552 					gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sai_offsets);
553 					gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sai_sizes);
554 					if (traf_clone->sample_encryption) {
555 						gf_isom_box_del_parent(&traf_clone->child_boxes, (GF_Box *) traf_clone->sample_encryption);
556 						traf_clone->sample_encryption = NULL;
557 					}
558 					if (traf_clone->sdtp) {
559 						gf_isom_box_del_parent(&traf_clone->child_boxes, (GF_Box *) traf_clone->sdtp);
560 						traf_clone->sdtp = NULL;
561 					}
562 				}
563 				gf_isom_box_size((GF_Box *)moof_clone);
564 				bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
565 
566 				if (trak->moov->mov->seg_styp) {
567 					gf_isom_box_size(trak->moov->mov->seg_styp);
568 					gf_isom_box_write(trak->moov->mov->seg_styp, bs);
569 				}
570 				if (trak->moov->mov->root_sidx) {
571 					gf_isom_box_size((GF_Box *)trak->moov->mov->root_sidx);
572 					gf_isom_box_write((GF_Box *)trak->moov->mov->root_sidx, bs);
573 				}
574 				if (trak->moov->mov->seg_ssix) {
575 					gf_isom_box_size(trak->moov->mov->seg_ssix);
576 					gf_isom_box_write(trak->moov->mov->seg_ssix, bs);
577 				}
578 				gf_isom_box_write((GF_Box *)moof_clone, bs);
579 				gf_isom_box_del((GF_Box*)moof_clone);
580 
581 				gf_bs_get_content(bs, &moof_template, &moof_template_size);
582 				gf_bs_del(bs);
583 			}
584 		}
585 		if (trak->moov->mov->seg_styp) {
586 			is_seg_start = GF_TRUE;
587 			seg_start = trak->moov->mov->styp_start_offset;
588 		}
589 		if (trak->moov->mov->root_sidx) {
590 			is_seg_start = GF_TRUE;
591 			sidx_start = trak->moov->mov->sidx_start_offset;
592 			sidx_end = trak->moov->mov->sidx_end_offset;
593 			if (! seg_start || (sidx_start<seg_start))
594 				seg_start = sidx_start;
595 		}
596 		frag_start = trak->moov->mov->current_top_box_start;
597 	}
598 	else if (trak->moov->mov->store_traf_map) {
599 		store_traf_map = GF_TRUE;
600 	}
601 
602 
603 	sample_index = 0;
604 	i=0;
605 	while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
606 		//merge the run
607 		for (j=0; j<trun->sample_count; j++) {
608 			GF_Err e;
609 			s32 cts_offset=0;
610 			ent = (GF_TrunEntry*)gf_list_get(trun->entries, j);
611 
612 			if (!ent) {
613 				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Track %d doesn't have enough trun entries (%d) compared to sample count (%d) in run\n", traf->trex->trackID, gf_list_count(trun->entries), trun->sample_count ));
614 				break;
615 			}
616 			size = def_size;
617 			duration = def_duration;
618 			flags = def_flags;
619 
620 			//CTS - if flag not set (trun or ctrn) defaults to 0 which is the base value after alloc
621 			//we just need to overrite its value if inherited
622 			cts_offset = ent->CTS_Offset;
623 
624 #ifdef GF_ENABLE_CTRN
625 			if (trun->use_ctrn) {
626 				if (!j && (trun->ctrn_flags & GF_ISOM_CTRN_FIRST_SAMPLE) ) {
627 					if (trun->ctrn_first_dur) duration = ent->Duration;
628 					if (trun->ctrn_first_size) size = ent->size;
629 					if (trun->ctrn_first_ctts) flags = ent->flags;
630 				} else {
631 					if (trun->ctrn_dur) duration = ent->Duration;
632 					if (trun->ctrn_size) size = ent->size;
633 					if (trun->ctrn_sample_flags) flags = ent->flags;
634 				}
635 				/*re-override*/
636 				if (trun->ctrn_flags & 0xF0) {
637 					GF_TrunEntry *ref_entry;
638 					if (!traf_ref) {
639 						GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Track %d use traf inheritance to track ID %d but reference traf not found\n", traf->trex->trackID, traf->trex->inherit_from_traf_id ));
640 						break;
641 					}
642 					ref_entry = traf_get_sample_entry(traf_ref, sample_index);
643 					if (!ref_entry) {
644 						GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Track %d use traf inheritance but sample %d not found in reference traf\n", traf->trex->trackID, sample_index+1 ));
645 						break;
646 					}
647 					if (trun->ctrn_flags & GF_ISOM_CTRN_INHERIT_DUR)
648 						duration = ref_entry->Duration;
649 					if (trun->ctrn_flags & GF_ISOM_CTRN_INHERIT_SIZE)
650 						size = ref_entry->size;
651 					if (trun->ctrn_flags & GF_ISOM_CTRN_INHERIT_FLAGS)
652 						flags = ref_entry->flags;
653 					if (trun->ctrn_flags & GF_ISOM_CTRN_INHERIT_CTSO)
654 						cts_offset = ref_entry->CTS_Offset;
655 				}
656 
657 			} else
658 #endif
659 			{
660 				if (trun->flags & GF_ISOM_TRUN_DURATION) duration = ent->Duration;
661 				if (trun->flags & GF_ISOM_TRUN_SIZE) size = ent->size;
662 				if (trun->flags & GF_ISOM_TRUN_FLAGS) {
663 					flags = ent->flags;
664 				} else if (!j && (trun->flags & GF_ISOM_TRUN_FIRST_FLAG)) {
665 					flags = trun->first_sample_flags;
666 				}
667 			}
668 			sample_index++;
669 			/*store the resolved value in case we have inheritance*/
670 			ent->size = size;
671 			ent->Duration = duration;
672 			ent->flags = flags;
673 			ent->CTS_Offset = cts_offset;
674 
675 			//add size first
676 			if (!trak->Media->information->sampleTable->SampleSize) {
677 				trak->Media->information->sampleTable->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STSZ);
678 				if (!trak->Media->information->sampleTable->SampleSize)
679 					return GF_OUT_OF_MEM;
680 			}
681 			e = stbl_AppendSize(trak->Media->information->sampleTable, size, ent->nb_pack);
682 			if (e) return e;
683 
684 			//then TS
685 			if (!trak->Media->information->sampleTable->TimeToSample) {
686 				trak->Media->information->sampleTable->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STTS);
687 				if (!trak->Media->information->sampleTable->TimeToSample)
688 					return GF_OUT_OF_MEM;
689 			}
690 			e = stbl_AppendTime(trak->Media->information->sampleTable, duration, ent->nb_pack);
691 			if (e) return e;
692 
693 			//add chunk on first sample
694 			if (!j) {
695 				u64 final_offset;
696 				data_offset = base_offset;
697 				//we have an explicit data offset for this trun
698 				if (trun->flags & GF_ISOM_TRUN_DATA_OFFSET) {
699 					data_offset += trun->data_offset;
700 					/*reset chunk size since data is now relative to this trun*/
701 					chunk_size = 0;
702 					/*remember this data offset for following trun*/
703 					prev_trun_data_offset = trun->data_offset;
704 					/*if mdat is located after the moof, and the moof was compressed, adjust offset
705 					otherwise the offset does not need adjustment*/
706 					if (trun->data_offset>=0) {
707 						data_offset -= compressed_diff;
708 						prev_trun_data_offset -= compressed_diff;
709 					}
710 				}
711 				//we had an explicit data offset for the previous trun, use it + chunk size
712 				else if (prev_trun_data_offset) {
713 					/*data offset is previous chunk size plus previous offset of the trun*/
714 					data_offset += prev_trun_data_offset + chunk_size;
715 				}
716 				//no explicit data offset, continuous data after last data in previous chunk
717 				else {
718 					data_offset += chunk_size;
719 					//data offset of first trun in first traf, adjust if compressed moof
720 					if ((i==1) && (trun->data_offset>=0)) {
721 						data_offset -= compressed_diff;
722 					}
723 				}
724 
725 				final_offset = data_offset;
726 				//adjust offset if moov was also compressed and we are still in the same file
727 				//so that later call to gf_isom_get_sample properly adjust back the offset
728 				if (trak->moov->compressed_diff) {
729 					final_offset += trak->moov->compressed_diff;
730 				}
731 
732 				if (!trak->Media->information->sampleTable->ChunkOffset) {
733 					trak->Media->information->sampleTable->ChunkOffset = gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STCO);
734 					if (!trak->Media->information->sampleTable->ChunkOffset)
735 						return GF_OUT_OF_MEM;
736 				}
737 				e = stbl_AppendChunk(trak->Media->information->sampleTable, final_offset);
738 				if (e) return e;
739 				//then sampleToChunk
740 				if (!trak->Media->information->sampleTable->SampleToChunk) {
741 					trak->Media->information->sampleTable->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STSC);
742 					if (!trak->Media->information->sampleTable->SampleToChunk)
743 						return GF_OUT_OF_MEM;
744 				}
745 				e = stbl_AppendSampleToChunk(trak->Media->information->sampleTable,
746 				                         DescIndex, trun->sample_count);
747 				if (e) return e;
748 			}
749 			chunk_size += size;
750 
751 			if (store_traf_map && first_samp_in_traf) {
752 				first_samp_in_traf = GF_FALSE;
753 				e = stbl_AppendTrafMap(trak->Media->information->sampleTable, is_seg_start, seg_start, frag_start, moof_template, moof_template_size, sidx_start, sidx_end);
754 				if (e) return e;
755 				//do not deallocate, the memory is now owned by traf map
756 				moof_template = NULL;
757 				moof_template_size = 0;
758 			}
759 			if (ent->nb_pack) {
760 				j+= ent->nb_pack-1;
761 				traf_duration += ent->nb_pack*duration;
762 				continue;
763 			}
764 
765 			traf_duration += duration;
766 
767 			e = stbl_AppendCTSOffset(trak->Media->information->sampleTable, cts_offset);
768 			if (e) return e;
769 			//flags
770 			sync = GF_ISOM_GET_FRAG_SYNC(flags);
771 			if (trak->Media->information->sampleTable->no_sync_found && sync) {
772 				trak->Media->information->sampleTable->no_sync_found = 0;
773 			}
774 			e = stbl_AppendRAP(trak->Media->information->sampleTable, sync);
775 			if (e) return e;
776 			pad = GF_ISOM_GET_FRAG_PAD(flags);
777 			if (pad) {
778 				e = stbl_AppendPadding(trak->Media->information->sampleTable, pad);
779 				if (e) return e;
780 			}
781 			degr = GF_ISOM_GET_FRAG_DEG(flags);
782 			if (degr) {
783 				e = stbl_AppendDegradation(trak->Media->information->sampleTable, degr);
784 				if (e) return e;
785 			}
786 			e = stbl_AppendDependencyType(trak->Media->information->sampleTable, GF_ISOM_GET_FRAG_LEAD(flags), GF_ISOM_GET_FRAG_DEPENDS(flags), GF_ISOM_GET_FRAG_DEPENDED(flags), GF_ISOM_GET_FRAG_REDUNDANT(flags));
787 			if (e) return e;
788 		}
789 	}
790 	if (traf_duration && trak->editBox && trak->editBox->editList) {
791 		for (i=0; i<gf_list_count(trak->editBox->editList->entryList); i++) {
792 			GF_EdtsEntry *edts_e = gf_list_get(trak->editBox->editList->entryList, i);
793 			if (edts_e->was_empty_dur) {
794 				u64 extend_dur = traf_duration;
795 				extend_dur *= trak->moov->mvhd->timeScale;
796 				extend_dur /= trak->Media->mediaHeader->timeScale;
797 				edts_e->segmentDuration += extend_dur;
798 			}
799 			else if (!edts_e->segmentDuration) {
800 				edts_e->was_empty_dur = GF_TRUE;
801 				if ((s64) traf_duration > edts_e->mediaTime)
802 					traf_duration -= edts_e->mediaTime;
803 				else
804 					traf_duration = 0;
805 
806 				edts_e->segmentDuration = traf_duration;
807 				edts_e->segmentDuration *= trak->moov->mvhd->timeScale;
808 				edts_e->segmentDuration /= trak->Media->mediaHeader->timeScale;
809 			}
810 
811 		}
812 	}
813 
814 	//in any case, update the cumulated offset
815 	//this will handle hypothetical files mixing MOOF offset and implicit non-moof offset
816 	*cumulated_offset = data_offset + chunk_size;
817 
818 	/*merge sample groups*/
819 	if (traf->sampleGroups) {
820 		GF_List *groups;
821 		GF_List *groupDescs;
822 		Bool is_identical_sgpd = GF_TRUE;
823 		u32 *new_idx = NULL;
824 
825 		if (!trak->Media->information->sampleTable->sampleGroups)
826 			trak->Media->information->sampleTable->sampleGroups = gf_list_new();
827 
828 		if (!trak->Media->information->sampleTable->sampleGroupsDescription)
829 			trak->Media->information->sampleTable->sampleGroupsDescription = gf_list_new();
830 
831 		groupDescs = trak->Media->information->sampleTable->sampleGroupsDescription;
832 		for (i=0; i<gf_list_count(traf->sampleGroupsDescription); i++) {
833 			GF_SampleGroupDescriptionBox *new_sgdesc = NULL;
834 			GF_SampleGroupDescriptionBox *sgdesc = gf_list_get(traf->sampleGroupsDescription, i);
835 			for (j=0; j<gf_list_count(groupDescs); j++) {
836 				new_sgdesc = gf_list_get(groupDescs, j);
837 				if (new_sgdesc->grouping_type==sgdesc->grouping_type) break;
838 				new_sgdesc = NULL;
839 			}
840 			/*new description, move it to our sample table*/
841 			if (!new_sgdesc) {
842 				gf_list_add(groupDescs, sgdesc);
843 				gf_list_add(trak->Media->information->sampleTable->child_boxes, sgdesc);
844 				gf_list_rem(traf->sampleGroupsDescription, i);
845 				gf_list_del_item(traf->child_boxes, sgdesc);
846 				i--;
847 			}
848 			/*merge descriptions*/
849 			else {
850 				u32 count;
851 
852 				is_identical_sgpd = gf_isom_is_identical_sgpd(new_sgdesc, sgdesc, 0);
853 				if (is_identical_sgpd)
854 					continue;
855 
856 				new_idx = (u32 *)gf_malloc(gf_list_count(sgdesc->group_descriptions)*sizeof(u32));
857 				if (!new_idx) return GF_OUT_OF_MEM;
858 
859 				count = 0;
860 				while (gf_list_count(sgdesc->group_descriptions)) {
861 					void *sgpd_entry = gf_list_get(sgdesc->group_descriptions, 0);
862 					Bool new_entry = GF_TRUE;
863 
864 					for (j = 0; j < gf_list_count(new_sgdesc->group_descriptions); j++) {
865 						void *ptr = gf_list_get(new_sgdesc->group_descriptions, j);
866 						if (gf_isom_is_identical_sgpd(sgpd_entry, ptr, new_sgdesc->grouping_type)) {
867 							new_idx[count] = j + 1;
868 							count ++;
869 							new_entry = GF_FALSE;
870 							gf_free(sgpd_entry);
871 							break;
872 						}
873 					}
874 
875 					if (new_entry) {
876 						gf_list_add(new_sgdesc->group_descriptions, sgpd_entry);
877 						new_idx[count] = gf_list_count(new_sgdesc->group_descriptions);
878 						count ++;
879 					}
880 
881 					gf_list_rem(sgdesc->group_descriptions, 0);
882 				}
883 			}
884 		}
885 
886 		groups = trak->Media->information->sampleTable->sampleGroups;
887 		for (i=0; i<gf_list_count(traf->sampleGroups); i++) {
888 			GF_SampleGroupBox *stbl_group = NULL;
889 			GF_SampleGroupBox *frag_group = gf_list_get(traf->sampleGroups, i);
890 
891 
892 			for (j=0; j<gf_list_count(groups); j++) {
893 				stbl_group = gf_list_get(groups, j);
894 				if ((frag_group->grouping_type==stbl_group->grouping_type) && (frag_group->grouping_type_parameter==stbl_group->grouping_type_parameter))
895 					break;
896 				stbl_group = NULL;
897 			}
898 			if (!stbl_group) {
899 				stbl_group = (GF_SampleGroupBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_SBGP);
900 				if (!stbl_group) return GF_OUT_OF_MEM;
901 				stbl_group->grouping_type = frag_group->grouping_type;
902 				stbl_group->grouping_type_parameter = frag_group->grouping_type_parameter;
903 				stbl_group->version = frag_group->version;
904 				gf_list_add(groups, stbl_group);
905 			}
906 
907 			if (is_identical_sgpd) {
908 				//adjust sgpd index: in traf index start at 0x1001
909 				for (j = 0; j < frag_group->entry_count; j++)
910 					frag_group->sample_entries[j].group_description_index &= 0x0FFFF;
911 				if (frag_group->entry_count && stbl_group->entry_count &&
912 				        (frag_group->sample_entries[0].group_description_index==stbl_group->sample_entries[stbl_group->entry_count-1].group_description_index)
913 				   ) {
914 					stbl_group->sample_entries[stbl_group->entry_count - 1].sample_count += frag_group->sample_entries[0].sample_count;
915 					if (frag_group->entry_count>1) {
916 						stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count - 1));
917 						memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[1], sizeof(GF_SampleGroupEntry) * (frag_group->entry_count - 1));
918 						stbl_group->entry_count += frag_group->entry_count - 1;
919 					}
920 				} else {
921 					stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count));
922 					memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[0], sizeof(GF_SampleGroupEntry) * frag_group->entry_count);
923 					stbl_group->entry_count += frag_group->entry_count;
924 				}
925 			} else {
926 				stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count));
927 				//adjust sgpd index
928 				for (j = 0; j < frag_group->entry_count; j++)
929 					frag_group->sample_entries[j].group_description_index = new_idx[j];
930 				memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[0], sizeof(GF_SampleGroupEntry) * frag_group->entry_count);
931 				stbl_group->entry_count += frag_group->entry_count;
932 			}
933 		}
934 
935 		if (new_idx) gf_free(new_idx);
936 	}
937 
938 	/*content is encrypted*/
939 	track_num = gf_isom_get_tracknum_from_id(trak->moov, trak->Header->trackID);
940 	if (gf_isom_is_cenc_media(trak->moov->mov, track_num, DescIndex)
941 		|| traf->sample_encryption) {
942 		/*Merge sample auxiliary encryption information*/
943 		GF_SampleEncryptionBox *senc = NULL;
944 		GF_List *sais = NULL;
945 		u32 scheme_type;
946 		gf_isom_get_cenc_info(trak->moov->mov, track_num, DescIndex, NULL, &scheme_type, NULL, NULL);
947 
948 		if (traf->sample_encryption) {
949 			for (i = 0; i < gf_list_count(trak->Media->information->sampleTable->child_boxes); i++) {
950 				GF_Box *a = (GF_Box *)gf_list_get(trak->Media->information->sampleTable->child_boxes, i);
951 				if (a->type != traf->sample_encryption->type) continue;
952 
953 				if ((a->type ==GF_ISOM_BOX_TYPE_UUID) && (((GF_UUIDBox *)a)->internal_4cc == GF_ISOM_BOX_UUID_PSEC)) {
954 					senc = (GF_SampleEncryptionBox *)a;
955 					break;
956 				}
957 				else if (a->type ==GF_ISOM_BOX_TYPE_SENC) {
958 					senc = (GF_SampleEncryptionBox *)a;
959 					break;
960 				}
961 			}
962 			if (!senc && trak->sample_encryption)
963 				senc = trak->sample_encryption;
964 
965 			if (!senc) {
966 				if (traf->sample_encryption->is_piff) {
967 					senc = (GF_SampleEncryptionBox *)gf_isom_create_piff_psec_box(1, 0x2, 0, 0, NULL);
968 				} else {
969 					senc = gf_isom_create_samp_enc_box(1, 0x2);
970 				}
971 
972 				if (!trak->Media->information->sampleTable->child_boxes) trak->Media->information->sampleTable->child_boxes = gf_list_new();
973 
974 				trak->sample_encryption = senc;
975 				if (!trak->child_boxes) trak->child_boxes = gf_list_new();
976 				gf_list_add(trak->child_boxes, senc);
977 			}
978 
979 			sais = traf->sample_encryption->samp_aux_info;
980 		}
981 
982 		/*get sample auxiliary information by saiz/saio rather than by parsing senc box*/
983 		if (gf_isom_cenc_has_saiz_saio_traf(traf, scheme_type)) {
984 			u32 nb_saio;
985 			u32 aux_info_type;
986 			u64 offset;
987 			GF_Err e;
988 			Bool is_encrypted;
989 			GF_SampleAuxiliaryInfoOffsetBox *saio = NULL;
990 			GF_SampleAuxiliaryInfoSizeBox *saiz = NULL;
991 
992 			offset = nb_saio = 0;
993 
994 			for (i = 0; i < gf_list_count(traf->sai_offsets); i++) {
995 				saio = (GF_SampleAuxiliaryInfoOffsetBox *)gf_list_get(traf->sai_offsets, i);
996 				aux_info_type = saio->aux_info_type;
997 				if (!aux_info_type) aux_info_type = scheme_type;
998 
999 				/*if we have only 1 sai_offsets, assume that its type is cenc*/
1000 				if ((aux_info_type == GF_ISOM_CENC_SCHEME) || (aux_info_type == GF_ISOM_CBC_SCHEME) ||
1001 					(aux_info_type == GF_ISOM_CENS_SCHEME) || (aux_info_type == GF_ISOM_CBCS_SCHEME) ||
1002 					(gf_list_count(traf->sai_offsets) == 1)) {
1003 					offset = saio->offsets[0] + moof_offset;
1004 					nb_saio = saio->entry_count;
1005 					break;
1006 				}
1007 			}
1008 			for (i = 0; i < gf_list_count(traf->sai_sizes); i++) {
1009 				saiz = (GF_SampleAuxiliaryInfoSizeBox *)gf_list_get(traf->sai_sizes, i);
1010 				aux_info_type = saiz->aux_info_type;
1011 				if (!aux_info_type) aux_info_type = scheme_type;
1012 				/*if we have only 1 sai_sizes, assume that its type is cenc*/
1013 				if ((aux_info_type == GF_ISOM_CENC_SCHEME) || (aux_info_type == GF_ISOM_CBC_SCHEME) ||
1014 					(aux_info_type == GF_ISOM_CENS_SCHEME) || (aux_info_type == GF_ISOM_CBCS_SCHEME) ||
1015 					(gf_list_count(traf->sai_sizes) == 1)) {
1016 					break;
1017 				}
1018 			}
1019 			if (saiz && saio) {
1020 				for (i = 0; i < saiz->sample_count; i++) {
1021 					GF_CENCSampleAuxInfo *sai;
1022 
1023 					u64 cur_position;
1024 					if (nb_saio != 1)
1025 						offset = saio->offsets[i] + moof_offset;
1026 					size = saiz->default_sample_info_size ? saiz->default_sample_info_size : saiz->sample_info_size[i];
1027 
1028 
1029 					cur_position = gf_bs_get_position(trak->moov->mov->movieFileMap->bs);
1030 					gf_bs_seek(trak->moov->mov->movieFileMap->bs, offset);
1031 
1032 					GF_SAFEALLOC(sai, GF_CENCSampleAuxInfo);
1033 					if (!sai) return GF_OUT_OF_MEM;
1034 
1035 					e = gf_isom_get_sample_cenc_info_ex(trak, traf, senc, i+1, &is_encrypted, &sai->IV_size, NULL, NULL, NULL, NULL, NULL);
1036 					if (e) {
1037 						GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] could not get cenc info for sample %d: %s\n", i+1, gf_error_to_string(e) ));
1038 						return e;
1039 					}
1040 
1041 					if (is_encrypted) {
1042 						gf_bs_read_data(trak->moov->mov->movieFileMap->bs, (char *)sai->IV, sai->IV_size);
1043 						if (size > sai->IV_size) {
1044 							sai->subsample_count = gf_bs_read_u16(trak->moov->mov->movieFileMap->bs);
1045 							sai->subsamples = (GF_CENCSubSampleEntry *)gf_malloc(sizeof(GF_CENCSubSampleEntry)*sai->subsample_count);
1046 							if (!sai->subsamples) return GF_OUT_OF_MEM;
1047 							for (j = 0; j < sai->subsample_count; j++) {
1048 								sai->subsamples[j].bytes_clear_data = gf_bs_read_u16(trak->moov->mov->movieFileMap->bs);
1049 								sai->subsamples[j].bytes_encrypted_data = gf_bs_read_u32(trak->moov->mov->movieFileMap->bs);
1050 							}
1051 						}
1052 					} else {
1053 						sai->IV_size=0;
1054 						sai->subsample_count=0;
1055 					}
1056 
1057 					gf_bs_seek(trak->moov->mov->movieFileMap->bs, cur_position);
1058 
1059 					gf_list_add(senc->samp_aux_info, sai);
1060 					if (sai->subsample_count) senc->flags = 0x00000002;
1061 					e = gf_isom_cenc_merge_saiz_saio(senc, trak->Media->information->sampleTable, offset, size);
1062 					if (e) return e;
1063 					if (nb_saio == 1)
1064 						offset += size;
1065 				}
1066 			}
1067 		} else if (sais) {
1068 			for (i = 0; i < gf_list_count(sais); i++) {
1069 				GF_CENCSampleAuxInfo *sai, *new_sai;
1070 
1071 				sai = (GF_CENCSampleAuxInfo *)gf_list_get(sais, i);
1072 
1073 				new_sai = (GF_CENCSampleAuxInfo *)gf_malloc(sizeof(GF_CENCSampleAuxInfo));
1074 				if (!new_sai) return GF_OUT_OF_MEM;
1075 				new_sai->IV_size = sai->IV_size;
1076 				memmove((char *)new_sai->IV, (const char*)sai->IV, 16);
1077 				new_sai->subsample_count = sai->subsample_count;
1078 				new_sai->subsamples = (GF_CENCSubSampleEntry *)gf_malloc(new_sai->subsample_count*sizeof(GF_CENCSubSampleEntry));
1079 				if (!new_sai->subsamples) return GF_OUT_OF_MEM;
1080 				memmove(new_sai->subsamples, sai->subsamples, new_sai->subsample_count*sizeof(GF_CENCSubSampleEntry));
1081 
1082 				gf_list_add(senc->samp_aux_info, new_sai);
1083 				if (sai->subsample_count) senc->flags = 0x00000002;
1084 			}
1085 		} else if (traf->sample_encryption) {
1086 			senc_Parse(trak->moov->mov->movieFileMap->bs, trak, traf, traf->sample_encryption);
1087 			trak->sample_encryption->AlgorithmID = traf->sample_encryption->AlgorithmID;
1088 			if (!trak->sample_encryption->IV_size)
1089 				trak->sample_encryption->IV_size = traf->sample_encryption->IV_size;
1090 			if (!trak->sample_encryption->samp_aux_info) trak->sample_encryption->samp_aux_info = gf_list_new();
1091 			gf_list_transfer(trak->sample_encryption->samp_aux_info, traf->sample_encryption->samp_aux_info);
1092 		}
1093 	}
1094 	return GF_OK;
1095 }
1096 
1097 #endif
1098 
1099 
1100 #ifndef GPAC_DISABLE_ISOM_WRITE
1101 
1102 //used to check if a TrackID is available
RequestTrack(GF_MovieBox * moov,GF_ISOTrackID TrackID)1103 u8 RequestTrack(GF_MovieBox *moov, GF_ISOTrackID TrackID)
1104 {
1105 	u32 i;
1106 	GF_TrackBox *trak;
1107 
1108 	i=0;
1109 	while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) {
1110 		if (trak->Header->trackID == TrackID) {
1111 			gf_isom_set_last_error(moov->mov, GF_BAD_PARAM);
1112 			return 0;
1113 		}
1114 	}
1115 	return 1;
1116 }
1117 
Track_RemoveRef(GF_TrackBox * trak,u32 ReferenceType)1118 GF_Err Track_RemoveRef(GF_TrackBox *trak, u32 ReferenceType)
1119 {
1120 	GF_TrackReferenceBox *ref;
1121 	GF_Box *a;
1122 	u32 i;
1123 	if (! trak) return GF_BAD_PARAM;
1124 	if (! trak->References) return GF_OK;
1125 	ref = trak->References;
1126 	i=0;
1127 	while ((a = (GF_Box *)gf_list_enum(ref->child_boxes, &i))) {
1128 		if (a->type == ReferenceType) {
1129 			gf_isom_box_del_parent(&ref->child_boxes, a);
1130 			return GF_OK;
1131 		}
1132 	}
1133 	return GF_OK;
1134 }
1135 
NewMedia(GF_MediaBox ** mdia,u32 MediaType,u32 TimeScale)1136 GF_Err NewMedia(GF_MediaBox **mdia, u32 MediaType, u32 TimeScale)
1137 {
1138 	GF_MediaHeaderBox *mdhd;
1139 	GF_Box *mediaInfo;
1140 	GF_HandlerBox *hdlr;
1141 	GF_MediaInformationBox *minf;
1142 	GF_DataInformationBox *dinf;
1143 	GF_SampleTableBox *stbl;
1144 	GF_DataReferenceBox *dref;
1145 	char *str="";
1146 
1147 	GF_Err e;
1148 
1149 	if (!mdia) return GF_BAD_PARAM;
1150 
1151 	minf = *mdia ? (*mdia)->information : NULL;
1152 	mdhd = *mdia ? (*mdia)->mediaHeader : NULL;
1153 	hdlr = *mdia ? (*mdia)->handler : NULL;
1154 	dinf =  minf ? minf->dataInformation : NULL;
1155 	stbl = minf ? minf->sampleTable : NULL;
1156 	dref = dinf ? dinf->dref : NULL;
1157 	mediaInfo = minf ? minf->InfoHeader : NULL;
1158 
1159 	//first create the media
1160 	if (!*mdia) {
1161 		*mdia = (GF_MediaBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDIA);
1162 		if (! *mdia) { e = GF_OUT_OF_MEM; goto err_exit; }
1163 	}
1164 	if (!mdhd) {
1165 		mdhd = (GF_MediaHeaderBox *) gf_isom_box_new_parent( & ((*mdia)->child_boxes), GF_ISOM_BOX_TYPE_MDHD);
1166 		if (! mdhd) { e = GF_OUT_OF_MEM; goto err_exit; }
1167 		e = mdia_on_child_box((GF_Box*)*mdia, (GF_Box *) mdhd);
1168 		if (e) goto err_exit;
1169 	}
1170 	if (!hdlr) {
1171 		hdlr = (GF_HandlerBox *) gf_isom_box_new_parent(& ((*mdia)->child_boxes), GF_ISOM_BOX_TYPE_HDLR);
1172 		if (! hdlr) { e = GF_OUT_OF_MEM; goto err_exit; }
1173 		e = mdia_on_child_box((GF_Box*)*mdia, (GF_Box *) hdlr);
1174 		if (e) goto err_exit;
1175 	}
1176 	if (!minf) {
1177 		minf = (GF_MediaInformationBox *) gf_isom_box_new_parent(& ((*mdia)->child_boxes), GF_ISOM_BOX_TYPE_MINF);
1178 		if (! minf) { e = GF_OUT_OF_MEM; goto err_exit; }
1179 		e = mdia_on_child_box((GF_Box*)*mdia, (GF_Box *) minf);
1180 		if (e) goto err_exit;
1181 	}
1182 	if (!dinf) {
1183 		dinf = (GF_DataInformationBox *) gf_isom_box_new_parent(&minf->child_boxes, GF_ISOM_BOX_TYPE_DINF);
1184 		if (! dinf) { e = GF_OUT_OF_MEM; goto err_exit; }
1185 		e = minf_on_child_box((GF_Box*)minf, (GF_Box *) dinf);
1186 		if (e) goto err_exit;
1187 	}
1188 
1189 	if (!mediaInfo) {
1190 		//"handler name" is for debugging purposes. Let's stick our name here ;)
1191 		switch (MediaType) {
1192 		case GF_ISOM_MEDIA_VISUAL:
1193 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
1194 			str = "GPAC ISO Video Handler";
1195 			break;
1196 		case GF_ISOM_MEDIA_AUXV:
1197 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
1198 			str = "GPAC ISO Auxiliary Video Handler";
1199 			break;
1200 		case GF_ISOM_MEDIA_PICT:
1201 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
1202 			str = "GPAC ISO Picture Sequence Handler";
1203 			break;
1204 		case GF_ISOM_MEDIA_AUDIO:
1205 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_SMHD);
1206 			str = "GPAC ISO Audio Handler";
1207 			break;
1208 		case GF_ISOM_MEDIA_HINT:
1209 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_HMHD);
1210 			str = "GPAC ISO Hint Handler";
1211 			break;
1212 		case GF_ISOM_MEDIA_META:
1213 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1214 			str = "GPAC Timed MetaData Handler";
1215 			break;
1216 		case GF_ISOM_MEDIA_OD:
1217 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1218 			str = "GPAC MPEG-4 OD Handler";
1219 			break;
1220 		case GF_ISOM_MEDIA_OCR:
1221 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1222 			str = "GPAC MPEG-4 OCR Handler";
1223 			break;
1224 		case GF_ISOM_MEDIA_SCENE:
1225 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1226 			str = "GPAC MPEG-4 Scene Description Handler";
1227 			break;
1228 		case GF_ISOM_MEDIA_MPEG7:
1229 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1230 			str = "GPAC MPEG-4 MPEG-7 Handler";
1231 			break;
1232 		case GF_ISOM_MEDIA_OCI:
1233 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1234 			str = "GPAC MPEG-4 OCI Handler";
1235 			break;
1236 		case GF_ISOM_MEDIA_IPMP:
1237 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1238 			str = "GPAC MPEG-4 IPMP Handler";
1239 			break;
1240 		case GF_ISOM_MEDIA_MPEGJ:
1241 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1242 			str = "GPAC MPEG-4 MPEG-J Handler";
1243 			break;
1244 		case GF_ISOM_MEDIA_TEXT:
1245 		case GF_ISOM_MEDIA_SUBT:
1246 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1247 			str = "GPAC Streaming Text Handler";
1248 			break;
1249 		case GF_ISOM_MEDIA_MPEG_SUBT:
1250 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_STHD);
1251 			str = "GPAC MPEG Subtitle Handler";
1252 			break;
1253 		case GF_ISOM_MEDIA_DIMS:
1254 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
1255 			MediaType = GF_ISOM_MEDIA_SCENE;
1256 			str = "GPAC DIMS Handler";
1257 			break;
1258 		case GF_ISOM_MEDIA_TMCD:
1259 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_GMHD);
1260 			str = "GPAC TMCD Handler";
1261 			break;
1262 		default:
1263 			mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1264 			str = "GPAC IsoMedia Handler";
1265 			break;
1266 		}
1267 		if (! mediaInfo) { e = GF_OUT_OF_MEM; goto err_exit; }
1268 		if (!minf->child_boxes) minf->child_boxes = gf_list_new();
1269 		gf_list_add(minf->child_boxes, mediaInfo);
1270 
1271 		e = minf_on_child_box((GF_Box*)minf, (GF_Box *) mediaInfo);
1272 		if (e) goto err_exit;
1273 	}
1274 
1275 	mdhd->timeScale = TimeScale;
1276 	hdlr->handlerType = MediaType;
1277 	if (!hdlr->nameUTF8)
1278 		hdlr->nameUTF8 = gf_strdup(str);
1279 
1280 	if (!dref) {
1281 		//Create a data reference WITHOUT DATA ENTRY (we don't know anything yet about the media Data)
1282 		dref = (GF_DataReferenceBox *) gf_isom_box_new_parent(&dinf->child_boxes, GF_ISOM_BOX_TYPE_DREF);
1283 		if (! dref) { e = GF_OUT_OF_MEM; goto err_exit; }
1284 		e = dinf_on_child_box((GF_Box*)dinf, (GF_Box *)dref);
1285 		if (e) goto err_exit;
1286 	}
1287 
1288 	if (!stbl) {
1289 		//first set-up the sample table...
1290 		stbl = (GF_SampleTableBox *) gf_isom_box_new_parent(&minf->child_boxes, GF_ISOM_BOX_TYPE_STBL);
1291 		if (! stbl) { e = GF_OUT_OF_MEM; goto err_exit; }
1292 
1293 		e = minf_on_child_box((GF_Box*)minf, (GF_Box *) stbl);
1294 		if (e) goto err_exit;
1295 	}
1296 	if (!stbl->SampleDescription) {
1297 		stbl->SampleDescription = (GF_SampleDescriptionBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSD);
1298 		if (! stbl->SampleDescription) { e = GF_OUT_OF_MEM; goto err_exit; }
1299 	}
1300 
1301 	//by default create a regular table, 32 but offset and normal sample size
1302 	if (!stbl->ChunkOffset) {
1303 		stbl->ChunkOffset = gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STCO);
1304 		if (! stbl->ChunkOffset) { e = GF_OUT_OF_MEM; goto err_exit; }
1305 	}
1306 	if (!stbl->SampleSize) {
1307 		stbl->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSZ);
1308 		if (! stbl->SampleSize) { e = GF_OUT_OF_MEM; goto err_exit; }
1309 	}
1310 	if (!stbl->SampleToChunk) {
1311 		stbl->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSC);
1312 		if (! stbl->SampleToChunk) { e = GF_OUT_OF_MEM; goto err_exit; }
1313 	}
1314 	if (!stbl->TimeToSample) {
1315 		stbl->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STTS);
1316 		if (! stbl->TimeToSample) { e = GF_OUT_OF_MEM; goto err_exit; }
1317 	}
1318 	if (!stbl->SampleDescription) {
1319 		stbl->SampleDescription = (GF_SampleDescriptionBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSD);
1320 		if (! stbl->SampleDescription) { e = GF_OUT_OF_MEM; goto err_exit; }
1321 	}
1322 	return GF_OK;
1323 
1324 err_exit:
1325 	if (mdhd) gf_isom_box_del_parent(& ((*mdia)->child_boxes), (GF_Box *)mdhd);
1326 	if (minf) gf_isom_box_del_parent(& ((*mdia)->child_boxes), (GF_Box *)minf);
1327 	if (hdlr) {
1328 		gf_isom_box_del_parent(& ((*mdia)->child_boxes) , (GF_Box *)hdlr);
1329 	}
1330 	return e;
1331 
1332 }
1333 
Track_SetStreamDescriptor(GF_TrackBox * trak,u32 StreamDescriptionIndex,u32 DataReferenceIndex,GF_ESD * esd,u32 * outStreamIndex)1334 GF_Err Track_SetStreamDescriptor(GF_TrackBox *trak, u32 StreamDescriptionIndex, u32 DataReferenceIndex, GF_ESD *esd, u32 *outStreamIndex)
1335 {
1336 	GF_Err e;
1337 	GF_MPEGSampleEntryBox *entry;
1338 	GF_MPEGVisualSampleEntryBox *entry_v;
1339 	GF_MPEGAudioSampleEntryBox *entry_a;
1340 	GF_TrackReferenceBox *tref;
1341 	GF_TrackReferenceTypeBox *dpnd;
1342 	u16 tmpRef;
1343 
1344 	entry = NULL;
1345 	tref = NULL;
1346 
1347 	if (!trak || !esd || (!outStreamIndex && !DataReferenceIndex) ) return GF_BAD_PARAM;
1348 	if (!Track_IsMPEG4Stream(trak->Media->handler->handlerType)) return GF_ISOM_INVALID_MEDIA;
1349 
1350 
1351 	esd->ESID = 0;
1352 	//set SL to predefined if no url
1353 	if (esd->URLString == NULL) {
1354 		if (!esd->slConfig) esd->slConfig = (GF_SLConfig*) gf_odf_desc_new(GF_ODF_SLC_TAG);
1355 		esd->slConfig->predefined = SLPredef_MP4;
1356 		esd->slConfig->durationFlag = 0;
1357 		esd->slConfig->useTimestampsFlag = 1;
1358 	}
1359 
1360 	//get the REF box if needed
1361 	if (esd->dependsOnESID || (esd->OCRESID  && (esd->OCRESID != trak->moov->mov->es_id_default_sync)) ) {
1362 		if (!trak->References) {
1363 			tref = (GF_TrackReferenceBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TREF);
1364 			if (!tref) return GF_OUT_OF_MEM;
1365 			e = trak_on_child_box((GF_Box*)trak, (GF_Box *)tref);
1366 			if (e) return e;
1367 		}
1368 		tref = trak->References;
1369 	}
1370 
1371 	//Update Stream dependencies
1372 	e = Track_FindRef(trak, GF_ISOM_REF_DECODE, &dpnd);
1373 	if (e) return e;
1374 
1375 	if (!dpnd && esd->dependsOnESID) {
1376 		e = Track_FindRef(trak, GF_ISOM_REF_BASE, &dpnd);
1377 		if (e) return e;
1378 	}
1379 
1380 	if (!dpnd && esd->dependsOnESID) {
1381 		dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT);
1382 		dpnd->reference_type = GF_ISOM_BOX_TYPE_DPND;
1383 		e = reftype_AddRefTrack(dpnd, esd->dependsOnESID, NULL);
1384 		if (e) return e;
1385 	} else if (dpnd && !esd->dependsOnESID) {
1386 		Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_DPND);
1387 	}
1388 	esd->dependsOnESID = 0;
1389 
1390 	//Update GF_Clock dependencies
1391 	e = Track_FindRef(trak, GF_ISOM_REF_OCR, &dpnd);
1392 	if (e) return e;
1393 	if (!dpnd && esd->OCRESID && (esd->OCRESID != trak->moov->mov->es_id_default_sync)) {
1394 		dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT);
1395 		if (!dpnd) return GF_OUT_OF_MEM;
1396 		dpnd->reference_type = GF_ISOM_BOX_TYPE_SYNC;
1397 		e = reftype_AddRefTrack(dpnd, esd->OCRESID, NULL);
1398 		if (e) return e;
1399 	} else if (dpnd && !esd->OCRESID) {
1400 		Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_SYNC);
1401 	} else if (dpnd && esd->OCRESID) {
1402 		if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA;
1403 		dpnd->trackIDs[0] = esd->OCRESID;
1404 	}
1405 	esd->OCRESID = 0;
1406 
1407 	//brand new case: we have to change the IPI desc
1408 	if (esd->ipiPtr) {
1409 		e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd);
1410 		if (e) return e;
1411 		if (!dpnd) {
1412 			tmpRef = 0;
1413 			dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT);
1414 			if (!dpnd) return GF_OUT_OF_MEM;
1415 			dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR;
1416 			e = reftype_AddRefTrack(dpnd, esd->ipiPtr->IPI_ES_Id, &tmpRef);
1417 			if (e) return e;
1418 			//and replace the tag and value...
1419 			esd->ipiPtr->IPI_ES_Id = tmpRef;
1420 			esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1421 		} else {
1422 			//Watch out! ONLY ONE IPI dependency is allowed per stream
1423 			if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA;
1424 			//if an existing one is there, what shall we do ???
1425 			//donno, erase it
1426 			dpnd->trackIDs[0] = esd->ipiPtr->IPI_ES_Id;
1427 			//and replace the tag and value...
1428 			esd->ipiPtr->IPI_ES_Id = 1;
1429 			esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1430 		}
1431 	}
1432 
1433 	/*don't store the lang desc in ESD, use the media header language info*/
1434 	if (esd->langDesc) {
1435 		trak->Media->mediaHeader->packedLanguage[0] = (esd->langDesc->langCode>>16)&0xFF;
1436 		trak->Media->mediaHeader->packedLanguage[1] = (esd->langDesc->langCode>>8)&0xFF;
1437 		trak->Media->mediaHeader->packedLanguage[2] = (esd->langDesc->langCode)&0xFF;
1438 		gf_odf_desc_del((GF_Descriptor *)esd->langDesc);
1439 		esd->langDesc = NULL;
1440 	}
1441 
1442 	//we have a streamDescritpionIndex, use it
1443 	if (StreamDescriptionIndex) {
1444 		u32 entry_type;
1445 		entry = (GF_MPEGSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1);
1446 		if (!entry) return GF_ISOM_INVALID_FILE;
1447 
1448 		entry_type = entry->type;
1449 		GF_ProtectionSchemeInfoBox *sinf = (GF_ProtectionSchemeInfoBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_SINF);
1450 		if (sinf && sinf->original_format) entry_type = sinf->original_format->data_format;
1451 
1452 		switch (entry_type) {
1453 		case GF_ISOM_BOX_TYPE_MP4S:
1454 			//OK, delete the previous ESD
1455 			gf_odf_desc_del((GF_Descriptor *) entry->esd->desc);
1456 			entry->esd->desc = esd;
1457 			break;
1458 		case GF_ISOM_BOX_TYPE_MP4V:
1459 			entry_v = (GF_MPEGVisualSampleEntryBox*) entry;
1460 			//OK, delete the previous ESD
1461 			gf_odf_desc_del((GF_Descriptor *) entry_v->esd->desc);
1462 			entry_v->esd->desc = esd;
1463 			break;
1464 		case GF_ISOM_BOX_TYPE_MP4A:
1465 			entry_a = (GF_MPEGAudioSampleEntryBox*) entry;
1466             if (entry_a->esd) { // some non-conformant files may not have an ESD ...
1467                 //OK, delete the previous ESD
1468                 gf_odf_desc_del((GF_Descriptor *) entry_a->esd->desc);
1469                 entry_a->esd->desc = esd;
1470             } else {
1471 				// can't return OK here otherwise we can't know if esd hasn't been used
1472 				// and need to be freed
1473 				return GF_ISOM_INVALID_MEDIA;
1474 			}
1475 			break;
1476 		case GF_ISOM_BOX_TYPE_AVC1:
1477 		case GF_ISOM_BOX_TYPE_AVC2:
1478 		case GF_ISOM_BOX_TYPE_AVC3:
1479 		case GF_ISOM_BOX_TYPE_AVC4:
1480 		case GF_ISOM_BOX_TYPE_SVC1:
1481 		case GF_ISOM_BOX_TYPE_MVC1:
1482 		case GF_ISOM_BOX_TYPE_HVC1:
1483 		case GF_ISOM_BOX_TYPE_HEV1:
1484 		case GF_ISOM_BOX_TYPE_HVC2:
1485 		case GF_ISOM_BOX_TYPE_HEV2:
1486 		case GF_ISOM_BOX_TYPE_LHE1:
1487 		case GF_ISOM_BOX_TYPE_LHV1:
1488 		case GF_ISOM_BOX_TYPE_HVT1:
1489 			e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry, esd);
1490 			if (e) return e;
1491 			break;
1492 		case GF_ISOM_BOX_TYPE_LSR1:
1493 			e = LSR_UpdateESD((GF_LASeRSampleEntryBox*)entry, esd);
1494 			if (e) return e;
1495 			break;
1496 		case GF_ISOM_BOX_TYPE_AV01:
1497 		case GF_ISOM_BOX_TYPE_AV1C:
1498 		case GF_ISOM_BOX_TYPE_OPUS:
1499 		case GF_ISOM_BOX_TYPE_DOPS:
1500 		case GF_ISOM_BOX_TYPE_STXT:
1501 		case GF_ISOM_BOX_TYPE_WVTT:
1502 		case GF_ISOM_BOX_TYPE_STPP:
1503 			if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
1504 			break;
1505 
1506 		default:
1507 			//silently fail, not an MPEG-4 esd
1508 			gf_odf_desc_del((GF_Descriptor *) esd);
1509 			return GF_OK;
1510 		}
1511 	} else {
1512 		//need to check we're not in URL mode where only ONE description is allowed...
1513 		StreamDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes);
1514 		if (StreamDescriptionIndex) {
1515 			entry = (GF_MPEGSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1);
1516 			if (!entry) return GF_ISOM_INVALID_FILE;
1517 			if (entry->esd && entry->esd->desc->URLString) return GF_BAD_PARAM;
1518 		}
1519 
1520 		//OK, check the handler and create the entry
1521 		switch (trak->Media->handler->handlerType) {
1522         case GF_ISOM_MEDIA_AUXV:
1523         case GF_ISOM_MEDIA_PICT:
1524 		case GF_ISOM_MEDIA_VISUAL:
1525 			if ((esd->decoderConfig->objectTypeIndication==GF_CODECID_AVC) || (esd->decoderConfig->objectTypeIndication==GF_CODECID_SVC) || (esd->decoderConfig->objectTypeIndication==GF_CODECID_MVC)) {
1526 				entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AVC1);
1527 				if (!entry_v) return GF_OUT_OF_MEM;
1528 				e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd);
1529 				if (e) return  e;
1530 			} else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_HEVC) {
1531 				entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HVC1);
1532 				if (!entry_v) return GF_OUT_OF_MEM;
1533 				e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd);
1534 				if (e) return  e;
1535 			} else {
1536 				entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4V);
1537 				if (!entry_v) return GF_OUT_OF_MEM;
1538 				entry_v->esd = (GF_ESDBox *) gf_isom_box_new_parent(&entry_v->child_boxes, GF_ISOM_BOX_TYPE_ESDS);
1539 				if (!entry_v->esd) return GF_OUT_OF_MEM;
1540 				entry_v->esd->desc = esd;
1541 			}
1542 
1543 			//type cast possible now
1544 			entry = (GF_MPEGSampleEntryBox*) entry_v;
1545 			break;
1546 		case GF_ISOM_MEDIA_AUDIO:
1547 			if (esd->decoderConfig->objectTypeIndication == GF_CODECID_OPUS) {
1548 				GF_MPEGAudioSampleEntryBox *opus = (GF_MPEGAudioSampleEntryBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_OPUS);
1549 				if (!opus) return GF_OUT_OF_MEM;
1550 				opus->cfg_opus = (GF_OpusSpecificBox *)gf_isom_box_new_parent(&opus->child_boxes, GF_ISOM_BOX_TYPE_DOPS);
1551 				if (!opus->cfg_opus) return GF_OUT_OF_MEM;
1552 				entry = (GF_MPEGSampleEntryBox*)opus;
1553 				gf_odf_desc_del((GF_Descriptor *) esd);
1554 			} else if (esd->decoderConfig->objectTypeIndication == GF_CODECID_AC3) {
1555 				GF_MPEGAudioSampleEntryBox *ac3 = (GF_MPEGAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AC3);
1556 				if (!ac3) return GF_OUT_OF_MEM;
1557 				ac3->cfg_ac3 = (GF_AC3ConfigBox *) gf_isom_box_new_parent(&ac3->child_boxes, GF_ISOM_BOX_TYPE_DAC3);
1558 				if (!ac3->cfg_ac3) return GF_OUT_OF_MEM;
1559 				entry = (GF_MPEGSampleEntryBox*) ac3;
1560 				gf_odf_desc_del((GF_Descriptor *) esd);
1561 			} else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_EAC3) {
1562 				GF_MPEGAudioSampleEntryBox *eac3 = (GF_MPEGAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_EC3);
1563 				if (!eac3) return GF_OUT_OF_MEM;
1564 				eac3->cfg_ac3 = (GF_AC3ConfigBox *) gf_isom_box_new_parent(&eac3->child_boxes, GF_ISOM_BOX_TYPE_DEC3);
1565 				if (!eac3->cfg_ac3) return GF_OUT_OF_MEM;
1566 				entry = (GF_MPEGSampleEntryBox*) eac3;
1567 				gf_odf_desc_del((GF_Descriptor *) esd);
1568 			} else {
1569 				entry_a = (GF_MPEGAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4A);
1570 				if (!entry_a) return GF_OUT_OF_MEM;
1571 				entry_a->samplerate_hi = trak->Media->mediaHeader->timeScale;
1572 				entry_a->esd = (GF_ESDBox *) gf_isom_box_new_parent(&entry_a->child_boxes, GF_ISOM_BOX_TYPE_ESDS);
1573 				if (!entry_a->esd) return GF_OUT_OF_MEM;
1574 				entry_a->esd->desc = esd;
1575 				//type cast possible now
1576 				entry = (GF_MPEGSampleEntryBox*) entry_a;
1577 			}
1578 			break;
1579 		default:
1580 			if ((esd->decoderConfig->streamType==0x03) && (esd->decoderConfig->objectTypeIndication==0x09)) {
1581 				entry = (GF_MPEGSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_LSR1);
1582 				if (!entry) return GF_OUT_OF_MEM;
1583 				e = LSR_UpdateESD((GF_LASeRSampleEntryBox*)entry, esd);
1584 				if (e) return  e;
1585 			} else {
1586 				entry = (GF_MPEGSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4S);
1587 				entry->esd = (GF_ESDBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_ESDS);
1588 				if (!entry->esd) return GF_OUT_OF_MEM;
1589 				entry->esd->desc = esd;
1590 			}
1591 			break;
1592 		}
1593 		entry->dataReferenceIndex = DataReferenceIndex;
1594 
1595 		if (!trak->Media->information->sampleTable->SampleDescription->child_boxes)
1596 			trak->Media->information->sampleTable->SampleDescription->child_boxes = gf_list_new();
1597 		gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, entry);
1598 
1599 		e = stsd_on_child_box((GF_Box*)trak->Media->information->sampleTable->SampleDescription, (GF_Box *) entry);
1600 		if (e) return e;
1601 		if(outStreamIndex) *outStreamIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes);
1602 	}
1603 	return GF_OK;
1604 }
1605 
1606 #endif	/*GPAC_DISABLE_ISOM_WRITE*/
1607 
1608 #endif /*GPAC_DISABLE_ISOM*/
1609