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