1 /*
2 *			GPAC - Multimedia Framework C SDK
3 *
4 *			Authors: Jean Le Feuvre
5 *			Copyright (c) Telecom ParisTech 2000-2012
6 *					All rights reserved
7 *
8 *  This file is part of GPAC / ISO Media File Format sub-project
9 *
10 *  GPAC is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU Lesser General Public License as published by
12 *  the Free Software Foundation; either version 2, or (at your option)
13 *  any later version.
14 *
15 *  GPAC is distributed in the hope that it will be useful,
16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 *  GNU Lesser General Public License for more details.
19 *
20 *  You should have received a copy of the GNU Lesser General Public
21 *  License along with this library; see the file COPYING.  If not, write to
22 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25 
26 #include <gpac/internal/isomedia_dev.h>
27 
28 #ifndef GPAC_DISABLE_ISOM
29 
30 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
31 
GetTrex(GF_MovieBox * moov,u32 TrackID)32 GF_TrackExtendsBox *GetTrex(GF_MovieBox *moov, u32 TrackID)
33 {
34 	u32 i;
35 	GF_TrackExtendsBox *trex;
36 	i = 0;
37 	while ((trex = (GF_TrackExtendsBox *)gf_list_enum(moov->mvex->TrackExList, &i))) {
38 		if (trex->trackID == TrackID) return trex;
39 	}
40 	return NULL;
41 }
42 
GetTrep(GF_MovieBox * moov,u32 TrackID)43 GF_TrackExtensionPropertiesBox *GetTrep(GF_MovieBox *moov, u32 TrackID)
44 {
45 	u32 i;
46 	GF_TrackExtensionPropertiesBox *trep;
47 	i = 0;
48 	while ((trep = (GF_TrackExtensionPropertiesBox*)gf_list_enum(moov->mvex->TrackExPropList, &i))) {
49 		if (trep->trackID == TrackID) return trep;
50 	}
51 	return NULL;
52 }
53 
GetTraf(GF_ISOFile * mov,u32 TrackID)54 GF_TrackFragmentBox *GetTraf(GF_ISOFile *mov, u32 TrackID)
55 {
56 	u32 i;
57 	GF_TrackFragmentBox *traf;
58 	if (!mov->moof) return NULL;
59 
60 	//reverse browse the TRAFs, as there may be more than one per track ...
61 	for (i = gf_list_count(mov->moof->TrackList); i>0; i--) {
62 		traf = (GF_TrackFragmentBox *)gf_list_get(mov->moof->TrackList, i - 1);
63 		if (traf->tfhd->trackID == TrackID) return traf;
64 	}
65 	return NULL;
66 }
67 
68 
69 #ifndef GPAC_DISABLE_ISOM_WRITE
gf_isom_set_movie_duration(GF_ISOFile * movie,u64 duration)70 GF_Err gf_isom_set_movie_duration(GF_ISOFile *movie, u64 duration)
71 {
72 	if (!movie->moov->mvex) return GF_BAD_PARAM;
73 	if (!movie->moov->mvex->mehd) {
74 		movie->moov->mvex->mehd = (GF_MovieExtendsHeaderBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MEHD);
75 	}
76 	movie->moov->mvex->mehd->fragment_duration = duration;
77 	movie->moov->mvhd->duration = 0;
78 	return GF_OK;
79 }
80 
81 
82 GF_EXPORT
gf_isom_finalize_for_fragment(GF_ISOFile * movie,u32 media_segment_type)83 GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *movie, u32 media_segment_type)
84 {
85 	GF_Err e;
86 	u32 i;
87 	Bool store_file = GF_TRUE;
88 	GF_TrackExtendsBox *trex;
89 	if (!movie || !movie->moov) return GF_BAD_PARAM;
90 
91 	if (movie->openMode == GF_ISOM_OPEN_CAT_FRAGMENTS) {
92 		/*from now on we are in write mode*/
93 		movie->openMode = GF_ISOM_OPEN_WRITE;
94 		store_file = GF_FALSE;
95 		movie->append_segment = GF_TRUE;
96 	}
97 	else {
98 		movie->NextMoofNumber = 1;
99 	}
100 
101 	//this is only allowed in write mode
102 	if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
103 
104 	if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_OK;
105 	movie->FragmentsFlags = 0;
106 
107 	if (store_file) {
108 		/*"dash" brand: this is a DASH Initialization Segment*/
109 		gf_isom_modify_alternate_brand(movie, GF_4CC('d', 'a', 's', 'h'), 1);
110 
111 		if (!movie->moov->mvex->mehd || !movie->moov->mvex->mehd->fragment_duration) {
112 			//update durations
113 			gf_isom_get_duration(movie);
114 		}
115 
116 		i = 0;
117 		while ((trex = (GF_TrackExtendsBox *)gf_list_enum(movie->moov->mvex->TrackExList, &i))) {
118 			GF_TrackExtensionPropertiesBox *trep;
119 			if (trex->type != GF_ISOM_BOX_TYPE_TREX) continue;
120 			trep = GetTrep(movie->moov, trex->trackID);
121 
122 			if (!trep) {
123 				trep = (GF_TrackExtensionPropertiesBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_TREP);
124 				trep->trackID = trex->trackID;
125 				gf_list_add(movie->moov->mvex->TrackExPropList, trep);
126 			}
127 
128 			if (trex->track->Media->information->sampleTable->CompositionToDecode) {
129 
130 				if (!trex->track->Media->information->sampleTable->SampleSize || !trex->track->Media->information->sampleTable->SampleSize->sampleCount) {
131 					gf_list_add(trep->other_boxes, trex->track->Media->information->sampleTable->CompositionToDecode);
132 					trex->track->Media->information->sampleTable->CompositionToDecode = NULL;
133 				}
134 				else {
135 					GF_CompositionToDecodeBox *cslg;
136 
137 					//clone it!
138 					GF_SAFEALLOC(cslg, GF_CompositionToDecodeBox);
139 					if (!cslg) return GF_OUT_OF_MEM;
140 					memcpy(cslg, trex->track->Media->information->sampleTable->CompositionToDecode, sizeof(GF_CompositionToDecodeBox));
141 					cslg->other_boxes = gf_list_new();
142 					gf_list_add(trep->other_boxes, trex->track->Media->information->sampleTable->CompositionToDecode);
143 				}
144 			}
145 
146 			if (movie->moov->mvex->mehd && movie->moov->mvex->mehd->fragment_duration) {
147 				trex->track->Header->duration = 0;
148 				Media_SetDuration(trex->track);
149 			}
150 		}
151 
152 		//write movie
153 		e = WriteToFile(movie);
154 		if (e) return e;
155 	}
156 
157 	//make sure we do have all we need. If not this is not an error, just consider
158 	//the file closed
159 	if (!movie->moov->mvex || !gf_list_count(movie->moov->mvex->TrackExList)) return GF_OK;
160 
161 	i = 0;
162 	while ((trex = (GF_TrackExtendsBox *)gf_list_enum(movie->moov->mvex->TrackExList, &i))) {
163 		if (!trex->trackID || !gf_isom_get_track_from_id(movie->moov, trex->trackID)) return GF_IO_ERR;
164 		//we could also check all our data refs are local but we'll do that at run time
165 		//in order to allow a mix of both (remote refs in MOOV and local in MVEX)
166 
167 		//one thing that MUST be done is OD cross-dependancies. The movie fragment spec
168 		//is broken here, since it cannot allow dynamic insertion of new ESD and their
169 		//dependancies
170 	}
171 
172 	//ok we are fine - note the data map is created at the beginning
173 	if (i) movie->FragmentsFlags |= GF_ISOM_FRAG_WRITE_READY;
174 
175 	if (media_segment_type) {
176 		movie->use_segments = GF_TRUE;
177 		movie->moof_list = gf_list_new();
178 	}
179 
180 	/*set brands for segment*/
181 
182 	/*"msdh": it's a media segment */
183 	gf_isom_set_brand_info(movie, GF_4CC('m', 's', 'd', 'h'), 0);
184 	/*remove all brands	*/
185 	gf_isom_reset_alt_brands(movie);
186 	/*
187 	msdh: it's a media segment
188 	sims: it's a media segment with an SSIX
189 	msix: it's a media segment with an index
190 	lmsg: it's the last media segment
191 	*/
192 
193 	return GF_OK;
194 }
195 
gf_isom_change_track_fragment_defaults(GF_ISOFile * movie,u32 TrackID,u32 DefaultSampleDescriptionIndex,u32 DefaultSampleDuration,u32 DefaultSampleSize,u8 DefaultSampleIsSync,u8 DefaultSamplePadding,u16 DefaultDegradationPriority)196 GF_Err gf_isom_change_track_fragment_defaults(GF_ISOFile *movie, u32 TrackID,
197 	u32 DefaultSampleDescriptionIndex,
198 	u32 DefaultSampleDuration,
199 	u32 DefaultSampleSize,
200 	u8 DefaultSampleIsSync,
201 	u8 DefaultSamplePadding,
202 	u16 DefaultDegradationPriority)
203 {
204 	GF_MovieExtendsBox *mvex;
205 	GF_TrackExtendsBox *trex;
206 	GF_TrackBox *trak;
207 
208 	if (!movie || !movie->moov) return GF_BAD_PARAM;
209 	//this is only allowed in write mode
210 	if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
211 
212 	trak = gf_isom_get_track_from_id(movie->moov, TrackID);
213 	if (!trak) return GF_BAD_PARAM;
214 
215 	mvex = movie->moov->mvex;
216 	if (!mvex) return GF_BAD_PARAM;
217 
218 	trex = GetTrex(movie->moov, TrackID);
219 	if (!trex)  return GF_BAD_PARAM;
220 
221 	trex->def_sample_desc_index = DefaultSampleDescriptionIndex;
222 	trex->def_sample_duration = DefaultSampleDuration;
223 	trex->def_sample_size = DefaultSampleSize;
224 	trex->def_sample_flags = GF_ISOM_FORMAT_FRAG_FLAGS(DefaultSamplePadding, DefaultSampleIsSync, DefaultDegradationPriority);
225 	//if sample is sync by default, set sample_depends_on flags to 2 (does not depend on other samples)
226 	if (DefaultSampleIsSync) {
227 		trex->def_sample_flags |= (2 << 24);
228 	}
229 
230 	return GF_OK;
231 }
232 
233 GF_EXPORT
gf_isom_setup_track_fragment(GF_ISOFile * movie,u32 TrackID,u32 DefaultSampleDescriptionIndex,u32 DefaultSampleDuration,u32 DefaultSampleSize,u8 DefaultSampleIsSync,u8 DefaultSamplePadding,u16 DefaultDegradationPriority)234 GF_Err gf_isom_setup_track_fragment(GF_ISOFile *movie, u32 TrackID,
235 	u32 DefaultSampleDescriptionIndex,
236 	u32 DefaultSampleDuration,
237 	u32 DefaultSampleSize,
238 	u8 DefaultSampleIsSync,
239 	u8 DefaultSamplePadding,
240 	u16 DefaultDegradationPriority)
241 {
242 	GF_MovieExtendsBox *mvex;
243 	GF_TrackExtendsBox *trex;
244 	GF_TrackBox *trak;
245 
246 	if (!movie || !movie->moov) return GF_BAD_PARAM;
247 	//this is only allowed in write mode
248 	if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
249 	//and only at setup
250 	if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_BAD_PARAM;
251 
252 
253 	trak = gf_isom_get_track_from_id(movie->moov, TrackID);
254 	if (!trak) return GF_BAD_PARAM;
255 
256 	//create MVEX if needed
257 	if (!movie->moov->mvex) {
258 		mvex = (GF_MovieExtendsBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MVEX);
259 		moov_AddBox((GF_Box*)movie->moov, (GF_Box *)mvex);
260 	}
261 	else {
262 		mvex = movie->moov->mvex;
263 	}
264 	if (!mvex->mehd) {
265 		mvex->mehd = (GF_MovieExtendsHeaderBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MEHD);
266 	}
267 
268 	trex = GetTrex(movie->moov, TrackID);
269 	if (!trex) {
270 		trex = (GF_TrackExtendsBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TREX);
271 		trex->trackID = TrackID;
272 		mvex_AddBox((GF_Box*)mvex, (GF_Box *)trex);
273 	}
274 	trex->track = trak;
275 	return gf_isom_change_track_fragment_defaults(movie, TrackID, DefaultSampleDescriptionIndex, DefaultSampleDuration, DefaultSampleSize, DefaultSampleIsSync, DefaultSamplePadding, DefaultDegradationPriority);
276 }
277 
278 
GetNumUsedValues(GF_TrackFragmentBox * traf,u32 value,u32 index)279 u32 GetNumUsedValues(GF_TrackFragmentBox *traf, u32 value, u32 index)
280 {
281 	u32 i, j, NumValue = 0;
282 	GF_TrackFragmentRunBox *trun;
283 	GF_TrunEntry *ent;
284 
285 	i = 0;
286 	while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
287 		j = 0;
288 		while ((ent = (GF_TrunEntry *)gf_list_enum(trun->entries, &j))) {
289 			switch (index) {
290 			case 1:
291 				if (value == ent->Duration) NumValue++;
292 				break;
293 			case 2:
294 				if (value == ent->size) NumValue++;
295 				break;
296 			case 3:
297 				if (value == ent->flags) NumValue++;
298 				break;
299 			}
300 		}
301 	}
302 	return NumValue;
303 }
304 
305 
ComputeFragmentDefaults(GF_TrackFragmentBox * traf)306 void ComputeFragmentDefaults(GF_TrackFragmentBox *traf)
307 {
308 	u32 i, j, MaxNum, DefValue, ret;
309 	GF_TrackFragmentRunBox *trun;
310 	GF_TrunEntry *ent;
311 
312 	//Duration default
313 	MaxNum = DefValue = 0;
314 	i = 0;
315 	while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
316 		j = 0;
317 		while ((ent = (GF_TrunEntry *)gf_list_enum(trun->entries, &j))) {
318 			ret = GetNumUsedValues(traf, ent->Duration, 1);
319 			if (ret>MaxNum) {
320 				//at least 2 duration, specify for all
321 				if (MaxNum) {
322 					DefValue = 0;
323 					goto escape_duration;
324 				}
325 				MaxNum = ret;
326 				DefValue = ent->Duration;
327 			}
328 		}
329 	}
330 escape_duration:
331 	//store if #
332 	if (DefValue && (DefValue != traf->trex->def_sample_duration)) {
333 		traf->tfhd->def_sample_duration = DefValue;
334 	}
335 
336 	//Size default
337 	MaxNum = DefValue = 0;
338 	i = 0;
339 	while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
340 		j = 0;
341 		while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) {
342 			ret = GetNumUsedValues(traf, ent->size, 2);
343 			if (ret>MaxNum || (ret == 1)) {
344 				//at least 2 sizes so we must specify all sizes
345 				if (MaxNum) {
346 					DefValue = 0;
347 					goto escape_size;
348 				}
349 				MaxNum = ret;
350 				DefValue = ent->size;
351 			}
352 		}
353 	}
354 
355 escape_size:
356 	//store if #
357 	if (DefValue && (DefValue != traf->trex->def_sample_size)) {
358 		traf->tfhd->def_sample_size = DefValue;
359 	}
360 
361 	//Flags default
362 	MaxNum = DefValue = 0;
363 	i = 0;
364 	while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
365 		j = 0;
366 		while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) {
367 			ret = GetNumUsedValues(traf, ent->flags, 3);
368 			if (ret>MaxNum) {
369 				MaxNum = ret;
370 				DefValue = ent->flags;
371 			}
372 		}
373 	}
374 	//store if #
375 	if (DefValue && (DefValue != traf->trex->def_sample_flags)) {
376 		traf->tfhd->def_sample_flags = DefValue;
377 	}
378 }
379 
380 
gf_isom_set_fragment_option(GF_ISOFile * movie,u32 TrackID,u32 Code,u32 Param)381 GF_Err gf_isom_set_fragment_option(GF_ISOFile *movie, u32 TrackID, u32 Code, u32 Param)
382 {
383 	GF_TrackFragmentBox *traf;
384 	if (!movie || !movie->moov) return GF_BAD_PARAM;
385 	//this is only allowed in write mode
386 	if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
387 
388 	traf = GetTraf(movie, TrackID);
389 	if (!traf) return GF_BAD_PARAM;
390 	switch (Code) {
391 	case GF_ISOM_TRAF_EMPTY:
392 		traf->tfhd->EmptyDuration = Param;
393 		break;
394 	case GF_ISOM_TRAF_RANDOM_ACCESS:
395 		traf->tfhd->IFrameSwitching = Param;
396 		break;
397 	case GF_ISOM_TRAF_DATA_CACHE:
398 		//don't cache only one sample ...
399 		traf->DataCache = Param > 1 ? Param : 0;
400 		break;
401 	}
402 	return GF_OK;
403 }
404 
405 //#define USE_BASE_DATA_OFFSET
406 
update_trun_offsets(GF_ISOFile * movie,s32 offset)407 void update_trun_offsets(GF_ISOFile *movie, s32 offset)
408 {
409 #ifndef USE_BASE_DATA_OFFSET
410 	GF_TrackFragmentRunBox *trun;
411 	u32 i, j;
412 	GF_TrackFragmentBox *traf;
413 	i = 0;
414 	while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
415 		/*remove base data*/
416 		traf->tfhd->base_data_offset = 0;
417 		j = 0;
418 		while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &j))) {
419 			if (j == 1) {
420 				trun->data_offset += offset;
421 			}
422 			else {
423 				trun->data_offset = 0;
424 			}
425 		}
426 	}
427 #endif
428 }
429 
UpdateRuns(GF_ISOFile * movie,GF_TrackFragmentBox * traf)430 u32 UpdateRuns(GF_ISOFile *movie, GF_TrackFragmentBox *traf)
431 {
432 	u32 sampleCount, i, j, RunSize, RunDur, RunFlags, NeedFlags, UseCTS, count;
433 	/* enum:
434 	0 - use values per sample in the trun box
435 	1 - use default values from track fragment header
436 	2 - use default values from track extends header */
437 	u32 UseDefaultSize, UseDefaultDur, UseDefaultFlag;
438 	GF_TrackFragmentRunBox *trun;
439 	GF_TrunEntry *ent, *first_ent;
440 
441 	sampleCount = 0;
442 
443 #ifndef USE_BASE_DATA_OFFSET
444 	if (movie->use_segments) {
445 		traf->tfhd->flags = GF_ISOM_MOOF_BASE_OFFSET;
446 	}
447 	else
448 #endif
449 	{
450 		traf->tfhd->flags = GF_ISOM_TRAF_BASE_OFFSET;
451 	}
452 
453 	//empty runs
454 	if (traf->tfhd->EmptyDuration) {
455 		while (gf_list_count(traf->TrackRuns)) {
456 			trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, 0);
457 			gf_list_rem(traf->TrackRuns, 0);
458 			gf_isom_box_del((GF_Box *)trun);
459 		}
460 		traf->tfhd->flags = GF_ISOM_TRAF_DUR_EMPTY;
461 		if (traf->tfhd->EmptyDuration != traf->trex->def_sample_duration) {
462 			traf->tfhd->def_sample_duration = traf->tfhd->EmptyDuration;
463 			traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DUR;
464 		}
465 		return 0;
466 	}
467 
468 
469 	UseDefaultSize = 0;
470 	UseDefaultDur = 0;
471 	UseDefaultFlag = 0;
472 
473 	i = 0;
474 	while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
475 		RunSize = 0;
476 		RunDur = 0;
477 		RunFlags = 0;
478 		UseCTS = 0;
479 		NeedFlags = 0;
480 
481 		first_ent = NULL;
482 		//process all samples in run
483 		count = gf_list_count(trun->entries);
484 		for (j = 0; j<count; j++) {
485 			ent = (GF_TrunEntry*)gf_list_get(trun->entries, j);
486 			if (!j) {
487 				first_ent = ent;
488 				RunSize = ent->size;
489 				RunDur = ent->Duration;
490 			}
491 			//we may have one entry only ...
492 			if (j || (count == 1)) {
493 				//flags are only after first entry
494 				if (j == 1 || (count == 1)) RunFlags = ent->flags;
495 
496 				if (ent->size != RunSize) RunSize = 0;
497 				if (ent->Duration != RunDur) RunDur = 0;
498 				if (j && (RunFlags != ent->flags)) NeedFlags = 1;
499 			}
500 			if (ent->CTS_Offset) UseCTS = 1;
501 		}
502 		//empty list
503 		if (!first_ent) {
504 			i--;
505 			gf_list_rem(traf->TrackRuns, i);
506 			continue;
507 		}
508 		trun->sample_count = gf_list_count(trun->entries);
509 		trun->flags = 0;
510 
511 		//size checking
512 		//constant size, check if this is from current fragment default or global default
513 		if (RunSize && (traf->trex->def_sample_size == RunSize)) {
514 			if (!UseDefaultSize) UseDefaultSize = 2;
515 			else if (UseDefaultSize == 1) RunSize = 0;
516 		}
517 		else if (RunSize && (traf->tfhd->def_sample_size == RunSize)) {
518 			if (!UseDefaultSize) UseDefaultSize = 1;
519 			else if (UseDefaultSize == 2) RunSize = 0;
520 		}
521 		//we could check for single entry runs and set the default size in the tfhd but
522 		//that's no bit saving...
523 		else {
524 			RunSize = 0;
525 		}
526 
527 		if (!RunSize) trun->flags |= GF_ISOM_TRUN_SIZE;
528 
529 		//duration checking
530 		if (RunDur && (traf->trex->def_sample_duration == RunDur)) {
531 			if (!UseDefaultDur) UseDefaultDur = 2;
532 			else if (UseDefaultDur == 1) RunDur = 0;
533 		}
534 		else if (RunDur && (traf->tfhd->def_sample_duration == RunDur)) {
535 			if (!UseDefaultDur) UseDefaultDur = 1;
536 			else if (UseDefaultDur == 2) RunDur = 0;
537 		}
538 		if (!RunDur) trun->flags |= GF_ISOM_TRUN_DURATION;
539 
540 		//flag checking
541 		if (!NeedFlags) {
542 			// all samples flags are the same after the 2nd entry
543 			if (RunFlags == traf->trex->def_sample_flags) {
544 				/* this run can use trex flags */
545 				if (!UseDefaultFlag) {
546 					/* if all previous runs used explicit flags per sample, we can still use trex flags for this run */
547 					UseDefaultFlag = 2;
548 				}
549 				else if (UseDefaultFlag == 1) {
550 					/* otherwise if one of the previous runs did use tfhd flags,
551 					we have no choice but to explicitly use flags per sample for this run */
552 					NeedFlags = GF_TRUE;
553 				}
554 			}
555 			else if (RunFlags == traf->tfhd->def_sample_flags) {
556 				/* this run can use tfhd flags */
557 				if (!UseDefaultFlag) {
558 					/* if all previous runs used explicit flags per sample, we can still use tfhd flags for this run */
559 					UseDefaultFlag = 1;
560 				}
561 				else if (UseDefaultFlag == 2) {
562 					/* otherwise if one of the previous runs did use trex flags,
563 					we have no choice but to explicitly use flags per sample for this run */
564 					NeedFlags = GF_TRUE;
565 				}
566 			}
567 			else {
568 				/* the flags for the 2nd and following entries are different from trex and tfhd default values
569 				(possible case: 2 samples in trun, and first sample was used to set default flags) */
570 				NeedFlags = GF_TRUE;
571 			}
572 		}
573 		if (NeedFlags) {
574 			//one flags entry per sample only
575 			trun->flags |= GF_ISOM_TRUN_FLAGS;
576 		}
577 		else {
578 			/* this run can use default flags for the 2nd and following entries,
579 			we just need to check if the first entry flags need to be singled out*/
580 			if (first_ent->flags != RunFlags) {
581 				trun->flags |= GF_ISOM_TRUN_FIRST_FLAG;
582 			}
583 		}
584 
585 		//CTS flag
586 		if (UseCTS) trun->flags |= GF_ISOM_TRUN_CTS_OFFSET;
587 
588 		//run data offset if the offset indicated is 0 (first sample in this MDAT) don't
589 		//indicate it
590 		if (trun->data_offset) trun->flags |= GF_ISOM_TRUN_DATA_OFFSET;
591 
592 		sampleCount += trun->sample_count;
593 	}
594 
595 	//after all runs in the traf are processed, update TRAF flags
596 	if (UseDefaultSize == 1) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_SIZE;
597 	if (UseDefaultDur == 1) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DUR;
598 	if (UseDefaultFlag == 1) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_FLAGS;
599 	if (traf->tfhd->sample_desc_index && traf->tfhd->sample_desc_index != traf->trex->def_sample_desc_index) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DESC;
600 
601 
602 	return sampleCount;
603 }
604 
moof_get_sap_info(GF_MovieFragmentBox * moof,u32 refTrackID,u32 * sap_delta,Bool * starts_with_sap)605 static u32 moof_get_sap_info(GF_MovieFragmentBox *moof, u32 refTrackID, u32 *sap_delta, Bool *starts_with_sap)
606 {
607 	u32 i, j, count, delta, earliest_cts, sap_type, sap_sample_num, cur_sample;
608 	Bool first = GF_TRUE;
609 	GF_TrunEntry *ent;
610 	GF_TrackFragmentBox *traf = NULL;
611 	GF_TrackFragmentRunBox *trun;
612 	sap_type = 0;
613 	*sap_delta = 0;
614 	*starts_with_sap = GF_FALSE;
615 	for (i = 0; i<gf_list_count(moof->TrackList); i++) {
616 		traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
617 		if (traf->tfhd->trackID == refTrackID) break;
618 		traf = NULL;
619 	}
620 	if (!traf) return sap_type;
621 	earliest_cts = 0;
622 
623 	/*first check if we have a roll/rap sample in this traf, and mark its sample count*/
624 	sap_type = 0;
625 	sap_sample_num = 0;
626 	/*check RAP and ROLL*/
627 	count = traf->sampleGroups ? gf_list_count(traf->sampleGroups) : 0;
628 	for (i = 0; i<count; i++) {
629 		GF_SampleGroupBox *sg;
630 		u32 j, first_sample;
631 		Bool rap_type = GF_FALSE;
632 		sg = (GF_SampleGroupBox*)gf_list_get(traf->sampleGroups, i);
633 
634 		switch (sg->grouping_type) {
635 		case GF_4CC('r', 'a', 'p', ' '):
636 			rap_type = GF_TRUE;
637 			break;
638 		case GF_4CC('r', 'o', 'l', 'l'):
639 			break;
640 		default:
641 			continue;
642 		}
643 		/*first entry is SAP*/
644 		first_sample = 1;
645 		for (j = 0; j<sg->entry_count; j++) {
646 			if (!sg->sample_entries[j].group_description_index) {
647 				first_sample += sg->sample_entries[j].sample_count;
648 				continue;
649 			}
650 			if (!j) {
651 				*starts_with_sap = GF_TRUE;
652 				sap_sample_num = 0;
653 			}
654 			if (!sap_sample_num || (sap_sample_num>first_sample)) {
655 				sap_type = rap_type ? 3 : 4;
656 				sap_sample_num = first_sample;
657 			}
658 			break;
659 		}
660 	}
661 
662 	/*then browse all samples, looking for SYNC flag or sap_sample_num*/
663 	cur_sample = 1;
664 	delta = 0;
665 	i = 0;
666 	while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
667 		if (trun->flags & GF_ISOM_TRUN_FIRST_FLAG) {
668 			if (GF_ISOM_GET_FRAG_SYNC(trun->flags)) {
669 				ent = (GF_TrunEntry*)gf_list_get(trun->entries, 0);
670 				//				if (!delta) earliest_cts = ent->CTS_Offset;
671 				*sap_delta = delta + ent->CTS_Offset - ent->CTS_Offset;
672 				*starts_with_sap = first;
673 				sap_type = ent->SAP_type;
674 				return sap_type;
675 			}
676 		}
677 		j = 0;
678 		while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) {
679 			if (!delta) earliest_cts = ent->CTS_Offset;
680 
681 			if (GF_ISOM_GET_FRAG_SYNC(ent->flags)) {
682 				*sap_delta = delta + ent->CTS_Offset - earliest_cts;
683 				*starts_with_sap = first;
684 				sap_type = ent->SAP_type;
685 				return sap_type;
686 			}
687 			/*we found our roll or rap sample*/
688 			if (cur_sample == sap_sample_num) {
689 				*sap_delta = delta + ent->CTS_Offset - earliest_cts;
690 				return sap_type;
691 			}
692 			delta += ent->Duration;
693 			first = GF_FALSE;
694 			cur_sample++;
695 		}
696 	}
697 	/*not found*/
698 	return 0;
699 }
700 
moof_get_duration(GF_MovieFragmentBox * moof,u32 refTrackID)701 u32 moof_get_duration(GF_MovieFragmentBox *moof, u32 refTrackID)
702 {
703 	u32 i, j, duration;
704 	GF_TrunEntry *ent;
705 	GF_TrackFragmentBox *traf = NULL;
706 	GF_TrackFragmentRunBox *trun;
707 	for (i = 0; i<gf_list_count(moof->TrackList); i++) {
708 		traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
709 		if (traf->tfhd->trackID == refTrackID) break;
710 		traf = NULL;
711 	}
712 	if (!traf) return 0;
713 
714 	duration = 0;
715 	i = 0;
716 	while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
717 		j = 0;
718 		while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) {
719 			if (ent->flags & GF_ISOM_TRAF_SAMPLE_DUR)
720 				duration += ent->Duration;
721 			else
722 				duration += traf->trex->def_sample_duration;
723 		}
724 	}
725 	return duration;
726 }
727 
moof_get_earliest_cts(GF_MovieFragmentBox * moof,u32 refTrackID)728 static u64 moof_get_earliest_cts(GF_MovieFragmentBox *moof, u32 refTrackID)
729 {
730 	u32 i, j;
731 	u64 cts, duration;
732 	GF_TrunEntry *ent;
733 	GF_TrackFragmentBox *traf = NULL;
734 	GF_TrackFragmentRunBox *trun;
735 	for (i = 0; i<gf_list_count(moof->TrackList); i++) {
736 		traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
737 		if (traf->tfhd->trackID == refTrackID) break;
738 		traf = NULL;
739 	}
740 	if (!traf) return 0;
741 
742 	duration = 0;
743 	cts = LLU_CAST(-1);
744 	i = 0;
745 	while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
746 		j = 0;
747 		while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) {
748 			if (duration + ent->CTS_Offset < cts)
749 				cts = duration + ent->CTS_Offset;
750 			duration += ent->Duration;
751 		}
752 	}
753 	return cts;
754 }
755 
StoreFragment(GF_ISOFile * movie,Bool load_mdat_only,s32 data_offset_diff,u32 * moof_size)756 GF_Err StoreFragment(GF_ISOFile *movie, Bool load_mdat_only, s32 data_offset_diff, u32 *moof_size)
757 {
758 	GF_Err e;
759 	u64 moof_start, pos;
760 	u32 size, i, s_count, mdat_size;
761 	s32 offset;
762 	char *buffer;
763 	GF_TrackFragmentBox *traf;
764 	GF_TrackFragmentRunBox *trun;
765 	GF_BitStream *bs;
766 	if (!movie->moof) return GF_OK;
767 
768 	bs = movie->editFileMap->bs;
769 	if (!movie->moof_first) load_mdat_only = GF_FALSE;
770 	mdat_size = 0;
771 	//1 - flush all caches
772 	i = 0;
773 	while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
774 		if (!traf->DataCache) continue;
775 		s_count = gf_list_count(traf->TrackRuns);
776 		if (!s_count) continue;
777 		trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, s_count - 1);
778 		if (!trun->cache || !trun->sample_count) continue;
779 
780 		//update offset
781 		trun->data_offset = (u32)(gf_bs_get_position(bs) - movie->moof->fragment_offset - 8);
782 		//write cache
783 		gf_bs_get_content(trun->cache, &buffer, &size);
784 		gf_bs_write_data(bs, buffer, size);
785 		gf_bs_del(trun->cache);
786 		gf_free(buffer);
787 		trun->cache = NULL;
788 		traf->DataCache = 0;
789 	}
790 
791 	if (load_mdat_only) {
792 		u64 pos = gf_bs_get_position(bs);
793 		//we assume we never write large MDATs in fragment mode which should always be true
794 		movie->moof->mdat_size = (u32)(pos - movie->moof->fragment_offset);
795 
796 		if (movie->segment_bs) {
797 			gf_bs_seek(bs, 0);
798 			/*write mdat size*/
799 			gf_bs_write_u32(bs, (u32)movie->moof->mdat_size);
800 			/*and get internal buffer*/
801 			gf_bs_seek(bs, movie->moof->mdat_size);
802 			gf_bs_get_content(bs, &movie->moof->mdat, &movie->moof->mdat_size);
803 
804 			gf_bs_del(bs);
805 			movie->editFileMap->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
806 		}
807 		else {
808 			u64 offset = movie->segment_start;
809 			gf_bs_seek(bs, offset);
810 			/*write mdat size*/
811 			gf_bs_write_u32(bs, (u32)movie->moof->mdat_size);
812 
813 			movie->moof->mdat = (char*)gf_malloc(sizeof(char) * movie->moof->mdat_size);
814 			if (!movie->moof->mdat) return GF_OUT_OF_MEM;
815 
816 			gf_bs_seek(bs, offset);
817 			gf_bs_read_data(bs, movie->moof->mdat, movie->moof->mdat_size);
818 
819 			gf_bs_seek(bs, offset);
820 			gf_bs_truncate(bs);
821 		}
822 
823 		return GF_OK;
824 	}
825 
826 	moof_start = gf_bs_get_position(bs);
827 
828 	if (movie->moof->ntp) {
829 		moof_start += 8 * 4;
830 	}
831 
832 	//2- update MOOF MDAT header
833 	if (!movie->moof->mdat) {
834 		gf_bs_seek(bs, movie->moof->fragment_offset);
835 		//we assume we never write large MDATs in fragment mode which should always be true
836 		mdat_size = (u32)(moof_start - movie->moof->fragment_offset);
837 		gf_bs_write_u32(bs, (u32)mdat_size);
838 		gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
839 		gf_bs_seek(bs, moof_start);
840 	}
841 
842 	/*estimate moof size and shift trun offsets*/
843 #ifndef USE_BASE_DATA_OFFSET
844 	offset = 0;
845 	if (movie->use_segments) {
846 		e = gf_isom_box_size((GF_Box *)movie->moof);
847 		if (e) return e;
848 		offset = (s32)movie->moof->size;
849 		/*mdat size & type*/
850 		offset += 8;
851 		update_trun_offsets(movie, offset);
852 	}
853 #endif
854 
855 	//3- clean our traf's
856 	i = 0;
857 	while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
858 		//compute default settings for the TRAF
859 		ComputeFragmentDefaults(traf);
860 		//updates all trun and set all flags, INCLUDING TRAF FLAGS (durations, ...)
861 		s_count = UpdateRuns(movie, traf);
862 		//empty fragment destroy it
863 		if (!traf->tfhd->EmptyDuration && !s_count) {
864 			i--;
865 			gf_list_rem(movie->moof->TrackList, i);
866 			gf_isom_box_del((GF_Box *)traf);
867 			continue;
868 		}
869 	}
870 
871 	buffer = NULL;
872 	/*rewind bitstream and load mdat in memory */
873 	if (movie->moof_first && !movie->moof->mdat) {
874 		buffer = (char*)gf_malloc(sizeof(char)*mdat_size);
875 		gf_bs_seek(bs, movie->moof->fragment_offset);
876 		gf_bs_read_data(bs, buffer, mdat_size);
877 		/*back to mdat start and erase with moov*/
878 		gf_bs_seek(bs, movie->moof->fragment_offset);
879 		gf_bs_truncate(bs);
880 	}
881 
882 	//4- Write moof
883 	e = gf_isom_box_size((GF_Box *)movie->moof);
884 	if (e) return e;
885 	/*moof first, update traf headers - THIS WILL IMPACT THE MOOF SIZE IF WE
886 	DECIDE NOT TO USE THE DATA-OFFSET FLAG*/
887 	if (movie->moof_first
888 #ifndef USE_BASE_DATA_OFFSET
889 		&& !movie->use_segments
890 #endif
891 		) {
892 		i = 0;
893 		while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
894 			/*offset increases by moof size*/
895 			traf->tfhd->base_data_offset += movie->moof->size;
896 			traf->tfhd->base_data_offset += data_offset_diff;
897 		}
898 	}
899 #ifndef USE_BASE_DATA_OFFSET
900 	else if (movie->use_segments) {
901 		if (offset != (movie->moof->size + 8)) {
902 			offset = (s32)(movie->moof->size + 8 - offset);
903 			update_trun_offsets(movie, offset);
904 			e = gf_isom_box_size((GF_Box *)movie->moof);
905 			if (e) return e;
906 		}
907 	}
908 #endif
909 
910 
911 	if (movie->moof->ntp) {
912 		gf_bs_write_u32(bs, 8 * 4);
913 		gf_bs_write_u32(bs, GF_4CC('p', 'r', 'f', 't'));
914 		gf_bs_write_u8(bs, 1);
915 		gf_bs_write_u24(bs, 0);
916 		gf_bs_write_u32(bs, movie->moof->reference_track_ID);
917 		gf_bs_write_u64(bs, movie->moof->ntp);
918 		gf_bs_write_u64(bs, movie->moof->timestamp);
919 	}
920 
921 	pos = gf_bs_get_position(bs);
922 	i = 0;
923 	while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
924 		traf->moof_start_in_bs = pos;
925 	}
926 
927 	e = gf_isom_box_write((GF_Box *)movie->moof, bs);
928 	if (e) return e;
929 
930 	//rewrite mdat after moof
931 	if (movie->moof->mdat) {
932 		gf_bs_write_data(bs, movie->moof->mdat, movie->moof->mdat_size);
933 		gf_free(movie->moof->mdat);
934 		movie->moof->mdat = NULL;
935 	}
936 	else if (buffer) {
937 		gf_bs_write_data(bs, buffer, mdat_size);
938 		gf_free(buffer);
939 	}
940 
941 	if (moof_size) *moof_size = (u32)movie->moof->size;
942 
943 	if (!movie->use_segments) {
944 		gf_isom_box_del((GF_Box *)movie->moof);
945 		movie->moof = NULL;
946 	}
947 	return GF_OK;
948 }
949 
sidx_rewrite(GF_SegmentIndexBox * sidx,GF_BitStream * bs,u64 start_pos)950 static GF_Err sidx_rewrite(GF_SegmentIndexBox *sidx, GF_BitStream *bs, u64 start_pos)
951 {
952 	GF_Err e;
953 	u64 pos = gf_bs_get_position(bs);
954 	/*write sidx*/
955 	gf_bs_seek(bs, start_pos);
956 	e = gf_isom_box_write((GF_Box *)sidx, bs);
957 	gf_bs_seek(bs, pos);
958 	return e;
959 }
960 
gf_isom_allocate_sidx(GF_ISOFile * movie,s32 subsegs_per_sidx,Bool daisy_chain_sidx,u32 nb_segs,u32 * frags_per_segment,u32 * start_range,u32 * end_range)961 GF_Err gf_isom_allocate_sidx(GF_ISOFile *movie, s32 subsegs_per_sidx, Bool daisy_chain_sidx, u32 nb_segs, u32 *frags_per_segment, u32 *start_range, u32 *end_range)
962 {
963 	GF_BitStream *bs;
964 	GF_Err e;
965 
966 	//and only at setup
967 	if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) return GF_BAD_PARAM;
968 	if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
969 	if (movie->root_sidx) return GF_BAD_PARAM;
970 	if (movie->moof) return GF_BAD_PARAM;
971 	if (gf_list_count(movie->moof_list)) return GF_BAD_PARAM;
972 
973 	movie->root_sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX);
974 	/*we don't write anything between sidx and following moov*/
975 	movie->root_sidx->first_offset = 0;
976 
977 	/*for now we only store one ref per subsegment and don't support daisy-chaining*/
978 	movie->root_sidx->nb_refs = nb_segs;
979 
980 	movie->root_sidx->refs = (GF_SIDXReference*)gf_malloc(sizeof(GF_SIDXReference) * movie->root_sidx->nb_refs);
981 	memset(movie->root_sidx->refs, 0, sizeof(GF_SIDXReference) * movie->root_sidx->nb_refs);
982 
983 	movie->root_sidx_index = 0;
984 
985 	/*remember start of sidx*/
986 	movie->root_sidx_offset = gf_bs_get_position(movie->editFileMap->bs);
987 
988 	bs = movie->editFileMap->bs;
989 
990 	e = gf_isom_box_size((GF_Box *)movie->root_sidx);
991 	if (e) return e;
992 	e = gf_isom_box_write((GF_Box *)movie->root_sidx, bs);
993 	if (e) return e;
994 
995 	if (start_range) *start_range = (u32)movie->root_sidx_offset;
996 	if (end_range) *end_range = (u32)gf_bs_get_position(bs) - 1;
997 
998 	return GF_OK;
999 }
1000 
1001 
gf_isom_write_styp(GF_ISOFile * movie,Bool last_segment)1002 static GF_Err gf_isom_write_styp(GF_ISOFile *movie, Bool last_segment)
1003 {
1004 	GF_Err e = GF_OK;
1005 	/*write STYP if we write to a different file or if we write the last segment*/
1006 	if (!movie->append_segment && !movie->segment_start && !movie->styp_written) {
1007 
1008 		/*modify brands STYP*/
1009 
1010 		/*"msix" brand: this is a DASH Initialization Segment*/
1011 		gf_isom_modify_alternate_brand(movie, GF_4CC('m', 's', 'i', 'x'), 1);
1012 		if (last_segment) {
1013 			/*"lmsg" brand: this is the last DASH Segment*/
1014 			gf_isom_modify_alternate_brand(movie, GF_4CC('l', 'm', 's', 'g'), 1);
1015 		}
1016 
1017 		movie->brand->type = GF_ISOM_BOX_TYPE_STYP;
1018 		e = gf_isom_box_size((GF_Box *)movie->brand);
1019 		if (e) return e;
1020 		e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs);
1021 		if (e) return e;
1022 
1023 		movie->styp_written = GF_TRUE;
1024 	}
1025 	return GF_OK;
1026 }
1027 
1028 GF_EXPORT
gf_isom_flush_fragments(GF_ISOFile * movie,Bool last_segment)1029 GF_Err gf_isom_flush_fragments(GF_ISOFile *movie, Bool last_segment)
1030 {
1031 	GF_BitStream *temp_bs = NULL;
1032 	GF_Err e;
1033 
1034 	if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) return GF_BAD_PARAM;
1035 	if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
1036 
1037 	/*flush our fragment (store in mem)*/
1038 	if (movie->moof) {
1039 		e = StoreFragment(movie, GF_TRUE, 0, NULL);
1040 		if (e) return e;
1041 	}
1042 
1043 	if (movie->segment_bs) {
1044 		temp_bs = movie->editFileMap->bs;
1045 		movie->editFileMap->bs = movie->segment_bs;
1046 	}
1047 
1048 	gf_bs_seek(movie->editFileMap->bs, movie->segment_start);
1049 	gf_bs_truncate(movie->editFileMap->bs);
1050 
1051 	/*write styp to file if needed*/
1052 	e = gf_isom_write_styp(movie, last_segment);
1053 	if (e) return e;
1054 
1055 	/*write all pending fragments to file*/
1056 	while (gf_list_count(movie->moof_list)) {
1057 		s32 offset_diff;
1058 		u32 moof_size;
1059 
1060 		movie->moof = (GF_MovieFragmentBox*)gf_list_get(movie->moof_list, 0);
1061 		gf_list_rem(movie->moof_list, 0);
1062 
1063 		offset_diff = (s32)(gf_bs_get_position(movie->editFileMap->bs) - movie->moof->fragment_offset);
1064 		movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs);
1065 
1066 		e = StoreFragment(movie, GF_FALSE, offset_diff, &moof_size);
1067 		if (e) return e;
1068 
1069 		gf_isom_box_del((GF_Box *)movie->moof);
1070 		movie->moof = NULL;
1071 	}
1072 
1073 	/*append mode: store fragment at the end of the regular movie bitstream, and delete the temp bitstream*/
1074 	if (movie->append_segment) {
1075 		char bloc[1024];
1076 		u32 seg_size = (u32)gf_bs_get_size(movie->editFileMap->bs);
1077 		gf_bs_seek(movie->editFileMap->bs, 0);
1078 		while (seg_size) {
1079 			u32 size = gf_bs_read_data(movie->editFileMap->bs, bloc, (seg_size>1024) ? 1024 : seg_size);
1080 			gf_bs_write_data(movie->movieFileMap->bs, bloc, size);
1081 			seg_size -= size;
1082 		}
1083 		gf_isom_datamap_flush(movie->movieFileMap);
1084 
1085 		gf_isom_datamap_del(movie->editFileMap);
1086 		movie->editFileMap = gf_isom_fdm_new_temp(NULL);
1087 	}
1088 	else {
1089 		gf_isom_datamap_flush(movie->editFileMap);
1090 	}
1091 	movie->segment_start = gf_bs_get_position(movie->editFileMap->bs);
1092 
1093 	if (temp_bs) {
1094 		movie->segment_bs = movie->editFileMap->bs;
1095 		movie->editFileMap->bs = temp_bs;
1096 	}
1097 	return GF_OK;
1098 }
1099 
1100 typedef struct
1101 {
1102 	GF_SegmentIndexBox *sidx;
1103 	u64 start_offset, end_offset;
1104 } SIDXEntry;
1105 
get_presentation_time(u64 media_time,s32 ts_shift)1106 static u64 get_presentation_time(u64 media_time, s32 ts_shift)
1107 {
1108 	if ((ts_shift<0) && (media_time < -ts_shift)) {
1109 		media_time = 0;
1110 	}
1111 	else {
1112 		media_time += ts_shift;
1113 	}
1114 	return media_time;
1115 }
1116 
1117 GF_EXPORT
gf_isom_close_segment(GF_ISOFile * movie,s32 subsegments_per_sidx,u32 referenceTrackID,u64 ref_track_decode_time,s32 ts_shift,u64 ref_track_next_cts,Bool daisy_chain_sidx,Bool last_segment,u32 segment_marker_4cc,u64 * index_start_range,u64 * index_end_range)1118 GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 subsegments_per_sidx, u32 referenceTrackID, u64 ref_track_decode_time, s32 ts_shift, u64 ref_track_next_cts, Bool daisy_chain_sidx, Bool last_segment, u32 segment_marker_4cc, u64 *index_start_range, u64 *index_end_range)
1119 {
1120 	GF_SegmentIndexBox *sidx = NULL;
1121 	GF_SegmentIndexBox *root_sidx = NULL;
1122 	GF_List *daisy_sidx = NULL;
1123 	u64 sidx_start, sidx_end;
1124 	Bool first_frag_in_subseg;
1125 	Bool no_sidx = GF_FALSE;
1126 	u32 count, cur_idx, cur_dur, sidx_dur, sidx_idx, idx_offset, frag_count;
1127 	u64 last_top_box_pos, root_prev_offset, local_sidx_start, local_sidx_end, prev_earliest_cts;
1128 	GF_TrackBox *trak = NULL;
1129 	GF_Err e;
1130 	/*number of subsegment in this segment (eg nb references in the first SIDX found)*/
1131 	u32 nb_subsegs = 0;
1132 	/*number of subsegment per sidx (eg number of references of any sub-SIDX*/
1133 	u32 subseg_per_sidx;
1134 	/*number of fragments per subsegment*/
1135 	u32 frags_per_subseg;
1136 	/*number of fragments per subsidx*/
1137 	u32 frags_per_subsidx;
1138 
1139 	sidx_start = sidx_end = 0;
1140 
1141 	if (index_start_range) *index_start_range = 0;
1142 	if (index_end_range) *index_end_range = 0;
1143 
1144 	//and only at setup
1145 	if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) return GF_BAD_PARAM;
1146 	if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
1147 
1148 	count = gf_list_count(movie->moov->mvex->TrackExList);
1149 	if (!count) return GF_BAD_PARAM;
1150 
1151 	/*store fragment*/
1152 	if (movie->moof) {
1153 		e = StoreFragment(movie, GF_TRUE, 0, NULL);
1154 		if (e) return e;
1155 	}
1156 	/*restore final bitstream*/
1157 	if (movie->segment_bs) {
1158 		gf_bs_del(movie->editFileMap->bs);
1159 		movie->editFileMap->bs = movie->segment_bs;
1160 		movie->segment_bs = NULL;
1161 	}
1162 
1163 	count = gf_list_count(movie->moof_list);
1164 	if (!count) {
1165 		/*append segment marker box*/
1166 		if (segment_marker_4cc) {
1167 			if (movie->append_segment) {
1168 				gf_bs_write_u32(movie->movieFileMap->bs, 8);	//write size field
1169 				gf_bs_write_u32(movie->movieFileMap->bs, segment_marker_4cc); //write box type field
1170 			}
1171 			else {
1172 				gf_bs_write_u32(movie->editFileMap->bs, 8);	//write size field
1173 				gf_bs_write_u32(movie->editFileMap->bs, segment_marker_4cc); //write box type field
1174 			}
1175 		}
1176 		return GF_OK;
1177 	}
1178 
1179 	gf_bs_seek(movie->editFileMap->bs, movie->segment_start);
1180 	gf_bs_truncate(movie->editFileMap->bs);
1181 
1182 	idx_offset = 0;
1183 
1184 	if (referenceTrackID) {
1185 		trak = gf_isom_get_track_from_id(movie->moov, referenceTrackID);
1186 		if (!trak) return GF_BAD_PARAM;
1187 	}
1188 
1189 	if (subsegments_per_sidx < 0) {
1190 		referenceTrackID = 0;
1191 		subsegments_per_sidx = 0;
1192 	}
1193 	if (!subsegments_per_sidx && !referenceTrackID) {
1194 		no_sidx = GF_TRUE;
1195 	}
1196 
1197 	e = gf_isom_write_styp(movie, last_segment);
1198 	if (e) return e;
1199 
1200 	frags_per_subseg = 0;
1201 	subseg_per_sidx = 0;
1202 	frags_per_subsidx = 0;
1203 
1204 	prev_earliest_cts = 0;
1205 
1206 	if (daisy_chain_sidx)
1207 		daisy_sidx = gf_list_new();
1208 
1209 	/*prepare SIDX: we write a blank SIDX box with the right number of entries, and will rewrite it later on*/
1210 	if (referenceTrackID) {
1211 		Bool is_root_sidx = GF_FALSE;
1212 
1213 		prev_earliest_cts = get_presentation_time(ref_track_decode_time + moof_get_earliest_cts((GF_MovieFragmentBox*)gf_list_get(movie->moof_list, 0), referenceTrackID), ts_shift);
1214 
1215 		if (movie->root_sidx) {
1216 			sidx = movie->root_sidx;
1217 		}
1218 		else {
1219 			sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX);
1220 		}
1221 		sidx->reference_ID = referenceTrackID;
1222 		sidx->timescale = trak->Media->mediaHeader->timeScale;
1223 		/*we don't write anything between sidx and following moov*/
1224 		sidx->first_offset = 0;
1225 
1226 		/*we allocated our sidx to have one ref per "segment" (eg per call to close_segment)*/
1227 		if (movie->root_sidx) {
1228 			if (!movie->root_sidx_index) {
1229 				sidx->earliest_presentation_time = prev_earliest_cts;
1230 			}
1231 			nb_subsegs = 1;
1232 			frags_per_subseg = count;
1233 			frags_per_subsidx = count;
1234 			subseg_per_sidx = 1;
1235 			daisy_chain_sidx = GF_FALSE;
1236 
1237 			idx_offset = movie->root_sidx_index;
1238 			sidx_end = gf_bs_get_position(movie->editFileMap->bs);
1239 		}
1240 		else {
1241 			sidx->earliest_presentation_time = prev_earliest_cts;
1242 
1243 			/*if more subsegments requested than fragments available, make a single sidx*/
1244 			if ((s32)count <= subsegments_per_sidx)
1245 				subsegments_per_sidx = 0;
1246 
1247 			if (daisy_chain_sidx && (subsegments_per_sidx<2))
1248 				subsegments_per_sidx = 2;
1249 
1250 			/*single SIDX, each fragment is a subsegment and we reference all subsegments*/
1251 			if (!subsegments_per_sidx) {
1252 				nb_subsegs = count;
1253 				/*we consider that each fragment is a subsegment - this could be controled by another parameter*/
1254 				frags_per_subseg = 1;
1255 				frags_per_subsidx = count;
1256 				subseg_per_sidx = count;
1257 
1258 				sidx->nb_refs = nb_subsegs;
1259 				daisy_chain_sidx = GF_FALSE;
1260 			}
1261 			/*daisy-chain SIDX: each SIDX describes a subsegment made of frags_per_subseg fragments plus next */
1262 			else if (daisy_chain_sidx) {
1263 				frags_per_subsidx = count / subsegments_per_sidx;
1264 				if (frags_per_subsidx * subsegments_per_sidx < count) frags_per_subsidx++;
1265 
1266 				nb_subsegs = subsegments_per_sidx;
1267 
1268 				/*we consider that each fragment is a subsegment - this could be controled by another parameter*/
1269 				frags_per_subseg = 1;
1270 				subseg_per_sidx = frags_per_subsidx / frags_per_subseg;
1271 				if (subseg_per_sidx * frags_per_subseg < frags_per_subsidx) subseg_per_sidx++;
1272 
1273 				sidx->nb_refs = subseg_per_sidx + 1;
1274 			}
1275 			/*hierarchical SIDX*/
1276 			else {
1277 				frags_per_subsidx = count / subsegments_per_sidx;
1278 				if (frags_per_subsidx * subsegments_per_sidx < count) frags_per_subsidx++;
1279 
1280 				nb_subsegs = subsegments_per_sidx;
1281 
1282 				/*we consider that each fragment is a subsegment - this could be controled by another parameter*/
1283 				frags_per_subseg = 1;
1284 				subseg_per_sidx = frags_per_subsidx / frags_per_subseg;
1285 				if (subseg_per_sidx * frags_per_subseg < frags_per_subsidx) subseg_per_sidx++;
1286 
1287 				sidx->nb_refs = nb_subsegs;
1288 				is_root_sidx = GF_TRUE;
1289 			}
1290 
1291 			sidx->refs = (GF_SIDXReference*)gf_malloc(sizeof(GF_SIDXReference)*sidx->nb_refs);
1292 			memset(sidx->refs, 0, sizeof(GF_SIDXReference)*sidx->nb_refs);
1293 
1294 			/*remember start of sidx*/
1295 			sidx_start = gf_bs_get_position(movie->editFileMap->bs);
1296 
1297 			e = gf_isom_box_size((GF_Box *)sidx);
1298 			if (e) return e;
1299 			e = gf_isom_box_write((GF_Box *)sidx, movie->editFileMap->bs);
1300 			if (e) return e;
1301 
1302 			sidx_end = gf_bs_get_position(movie->editFileMap->bs);
1303 
1304 			if (daisy_sidx) {
1305 				SIDXEntry *entry;
1306 				GF_SAFEALLOC(entry, SIDXEntry);
1307 				entry->sidx = sidx;
1308 				entry->start_offset = sidx_start;
1309 				gf_list_add(daisy_sidx, entry);
1310 			}
1311 		}
1312 
1313 		if (is_root_sidx) {
1314 			root_sidx = sidx;
1315 			sidx = NULL;
1316 		}
1317 		count = cur_idx = 0;
1318 	}
1319 
1320 
1321 	last_top_box_pos = root_prev_offset = sidx_end;
1322 	sidx_idx = 0;
1323 	sidx_dur = 0;
1324 	local_sidx_start = local_sidx_end = 0;
1325 
1326 	/*cumulated segments duration since start of the sidx */
1327 	frag_count = frags_per_subsidx;
1328 	cur_dur = 0;
1329 	cur_idx = 0;
1330 	first_frag_in_subseg = GF_TRUE;
1331 	e = GF_OK;
1332 	while (gf_list_count(movie->moof_list)) {
1333 		s32 offset_diff;
1334 		u32 moof_size;
1335 
1336 		movie->moof = (GF_MovieFragmentBox*)gf_list_get(movie->moof_list, 0);
1337 		gf_list_rem(movie->moof_list, 0);
1338 
1339 		/*hierarchical or daisy-chain SIDXs*/
1340 		if (!no_sidx && !sidx && (root_sidx || daisy_chain_sidx)) {
1341 			u32 subsegments_remaining;
1342 			sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX);
1343 			sidx->reference_ID = referenceTrackID;
1344 			sidx->timescale = trak ? trak->Media->mediaHeader->timeScale : 1000;
1345 			sidx->earliest_presentation_time = get_presentation_time(ref_track_decode_time + sidx_dur + moof_get_earliest_cts(movie->moof, referenceTrackID), ts_shift);
1346 
1347 			frag_count = frags_per_subsidx;
1348 
1349 			/*last segment, add only one ref*/
1350 			subsegments_remaining = 1 + gf_list_count(movie->moof_list);
1351 			if (subseg_per_sidx*frags_per_subseg > subsegments_remaining) {
1352 				subseg_per_sidx = subsegments_remaining / frags_per_subseg;
1353 				if (subseg_per_sidx * frags_per_subseg < subsegments_remaining) subseg_per_sidx++;
1354 			}
1355 			/*we don't write anything between sidx and following moov*/
1356 			sidx->first_offset = 0;
1357 			sidx->nb_refs = subseg_per_sidx;
1358 			if (daisy_chain_sidx && (nb_subsegs>1)) {
1359 				sidx->nb_refs += 1;
1360 			}
1361 			sidx->refs = (GF_SIDXReference*)gf_malloc(sizeof(GF_SIDXReference)*sidx->nb_refs);
1362 			memset(sidx->refs, 0, sizeof(GF_SIDXReference)*sidx->nb_refs);
1363 
1364 			if (root_sidx)
1365 				root_sidx->refs[sidx_idx].reference_type = GF_TRUE;
1366 
1367 			/*remember start of sidx*/
1368 			local_sidx_start = gf_bs_get_position(movie->editFileMap->bs);
1369 
1370 			/*write it*/
1371 			e = gf_isom_box_size((GF_Box *)sidx);
1372 			if (e) return e;
1373 			e = gf_isom_box_write((GF_Box *)sidx, movie->editFileMap->bs);
1374 			if (e) return e;
1375 
1376 			local_sidx_end = gf_bs_get_position(movie->editFileMap->bs);
1377 
1378 			/*adjust prev offset*/
1379 			last_top_box_pos = local_sidx_end;
1380 
1381 			if (daisy_sidx) {
1382 				SIDXEntry *entry;
1383 				GF_SAFEALLOC(entry, SIDXEntry);
1384 				if (!entry) return GF_OUT_OF_MEM;
1385 				entry->sidx = sidx;
1386 				entry->start_offset = local_sidx_start;
1387 				gf_list_add(daisy_sidx, entry);
1388 			}
1389 		}
1390 
1391 		offset_diff = (s32)(gf_bs_get_position(movie->editFileMap->bs) - movie->moof->fragment_offset);
1392 		movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs);
1393 
1394 		if (!e) {
1395 			e = StoreFragment(movie, GF_FALSE, offset_diff, &moof_size);
1396 
1397 
1398 			if (sidx) {
1399 				u32 cur_index = idx_offset + cur_idx;
1400 
1401 				/*do not compute earliest CTS if single segment sidx since we only have set the info for one subsegment*/
1402 				if (!movie->root_sidx && first_frag_in_subseg) {
1403 					u64 first_cts = get_presentation_time(ref_track_decode_time + sidx_dur + cur_dur + moof_get_earliest_cts(movie->moof, referenceTrackID), ts_shift);
1404 					u32 subseg_dur = (u32)(first_cts - prev_earliest_cts);
1405 					if (cur_index) {
1406 						sidx->refs[cur_index - 1].subsegment_duration = subseg_dur;
1407 						if (root_sidx) root_sidx->refs[sidx_idx].subsegment_duration += subseg_dur;
1408 					}
1409 					prev_earliest_cts = first_cts;
1410 					first_frag_in_subseg = GF_FALSE;
1411 				}
1412 
1413 
1414 				/*we refer to next moof*/
1415 				sidx->refs[cur_index].reference_type = GF_FALSE;
1416 				if (!sidx->refs[cur_index].SAP_type) {
1417 					sidx->refs[cur_index].SAP_type = moof_get_sap_info(movie->moof, referenceTrackID, &sidx->refs[cur_index].SAP_delta_time, &sidx->refs[cur_index].starts_with_SAP);
1418 					if (sidx->refs[cur_index].SAP_type) {
1419 						if (root_sidx && !root_sidx->refs[sidx_idx].SAP_type) {
1420 							root_sidx->refs[sidx_idx].SAP_type = sidx->refs[cur_index].SAP_type;
1421 							root_sidx->refs[sidx_idx].SAP_delta_time = sidx->refs[cur_index].SAP_delta_time;
1422 							root_sidx->refs[sidx_idx].starts_with_SAP = sidx->refs[cur_index].starts_with_SAP;
1423 						}
1424 					}
1425 				}
1426 				cur_dur += moof_get_duration(movie->moof, referenceTrackID);
1427 
1428 				/*reference size is end of the moof we just wrote minus last_box_pos*/
1429 				sidx->refs[cur_index].reference_size += (u32)(gf_bs_get_position(movie->editFileMap->bs) - last_top_box_pos);
1430 				last_top_box_pos = gf_bs_get_position(movie->editFileMap->bs);
1431 
1432 				count++;
1433 
1434 				/*we are switching subsegment*/
1435 				frag_count--;
1436 
1437 				if (count == frags_per_subseg) {
1438 					count = 0;
1439 					first_frag_in_subseg = GF_TRUE;
1440 					cur_idx++;
1441 				}
1442 
1443 				/*switching to next SIDX*/
1444 				if ((cur_idx == subseg_per_sidx) || !frag_count) {
1445 					u32 subseg_dur;
1446 					u64 next_cts;
1447 					/*update last ref duration*/
1448 					if (gf_list_count(movie->moof_list)) {
1449 						next_cts = get_presentation_time(ref_track_decode_time + sidx_dur + cur_dur + moof_get_earliest_cts((GF_MovieFragmentBox*)gf_list_get(movie->moof_list, 0), referenceTrackID), ts_shift);
1450 					}
1451 					else {
1452 						next_cts = get_presentation_time(ref_track_next_cts, ts_shift);
1453 					}
1454 					subseg_dur = (u32)(next_cts - prev_earliest_cts);
1455 					if (movie->root_sidx) {
1456 						sidx->refs[idx_offset].subsegment_duration = subseg_dur;
1457 					}
1458 					/*if daisy chain and not the last sidx, we have an extra entry at the end*/
1459 					else if (daisy_chain_sidx && (nb_subsegs>1)) {
1460 						sidx->refs[sidx->nb_refs - 2].subsegment_duration = subseg_dur;
1461 					}
1462 					else {
1463 						sidx->refs[sidx->nb_refs - 1].subsegment_duration = subseg_dur;
1464 					}
1465 					if (root_sidx) root_sidx->refs[sidx_idx].subsegment_duration += subseg_dur;
1466 
1467 					if (root_sidx) {
1468 						root_sidx->refs[sidx_idx].reference_size = (u32)(gf_bs_get_position(movie->editFileMap->bs) - local_sidx_start);
1469 						if (!sidx_idx) {
1470 							root_sidx->earliest_presentation_time = sidx->earliest_presentation_time;
1471 						}
1472 						sidx_rewrite(sidx, movie->editFileMap->bs, local_sidx_start);
1473 						gf_isom_box_del((GF_Box*)sidx);
1474 						sidx = NULL;
1475 					}
1476 					else if (daisy_chain_sidx) {
1477 						SIDXEntry *entry = (SIDXEntry*)gf_list_last(daisy_sidx);
1478 						entry->end_offset = gf_bs_get_position(movie->editFileMap->bs);
1479 						nb_subsegs--;
1480 						sidx = NULL;
1481 					}
1482 					sidx_dur += cur_dur;
1483 					cur_dur = 0;
1484 					count = 0;
1485 					cur_idx = 0;
1486 					if (movie->root_sidx)
1487 						movie->root_sidx_index++;
1488 					sidx_idx++;
1489 				}
1490 			}
1491 		}
1492 		gf_isom_box_del((GF_Box *)movie->moof);
1493 		movie->moof = NULL;
1494 	}
1495 
1496 	/*append segment marker box*/
1497 	if (segment_marker_4cc) {
1498 		gf_bs_write_u32(movie->editFileMap->bs, 8);	//write size field
1499 		gf_bs_write_u32(movie->editFileMap->bs, segment_marker_4cc); //write box type field
1500 	}
1501 
1502 	if (movie->root_sidx) {
1503 		if (last_segment) {
1504 			assert(movie->root_sidx_index == movie->root_sidx->nb_refs);
1505 
1506 			sidx_rewrite(movie->root_sidx, movie->editFileMap->bs, movie->root_sidx_offset);
1507 			gf_isom_box_del((GF_Box*)movie->root_sidx);
1508 			movie->root_sidx = NULL;
1509 		}
1510 		return GF_OK;
1511 	}
1512 
1513 	if (sidx) {
1514 		assert(!root_sidx);
1515 		sidx_rewrite(sidx, movie->editFileMap->bs, sidx_start);
1516 		gf_isom_box_del((GF_Box*)sidx);
1517 	}
1518 	if (daisy_sidx) {
1519 		u32 i, j;
1520 		u64 last_entry_end_offset = 0;
1521 		u32 count = gf_list_count(daisy_sidx);
1522 		for (i = count; i>1; i--) {
1523 			SIDXEntry *entry = (SIDXEntry*)gf_list_get(daisy_sidx, i - 2);
1524 			SIDXEntry *next_entry = (SIDXEntry*)gf_list_get(daisy_sidx, i - 1);
1525 
1526 			if (!last_entry_end_offset) {
1527 				last_entry_end_offset = next_entry->end_offset;
1528 				/*rewrite last sidx*/
1529 				sidx_rewrite(next_entry->sidx, movie->editFileMap->bs, next_entry->start_offset);
1530 			}
1531 			/*copy over SAP info for last item (which points to next item !)*/
1532 			entry->sidx->refs[entry->sidx->nb_refs - 1] = next_entry->sidx->refs[0];
1533 			/*and rewrite reference type, size and dur*/
1534 			entry->sidx->refs[entry->sidx->nb_refs - 1].reference_type = GF_TRUE;
1535 			entry->sidx->refs[entry->sidx->nb_refs - 1].reference_size = (u32)(last_entry_end_offset - next_entry->start_offset);
1536 			entry->sidx->refs[entry->sidx->nb_refs - 1].subsegment_duration = 0;
1537 			for (j = 0; j<next_entry->sidx->nb_refs; j++) {
1538 				entry->sidx->refs[entry->sidx->nb_refs - 1].subsegment_duration += next_entry->sidx->refs[j].subsegment_duration;
1539 			}
1540 			sidx_rewrite(entry->sidx, movie->editFileMap->bs, entry->start_offset);
1541 		}
1542 		while (gf_list_count(daisy_sidx)) {
1543 			SIDXEntry *entry = (SIDXEntry*)gf_list_last(daisy_sidx);
1544 			gf_isom_box_del((GF_Box*)entry->sidx);
1545 			gf_free(entry);
1546 			gf_list_rem_last(daisy_sidx);
1547 		}
1548 		gf_list_del(daisy_sidx);
1549 	}
1550 	if (root_sidx) {
1551 		sidx_rewrite(root_sidx, movie->editFileMap->bs, sidx_start);
1552 		gf_isom_box_del((GF_Box*)root_sidx);
1553 	}
1554 
1555 	if ((root_sidx || sidx) && !daisy_chain_sidx) {
1556 		if (index_start_range) *index_start_range = sidx_start;
1557 		if (index_end_range) *index_end_range = sidx_end - 1;
1558 	}
1559 
1560 	if (movie->append_segment) {
1561 		char bloc[1024];
1562 		u32 seg_size = (u32)gf_bs_get_size(movie->editFileMap->bs);
1563 		gf_bs_seek(movie->editFileMap->bs, 0);
1564 		while (seg_size) {
1565 			u32 size = gf_bs_read_data(movie->editFileMap->bs, bloc, (seg_size>1024) ? 1024 : seg_size);
1566 			gf_bs_write_data(movie->movieFileMap->bs, bloc, size);
1567 			seg_size -= size;
1568 		}
1569 		gf_isom_datamap_del(movie->editFileMap);
1570 		movie->editFileMap = gf_isom_fdm_new_temp(NULL);
1571 	}
1572 
1573 	return e;
1574 }
1575 
1576 GF_EXPORT
gf_isom_close_fragments(GF_ISOFile * movie)1577 GF_Err gf_isom_close_fragments(GF_ISOFile *movie)
1578 {
1579 	if (movie->use_segments) {
1580 		return gf_isom_close_segment(movie, 0, 0, 0, 0, 0, GF_FALSE, 1, 0, NULL, NULL);
1581 	}
1582 	else {
1583 		return StoreFragment(movie, GF_FALSE, 0, NULL);
1584 	}
1585 }
1586 
1587 GF_EXPORT
gf_isom_start_segment(GF_ISOFile * movie,const char * SegName,Bool memory_mode)1588 GF_Err gf_isom_start_segment(GF_ISOFile *movie, const char *SegName, Bool memory_mode)
1589 {
1590 	GF_Err e;
1591 	//and only at setup
1592 	if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) return GF_BAD_PARAM;
1593 	if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
1594 
1595 	if (gf_list_count(movie->moof_list))
1596 		return GF_BAD_PARAM;
1597 
1598 	movie->segment_bs = NULL;
1599 	movie->append_segment = GF_FALSE;
1600 	/*update segment file*/
1601 	if (SegName) {
1602 		gf_isom_datamap_del(movie->editFileMap);
1603 		e = gf_isom_datamap_new(SegName, NULL, GF_ISOM_DATA_MAP_WRITE, &movie->editFileMap);
1604 		movie->segment_start = 0;
1605 		movie->styp_written = GF_FALSE;
1606 		if (e) return e;
1607 	}
1608 	else {
1609 		assert(gf_list_count(movie->moof_list) == 0);
1610 		movie->segment_start = gf_bs_get_position(movie->editFileMap->bs);
1611 		/*if movieFileMap is not null, we are concatenating segments to the original movie, force a copy*/
1612 		if (movie->movieFileMap)
1613 			movie->append_segment = GF_TRUE;
1614 	}
1615 
1616 	/*create a memory bitstream for all file IO until final flush*/
1617 	if (memory_mode) {
1618 		movie->segment_bs = movie->editFileMap->bs;
1619 		movie->editFileMap->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
1620 	}
1621 	return GF_OK;
1622 }
1623 
1624 GF_EXPORT
gf_isom_set_fragment_reference_time(GF_ISOFile * movie,u32 reference_track_ID,u64 ntp,u64 timestamp)1625 GF_Err gf_isom_set_fragment_reference_time(GF_ISOFile *movie, u32 reference_track_ID, u64 ntp, u64 timestamp)
1626 {
1627 	if (!movie->moof) return GF_BAD_PARAM;
1628 	movie->moof->reference_track_ID = reference_track_ID;
1629 	movie->moof->ntp = ntp;
1630 	movie->moof->timestamp = timestamp;
1631 	return GF_OK;
1632 }
1633 
1634 GF_EXPORT
gf_isom_set_traf_mss_timeext(GF_ISOFile * movie,u32 reference_track_ID,u64 ntp_in_track_timescale,u64 traf_duration_in_track_timescale)1635 GF_Err gf_isom_set_traf_mss_timeext(GF_ISOFile *movie, u32 reference_track_ID, u64 ntp_in_track_timescale, u64 traf_duration_in_track_timescale)
1636 {
1637 	u32 i;
1638 	if (!movie || !movie->moof)
1639 		return GF_BAD_PARAM;
1640 	for (i = 0; i<gf_list_count(movie->moof->TrackList); i++) {
1641 		GF_TrackFragmentBox *traf = (GF_TrackFragmentBox*)gf_list_get(movie->moof->TrackList, i);
1642 		if (!traf)
1643 			return GF_BAD_PARAM;
1644 		if (traf->tfxd)
1645 			gf_isom_box_del((GF_Box*)traf->tfxd);
1646 		traf->tfxd = (GF_MSSTimeExtBox *)gf_isom_box_new(GF_ISOM_BOX_UUID_TFXD);
1647 		traf->tfxd->absolute_time_in_track_timescale = ntp_in_track_timescale;
1648 		traf->tfxd->fragment_duration_in_track_timescale = traf_duration_in_track_timescale;
1649 	}
1650 	return GF_OK;
1651 }
1652 
1653 GF_EXPORT
gf_isom_start_fragment(GF_ISOFile * movie,Bool moof_first)1654 GF_Err gf_isom_start_fragment(GF_ISOFile *movie, Bool moof_first)
1655 {
1656 	u32 i, count;
1657 	GF_TrackExtendsBox *trex;
1658 	GF_TrackFragmentBox *traf;
1659 	GF_Err e;
1660 	//and only at setup
1661 	if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY))
1662 		return GF_BAD_PARAM;
1663 	if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
1664 
1665 	count = gf_list_count(movie->moov->mvex->TrackExList);
1666 	if (!count)
1667 		return GF_BAD_PARAM;
1668 
1669 	/*always force cached mode when writing movie segments*/
1670 	if (movie->use_segments) moof_first = GF_TRUE;
1671 	movie->moof_first = moof_first;
1672 
1673 	//store existing fragment
1674 	if (movie->moof) {
1675 		e = StoreFragment(movie, movie->use_segments ? GF_TRUE : GF_FALSE, 0, NULL);
1676 		if (e) return e;
1677 	}
1678 
1679 	//create new fragment
1680 	movie->moof = (GF_MovieFragmentBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MOOF);
1681 	movie->moof->mfhd = (GF_MovieFragmentHeaderBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MFHD);
1682 	movie->moof->mfhd->sequence_number = movie->NextMoofNumber;
1683 	movie->NextMoofNumber++;
1684 	if (movie->use_segments)
1685 		gf_list_add(movie->moof_list, movie->moof);
1686 
1687 
1688 	/*remember segment offset*/
1689 	movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs);
1690 	/*prepare MDAT*/
1691 	gf_bs_write_u32(movie->editFileMap->bs, 0);
1692 	gf_bs_write_u32(movie->editFileMap->bs, GF_ISOM_BOX_TYPE_MDAT);
1693 
1694 	//we create a TRAF for each setup track, unused ones will be removed at store time
1695 	for (i = 0; i<count; i++) {
1696 		trex = (GF_TrackExtendsBox*)gf_list_get(movie->moov->mvex->TrackExList, i);
1697 		traf = (GF_TrackFragmentBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TRAF);
1698 		traf->trex = trex;
1699 		traf->tfhd = (GF_TrackFragmentHeaderBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TFHD);
1700 		traf->tfhd->trackID = trex->trackID;
1701 		//add 8 bytes (MDAT size+type) to avoid the data_offset in the first trun
1702 		traf->tfhd->base_data_offset = movie->moof->fragment_offset + 8;
1703 		gf_list_add(movie->moof->TrackList, traf);
1704 	}
1705 
1706 	return GF_OK;
1707 }
1708 
GetRunSize(GF_TrackFragmentRunBox * trun)1709 u32 GetRunSize(GF_TrackFragmentRunBox *trun)
1710 {
1711 	u32 i, size;
1712 	GF_TrunEntry *ent;
1713 	size = 0;
1714 	i = 0;
1715 	while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &i))) {
1716 		size += ent->size;
1717 	}
1718 	return size;
1719 }
1720 
1721 GF_EXPORT
gf_isom_fragment_add_sample(GF_ISOFile * movie,u32 TrackID,const GF_ISOSample * sample,u32 DescIndex,u32 Duration,u8 PaddingBits,u16 DegradationPriority,Bool redundant_coding)1722 GF_Err gf_isom_fragment_add_sample(GF_ISOFile *movie, u32 TrackID, const GF_ISOSample *sample, u32 DescIndex,
1723 	u32 Duration, u8 PaddingBits, u16 DegradationPriority, Bool redundant_coding)
1724 {
1725 	u32 count, buffer_size;
1726 	char *buffer;
1727 	u64 pos;
1728 	GF_ISOSample *od_sample = NULL;
1729 	GF_TrunEntry *ent;
1730 	GF_TrackFragmentBox *traf, *traf_2;
1731 	GF_TrackFragmentRunBox *trun;
1732 	if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) || !sample)
1733 		return GF_BAD_PARAM;
1734 
1735 	traf = GetTraf(movie, TrackID);
1736 	if (!traf)
1737 		return GF_BAD_PARAM;
1738 
1739 	if (!traf->tfhd->sample_desc_index) traf->tfhd->sample_desc_index = DescIndex ? DescIndex : traf->trex->def_sample_desc_index;
1740 
1741 	pos = gf_bs_get_position(movie->editFileMap->bs);
1742 
1743 
1744 	//WARNING: we change stream description, create a new TRAF
1745 	if (DescIndex && (traf->tfhd->sample_desc_index != DescIndex)) {
1746 		//if we're caching flush the current run
1747 		if (traf->DataCache) {
1748 			count = gf_list_count(traf->TrackRuns);
1749 			if (count) {
1750 				trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count - 1);
1751 				trun->data_offset = (u32)(pos - movie->moof->fragment_offset - 8);
1752 				gf_bs_get_content(trun->cache, &buffer, &buffer_size);
1753 				gf_bs_write_data(movie->editFileMap->bs, buffer, buffer_size);
1754 				gf_bs_del(trun->cache);
1755 				trun->cache = NULL;
1756 				gf_free(buffer);
1757 			}
1758 		}
1759 		traf_2 = (GF_TrackFragmentBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TRAF);
1760 		traf_2->trex = traf->trex;
1761 		traf_2->tfhd = (GF_TrackFragmentHeaderBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TFHD);
1762 		traf_2->tfhd->trackID = traf->tfhd->trackID;
1763 		//keep the same offset
1764 		traf_2->tfhd->base_data_offset = movie->moof->fragment_offset + 8;
1765 		gf_list_add(movie->moof->TrackList, traf_2);
1766 
1767 		//duplicate infos
1768 		traf_2->tfhd->IFrameSwitching = traf->tfhd->IFrameSwitching;
1769 		traf_2->DataCache = traf->DataCache;
1770 		traf_2->tfhd->sample_desc_index = DescIndex;
1771 
1772 		//switch them ...
1773 		traf = traf_2;
1774 	}
1775 
1776 	pos = gf_bs_get_position(movie->editFileMap->bs);
1777 	//add TRUN entry
1778 	count = gf_list_count(traf->TrackRuns);
1779 	if (count) {
1780 		trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count - 1);
1781 		//check data offset when no caching as trun entries shall ALWAYS be contiguous samples
1782 		if (!traf->DataCache && (movie->moof->fragment_offset + 8 + trun->data_offset + GetRunSize(trun) != pos))
1783 			count = 0;
1784 
1785 		//check I-frame detection
1786 		if (traf->tfhd->IFrameSwitching && sample->IsRAP)
1787 			count = 0;
1788 
1789 		if (traf->DataCache && (traf->DataCache == trun->sample_count))
1790 			count = 0;
1791 
1792 		//if data cache is on and we're changing TRUN, store the cache and update data offset
1793 		if (!count && traf->DataCache) {
1794 			trun->data_offset = (u32)(pos - movie->moof->fragment_offset - 8);
1795 			gf_bs_get_content(trun->cache, &buffer, &buffer_size);
1796 			gf_bs_write_data(movie->editFileMap->bs, buffer, buffer_size);
1797 			gf_bs_del(trun->cache);
1798 			trun->cache = NULL;
1799 			gf_free(buffer);
1800 		}
1801 	}
1802 
1803 	//new run
1804 	if (!count) {
1805 		trun = (GF_TrackFragmentRunBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TRUN);
1806 		//store data offset (we have the 8 btyes offset of the MDAT)
1807 		trun->data_offset = (u32)(pos - movie->moof->fragment_offset - 8);
1808 		gf_list_add(traf->TrackRuns, trun);
1809 
1810 		//if we use data caching, create a bitstream
1811 		if (traf->DataCache)
1812 			trun->cache = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
1813 	}
1814 
1815 	GF_SAFEALLOC(ent, GF_TrunEntry);
1816 	if (!ent) return GF_OUT_OF_MEM;
1817 	ent->CTS_Offset = sample->CTS_Offset;
1818 	ent->Duration = Duration;
1819 	ent->size = sample->dataLength;
1820 	ent->flags = GF_ISOM_FORMAT_FRAG_FLAGS(PaddingBits, sample->IsRAP, DegradationPriority);
1821 	if (sample->IsRAP) {
1822 		ent->flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(0, 2, 0, (redundant_coding ? 1 : 0));
1823 		ent->SAP_type = sample->IsRAP;
1824 	}
1825 	gf_list_add(trun->entries, ent);
1826 
1827 	if (sample->CTS_Offset<0) {
1828 		trun->version = 1;
1829 	}
1830 	trun->sample_count += 1;
1831 
1832 	//rewrite OD frames
1833 	if (traf->trex->track->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
1834 		//this may fail if depandancies are not well done ...
1835 		Media_ParseODFrame(traf->trex->track->Media, sample, &od_sample);
1836 		sample = od_sample;
1837 	}
1838 
1839 	//finally write the data
1840 	if (sample->dataLength) {
1841 		if (!traf->DataCache) {
1842 			if (!gf_bs_write_data(movie->editFileMap->bs, sample->data, sample->dataLength)) {
1843 				GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso fragment] Could not add a sample with a size of %u bytes (no DataCache)\n", sample->dataLength));
1844 				return GF_OUT_OF_MEM;
1845 			}
1846 		}
1847 		else if (trun->cache) {
1848 			if (!gf_bs_write_data(trun->cache, sample->data, sample->dataLength)) {
1849 				GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso fragment] Could not add a sample with a size of %u bytes (with cache)\n", sample->dataLength));
1850 				return GF_OUT_OF_MEM;
1851 			}
1852 		}
1853 		else {
1854 			return GF_BAD_PARAM;
1855 		}
1856 	}
1857 	if (od_sample) gf_isom_sample_del(&od_sample);
1858 	return GF_OK;
1859 }
1860 
1861 GF_EXPORT
gf_isom_fragment_add_sai(GF_ISOFile * output,GF_ISOFile * input,u32 TrackID,u32 SampleNum)1862 GF_Err gf_isom_fragment_add_sai(GF_ISOFile *output, GF_ISOFile *input, u32 TrackID, u32 SampleNum)
1863 {
1864 	u32 trackNum;
1865 	GF_Err e = GF_OK;
1866 
1867 	trackNum = gf_isom_get_track_by_id(input, TrackID);
1868 	if (gf_isom_is_cenc_media(input, trackNum, 1)) {
1869 		GF_CENCSampleAuxInfo *sai;
1870 		GF_TrackFragmentBox  *traf = GetTraf(output, TrackID);
1871 		GF_TrackBox  *src_trak = gf_isom_get_track_from_file(input, TrackID);
1872 		u32 boxType;
1873 		GF_SampleEncryptionBox *senc;
1874 		u8 IV_size;
1875 		u32 IsEncrypted;
1876 
1877 		if (!traf)  return GF_BAD_PARAM;
1878 
1879 		sai = NULL;
1880 		gf_isom_get_sample_cenc_info(input, trackNum, SampleNum, &IsEncrypted, &IV_size, NULL, NULL, NULL, NULL, NULL);
1881 		e = gf_isom_cenc_get_sample_aux_info(input, trackNum, SampleNum, &sai, &boxType);
1882 		if (e) return e;
1883 		sai->IV_size = IV_size;
1884 
1885 		switch (boxType) {
1886 		case GF_ISOM_BOX_UUID_PSEC:
1887 			if (!traf->piff_sample_encryption) {
1888 				GF_PIFFSampleEncryptionBox *psec = (GF_PIFFSampleEncryptionBox *)src_trak->Media->information->sampleTable->piff_psec;
1889 				if (!psec) return GF_ISOM_INVALID_FILE;
1890 				traf->piff_sample_encryption = gf_isom_create_piff_psec_box(1, 0, psec->AlgorithmID, psec->IV_size, psec->KID);
1891 				traf->piff_sample_encryption->traf = traf;
1892 			}
1893 
1894 			if (!traf->piff_sample_encryption) {
1895 				return GF_IO_ERR;
1896 			}
1897 			senc = (GF_SampleEncryptionBox *)traf->piff_sample_encryption;
1898 			break;
1899 		case GF_ISOM_BOX_TYPE_SENC:
1900 			if (!traf->sample_encryption) {
1901 				traf->sample_encryption = gf_isom_create_samp_enc_box(0, 0);
1902 				traf->sample_encryption->traf = traf;
1903 			}
1904 
1905 			if (!traf->sample_encryption) {
1906 				return GF_IO_ERR;
1907 			}
1908 			senc = (GF_SampleEncryptionBox *)traf->sample_encryption;
1909 			break;
1910 		default:
1911 			return GF_NOT_SUPPORTED;
1912 		}
1913 
1914 		gf_list_add(senc->samp_aux_info, sai);
1915 		if (sai->subsample_count) senc->flags = 0x00000002;
1916 		//this assumes that we don't have a cenc sample info with subsamples indicated and subsample_count=0
1917 		gf_isom_cenc_set_saiz_saio(senc, NULL, traf, IsEncrypted ? IV_size + (2 + 6)*sai->subsample_count : 0);
1918 	}
1919 
1920 	return GF_OK;
1921 }
1922 
1923 
gf_isom_fragment_append_data(GF_ISOFile * movie,u32 TrackID,char * data,u32 data_size,u8 PaddingBits)1924 GF_Err gf_isom_fragment_append_data(GF_ISOFile *movie, u32 TrackID, char *data, u32 data_size, u8 PaddingBits)
1925 {
1926 	u32 count;
1927 	u8 rap;
1928 	u16 degp;
1929 	GF_TrunEntry *ent;
1930 	GF_TrackFragmentBox *traf;
1931 	GF_TrackFragmentRunBox *trun;
1932 	if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) return GF_BAD_PARAM;
1933 
1934 	traf = GetTraf(movie, TrackID);
1935 	if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
1936 
1937 	//add TRUN entry
1938 	count = gf_list_count(traf->TrackRuns);
1939 	if (!count) return GF_BAD_PARAM;
1940 
1941 	trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count - 1);
1942 	count = gf_list_count(trun->entries);
1943 	if (!count) return GF_BAD_PARAM;
1944 	ent = (GF_TrunEntry *)gf_list_get(trun->entries, count - 1);
1945 	ent->size += data_size;
1946 
1947 	rap = GF_ISOM_GET_FRAG_SYNC(ent->flags);
1948 	degp = GF_ISOM_GET_FRAG_DEG(ent->flags);
1949 	ent->flags = GF_ISOM_FORMAT_FRAG_FLAGS(PaddingBits, rap, degp);
1950 
1951 	//finally write the data
1952 	if (!traf->DataCache) {
1953 		gf_bs_write_data(movie->editFileMap->bs, data, data_size);
1954 	}
1955 	else if (trun->cache) {
1956 		gf_bs_write_data(trun->cache, data, data_size);
1957 	}
1958 	else {
1959 		return GF_BAD_PARAM;
1960 	}
1961 	return GF_OK;
1962 }
1963 
gf_isom_fragment_add_subsample(GF_ISOFile * movie,u32 TrackID,u32 flags,u32 subSampleSize,u8 priority,u32 reserved,Bool discardable)1964 GF_Err gf_isom_fragment_add_subsample(GF_ISOFile *movie, u32 TrackID, u32 flags, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable)
1965 {
1966 	u32 i, count, last_sample;
1967 	GF_TrackFragmentBox *traf;
1968 	GF_SubSampleInformationBox *subs = NULL;
1969 	if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) return GF_BAD_PARAM;
1970 
1971 	traf = GetTraf(movie, TrackID);
1972 	if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
1973 
1974 	/*compute last sample number in traf*/
1975 	last_sample = 0;
1976 	count = gf_list_count(traf->TrackRuns);
1977 	for (i = 0; i<count; i++) {
1978 		GF_TrackFragmentRunBox *trun = (GF_TrackFragmentRunBox*)gf_list_get(traf->TrackRuns, i);
1979 		last_sample += trun->sample_count;
1980 	}
1981 
1982 	if (!traf->sub_samples) {
1983 		traf->sub_samples = gf_list_new();
1984 	}
1985 	count = gf_list_count(traf->sub_samples);
1986 	for (i = 0; i<count; i++) {
1987 		subs = gf_list_get(traf->sub_samples, i);
1988 		if (subs->flags == flags) break;
1989 		subs = NULL;
1990 	}
1991 	if (!subs) {
1992 		subs = (GF_SubSampleInformationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SUBS);
1993 		subs->version = (subSampleSize>0xFFFF) ? 1 : 0;
1994 		subs->flags = flags;
1995 	}
1996 	return gf_isom_add_subsample_info(subs, last_sample, subSampleSize, priority, reserved, discardable);
1997 }
1998 
gf_isom_fragment_copy_subsample(GF_ISOFile * dest,u32 TrackID,GF_ISOFile * orig,u32 track,u32 sampleNumber,Bool sgpd_in_traf)1999 GF_Err gf_isom_fragment_copy_subsample(GF_ISOFile *dest, u32 TrackID, GF_ISOFile *orig, u32 track, u32 sampleNumber, Bool sgpd_in_traf)
2000 {
2001 	u32 i, count, last_sample, idx, subs_flags;
2002 	GF_SubSampleInfoEntry *sub_sample;
2003 	GF_Err e;
2004 	GF_TrackBox *trak;
2005 	GF_TrackFragmentBox *traf;
2006 	GF_TrunEntry *ent;
2007 	GF_TrackFragmentRunBox *trun;
2008 	if (!dest->moof || !(dest->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) return GF_BAD_PARAM;
2009 
2010 	traf = GetTraf(dest, TrackID);
2011 	if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
2012 
2013 	trak = gf_isom_get_track_from_file(orig, track);
2014 	if (!trak) return GF_BAD_PARAM;
2015 
2016 	/*modify depends flags*/
2017 	if (trak->Media->information->sampleTable->SampleDep) {
2018 		u32 isLeading, dependsOn, dependedOn, redundant;
2019 
2020 		isLeading = dependsOn = dependedOn = redundant = 0;
2021 		count = gf_list_count(traf->TrackRuns);
2022 		if (!count) return GF_BAD_PARAM;
2023 		trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count - 1);
2024 		count = gf_list_count(trun->entries);
2025 		if (!count) return GF_BAD_PARAM;
2026 
2027 		ent = (GF_TrunEntry *)gf_list_get(trun->entries, count - 1);
2028 		e = stbl_GetSampleDepType(trak->Media->information->sampleTable->SampleDep, sampleNumber, &isLeading, &dependsOn, &dependedOn, &redundant);
2029 		if (e) return e;
2030 
2031 		GF_ISOM_RESET_FRAG_DEPEND_FLAGS(ent->flags);
2032 		ent->flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(0, dependsOn, dependedOn, redundant);
2033 	}
2034 
2035 	/*copy subsample info if any*/
2036 	idx = 1;
2037 	while (gf_isom_get_subsample_types(orig, track, idx, &subs_flags)) {
2038 		GF_SubSampleInformationBox *subs_traf = NULL;
2039 		idx++;
2040 		if (!gf_isom_sample_get_subsample_entry(orig, track, sampleNumber, subs_flags, &sub_sample))
2041 			continue;
2042 
2043 		if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
2044 
2045 		/*compute last sample number in traf*/
2046 		last_sample = 0;
2047 		count = gf_list_count(traf->TrackRuns);
2048 		for (i = 0; i<count; i++) {
2049 			GF_TrackFragmentRunBox *trun = (GF_TrackFragmentRunBox*)gf_list_get(traf->TrackRuns, i);
2050 			last_sample += trun->sample_count;
2051 		}
2052 
2053 		/*create subsample if needed*/
2054 		if (!traf->sub_samples) {
2055 			traf->sub_samples = gf_list_new();
2056 		}
2057 		count = gf_list_count(traf->sub_samples);
2058 		for (i = 0; i<count; i++) {
2059 			subs_traf = gf_list_get(traf->sub_samples, i);
2060 			if (subs_traf->flags == subs_flags) break;
2061 			subs_traf = NULL;
2062 		}
2063 		if (!subs_traf) {
2064 			subs_traf = (GF_SubSampleInformationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SUBS);
2065 			subs_traf->version = 0;
2066 			subs_traf->flags = subs_flags;
2067 			gf_list_add(traf->sub_samples, subs_traf);
2068 		}
2069 
2070 		count = gf_list_count(sub_sample->SubSamples);
2071 		for (i = 0; i<count; i++) {
2072 			GF_SubSampleEntry *entry = (GF_SubSampleEntry*)gf_list_get(sub_sample->SubSamples, i);
2073 			e = gf_isom_add_subsample_info(subs_traf, last_sample, entry->subsample_size, entry->subsample_priority, entry->reserved, entry->discardable);
2074 			if (e) return e;
2075 		}
2076 	}
2077 	/*copy sampleToGroup info if any*/
2078 	if (trak->Media->information->sampleTable->sampleGroups) {
2079 		count = gf_list_count(trak->Media->information->sampleTable->sampleGroups);
2080 		for (i = 0; i<count; i++) {
2081 			GF_SampleGroupBox *sg;
2082 			u32 j;
2083 			u32 first_sample_in_entry, last_sample_in_entry;
2084 			first_sample_in_entry = 1;
2085 
2086 			sg = (GF_SampleGroupBox*)gf_list_get(trak->Media->information->sampleTable->sampleGroups, i);
2087 			for (j = 0; j<sg->entry_count; j++) {
2088 				last_sample_in_entry = first_sample_in_entry + sg->sample_entries[j].sample_count - 1;
2089 				if ((sampleNumber<first_sample_in_entry) || (sampleNumber>last_sample_in_entry)) {
2090 					first_sample_in_entry = last_sample_in_entry + 1;
2091 					continue;
2092 				}
2093 
2094 				if (!traf->sampleGroups)
2095 					traf->sampleGroups = gf_list_new();
2096 
2097 				/*found our sample, add it to trak->sampleGroups*/
2098 				e = gf_isom_copy_sample_group_entry_to_traf(traf, trak->Media->information->sampleTable, sg->grouping_type, sg->grouping_type_parameter, sg->sample_entries[j].group_description_index, sgpd_in_traf);
2099 				if (e) return e;
2100 
2101 				break;
2102 			}
2103 		}
2104 	}
2105 	return GF_OK;
2106 }
2107 
2108 
2109 #endif	/*GPAC_DISABLE_ISOM_WRITE*/
2110 
2111 GF_EXPORT
gf_isom_is_track_fragmented(GF_ISOFile * movie,u32 TrackID)2112 u32 gf_isom_is_track_fragmented(GF_ISOFile *movie, u32 TrackID)
2113 {
2114 	if (!movie || !movie->moov || !movie->moov->mvex) return 0;
2115 	return (GetTrex(movie->moov, TrackID) != NULL) ? 1 : 0;
2116 }
2117 
2118 GF_EXPORT
gf_isom_is_fragmented(GF_ISOFile * movie)2119 u32 gf_isom_is_fragmented(GF_ISOFile *movie)
2120 {
2121 	if (!movie || !movie->moov) return 0;
2122 	/* By default if the Moov has an mvex, the file is fragmented */
2123 	if (movie->moov->mvex) return 1;
2124 	/* deprecated old code */
2125 	/*only check moof number (in read mode, mvex can be deleted on the fly)*/
2126 	//return movie->NextMoofNumber ? 1 : 0;
2127 	return 0;
2128 }
2129 
2130 GF_EXPORT
gf_isom_set_traf_base_media_decode_time(GF_ISOFile * movie,u32 TrackID,u64 decode_time)2131 GF_Err gf_isom_set_traf_base_media_decode_time(GF_ISOFile *movie, u32 TrackID, u64 decode_time)
2132 {
2133 	GF_TrackFragmentBox *traf;
2134 	if (!movie || !movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) return GF_BAD_PARAM;
2135 
2136 	traf = GetTraf(movie, TrackID);
2137 	if (!traf) return GF_BAD_PARAM;
2138 
2139 	if (!traf->tfdt) {
2140 		traf->tfdt = (GF_TFBaseMediaDecodeTimeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TFDT);
2141 		if (!traf->tfdt) return GF_OUT_OF_MEM;
2142 	}
2143 	traf->tfdt->baseMediaDecodeTime = decode_time;
2144 	return GF_OK;
2145 }
2146 
2147 #else
2148 
gf_isom_finalize_for_fragment(GF_ISOFile * the_file,u32 media_segment_type)2149 GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *the_file, u32 media_segment_type)
2150 {
2151 	return GF_NOT_SUPPORTED;
2152 }
2153 
gf_isom_setup_track_fragment(GF_ISOFile * the_file,u32 TrackID,u32 DefaultSampleDescriptionIndex,u32 DefaultSampleDuration,u32 DefaultSampleSize,u8 DefaultSampleIsSync,u8 DefaultSamplePadding,u16 DefaultDegradationPriority)2154 GF_Err gf_isom_setup_track_fragment(GF_ISOFile *the_file, u32 TrackID,
2155 	u32 DefaultSampleDescriptionIndex,
2156 	u32 DefaultSampleDuration,
2157 	u32 DefaultSampleSize,
2158 	u8 DefaultSampleIsSync,
2159 	u8 DefaultSamplePadding,
2160 	u16 DefaultDegradationPriority)
2161 {
2162 	return GF_NOT_SUPPORTED;
2163 }
2164 
gf_isom_set_fragment_option(GF_ISOFile * the_file,u32 TrackID,u32 Code,u32 Param)2165 GF_Err gf_isom_set_fragment_option(GF_ISOFile *the_file, u32 TrackID, u32 Code, u32 Param)
2166 {
2167 	return GF_NOT_SUPPORTED;
2168 }
2169 
gf_isom_start_fragment(GF_ISOFile * the_file,u32 free_data_insert_size)2170 GF_Err gf_isom_start_fragment(GF_ISOFile *the_file, u32 free_data_insert_size)
2171 {
2172 	return GF_NOT_SUPPORTED;
2173 }
2174 
gf_isom_fragment_add_sample(GF_ISOFile * the_file,u32 TrackID,const GF_ISOSample * sample,u32 DescIndex,u32 Duration,u8 PaddingBits,u16 DegradationPriority,Bool redCoded)2175 GF_Err gf_isom_fragment_add_sample(GF_ISOFile *the_file, u32 TrackID, const GF_ISOSample *sample, u32 DescIndex,
2176 	u32 Duration, u8 PaddingBits, u16 DegradationPriority, Bool redCoded)
2177 {
2178 	return GF_NOT_SUPPORTED;
2179 }
2180 
2181 
2182 GF_EXPORT
gf_isom_is_track_fragmented(GF_ISOFile * the_file,u32 TrackID)2183 u32 gf_isom_is_track_fragmented(GF_ISOFile *the_file, u32 TrackID)
2184 {
2185 	return 0;
2186 }
2187 
2188 GF_EXPORT
gf_isom_is_fragmented(GF_ISOFile * the_file)2189 u32 gf_isom_is_fragmented(GF_ISOFile *the_file)
2190 {
2191 	return 0;
2192 }
2193 
gf_isom_fragment_add_subsample(GF_ISOFile * movie,u32 TrackID,u32 flags,u32 subSampleSize,u8 priority,u32 reserved,Bool discardable)2194 GF_Err gf_isom_fragment_add_subsample(GF_ISOFile *movie, u32 TrackID, u32 flags, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable)
2195 {
2196 	return GF_NOT_SUPPORTED;
2197 }
2198 
gf_isom_fragment_copy_subsample(GF_ISOFile * dest,u32 TrackID,GF_ISOFile * orig,u32 track,u32 sampleNumber,Bool sgpd_in_traf)2199 GF_Err gf_isom_fragment_copy_subsample(GF_ISOFile *dest, u32 TrackID, GF_ISOFile *orig, u32 track, u32 sampleNumber, Bool sgpd_in_traf)
2200 {
2201 	return GF_NOT_SUPPORTED;
2202 }
2203 
gf_isom_set_traf_base_media_decode_time(GF_ISOFile * movie,u32 TrackID,u64 decode_time)2204 GF_Err gf_isom_set_traf_base_media_decode_time(GF_ISOFile *movie, u32 TrackID, u64 decode_time)
2205 {
2206 	return GF_NOT_SUPPORTED;
2207 }
2208 
gf_isom_set_traf_mss_timeext(GF_ISOFile * movie,u32 reference_track_ID,u64 ntp_in_10mhz,u64 traf_duration_in_10mhz)2209 GF_Err gf_isom_set_traf_mss_timeext(GF_ISOFile *movie, u32 reference_track_ID, u64 ntp_in_10mhz, u64 traf_duration_in_10mhz)
2210 {
2211 	return GF_NOT_SUPPORTED;
2212 }
2213 
2214 #endif /*GPAC_DISABLE_ISOM_FRAGMENTS)*/
2215 
2216 
2217 GF_EXPORT
gf_isom_set_next_moof_number(GF_ISOFile * movie,u32 value)2218 void gf_isom_set_next_moof_number(GF_ISOFile *movie, u32 value)
2219 {
2220 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
2221 	if (movie) movie->NextMoofNumber = value;
2222 #endif
2223 }
2224 
2225 GF_EXPORT
gf_isom_get_next_moof_number(GF_ISOFile * movie)2226 u32 gf_isom_get_next_moof_number(GF_ISOFile *movie)
2227 {
2228 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
2229 	if (movie) return movie->NextMoofNumber;
2230 #endif
2231 	return 0;
2232 }
2233 
2234 #endif /*GPAC_DISABLE_ISOM*/
2235