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 #include <gpac/constants.h>
28 
29 #ifndef GPAC_DISABLE_ISOM
30 
GetTrackbyID(GF_MovieBox * moov,u32 TrackID)31 GF_TrackBox *GetTrackbyID(GF_MovieBox *moov, u32 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,u32 trackID)54 u32 gf_isom_get_tracknum_from_id(GF_MovieBox *moov, u32 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,u32 trackID,u32 StreamDescIndex,GF_ESD ** outESD)66 GF_Err GetESD(GF_MovieBox *moov, u32 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 
90 	e = Media_GetSampleDesc(trak->Media, StreamDescIndex, (GF_SampleEntryBox **)&entry, NULL);
91 	if (e) return e;
92 	//set the ID
93 	esd->ESID = trackID;
94 
95 	//find stream dependencies
96 	for (k = 0; k<2; k++) {
97 		u32 ref = GF_ISOM_BOX_TYPE_DPND;
98 		if (k == 1) ref = GF_4CC('s', 'b', 'a', 's');
99 
100 		e = Track_FindRef(trak, ref, &dpnd);
101 		if (e) return e;
102 		if (dpnd) {
103 			//ONLY ONE STREAM DEPENDENCY IS ALLOWED
104 			if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA;
105 			//fix the spec: where is the index located ??
106 			esd->dependsOnESID = dpnd->trackIDs[0];
107 			break;
108 		}
109 		else {
110 			esd->dependsOnESID = 0;
111 		}
112 	}
113 
114 	if (trak->udta) {
115 		GF_UserDataMap *map;
116 		u32 i = 0;
117 		while ((map = (GF_UserDataMap*)gf_list_enum(trak->udta->recordList, &i))) {
118 			if (map->boxType == GF_4CC('A', 'U', 'X', 'V')) {
119 				GF_Descriptor *d = gf_odf_desc_new(GF_ODF_AUX_VIDEO_DATA);
120 				gf_list_add(esd->extensionDescriptors, d);
121 				break;
122 			}
123 		}
124 	}
125 
126 	//OK, get the OCR (in a REAL MP4File, OCR is 0 in ESD and is specified through track reference
127 	dpnd = NULL;
128 	OCRTrack = NULL;
129 	//find OCR dependencies
130 	e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_SYNC, &dpnd);
131 	if (e) return e;
132 	if (dpnd) {
133 		if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA;
134 		esd->OCRESID = dpnd->trackIDs[0];
135 		OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]);
136 
137 		while (OCRTrack) {
138 			/*if we have a dependency on a track that doesn't have OCR dep, remove that dependency*/
139 			e = Track_FindRef(OCRTrack, GF_ISOM_BOX_TYPE_SYNC, &dpnd);
140 			if (e || !dpnd || !dpnd->trackIDCount) {
141 				OCRTrack = NULL;
142 				goto default_sync;
143 			}
144 			/*this is explicit desync*/
145 			if (dpnd && ((dpnd->trackIDs[0] == 0) || (dpnd->trackIDs[0] == OCRTrack->Header->trackID))) break;
146 			/*loop in OCRs, break it*/
147 			if (esd->ESID == OCRTrack->Header->trackID) {
148 				OCRTrack = NULL;
149 				goto default_sync;
150 			}
151 			/*check next*/
152 			OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]);
153 		}
154 		if (!OCRTrack) goto default_sync;
155 	}
156 	else {
157 	default_sync:
158 		/*all tracks are sync'ed by default*/
159 		if (trak->moov->mov->es_id_default_sync<0) {
160 			if (esd->OCRESID)
161 				trak->moov->mov->es_id_default_sync = esd->OCRESID;
162 			else
163 				trak->moov->mov->es_id_default_sync = esd->ESID;
164 		}
165 		if (trak->moov->mov->es_id_default_sync) esd->OCRESID = (u16)trak->moov->mov->es_id_default_sync;
166 		/*cf ESD writer*/
167 		if (esd->OCRESID == esd->ESID) esd->OCRESID = 0;
168 	}
169 
170 
171 
172 	//update the IPI stuff if needed
173 	if (esd->ipiPtr != NULL) {
174 		dpnd = NULL;
175 		e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_IPIR, &dpnd);
176 		if (e) return e;
177 		if (dpnd) {
178 			if (esd->ipiPtr->tag != GF_ODF_ISOM_IPI_PTR_TAG) return GF_ISOM_INVALID_FILE;
179 			//OK, retrieve the ID: the IPI_ES_Id is currently the ref track
180 			esd->ipiPtr->IPI_ES_Id = dpnd->trackIDs[esd->ipiPtr->IPI_ES_Id - 1];
181 			//and change the tag
182 			esd->ipiPtr->tag = GF_ODF_IPI_PTR_TAG;
183 		}
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 		char *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 			}
212 			else {
213 				esd->decoderConfig->rvc_config = (GF_DefaultDescriptor *)gf_odf_desc_new(GF_ODF_DSI_TAG);
214 				if (mime_type && !strcmp(mime_type, "application/rvc-config+xml+gz")) {
215 #if !defined(GPAC_DISABLE_CORE_TOOLS) && !defined(GPAC_DISABLE_ZLIB)
216 					gf_gz_decompress_payload(rvc_cfg_data, rvc_cfg_size, &esd->decoderConfig->rvc_config->data, &esd->decoderConfig->rvc_config->dataLength);
217 					gf_free(rvc_cfg_data);
218 #endif
219 				}
220 				else {
221 					esd->decoderConfig->rvc_config->data = rvc_cfg_data;
222 					esd->decoderConfig->rvc_config->dataLength = rvc_cfg_size;
223 				}
224 			}
225 		}
226 	}
227 
228 
229 	/*normally all files shall be stored with predefined=SLPredef_MP4, but of course some are broken (philips)
230 	so we just check the ESD_URL. If set, use the given cfg, otherwise always rewrite it*/
231 	if (esd->URLString != NULL) {
232 		*outESD = esd;
233 		return GF_OK;
234 	}
235 
236 	//if we are in publishing mode and we have an SLConfig specified, use it as is
237 	switch (entry->type) {
238 	case GF_ISOM_BOX_TYPE_MP4V:
239 		slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc;
240 		break;
241 	case GF_ISOM_BOX_TYPE_MP4A:
242 		slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc;
243 		break;
244 	case GF_ISOM_BOX_TYPE_MP4S:
245 		slc = entry->slc;
246 		break;
247 	default:
248 		slc = NULL;
249 		break;
250 	}
251 	if (slc) {
252 		gf_odf_desc_del((GF_Descriptor *)esd->slConfig);
253 		gf_odf_desc_copy((GF_Descriptor *)slc, (GF_Descriptor **)&esd->slConfig);
254 		*outESD = esd;
255 		return GF_OK;
256 	}
257 	//otherwise use the regular mapping
258 
259 	//this is a desc for a media in the file, let's rewrite some param
260 	esd->slConfig->timestampLength = 32;
261 	esd->slConfig->timestampResolution = trak->Media->mediaHeader->timeScale;
262 	//NO OCR from MP4File streams (eg, constant OC Res one)
263 	esd->slConfig->OCRLength = 0;
264 	esd->slConfig->OCRResolution = 0;
265 	//	if (OCRTrack) esd->slConfig->OCRResolution = OCRTrack->Media->mediaHeader->timeScale;
266 
267 	stbl = trak->Media->information->sampleTable;
268 	// a little optimization here: if all our samples are sync,
269 	//set the RAPOnly to true... for external users...
270 	if (!stbl->SyncSample) {
271 		if (
272 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
273 			moov->mvex &&
274 #endif
275 			(esd->decoderConfig->streamType == GF_STREAM_VISUAL)) {
276 			esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0;
277 			esd->slConfig->useRandomAccessPointFlag = 1;
278 			stbl->SyncSample = (GF_SyncSampleBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS);
279 		}
280 		else {
281 			esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1;
282 			esd->slConfig->useRandomAccessPointFlag = 0;
283 		}
284 	}
285 	else {
286 		esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0;
287 		//signal we are NOT using sync points if no info is present in the table
288 		esd->slConfig->useRandomAccessPointFlag = stbl->SyncSample->nb_entries ? 1 : 0;
289 	}
290 	//do we have DegradationPriority ?
291 	if (stbl->DegradationPriority) {
292 		esd->slConfig->degradationPriorityLength = 15;
293 	}
294 	else {
295 		esd->slConfig->degradationPriorityLength = 0;
296 	}
297 	//paddingBits
298 	if (stbl->PaddingBits) {
299 		esd->slConfig->usePaddingFlag = 1;
300 	}
301 	//change to support reflecting OD streams
302 	esd->slConfig->useAccessUnitEndFlag = 1;
303 	esd->slConfig->useAccessUnitStartFlag = 1;
304 
305 	//signal we do have padding flag (since we only use logical SL packet
306 	//the user can decide whether to use the info or not
307 	esd->slConfig->usePaddingFlag = stbl->PaddingBits ? 1 : 0;
308 
309 	//same with degradation priority
310 	esd->slConfig->degradationPriorityLength = stbl->DegradationPriority ? 32 : 0;
311 
312 	//this new SL will be OUT OF THE FILE. Let's set its predefined to 0
313 	esd->slConfig->predefined = 0;
314 
315 
316 	*outESD = esd;
317 	return GF_OK;
318 }
319 
320 
321 //extraction of the ESD from the track for the given time
GetESDForTime(GF_MovieBox * moov,u32 trackID,u64 CTS,GF_ESD ** outESD)322 GF_Err GetESDForTime(GF_MovieBox *moov, u32 trackID, u64 CTS, GF_ESD **outESD)
323 {
324 	GF_Err e;
325 	u32 sampleDescIndex;
326 	GF_TrackBox *trak;
327 
328 	trak = gf_isom_get_track(moov, gf_isom_get_tracknum_from_id(moov, trackID));
329 	if (!trak) return GF_ISOM_INVALID_FILE;
330 
331 	e = Media_GetSampleDescIndex(trak->Media, CTS, &sampleDescIndex);
332 	if (e) return e;
333 	return GetESD(moov, trackID, sampleDescIndex, outESD);
334 }
335 
336 
Track_FindRef(GF_TrackBox * trak,u32 ReferenceType,GF_TrackReferenceTypeBox ** dpnd)337 GF_Err Track_FindRef(GF_TrackBox *trak, u32 ReferenceType, GF_TrackReferenceTypeBox **dpnd)
338 {
339 	GF_TrackReferenceBox *ref;
340 	GF_TrackReferenceTypeBox *a;
341 	u32 i;
342 	if (!trak) return GF_BAD_PARAM;
343 	if (!trak->References) {
344 		*dpnd = NULL;
345 		return GF_OK;
346 	}
347 	ref = trak->References;
348 	i = 0;
349 	while ((a = (GF_TrackReferenceTypeBox *)gf_list_enum(ref->other_boxes, &i))) {
350 		if (a->reference_type == ReferenceType) {
351 			*dpnd = a;
352 			return GF_OK;
353 		}
354 	}
355 	*dpnd = NULL;
356 	return GF_OK;
357 }
358 
Track_IsMPEG4Stream(u32 HandlerType)359 Bool Track_IsMPEG4Stream(u32 HandlerType)
360 {
361 	switch (HandlerType) {
362 	case GF_ISOM_MEDIA_VISUAL:
363 	case GF_ISOM_MEDIA_AUDIO:
364 	case GF_ISOM_MEDIA_SUBPIC:
365 	case GF_ISOM_MEDIA_OD:
366 	case GF_ISOM_MEDIA_OCR:
367 	case GF_ISOM_MEDIA_SCENE:
368 	case GF_ISOM_MEDIA_MPEG7:
369 	case GF_ISOM_MEDIA_OCI:
370 	case GF_ISOM_MEDIA_IPMP:
371 	case GF_ISOM_MEDIA_MPEGJ:
372 	case GF_ISOM_MEDIA_ESM:
373 		return 1;
374 		/*Timedtext is NOT an MPEG-4 stream*/
375 	default:
376 		/*consider xxsm as MPEG-4 handlers*/
377 		if ((((HandlerType >> 8) & 0xFF) == 's') && ((HandlerType & 0xFF) == 'm'))
378 			return 1;
379 		return 0;
380 	}
381 }
382 
383 
SetTrackDuration(GF_TrackBox * trak)384 GF_Err SetTrackDuration(GF_TrackBox *trak)
385 {
386 	u64 trackDuration;
387 	u32 i;
388 	GF_EdtsEntry *ent;
389 	GF_EditListBox *elst;
390 	GF_Err e;
391 
392 	//the total duration is the media duration: adjust it in case...
393 	e = Media_SetDuration(trak);
394 	if (e) return e;
395 
396 	//assert the timeScales are non-NULL
397 	if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) return GF_ISOM_INVALID_FILE;
398 	trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
399 
400 	//if we have an edit list, the duration is the sum of all the editList
401 	//entries' duration (always expressed in MovieTimeScale)
402 	if (trak->editBox && trak->editBox->editList) {
403 		trackDuration = 0;
404 		elst = trak->editBox->editList;
405 		i = 0;
406 		while ((ent = (GF_EdtsEntry*)gf_list_enum(elst->entryList, &i))) {
407 			trackDuration += ent->segmentDuration;
408 		}
409 	}
410 	if (!trackDuration) {
411 		trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
412 	}
413 	trak->Header->duration = trackDuration;
414 	if (!trak->moov->mov->keep_utc)
415 		trak->Header->modificationTime = gf_isom_get_mp4time();
416 	return GF_OK;
417 }
418 
419 
420 #ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
421 
MergeTrack(GF_TrackBox * trak,GF_TrackFragmentBox * traf,u64 moof_offset,Bool is_first_merge)422 GF_Err MergeTrack(GF_TrackBox *trak, GF_TrackFragmentBox *traf, u64 moof_offset, Bool is_first_merge)
423 {
424 	u32 i, j, chunk_size;
425 	u64 base_offset, data_offset;
426 	u32 def_duration, DescIndex, def_size, def_flags;
427 	u32 duration, size, flags, cts_offset, prev_trun_data_offset;
428 	u8 pad, sync;
429 	u16 degr;
430 	GF_TrackFragmentRunBox *trun;
431 	GF_TrunEntry *ent;
432 
433 	void stbl_AppendTime(GF_SampleTableBox *stbl, u32 duration);
434 	void stbl_AppendSize(GF_SampleTableBox *stbl, u32 size);
435 	void stbl_AppendChunk(GF_SampleTableBox *stbl, u64 offset);
436 	void stbl_AppendSampleToChunk(GF_SampleTableBox *stbl, u32 DescIndex, u32 samplesInChunk);
437 	void stbl_AppendCTSOffset(GF_SampleTableBox *stbl, s32 CTSOffset);
438 	void stbl_AppendRAP(GF_SampleTableBox *stbl, u8 isRap);
439 	void stbl_AppendPadding(GF_SampleTableBox *stbl, u8 padding);
440 	void stbl_AppendDegradation(GF_SampleTableBox *stbl, u16 DegradationPriority);
441 
442 	if (trak->Header->trackID != traf->tfhd->trackID) return GF_OK;
443 
444 	//setup all our defaults
445 	DescIndex = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DESC) ? traf->tfhd->sample_desc_index : traf->trex->def_sample_desc_index;
446 	def_duration = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DUR) ? traf->tfhd->def_sample_duration : traf->trex->def_sample_duration;
447 	def_size = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_SIZE) ? traf->tfhd->def_sample_size : traf->trex->def_sample_size;
448 	def_flags = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) ? traf->tfhd->def_sample_flags : traf->trex->def_sample_flags;
449 
450 	//locate base offset
451 	base_offset = (traf->tfhd->flags & GF_ISOM_TRAF_BASE_OFFSET) ? traf->tfhd->base_data_offset : moof_offset;
452 
453 	chunk_size = 0;
454 	prev_trun_data_offset = 0;
455 
456 	/*in playback mode*/
457 	if (traf->tfdt && is_first_merge) {
458 #ifndef GPAC_DISABLE_LOG
459 		if (trak->moov->mov->NextMoofNumber && trak->present_in_scalable_segment && trak->sample_count_at_seg_start && (trak->dts_at_seg_start != traf->tfdt->baseMediaDecodeTime)) {
460 			s32 drift = (s32)((s64)traf->tfdt->baseMediaDecodeTime - (s64)trak->dts_at_seg_start);
461 			if (drift<0) {
462 				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));
463 			}
464 			else {
465 				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));
466 			}
467 		}
468 #endif
469 		trak->dts_at_seg_start = traf->tfdt->baseMediaDecodeTime;
470 	}
471 
472 	i = 0;
473 	while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
474 		//merge the run
475 		for (j = 0; j<trun->sample_count; j++) {
476 			ent = (GF_TrunEntry*)gf_list_get(trun->entries, j);
477 			size = def_size;
478 			duration = def_duration;
479 			flags = def_flags;
480 
481 			if (ent) {
482 				if (trun->flags & GF_ISOM_TRUN_DURATION) duration = ent->Duration;
483 				if (trun->flags & GF_ISOM_TRUN_SIZE) size = ent->size;
484 				if (trun->flags & GF_ISOM_TRUN_FLAGS) {
485 					flags = ent->flags;
486 				}
487 				else if (!j && (trun->flags & GF_ISOM_TRUN_FIRST_FLAG)) {
488 					flags = trun->first_sample_flags;
489 				}
490 			}
491 			//add size first
492 			stbl_AppendSize(trak->Media->information->sampleTable, size);
493 			//then TS
494 			stbl_AppendTime(trak->Media->information->sampleTable, duration);
495 			//add chunk on first sample
496 			if (!j) {
497 				data_offset = base_offset;
498 				//aggregated offset if base-data-offset-present is not set AND if default-base-is-moof is not set
499 				if (!(traf->tfhd->flags & GF_ISOM_TRAF_BASE_OFFSET) && !(traf->tfhd->flags & GF_ISOM_MOOF_BASE_OFFSET))
500 					data_offset += chunk_size;
501 
502 				if (trun->flags & GF_ISOM_TRUN_DATA_OFFSET) {
503 					data_offset += trun->data_offset;
504 					/*reset chunk size since data is now relative to this trun*/
505 					chunk_size = 0;
506 					/*remember this data offset for following trun*/
507 					prev_trun_data_offset = trun->data_offset;
508 				}
509 				else {
510 					/*data offset is previous chunk size plus previous offset of the trun*/
511 					data_offset += prev_trun_data_offset;
512 				}
513 				stbl_AppendChunk(trak->Media->information->sampleTable, data_offset);
514 				//then sampleToChunk
515 				stbl_AppendSampleToChunk(trak->Media->information->sampleTable,
516 					DescIndex, trun->sample_count);
517 			}
518 			chunk_size += size;
519 
520 
521 			//CTS
522 			cts_offset = (trun->flags & GF_ISOM_TRUN_CTS_OFFSET) ? ent->CTS_Offset : 0;
523 			stbl_AppendCTSOffset(trak->Media->information->sampleTable, cts_offset);
524 
525 			//flags
526 			sync = GF_ISOM_GET_FRAG_SYNC(flags);
527 			if (trak->Media->information->sampleTable->no_sync_found && sync) {
528 				trak->Media->information->sampleTable->no_sync_found = 0;
529 			}
530 			stbl_AppendRAP(trak->Media->information->sampleTable, sync);
531 			pad = GF_ISOM_GET_FRAG_PAD(flags);
532 			if (pad) stbl_AppendPadding(trak->Media->information->sampleTable, pad);
533 			degr = GF_ISOM_GET_FRAG_DEG(flags);
534 			if (degr) stbl_AppendDegradation(trak->Media->information->sampleTable, degr);
535 
536 			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));
537 		}
538 	}
539 	/*merge sample groups*/
540 	if (traf->sampleGroups) {
541 		GF_List *groups;
542 		GF_List *groupDescs;
543 		Bool is_identical_sgpd = GF_TRUE;
544 		u32 *new_idx = NULL;
545 
546 		if (!trak->Media->information->sampleTable->sampleGroups)
547 			trak->Media->information->sampleTable->sampleGroups = gf_list_new();
548 
549 		if (!trak->Media->information->sampleTable->sampleGroupsDescription)
550 			trak->Media->information->sampleTable->sampleGroupsDescription = gf_list_new();
551 
552 		groupDescs = trak->Media->information->sampleTable->sampleGroupsDescription;
553 		for (i = 0; i<gf_list_count(traf->sampleGroupsDescription); i++) {
554 			GF_SampleGroupDescriptionBox *new_sgdesc = NULL;
555 			GF_SampleGroupDescriptionBox *sgdesc = gf_list_get(traf->sampleGroupsDescription, i);
556 			for (j = 0; j<gf_list_count(groupDescs); j++) {
557 				new_sgdesc = gf_list_get(groupDescs, j);
558 				if (new_sgdesc->grouping_type == sgdesc->grouping_type) break;
559 				new_sgdesc = NULL;
560 			}
561 			/*new description, move it to our sample table*/
562 			if (!new_sgdesc) {
563 				gf_list_add(groupDescs, sgdesc);
564 				gf_list_rem(traf->sampleGroupsDescription, i);
565 				i--;
566 			}
567 			/*merge descriptions*/
568 			else {
569 				u32 count;
570 
571 				is_identical_sgpd = gf_isom_is_identical_sgpd(new_sgdesc, sgdesc, 0);
572 				if (is_identical_sgpd)
573 					continue;
574 
575 				new_idx = (u32 *)gf_malloc(gf_list_count(sgdesc->group_descriptions) * sizeof(u32));
576 				count = 0;
577 				while (gf_list_count(sgdesc->group_descriptions)) {
578 					void *sgpd_entry = gf_list_get(sgdesc->group_descriptions, 0);
579 					Bool new_entry = GF_TRUE;
580 
581 					for (j = 0; j < gf_list_count(new_sgdesc->group_descriptions); j++) {
582 						void *ptr = gf_list_get(new_sgdesc->group_descriptions, j);
583 						if (gf_isom_is_identical_sgpd(sgpd_entry, ptr, new_sgdesc->grouping_type)) {
584 							new_idx[count] = j + 1;
585 							count++;
586 							new_entry = GF_FALSE;
587 							gf_free(sgpd_entry);
588 							break;
589 						}
590 					}
591 
592 					if (new_entry) {
593 						gf_list_add(new_sgdesc->group_descriptions, sgpd_entry);
594 						new_idx[count] = gf_list_count(new_sgdesc->group_descriptions);
595 						count++;
596 					}
597 
598 					gf_list_rem(sgdesc->group_descriptions, 0);
599 				}
600 			}
601 		}
602 
603 		groups = trak->Media->information->sampleTable->sampleGroups;
604 		for (i = 0; i<gf_list_count(traf->sampleGroups); i++) {
605 			GF_SampleGroupBox *stbl_group = NULL;
606 			GF_SampleGroupBox *frag_group = gf_list_get(traf->sampleGroups, i);
607 
608 
609 			for (j = 0; j<gf_list_count(groups); j++) {
610 				stbl_group = gf_list_get(groups, j);
611 				if ((frag_group->grouping_type == stbl_group->grouping_type) && (frag_group->grouping_type_parameter == stbl_group->grouping_type_parameter))
612 					break;
613 				stbl_group = NULL;
614 			}
615 			if (!stbl_group) {
616 				stbl_group = (GF_SampleGroupBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SBGP);
617 				stbl_group->grouping_type = frag_group->grouping_type;
618 				stbl_group->grouping_type_parameter = frag_group->grouping_type_parameter;
619 				stbl_group->version = frag_group->version;
620 				gf_list_add(groups, stbl_group);
621 			}
622 
623 			if (is_identical_sgpd) {
624 				//adjust sgpd index: in traf index start at 0x1001
625 				for (j = 0; j < frag_group->entry_count; j++)
626 					frag_group->sample_entries[j].group_description_index &= 0x0FFFF;
627 				if (frag_group->entry_count && stbl_group->entry_count &&
628 					(frag_group->sample_entries[0].group_description_index == stbl_group->sample_entries[stbl_group->entry_count - 1].group_description_index)
629 					) {
630 					stbl_group->sample_entries[stbl_group->entry_count - 1].sample_count += frag_group->sample_entries[0].sample_count;
631 					if (frag_group->entry_count>1) {
632 						stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count - 1));
633 						memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[1], sizeof(GF_SampleGroupEntry) * (frag_group->entry_count - 1));
634 						stbl_group->entry_count += frag_group->entry_count - 1;
635 					}
636 				}
637 				else {
638 					stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count));
639 					memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[0], sizeof(GF_SampleGroupEntry) * frag_group->entry_count);
640 					stbl_group->entry_count += frag_group->entry_count;
641 				}
642 			}
643 			else {
644 				stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count));
645 				//adjust sgpd index
646 				for (j = 0; j < frag_group->entry_count; j++)
647 					frag_group->sample_entries[j].group_description_index = new_idx[j];
648 				memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[0], sizeof(GF_SampleGroupEntry) * frag_group->entry_count);
649 				stbl_group->entry_count += frag_group->entry_count;
650 			}
651 		}
652 
653 		if (new_idx) gf_free(new_idx);
654 	}
655 
656 	if (gf_isom_is_cenc_media(trak->moov->mov, gf_isom_get_tracknum_from_id(trak->moov, trak->Header->trackID), 1)) {
657 		/*Merge sample auxiliary encryption information*/
658 		GF_SampleEncryptionBox *senc = NULL;
659 		GF_List *sais = NULL;
660 
661 		if (traf->piff_sample_encryption) {
662 			for (i = 0; i < gf_list_count(trak->Media->information->sampleTable->other_boxes); i++) {
663 				GF_Box *a = (GF_Box *)gf_list_get(trak->Media->information->sampleTable->other_boxes, i);
664 				if ((a->type == GF_ISOM_BOX_TYPE_UUID) && (((GF_UUIDBox *)a)->internal_4cc == GF_ISOM_BOX_UUID_PSEC)) {
665 					senc = (GF_SampleEncryptionBox *)a;
666 					break;
667 				}
668 			}
669 			if (!senc) {
670 				senc = (GF_SampleEncryptionBox *)gf_isom_create_piff_psec_box(1, 0x2, 0, 0, NULL);
671 				if (!trak->Media->information->sampleTable->other_boxes) trak->Media->information->sampleTable->other_boxes = gf_list_new();
672 				gf_list_add(trak->Media->information->sampleTable->other_boxes, senc);
673 			}
674 
675 			sais = traf->piff_sample_encryption->samp_aux_info;
676 		}
677 		else if (traf->sample_encryption) {
678 			for (i = 0; i < gf_list_count(trak->Media->information->sampleTable->other_boxes); i++) {
679 				GF_Box *a = (GF_Box *)gf_list_get(trak->Media->information->sampleTable->other_boxes, i);
680 				if (a->type == GF_ISOM_BOX_TYPE_SENC) {
681 					senc = (GF_SampleEncryptionBox *)a;
682 					break;
683 				}
684 			}
685 			if (!senc) {
686 				senc = gf_isom_create_samp_enc_box(1, 0x2);
687 				if (!trak->Media->information->sampleTable->other_boxes) trak->Media->information->sampleTable->other_boxes = gf_list_new();
688 				gf_list_add(trak->Media->information->sampleTable->other_boxes, senc);
689 			}
690 
691 			sais = traf->sample_encryption->samp_aux_info;
692 		}
693 
694 		/*get sample auxiliary information by saiz/saio rather than by parsing senc box*/
695 		if (gf_isom_cenc_has_saiz_saio_traf(traf)) {
696 			//GF_BitStream *bs;
697 			u32 size, nb_saio;
698 			u64 offset;
699 			GF_SampleAuxiliaryInfoOffsetBox *saio = NULL;
700 			GF_SampleAuxiliaryInfoSizeBox *saiz = NULL;
701 			//GF_CENCSampleAuxInfo *sai;
702 			//char *buffer;
703 
704 			offset = nb_saio = 0;
705 
706 			for (i = 0; i < gf_list_count(traf->sai_offsets); i++) {
707 				saio = (GF_SampleAuxiliaryInfoOffsetBox *)gf_list_get(traf->sai_offsets, i);
708 				/*if we have only 1 sai_offsets, assume that its type is cenc*/
709 				if ((saio->aux_info_type == GF_4CC('c', 'e', 'n', 'c')) || (gf_list_count(traf->sai_offsets) == 1)) {
710 					offset = (saio->version ? saio->offsets_large[0] : saio->offsets[0]) + moof_offset;
711 					nb_saio = saio->entry_count;
712 					break;
713 				}
714 			}
715 			for (i = 0; i < gf_list_count(traf->sai_sizes); i++) {
716 				saiz = (GF_SampleAuxiliaryInfoSizeBox *)gf_list_get(traf->sai_sizes, i);
717 				/*if we have only 1 sai_sizes, assume that its type is cenc*/
718 				if ((saiz->aux_info_type == GF_4CC('c', 'e', 'n', 'c')) || (gf_list_count(traf->sai_sizes) == 1)) {
719 					break;
720 				}
721 			}
722 			if (saiz && saio) {
723 				for (i = 0; i < saiz->sample_count; i++) {
724 					if (nb_saio != 1)
725 						offset = (saio->version ? saio->offsets_large[i] : saio->offsets[i]) + moof_offset;
726 					size = saiz->default_sample_info_size ? saiz->default_sample_info_size : saiz->sample_info_size[i];
727 
728 					/*cur_position = gf_bs_get_position(trak->moov->mov->movieFileMap->bs);
729 					gf_bs_seek(trak->moov->mov->movieFileMap->bs, offset);
730 					buffer = (char *)gf_malloc(size);
731 					gf_bs_read_data(trak->moov->mov->movieFileMap->bs, buffer, size);
732 					gf_bs_seek(trak->moov->mov->movieFileMap->bs, cur_position);
733 
734 					sai = (GF_CENCSampleAuxInfo *)gf_malloc(sizeof(GF_CENCSampleAuxInfo));
735 					bs = gf_bs_new(buffer, size, GF_BITSTREAM_READ);
736 					gf_bs_read_data(bs, (char *)sai->IV, 16);
737 					if (size > 16) {
738 					sai->subsample_count = gf_bs_read_u16(bs);
739 					sai->subsamples = (GF_CENCSubSampleEntry *)gf_malloc(sizeof(GF_CENCSubSampleEntry)*sai->subsample_count);
740 					for (j = 0; j < sai->subsample_count; j++) {
741 					sai->subsamples[j].bytes_clear_data = gf_bs_read_u16(bs);
742 					sai->subsamples[j].bytes_encrypted_data = gf_bs_read_u32(bs);
743 					}
744 					gf_bs_del(bs);
745 					}
746 					gf_list_add(senc->samp_aux_info, sai);
747 					if (sai->subsample_count) senc->flags = 0x00000002;*/
748 					gf_isom_cenc_merge_saiz_saio(senc, trak->Media->information->sampleTable, offset, size);
749 					if (nb_saio == 1)
750 						offset += size;
751 				}
752 			}
753 		}
754 		else if (sais) {
755 			for (i = 0; i < gf_list_count(sais); i++) {
756 				GF_CENCSampleAuxInfo *sai, *new_sai;
757 
758 				sai = (GF_CENCSampleAuxInfo *)gf_list_get(sais, i);
759 
760 				new_sai = (GF_CENCSampleAuxInfo *)gf_malloc(sizeof(GF_CENCSampleAuxInfo));
761 				new_sai->IV_size = sai->IV_size;
762 				memmove((char *)new_sai->IV, (const char*)sai->IV, 16);
763 				new_sai->subsample_count = sai->subsample_count;
764 				new_sai->subsamples = (GF_CENCSubSampleEntry *)gf_malloc(new_sai->subsample_count * sizeof(GF_CENCSubSampleEntry));
765 				memmove(new_sai->subsamples, sai->subsamples, new_sai->subsample_count * sizeof(GF_CENCSubSampleEntry));
766 
767 				gf_list_add(senc->samp_aux_info, new_sai);
768 				if (sai->subsample_count) senc->flags = 0x00000002;
769 			}
770 		}
771 	}
772 	return GF_OK;
773 }
774 
775 #endif
776 
777 
778 #ifndef GPAC_DISABLE_ISOM_WRITE
779 
780 //used to check if a TrackID is available
RequestTrack(GF_MovieBox * moov,u32 TrackID)781 u8 RequestTrack(GF_MovieBox *moov, u32 TrackID)
782 {
783 	u32 i;
784 	GF_TrackBox *trak;
785 
786 	i = 0;
787 	while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) {
788 		if (trak->Header->trackID == TrackID) {
789 			gf_isom_set_last_error(moov->mov, GF_BAD_PARAM);
790 			return 0;
791 		}
792 	}
793 	return 1;
794 }
795 
Track_RemoveRef(GF_TrackBox * trak,u32 ReferenceType)796 GF_Err Track_RemoveRef(GF_TrackBox *trak, u32 ReferenceType)
797 {
798 	GF_TrackReferenceBox *ref;
799 	GF_Box *a;
800 	u32 i;
801 	if (!trak) return GF_BAD_PARAM;
802 	if (!trak->References) return GF_OK;
803 	ref = trak->References;
804 	i = 0;
805 	while ((a = (GF_Box *)gf_list_enum(ref->other_boxes, &i))) {
806 		if (a->type == ReferenceType) {
807 			gf_isom_box_del(a);
808 			gf_list_rem(ref->other_boxes, i - 1);
809 			return GF_OK;
810 		}
811 	}
812 	return GF_OK;
813 }
814 
NewMedia(GF_MediaBox ** mdia,u32 MediaType,u32 TimeScale)815 GF_Err NewMedia(GF_MediaBox **mdia, u32 MediaType, u32 TimeScale)
816 {
817 	GF_MediaHeaderBox *mdhd;
818 	GF_Box *mediaInfo;
819 	GF_HandlerBox *hdlr;
820 	GF_MediaInformationBox *minf;
821 	GF_DataInformationBox *dinf;
822 	GF_SampleTableBox *stbl;
823 	GF_DataReferenceBox *dref;
824 	char *str;
825 
826 	GF_Err e;
827 
828 	if (*mdia || !mdia) return GF_BAD_PARAM;
829 
830 	minf = NULL;
831 	mdhd = NULL;
832 	hdlr = NULL;
833 	dinf = NULL;
834 	stbl = NULL;
835 	dref = NULL;
836 	mediaInfo = NULL;
837 
838 	//first create the media
839 	*mdia = (GF_MediaBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MDIA);
840 	mdhd = (GF_MediaHeaderBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MDHD);
841 
842 	//"handler name" is for debugging purposes. Let's stick our name here ;)
843 	switch (MediaType) {
844 	case GF_ISOM_MEDIA_VISUAL:
845 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
846 		str = "GPAC ISO Video Handler";
847 		break;
848 	case GF_ISOM_MEDIA_AUDIO:
849 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_SMHD);
850 		str = "GPAC ISO Audio Handler";
851 		break;
852 	case GF_ISOM_MEDIA_HINT:
853 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_HMHD);
854 		str = "GPAC ISO Hint Handler";
855 		break;
856 	case GF_ISOM_MEDIA_META:
857 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
858 		str = "GPAC Timed MetaData Handler";
859 		break;
860 	case GF_ISOM_MEDIA_OD:
861 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
862 		str = "GPAC MPEG-4 OD Handler";
863 		break;
864 	case GF_ISOM_MEDIA_OCR:
865 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
866 		str = "GPAC MPEG-4 OCR Handler";
867 		break;
868 	case GF_ISOM_MEDIA_SCENE:
869 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
870 		str = "GPAC MPEG-4 Scene Description Handler";
871 		break;
872 	case GF_ISOM_MEDIA_MPEG7:
873 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
874 		str = "GPAC MPEG-4 MPEG-7 Handler";
875 		break;
876 	case GF_ISOM_MEDIA_OCI:
877 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
878 		str = "GPAC MPEG-4 OCI Handler";
879 		break;
880 	case GF_ISOM_MEDIA_IPMP:
881 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
882 		str = "GPAC MPEG-4 IPMP Handler";
883 		break;
884 	case GF_ISOM_MEDIA_MPEGJ:
885 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
886 		str = "GPAC MPEG-4 MPEG-J Handler";
887 		break;
888 	case GF_ISOM_MEDIA_TEXT:
889 	case GF_ISOM_MEDIA_SUBT:
890 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
891 		str = "GPAC Streaming Text Handler";
892 		break;
893 	case GF_ISOM_MEDIA_MPEG_SUBT:
894 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_STHD);
895 		str = "GPAC MPEG Subtitle Handler";
896 		break;
897 	case GF_ISOM_MEDIA_DIMS:
898 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
899 		MediaType = GF_ISOM_MEDIA_SCENE;
900 		str = "GPAC DIMS Handler";
901 		break;
902 	default:
903 		mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
904 		str = "GPAC IsoMedia Handler";
905 		break;
906 	}
907 	hdlr = (GF_HandlerBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_HDLR);
908 	minf = (GF_MediaInformationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MINF);
909 
910 	mdhd->timeScale = TimeScale;
911 	hdlr->handlerType = MediaType;
912 	hdlr->nameUTF8 = gf_strdup(str);
913 
914 	//first set-up the sample table...
915 	stbl = (GF_SampleTableBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL);
916 	dinf = (GF_DataInformationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_DINF);
917 	stbl->SampleDescription = (GF_SampleDescriptionBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSD);
918 	stbl->ChunkOffset = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO);
919 	//by default create a regular table
920 	stbl->SampleSize = (GF_SampleSizeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSZ);
921 	stbl->SampleToChunk = (GF_SampleToChunkBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
922 	stbl->TimeToSample = (GF_TimeToSampleBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STTS);
923 
924 	//Create a data reference WITHOUT DATA ENTRY (we don't know anything yet about the media Data)
925 	dref = (GF_DataReferenceBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_DREF);
926 	e = dinf_AddBox((GF_Box*)dinf, (GF_Box *)dref);
927 	if (e) goto err_exit;
928 
929 	e = minf_AddBox((GF_Box*)minf, (GF_Box *)mediaInfo);
930 	if (e) goto err_exit;
931 	e = minf_AddBox((GF_Box*)minf, (GF_Box *)stbl);
932 	if (e) goto err_exit;
933 	e = minf_AddBox((GF_Box*)minf, (GF_Box *)dinf);
934 	if (e) goto err_exit;
935 
936 	e = mdia_AddBox((GF_Box*)*mdia, (GF_Box *)mdhd);
937 	if (e) goto err_exit;
938 	e = mdia_AddBox((GF_Box*)*mdia, (GF_Box *)minf);
939 	if (e) goto err_exit;
940 	e = mdia_AddBox((GF_Box*)*mdia, (GF_Box *)hdlr);
941 	if (e) goto err_exit;
942 
943 	return GF_OK;
944 
945 err_exit:
946 	if (mdhd) gf_isom_box_del((GF_Box *)mdhd);
947 	if (minf) gf_isom_box_del((GF_Box *)minf);
948 	if (hdlr) {
949 		if (hdlr->nameUTF8) gf_free(hdlr->nameUTF8);
950 		gf_isom_box_del((GF_Box *)hdlr);
951 	}
952 	return e;
953 
954 }
955 
Track_SetStreamDescriptor(GF_TrackBox * trak,u32 StreamDescriptionIndex,u32 DataReferenceIndex,GF_ESD * esd,u32 * outStreamIndex)956 GF_Err Track_SetStreamDescriptor(GF_TrackBox *trak, u32 StreamDescriptionIndex, u32 DataReferenceIndex, GF_ESD *esd, u32 *outStreamIndex)
957 {
958 	GF_Err e;
959 	GF_MPEGSampleEntryBox *entry;
960 	GF_MPEGVisualSampleEntryBox *entry_v;
961 	GF_MPEGAudioSampleEntryBox *entry_a;
962 	GF_TrackReferenceBox *tref;
963 	GF_TrackReferenceTypeBox *dpnd;
964 	u16 tmpRef;
965 
966 	entry = NULL;
967 	tref = NULL;
968 
969 	if (!trak || !esd || (!outStreamIndex && !DataReferenceIndex)) return GF_BAD_PARAM;
970 	if (!Track_IsMPEG4Stream(trak->Media->handler->handlerType)) return GF_ISOM_INVALID_MEDIA;
971 
972 
973 	esd->ESID = 0;
974 	//set SL to predefined if no url
975 	if (esd->URLString == NULL) {
976 		if (!esd->slConfig) esd->slConfig = (GF_SLConfig*)gf_odf_desc_new(GF_ODF_SLC_TAG);
977 		esd->slConfig->predefined = SLPredef_MP4;
978 		esd->slConfig->durationFlag = 0;
979 		esd->slConfig->useTimestampsFlag = 1;
980 	}
981 
982 	//get the REF box if needed
983 	if (esd->dependsOnESID || esd->OCRESID) {
984 		if (!trak->References) {
985 			tref = (GF_TrackReferenceBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF);
986 			e = trak_AddBox((GF_Box*)trak, (GF_Box *)tref);
987 			if (e) return e;
988 		}
989 		tref = trak->References;
990 	}
991 
992 	//Update Stream dependancies
993 	e = Track_FindRef(trak, GF_ISOM_REF_DECODE, &dpnd);
994 	if (e) return e;
995 	if (!dpnd && esd->dependsOnESID) {
996 		dpnd = (GF_TrackReferenceTypeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT);
997 		dpnd->reference_type = GF_ISOM_BOX_TYPE_DPND;
998 		e = tref_AddBox((GF_Box*)tref, (GF_Box *)dpnd);
999 		if (e) return e;
1000 		e = reftype_AddRefTrack(dpnd, esd->dependsOnESID, NULL);
1001 		if (e) return e;
1002 	}
1003 	else if (dpnd && !esd->dependsOnESID) {
1004 		Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_DPND);
1005 	}
1006 	esd->dependsOnESID = 0;
1007 
1008 	//Update GF_Clock dependancies
1009 	e = Track_FindRef(trak, GF_ISOM_REF_OCR, &dpnd);
1010 	if (e) return e;
1011 	if (!dpnd && esd->OCRESID) {
1012 		dpnd = (GF_TrackReferenceTypeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT);
1013 		dpnd->reference_type = GF_ISOM_BOX_TYPE_SYNC;
1014 		e = tref_AddBox((GF_Box*)tref, (GF_Box *)dpnd);
1015 		if (e) return e;
1016 		e = reftype_AddRefTrack(dpnd, esd->OCRESID, NULL);
1017 		if (e) return e;
1018 	}
1019 	else if (dpnd && !esd->OCRESID) {
1020 		Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_SYNC);
1021 	}
1022 	else if (dpnd && esd->OCRESID) {
1023 		if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA;
1024 		dpnd->trackIDs[0] = esd->OCRESID;
1025 	}
1026 	esd->OCRESID = 0;
1027 
1028 	//brand new case: we have to change the IPI desc
1029 	if (esd->ipiPtr) {
1030 		e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd);
1031 		if (e) return e;
1032 		if (!dpnd) {
1033 			tmpRef = 0;
1034 			dpnd = (GF_TrackReferenceTypeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT);
1035 			dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR;
1036 			e = tref_AddBox((GF_Box*)tref, (GF_Box *)dpnd);
1037 			if (e) return e;
1038 			e = reftype_AddRefTrack(dpnd, esd->ipiPtr->IPI_ES_Id, &tmpRef);
1039 			if (e) return e;
1040 			//and replace the tag and value...
1041 			esd->ipiPtr->IPI_ES_Id = tmpRef;
1042 			esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1043 		}
1044 		else {
1045 			//Watch out! ONLY ONE IPI dependancy is allowed per stream
1046 			if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA;
1047 			//if an existing one is there, what shall we do ???
1048 			//donno, erase it
1049 			dpnd->trackIDs[0] = esd->ipiPtr->IPI_ES_Id;
1050 			//and replace the tag and value...
1051 			esd->ipiPtr->IPI_ES_Id = 1;
1052 			esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1053 		}
1054 	}
1055 
1056 	/*don't store the lang desc in ESD, use the media header language info*/
1057 	if (esd->langDesc) {
1058 		trak->Media->mediaHeader->packedLanguage[0] = (esd->langDesc->langCode >> 16) & 0xFF;
1059 		trak->Media->mediaHeader->packedLanguage[1] = (esd->langDesc->langCode >> 8) & 0xFF;
1060 		trak->Media->mediaHeader->packedLanguage[2] = (esd->langDesc->langCode) & 0xFF;
1061 		gf_odf_desc_del((GF_Descriptor *)esd->langDesc);
1062 		esd->langDesc = NULL;
1063 	}
1064 
1065 	//we have a streamDescritpionIndex, use it
1066 	if (StreamDescriptionIndex) {
1067 		entry = (GF_MPEGSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, StreamDescriptionIndex - 1);
1068 		if (!entry) return GF_ISOM_INVALID_FILE;
1069 
1070 		switch (entry->type) {
1071 		case GF_ISOM_BOX_TYPE_MP4S:
1072 			//OK, delete the previous ESD
1073 			gf_odf_desc_del((GF_Descriptor *)entry->esd->desc);
1074 			entry->esd->desc = esd;
1075 			break;
1076 		case GF_ISOM_BOX_TYPE_MP4V:
1077 			entry_v = (GF_MPEGVisualSampleEntryBox*)entry;
1078 			//OK, delete the previous ESD
1079 			gf_odf_desc_del((GF_Descriptor *)entry_v->esd->desc);
1080 			entry_v->esd->desc = esd;
1081 			break;
1082 		case GF_ISOM_BOX_TYPE_MP4A:
1083 			entry_a = (GF_MPEGAudioSampleEntryBox*)entry;
1084 			//OK, delete the previous ESD
1085 			gf_odf_desc_del((GF_Descriptor *)entry_a->esd->desc);
1086 			entry_a->esd->desc = esd;
1087 			break;
1088 		case GF_ISOM_BOX_TYPE_AVC1:
1089 		case GF_ISOM_BOX_TYPE_AVC2:
1090 		case GF_ISOM_BOX_TYPE_AVC3:
1091 		case GF_ISOM_BOX_TYPE_AVC4:
1092 		case GF_ISOM_BOX_TYPE_SVC1:
1093 		case GF_ISOM_BOX_TYPE_HVC1:
1094 		case GF_ISOM_BOX_TYPE_HEV1:
1095 		case GF_ISOM_BOX_TYPE_HVC2:
1096 		case GF_ISOM_BOX_TYPE_HEV2:
1097 		case GF_ISOM_BOX_TYPE_LHE1:
1098 		case GF_ISOM_BOX_TYPE_LHV1:
1099 		case GF_ISOM_BOX_TYPE_HVT1:
1100 			e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry, esd);
1101 			if (e) return e;
1102 			break;
1103 		case GF_ISOM_BOX_TYPE_LSR1:
1104 			e = LSR_UpdateESD((GF_LASeRSampleEntryBox*)entry, esd);
1105 			if (e) return e;
1106 			break;
1107 		case GF_ISOM_BOX_TYPE_STXT:
1108 		case GF_ISOM_BOX_TYPE_WVTT:
1109 		case GF_ISOM_BOX_TYPE_STPP:
1110 			/* TODO */
1111 			assert(0);
1112 			break;
1113 
1114 		default:
1115 			//gf_odf_desc_del((GF_Descriptor *) esd);
1116 			break;
1117 		}
1118 	}
1119 	else {
1120 		//need to check we're not in URL mode where only ONE description is allowed...
1121 		StreamDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes);
1122 		if (StreamDescriptionIndex) {
1123 			entry = (GF_MPEGSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, StreamDescriptionIndex - 1);
1124 			if (!entry) return GF_ISOM_INVALID_FILE;
1125 			if (entry->esd && entry->esd->desc->URLString) return GF_BAD_PARAM;
1126 		}
1127 
1128 		//OK, check the handler and create the entry
1129 		switch (trak->Media->handler->handlerType) {
1130 		case GF_ISOM_MEDIA_VISUAL:
1131 			if ((esd->decoderConfig->objectTypeIndication == GPAC_OTI_VIDEO_AVC) || (esd->decoderConfig->objectTypeIndication == GPAC_OTI_VIDEO_SVC)) {
1132 				entry_v = (GF_MPEGVisualSampleEntryBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_AVC1);
1133 				if (!entry_v) return GF_OUT_OF_MEM;
1134 				e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd);
1135 				if (e) return  e;
1136 			}
1137 			else if (esd->decoderConfig->objectTypeIndication == GPAC_OTI_VIDEO_HEVC) {
1138 				entry_v = (GF_MPEGVisualSampleEntryBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_HVC1);
1139 				if (!entry_v) return GF_OUT_OF_MEM;
1140 				e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd);
1141 				if (e) return  e;
1142 			}
1143 			else {
1144 				entry_v = (GF_MPEGVisualSampleEntryBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4V);
1145 				if (!entry_v) return GF_OUT_OF_MEM;
1146 				entry_v->esd = (GF_ESDBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_ESDS);
1147 				entry_v->esd->desc = esd;
1148 			}
1149 
1150 			//type cast possible now
1151 			entry = (GF_MPEGSampleEntryBox*)entry_v;
1152 			break;
1153 		case GF_ISOM_MEDIA_AUDIO:
1154 			if (esd->decoderConfig->objectTypeIndication == GPAC_OTI_AUDIO_AC3) {
1155 				GF_AC3SampleEntryBox *ac3 = (GF_AC3SampleEntryBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_AC3);
1156 				if (!ac3) return GF_OUT_OF_MEM;
1157 				ac3->info = (GF_AC3ConfigBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_DAC3);
1158 				entry = (GF_MPEGSampleEntryBox*)ac3;
1159 			}
1160 			else {
1161 				entry_a = (GF_MPEGAudioSampleEntryBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4A);
1162 				if (!entry_a) return GF_OUT_OF_MEM;
1163 				entry_a->samplerate_hi = trak->Media->mediaHeader->timeScale;
1164 				entry_a->esd = (GF_ESDBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_ESDS);
1165 				entry_a->esd->desc = esd;
1166 				//type cast possible now
1167 				entry = (GF_MPEGSampleEntryBox*)entry_a;
1168 			}
1169 			break;
1170 		default:
1171 			if ((esd->decoderConfig->streamType == 0x03) && (esd->decoderConfig->objectTypeIndication == 0x09)) {
1172 				entry = (GF_MPEGSampleEntryBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_LSR1);
1173 				if (!entry) return GF_OUT_OF_MEM;
1174 				e = LSR_UpdateESD((GF_LASeRSampleEntryBox*)entry, esd);
1175 				if (e) return  e;
1176 			}
1177 			else {
1178 				entry = (GF_MPEGSampleEntryBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4S);
1179 				entry->esd = (GF_ESDBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_ESDS);
1180 				entry->esd->desc = esd;
1181 			}
1182 			break;
1183 		}
1184 		entry->dataReferenceIndex = DataReferenceIndex;
1185 
1186 		//and add the entry to our table...
1187 		e = stsd_AddBox(trak->Media->information->sampleTable->SampleDescription, (GF_Box *)entry);
1188 		if (e) return e;
1189 		if (outStreamIndex) *outStreamIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes);
1190 	}
1191 	return GF_OK;
1192 }
1193 
1194 #endif	/*GPAC_DISABLE_ISOM_WRITE*/
1195 
1196 #endif /*GPAC_DISABLE_ISOM*/
1197