Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2021
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 :
32 864 : GF_TrackExtendsBox *GetTrex(GF_MovieBox *moov, GF_ISOTrackID TrackID)
33 : {
34 : u32 i;
35 : GF_TrackExtendsBox *trex;
36 864 : i=0;
37 2064 : while ((trex = (GF_TrackExtendsBox *)gf_list_enum(moov->mvex->TrackExList, &i))) {
38 809 : if (trex->trackID == TrackID) return trex;
39 : }
40 : return NULL;
41 : }
42 :
43 :
44 151318 : GF_TrackFragmentBox *gf_isom_get_traf(GF_ISOFile *mov, GF_ISOTrackID TrackID)
45 : {
46 : u32 i;
47 151318 : if (!mov->moof) return NULL;
48 :
49 : //reverse browse the TRAFs, as there may be more than one per track ...
50 330156 : for (i=gf_list_count(mov->moof->TrackList); i>0; i--) {
51 178838 : GF_TrackFragmentBox *traf = (GF_TrackFragmentBox *)gf_list_get(mov->moof->TrackList, i-1);
52 178838 : if (traf->tfhd->trackID == TrackID) return traf;
53 : }
54 : return NULL;
55 : }
56 :
57 :
58 : #ifndef GPAC_DISABLE_ISOM_WRITE
59 327 : GF_Err gf_isom_set_movie_duration(GF_ISOFile *movie, u64 duration, Bool remove_mehd)
60 : {
61 327 : if (!movie || !movie->moov || !movie->moov->mvex) return GF_BAD_PARAM;
62 :
63 327 : if (remove_mehd) {
64 0 : if (!movie->moov->mvex->mehd) {
65 0 : gf_isom_box_del_parent(&movie->moov->mvex->child_boxes, (GF_Box*)movie->moov->mvex->mehd);
66 0 : movie->moov->mvex->mehd = NULL;
67 : }
68 : } else {
69 327 : if (!movie->moov->mvex->mehd) {
70 0 : movie->moov->mvex->mehd = (GF_MovieExtendsHeaderBox *) gf_isom_box_new_parent(&movie->moov->mvex->child_boxes, GF_ISOM_BOX_TYPE_MEHD);
71 0 : if (!movie->moov->mvex->mehd) return GF_OUT_OF_MEM;
72 : }
73 327 : movie->moov->mvex->mehd->fragment_duration = duration;
74 : }
75 327 : movie->moov->mvhd->duration = 0;
76 327 : return GF_OK;
77 : }
78 :
79 :
80 : GF_EXPORT
81 339 : GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *movie, u32 media_segment_type, Bool mvex_after_tracks)
82 : {
83 : GF_Err e;
84 : u32 i;
85 : Bool store_file = GF_TRUE;
86 : GF_TrackExtendsBox *trex;
87 339 : if (!movie || !movie->moov) return GF_BAD_PARAM;
88 :
89 : #if 0
90 : if (movie->openMode==GF_ISOM_OPEN_CAT_FRAGMENTS) {
91 : /*from now on we are in write mode*/
92 : movie->openMode = GF_ISOM_OPEN_WRITE;
93 : store_file = GF_FALSE;
94 : movie->append_segment = GF_TRUE;
95 : } else
96 : #endif
97 : {
98 339 : movie->NextMoofNumber = 1;
99 : }
100 339 : movie->moov->mvex_after_traks = mvex_after_tracks;
101 :
102 : //this is only allowed in write mode
103 339 : if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
104 :
105 339 : if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_OK;
106 339 : movie->FragmentsFlags = 0;
107 :
108 : if (store_file) {
109 : /* add DASH brand if requested*/
110 339 : if (media_segment_type)
111 326 : gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_DASH, GF_TRUE);
112 :
113 339 : if (!movie->moov->mvex->mehd || !movie->moov->mvex->mehd->fragment_duration) {
114 : //update durations
115 12 : gf_isom_get_duration(movie);
116 : }
117 :
118 339 : i=0;
119 1067 : while ((trex = (GF_TrackExtendsBox *)gf_list_enum(movie->moov->mvex->TrackExList, &i))) {
120 389 : if (trex->type != GF_ISOM_BOX_TYPE_TREX) continue;
121 389 : if (trex->track->Media->information->sampleTable->CompositionToDecode) {
122 0 : u32 k=0;
123 : GF_TrackExtensionPropertiesBox *trep;
124 0 : while ((trep = (GF_TrackExtensionPropertiesBox*) gf_list_enum(movie->moov->mvex->TrackExPropList, &k))) {
125 0 : if (trep->trackID == trex->trackID) break;
126 : }
127 :
128 0 : if (!trep) {
129 0 : trep = (GF_TrackExtensionPropertiesBox*) gf_isom_box_new_parent(&movie->moov->mvex->child_boxes, GF_ISOM_BOX_TYPE_TREP);
130 0 : if (!trep) return GF_OUT_OF_MEM;
131 0 : trep->trackID = trex->trackID;
132 0 : gf_list_add(movie->moov->mvex->TrackExPropList, trep);
133 : }
134 :
135 0 : if (!trex->track->Media->information->sampleTable->SampleSize || ! trex->track->Media->information->sampleTable->SampleSize->sampleCount) {
136 0 : gf_list_add(trep->child_boxes, trex->track->Media->information->sampleTable->CompositionToDecode);
137 0 : trex->track->Media->information->sampleTable->CompositionToDecode = NULL;
138 : } else {
139 : GF_CompositionToDecodeBox *cslg;
140 :
141 : //clone it!
142 0 : GF_SAFEALLOC(cslg, GF_CompositionToDecodeBox);
143 0 : if (!cslg) return GF_OUT_OF_MEM;
144 0 : memcpy(cslg, trex->track->Media->information->sampleTable->CompositionToDecode, sizeof(GF_CompositionToDecodeBox) );
145 0 : cslg->child_boxes = gf_list_new();
146 0 : gf_list_add(trep->child_boxes, trex->track->Media->information->sampleTable->CompositionToDecode);
147 : }
148 : }
149 :
150 389 : if (movie->moov->mvex->mehd && movie->moov->mvex->mehd->fragment_duration) {
151 377 : trex->track->Header->duration = 0;
152 377 : Media_SetDuration(trex->track);
153 377 : if (trex->track->editBox && trex->track->editBox->editList) {
154 59 : GF_EdtsEntry *edts = gf_list_last(trex->track->editBox->editList->entryList);
155 59 : edts->segmentDuration = 0;
156 : }
157 : }
158 : }
159 :
160 : //write movie
161 339 : e = WriteToFile(movie, GF_TRUE);
162 339 : if (e) return e;
163 :
164 339 : if (movie->on_block_out) {
165 339 : gf_bs_seek(movie->editFileMap->bs, 0);
166 339 : gf_bs_truncate(movie->editFileMap->bs);
167 : }
168 : }
169 :
170 : //make sure we do have all we need. If not this is not an error, just consider
171 : //the file closed
172 339 : if (!movie->moov->mvex || !gf_list_count(movie->moov->mvex->TrackExList)) return GF_OK;
173 :
174 339 : i=0;
175 1067 : while ((trex = (GF_TrackExtendsBox *)gf_list_enum(movie->moov->mvex->TrackExList, &i))) {
176 389 : if (!trex->trackID || !gf_isom_get_track_from_id(movie->moov, trex->trackID)) return GF_IO_ERR;
177 : //we could also check all our data refs are local but we'll do that at run time
178 : //in order to allow a mix of both (remote refs in MOOV and local in MVEX)
179 :
180 : //one thing that MUST be done is OD cross-dependencies. The movie fragment spec
181 : //is broken here, since it cannot allow dynamic insertion of new ESD and their
182 : //dependancies
183 : }
184 :
185 : //ok we are fine - note the data map is created at the beginning
186 339 : if (i) movie->FragmentsFlags |= GF_ISOM_FRAG_WRITE_READY;
187 :
188 339 : if (media_segment_type) {
189 326 : movie->use_segments = GF_TRUE;
190 326 : movie->moof_list = gf_list_new();
191 13 : } else if (movie->on_block_out) {
192 13 : movie->moof_list = gf_list_new();
193 : }
194 :
195 : /*set brands for segment*/
196 :
197 : /*"msdh": it's a media segment */
198 339 : gf_isom_set_brand_info(movie, GF_ISOM_BRAND_MSDH, 0);
199 : /*remove all brands */
200 339 : gf_isom_reset_alt_brands(movie);
201 : /*
202 : msdh: it's a media segment
203 : sims: it's a media segment with an SSIX
204 : msix: it's a media segment with an index
205 : lmsg: it's the last media segment
206 : */
207 :
208 339 : return GF_OK;
209 : }
210 :
211 389 : GF_Err gf_isom_change_track_fragment_defaults(GF_ISOFile *movie, GF_ISOTrackID TrackID,
212 : u32 DefaultSampleDescriptionIndex,
213 : u32 DefaultSampleDuration,
214 : u32 DefaultSampleSize,
215 : u8 DefaultSampleIsSync,
216 : u8 DefaultSamplePadding,
217 : u16 DefaultDegradationPriority,
218 : u8 force_traf_flags)
219 : {
220 : GF_MovieExtendsBox *mvex;
221 : GF_TrackExtendsBox *trex;
222 : GF_TrackBox *trak;
223 :
224 389 : if (!movie || !movie->moov) return GF_BAD_PARAM;
225 : //this is only allowed in write mode
226 389 : if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
227 :
228 389 : trak = gf_isom_get_track_from_id(movie->moov, TrackID);
229 389 : if (!trak) return GF_BAD_PARAM;
230 :
231 389 : mvex = movie->moov->mvex;
232 389 : if (!mvex) return GF_BAD_PARAM;
233 :
234 389 : trex = GetTrex(movie->moov, TrackID);
235 389 : if (!trex) return GF_BAD_PARAM;
236 :
237 389 : trex->def_sample_desc_index = DefaultSampleDescriptionIndex;
238 389 : trex->def_sample_duration = DefaultSampleDuration;
239 389 : trex->def_sample_size = DefaultSampleSize;
240 389 : trex->def_sample_flags = GF_ISOM_FORMAT_FRAG_FLAGS(DefaultSamplePadding, DefaultSampleIsSync, DefaultDegradationPriority);
241 : //if sample is sync by default, set sample_depends_on flags to 2 (does not depend on other samples)
242 389 : if (DefaultSampleIsSync) {
243 89 : trex->def_sample_flags |= (2<<24);
244 : }
245 389 : trex->cannot_use_default = GF_FALSE;
246 :
247 389 : if (force_traf_flags) {
248 11 : trex->cannot_use_default = GF_TRUE;
249 378 : } else if (DefaultSampleDescriptionIndex == 0 && DefaultSampleDuration == 0 && DefaultSampleSize == 0
250 0 : && DefaultSampleIsSync == 0 && DefaultSamplePadding == 0 && DefaultDegradationPriority == 0) {
251 0 : trex->cannot_use_default = GF_TRUE;
252 : }
253 : return GF_OK;
254 : }
255 :
256 : GF_EXPORT
257 389 : GF_Err gf_isom_setup_track_fragment(GF_ISOFile *movie, GF_ISOTrackID TrackID,
258 : u32 DefaultSampleDescriptionIndex,
259 : u32 DefaultSampleDuration,
260 : u32 DefaultSampleSize,
261 : u8 DefaultSampleIsSync,
262 : u8 DefaultSamplePadding,
263 : u16 DefaultDegradationPriority,
264 : Bool force_traf_flags)
265 : {
266 : GF_MovieExtendsBox *mvex;
267 : GF_TrackExtendsBox *trex;
268 : GF_TrackBox *trak;
269 :
270 389 : if (!movie || !movie->moov) return GF_BAD_PARAM;
271 : //this is only allowed in write mode
272 389 : if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
273 : //and only at setup
274 389 : if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_BAD_PARAM;
275 :
276 :
277 389 : trak = gf_isom_get_track_from_id(movie->moov, TrackID);
278 389 : if (!trak) return GF_BAD_PARAM;
279 :
280 : //create MVEX if needed
281 389 : if (!movie->moov->mvex) {
282 339 : mvex = (GF_MovieExtendsBox *) gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_MVEX);
283 339 : if (!mvex) return GF_OUT_OF_MEM;
284 339 : moov_on_child_box((GF_Box*)movie->moov, (GF_Box *) mvex, GF_FALSE);
285 : } else {
286 : mvex = movie->moov->mvex;
287 : }
288 389 : if (!mvex->mehd) {
289 339 : mvex->mehd = (GF_MovieExtendsHeaderBox *) gf_isom_box_new_parent(&mvex->child_boxes, GF_ISOM_BOX_TYPE_MEHD);
290 339 : if (!mvex->mehd) return GF_OUT_OF_MEM;
291 : }
292 :
293 389 : trex = GetTrex(movie->moov, TrackID);
294 389 : if (!trex) {
295 389 : trex = (GF_TrackExtendsBox *) gf_isom_box_new_parent(&mvex->child_boxes, GF_ISOM_BOX_TYPE_TREX);
296 389 : if (!trex) return GF_OUT_OF_MEM;
297 389 : trex->trackID = TrackID;
298 389 : mvex_on_child_box((GF_Box*)mvex, (GF_Box *) trex, GF_FALSE);
299 : }
300 389 : trex->track = trak;
301 389 : return gf_isom_change_track_fragment_defaults(movie, TrackID, DefaultSampleDescriptionIndex, DefaultSampleDuration, DefaultSampleSize, DefaultSampleIsSync, DefaultSamplePadding, DefaultDegradationPriority, force_traf_flags);
302 : }
303 :
304 : #ifdef GF_ENABLE_CTRN
305 : GF_EXPORT
306 : GF_Err gf_isom_enable_traf_inherit(GF_ISOFile *movie, GF_ISOTrackID TrackID, GF_ISOTrackID BaseTrackID)
307 : {
308 : GF_TrackBox *trak;
309 : GF_TrackExtendsBox *trex;
310 : GF_Err e=GF_OK;
311 : u32 track_num;
312 : if (!movie || !TrackID || !BaseTrackID)
313 : return GF_BAD_PARAM;
314 : trak = gf_isom_get_track_from_id(movie->moov, TrackID);
315 : if (!trak) return GF_BAD_PARAM;
316 : track_num = 1 + gf_list_find(movie->moov->trackList, trak);
317 :
318 : e = gf_isom_set_track_reference(movie, track_num, GF_ISOM_REF_TRIN, BaseTrackID);
319 : if (e) return e;
320 :
321 : trex = GetTrex(movie->moov, TrackID);
322 : if (!trex) return GF_BAD_PARAM;
323 : trex->inherit_from_traf_id = BaseTrackID;
324 : return GF_OK;
325 : }
326 : #endif
327 :
328 : GF_EXPORT
329 5 : GF_Err gf_isom_setup_track_fragment_template(GF_ISOFile *movie, GF_ISOTrackID TrackID, u8 *boxes, u32 boxes_size, u8 force_traf_flags)
330 : {
331 : GF_MovieExtendsBox *mvex;
332 : GF_TrackBox *trak;
333 : GF_BitStream *bs;
334 : GF_Err e=GF_OK;
335 5 : trak = gf_isom_get_track_from_id(movie->moov, TrackID);
336 5 : if (!trak) return GF_BAD_PARAM;
337 :
338 5 : bs = gf_bs_new(boxes, boxes_size, GF_BITSTREAM_READ);
339 15 : while (gf_bs_available(bs)) {
340 5 : GF_Box *box=NULL;
341 5 : gf_isom_box_parse(&box, bs);
342 5 : if (!box) {
343 : e = GF_BAD_PARAM;
344 0 : break;
345 : }
346 :
347 5 : if (box->type==GF_ISOM_BOX_TYPE_TREX) {
348 : GF_TrackExtendsBox *trex_o=NULL;
349 : GF_TrackExtendsBox *trex = (GF_TrackExtendsBox *) box;
350 :
351 : //create MVEX if needed
352 5 : if (!movie->moov->mvex) {
353 2 : mvex = (GF_MovieExtendsBox *) gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_MVEX);
354 2 : moov_on_child_box((GF_Box*)movie->moov, (GF_Box *) mvex, GF_FALSE);
355 : } else {
356 : mvex = movie->moov->mvex;
357 : }
358 5 : if (!mvex->mehd) {
359 2 : mvex->mehd = (GF_MovieExtendsHeaderBox *) gf_isom_box_new_parent(&mvex->child_boxes, GF_ISOM_BOX_TYPE_MEHD);
360 : }
361 :
362 5 : trex_o = GetTrex(movie->moov, TrackID);
363 5 : if (trex_o) {
364 3 : gf_list_del_item(movie->moov->mvex->TrackExList, trex_o);
365 3 : gf_isom_box_del_parent(&movie->moov->mvex->child_boxes, (GF_Box *)trex_o);
366 : }
367 5 : trex->trackID = TrackID;
368 5 : trex->track = trak;
369 5 : if (force_traf_flags) trex->cannot_use_default = GF_TRUE;
370 5 : gf_list_add(mvex->child_boxes, trex);
371 5 : mvex_on_child_box((GF_Box*)mvex, (GF_Box *) trex, GF_FALSE);
372 : }
373 : }
374 5 : gf_bs_del(bs);
375 5 : return e;
376 : }
377 :
378 :
379 232991 : u32 GetNumUsedValues(GF_TrackFragmentBox *traf, u32 value, u32 index)
380 : {
381 : u32 i, j, NumValue = 0;
382 : GF_TrackFragmentRunBox *trun;
383 :
384 232991 : i=0;
385 699393 : while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
386 12021319 : for (j=0; j<trun->nb_samples; j++) {
387 12021319 : GF_TrunEntry *ent = &trun->samples[j];
388 12021319 : switch (index) {
389 5848823 : case 1:
390 5848823 : if (value == ent->Duration) NumValue ++;
391 : break;
392 292914 : case 2:
393 292914 : if (value == ent->size) NumValue ++;
394 : break;
395 5879582 : case 3:
396 5879582 : if (value == ent->flags) NumValue ++;
397 : break;
398 : }
399 : }
400 : }
401 232991 : return NumValue;
402 : }
403 :
404 :
405 4584 : void ComputeFragmentDefaults(GF_TrackFragmentBox *traf)
406 : {
407 : u32 i, j, MaxNum, DefValue, ret;
408 : GF_TrackFragmentRunBox *trun;
409 : GF_TrunEntry *ent;
410 :
411 : //Duration default
412 : MaxNum = DefValue = 0;
413 4584 : i=0;
414 12920 : while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
415 111103 : for (j=0; j<trun->nb_samples; j++) {
416 111126 : ent = &trun->samples[j];
417 111126 : ret = GetNumUsedValues(traf, ent->Duration, 1);
418 111126 : if (ret>MaxNum) {
419 : //at least 2 duration, specify for all
420 3787 : if (MaxNum) {
421 : DefValue = 0;
422 : goto escape_duration;
423 : }
424 : MaxNum = ret;
425 3764 : DefValue = ent->Duration;
426 : }
427 : }
428 : }
429 4561 : escape_duration:
430 : //store if #
431 4584 : if (DefValue && ((DefValue != traf->trex->def_sample_duration) || traf->trex->cannot_use_default ) ) {
432 444 : traf->tfhd->def_sample_duration = DefValue;
433 : }
434 :
435 : //Size default
436 : MaxNum = DefValue = 0;
437 4584 : i=0;
438 9347 : while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
439 6366 : for (j=0; j<trun->nb_samples; j++) {
440 : u32 ssize;
441 9951 : ent = &trun->samples[j];
442 9951 : ssize = ent->size;
443 9951 : if (ent->nb_pack>1)
444 0 : ssize /= ent->nb_pack;
445 9951 : ret = GetNumUsedValues(traf, ssize, 2);
446 9951 : if (ret>MaxNum || (ret==1)) {
447 : //at least 2 sizes so we must specify all sizes
448 7349 : if (MaxNum) {
449 : DefValue = 0;
450 : goto escape_size;
451 : }
452 : MaxNum = ret;
453 : DefValue = ssize;
454 : }
455 : }
456 : }
457 :
458 999 : escape_size:
459 : //store if #
460 4584 : if (DefValue && (DefValue != traf->trex->def_sample_size)) {
461 179 : traf->tfhd->def_sample_size = DefValue;
462 : }
463 :
464 : //Flags default
465 : MaxNum = DefValue = 0;
466 4584 : i=0;
467 12943 : while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
468 111914 : for (j=0; j<trun->nb_samples; j++) {
469 111914 : ent = &trun->samples[j];
470 111914 : ret = GetNumUsedValues(traf, ent->flags, 3);
471 111914 : if (ret>MaxNum) {
472 : MaxNum = ret;
473 6233 : DefValue = ent->flags;
474 : }
475 : }
476 : }
477 : //store if #
478 4584 : if (traf->trex->cannot_use_default || (DefValue && (DefValue != traf->trex->def_sample_flags))) {
479 394 : traf->tfhd->def_sample_flags = DefValue;
480 : }
481 4584 : }
482 :
483 : GF_EXPORT
484 472 : GF_Err gf_isom_set_fragment_option(GF_ISOFile *movie, GF_ISOTrackID TrackID, GF_ISOTrackFragmentOption Code, u32 Param)
485 : {
486 : GF_TrackFragmentBox *traf;
487 472 : if (!movie || !movie->moov) return GF_BAD_PARAM;
488 : //this is only allowed in write mode
489 472 : if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
490 :
491 472 : switch (Code) {
492 0 : case GF_ISOM_TRAF_EMPTY:
493 0 : traf = gf_isom_get_traf(movie, TrackID);
494 0 : if (!traf) return GF_BAD_PARAM;
495 0 : traf->tfhd->EmptyDuration = Param;
496 0 : break;
497 137 : case GF_ISOM_TRAF_RANDOM_ACCESS:
498 137 : traf = gf_isom_get_traf(movie, TrackID);
499 137 : if (!traf) return GF_BAD_PARAM;
500 137 : traf->IFrameSwitching = Param;
501 137 : break;
502 0 : case GF_ISOM_TRAF_DATA_CACHE:
503 0 : traf = gf_isom_get_traf(movie, TrackID);
504 0 : if (!traf) return GF_BAD_PARAM;
505 : //don't cache only one sample ...
506 0 : traf->DataCache = Param > 1 ? Param : 0;
507 0 : break;
508 335 : case GF_ISOM_TFHD_FORCE_MOOF_BASE_OFFSET:
509 335 : movie->force_moof_base_offset = Param;
510 335 : break;
511 0 : case GF_ISOM_TRAF_USE_SAMPLE_DEPS_BOX:
512 0 : traf = gf_isom_get_traf(movie, TrackID);
513 0 : if (!traf) return GF_BAD_PARAM;
514 0 : traf->use_sdtp = (u8) Param;
515 0 : break;
516 0 : case GF_ISOM_TRUN_FORCE:
517 0 : traf = gf_isom_get_traf(movie, TrackID);
518 0 : if (!traf) return GF_BAD_PARAM;
519 0 : traf->force_new_trun = 1;
520 0 : break;
521 0 : case GF_ISOM_TRUN_SET_INTERLEAVE_ID:
522 0 : traf = gf_isom_get_traf(movie, TrackID);
523 0 : if (!traf) return GF_BAD_PARAM;
524 0 : traf->DataCache = 1;
525 0 : traf->use_sample_interleave = 1;
526 0 : if (traf->interleave_id != Param) {
527 0 : traf->force_new_trun = 1;
528 0 : traf->interleave_id = Param;
529 : }
530 : break;
531 0 : case GF_ISOM_TRAF_TRUNS_FIRST:
532 0 : traf = gf_isom_get_traf(movie, TrackID);
533 0 : if (!traf) return GF_BAD_PARAM;
534 0 : traf->truns_first = Param;
535 0 : break;
536 0 : case GF_ISOM_TRAF_TRUN_V1:
537 0 : traf = gf_isom_get_traf(movie, TrackID);
538 0 : if (!traf) return GF_BAD_PARAM;
539 0 : traf->truns_v1 = Param;
540 0 : break;
541 0 : case GF_ISOM_TRAF_USE_LARGE_TFDT:
542 0 : traf = gf_isom_get_traf(movie, TrackID);
543 0 : if (!traf) return GF_BAD_PARAM;
544 0 : traf->large_tfdt = Param;
545 0 : movie->force_sidx_v1 = Param ? GF_TRUE : GF_FALSE;
546 0 : break;
547 : }
548 : return GF_OK;
549 : }
550 :
551 : //#define USE_BASE_DATA_OFFSET
552 :
553 7028 : void update_trun_offsets(GF_ISOFile *movie, s32 offset)
554 : {
555 : #ifndef USE_BASE_DATA_OFFSET
556 : u32 i, j;
557 : GF_TrackFragmentBox *traf;
558 7028 : i=0;
559 22207 : while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
560 : GF_TrackFragmentRunBox *trun;
561 : /*remove base data*/
562 8151 : traf->tfhd->base_data_offset = 0;
563 8151 : j=0;
564 23654 : while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &j))) {
565 7352 : if ((j==1) || traf->use_sample_interleave) {
566 7334 : trun->data_offset += offset;
567 : } else {
568 18 : trun->data_offset = 0;
569 : }
570 : }
571 : }
572 : #endif
573 7028 : }
574 :
575 : static
576 4584 : u32 UpdateRuns(GF_ISOFile *movie, GF_TrackFragmentBox *traf)
577 : {
578 : u32 sampleCount, i, j, RunSize, RunDur, RunFlags, NeedFlags, UseCTS;
579 : /* enum:
580 : 0 - use values per sample in the trun box
581 : 1 - use default values from track fragment header
582 : 2 - use default values from track extends header */
583 : u32 UseDefaultSize, UseDefaultDur, UseDefaultFlag;
584 : GF_TrackFragmentRunBox *trun;
585 : GF_TrunEntry *ent;
586 :
587 : sampleCount = 0;
588 :
589 : #ifndef USE_BASE_DATA_OFFSET
590 4584 : if (movie->use_segments) {
591 4436 : traf->tfhd->flags = GF_ISOM_MOOF_BASE_OFFSET;
592 : } else
593 : #endif
594 : {
595 148 : if (movie->force_moof_base_offset) {
596 48 : traf->tfhd->flags = GF_ISOM_MOOF_BASE_OFFSET;
597 : } else {
598 100 : traf->tfhd->flags = GF_ISOM_TRAF_BASE_OFFSET;
599 : }
600 : }
601 :
602 : //empty runs
603 4584 : if (traf->tfhd->EmptyDuration) {
604 0 : while (gf_list_count(traf->TrackRuns)) {
605 0 : trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, 0);
606 0 : gf_list_rem(traf->TrackRuns, 0);
607 0 : gf_isom_box_del_parent(&traf->child_boxes, (GF_Box *)trun);
608 : }
609 0 : traf->tfhd->flags |= GF_ISOM_TRAF_DUR_EMPTY;
610 0 : if (traf->tfhd->EmptyDuration != traf->trex->def_sample_duration) {
611 0 : traf->tfhd->def_sample_duration = traf->tfhd->EmptyDuration;
612 0 : traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DUR;
613 : }
614 : return 0;
615 : }
616 :
617 :
618 : UseDefaultSize = 0;
619 : UseDefaultDur = 0;
620 : UseDefaultFlag = 0;
621 :
622 4584 : i=0;
623 8359 : while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
624 : GF_TrunEntry *first_ent = NULL;
625 : RunSize = 0;
626 : RunDur = 0;
627 : RunFlags = 0;
628 : UseCTS = 0;
629 : NeedFlags = 0;
630 :
631 : //process all samples in run
632 111914 : for (j=0; j<trun->nb_samples; j++) {
633 111914 : ent = &trun->samples[j];
634 111914 : if (!j) {
635 : first_ent = ent;
636 3775 : RunSize = ent->size;
637 3775 : if (ent->nb_pack) RunSize /= ent->nb_pack;
638 3775 : RunDur = ent->Duration;
639 : }
640 : //we may have one entry only ...
641 111914 : if (j || (trun->nb_samples==1)) {
642 108209 : u32 ssize = ent->size;
643 108209 : if (ent->nb_pack) ssize /= ent->nb_pack;
644 :
645 : //flags are only after first entry
646 108209 : if (j==1 || (trun->nb_samples==1) ) RunFlags = ent->flags;
647 :
648 108209 : if (ssize != RunSize) RunSize = 0;
649 108209 : if (ent->Duration != RunDur)
650 : RunDur = 0;
651 108209 : if (j && (RunFlags != ent->flags)) NeedFlags = 1;
652 : }
653 111914 : if (ent->CTS_Offset) UseCTS = 1;
654 : }
655 : //empty list
656 3775 : if (!first_ent) {
657 0 : i--;
658 0 : gf_list_rem(traf->TrackRuns, i);
659 0 : continue;
660 : }
661 3775 : trun->flags = 0;
662 :
663 : //size checking
664 : //constant size, check if this is from current fragment default or global default
665 3775 : if (RunSize && (traf->trex->def_sample_size == RunSize) && !traf->trex->cannot_use_default) {
666 0 : if (!UseDefaultSize) UseDefaultSize = 2;
667 0 : else if (UseDefaultSize==1) RunSize = 0;
668 3775 : } else if (RunSize && (traf->tfhd->def_sample_size == RunSize)) {
669 179 : if (!UseDefaultSize) UseDefaultSize = 1;
670 0 : else if (UseDefaultSize==2) RunSize = 0;
671 : }
672 : //we could check for single entry runs and set the default size in the tfhd but
673 : //that's no bit saving...
674 : else {
675 : RunSize=0;
676 : }
677 :
678 179 : if (!RunSize) trun->flags |= GF_ISOM_TRUN_SIZE;
679 :
680 : //duration checking
681 3775 : if (RunDur && (traf->trex->def_sample_duration == RunDur) && !traf->trex->cannot_use_default) {
682 3291 : if (!UseDefaultDur) UseDefaultDur = 2;
683 11 : else if (UseDefaultDur==1) RunDur = 0;
684 484 : } else if (RunDur && (traf->tfhd->def_sample_duration == RunDur)) {
685 374 : if (!UseDefaultDur) UseDefaultDur = 1;
686 0 : else if (UseDefaultDur==2) RunDur = 0;
687 : }
688 3775 : if (!RunDur) trun->flags |= GF_ISOM_TRUN_DURATION;
689 :
690 : //flag checking
691 3775 : if (!NeedFlags) {
692 : // all samples flags are the same after the 2nd entry
693 3635 : if (RunFlags == traf->trex->def_sample_flags && !traf->trex->cannot_use_default) {
694 : /* this run can use trex flags */
695 3287 : if (!UseDefaultFlag) {
696 : /* if all previous runs used explicit flags per sample, we can still use trex flags for this run */
697 : UseDefaultFlag = 2;
698 11 : } else if (UseDefaultFlag==1) {
699 : /* otherwise if one of the previous runs did use tfhd flags,
700 : we have no choice but to explicitly use flags per sample for this run */
701 : NeedFlags = GF_TRUE;
702 : }
703 348 : } else if (RunFlags == traf->tfhd->def_sample_flags) {
704 : /* this run can use tfhd flags */
705 348 : if (!UseDefaultFlag) {
706 : /* if all previous runs used explicit flags per sample, we can still use tfhd flags for this run */
707 : UseDefaultFlag = 1;
708 0 : } else if(UseDefaultFlag==2) {
709 : /* otherwise if one of the previous runs did use trex flags,
710 : we have no choice but to explicitly use flags per sample for this run */
711 : NeedFlags = GF_TRUE;
712 : }
713 : } else {
714 : /* the flags for the 2nd and following entries are different from trex and tfhd default values
715 : (possible case: 2 samples in trun, and first sample was used to set default flags) */
716 : NeedFlags = GF_TRUE;
717 : }
718 : }
719 3775 : if (NeedFlags) {
720 : //one flags entry per sample only
721 140 : trun->flags |= GF_ISOM_TRUN_FLAGS;
722 : } else {
723 : /* this run can use default flags for the 2nd and following entries,
724 : we just need to check if the first entry flags need to be singled out*/
725 3635 : if (first_ent->flags != RunFlags) {
726 2386 : trun->flags |= GF_ISOM_TRUN_FIRST_FLAG;
727 : }
728 : }
729 :
730 : //CTS flag
731 3775 : if (UseCTS) trun->flags |= GF_ISOM_TRUN_CTS_OFFSET;
732 :
733 : //run data offset if the offset indicated is 0 (first sample in this MDAT) don't
734 : //indicate it
735 3775 : if (trun->data_offset)
736 3709 : trun->flags |= GF_ISOM_TRUN_DATA_OFFSET;
737 :
738 3775 : sampleCount += trun->sample_count;
739 : }
740 :
741 : //after all runs in the traf are processed, update TRAF flags
742 4584 : if (UseDefaultSize==1)
743 179 : traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_SIZE;
744 4584 : if (UseDefaultDur==1)
745 374 : traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DUR;
746 4584 : if (UseDefaultFlag==1)
747 348 : traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_FLAGS;
748 4584 : if (traf->trex->cannot_use_default || (traf->tfhd->sample_desc_index != traf->trex->def_sample_desc_index))
749 1126 : traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DESC;
750 :
751 :
752 : return sampleCount;
753 : }
754 :
755 2442 : static u32 moof_get_sap_info(GF_MovieFragmentBox *moof, GF_ISOTrackID refTrackID, u32 *sap_delta, Bool *starts_with_sap)
756 : {
757 : u32 i, j, count, delta, earliest_cts, sap_type, sap_sample_num, cur_sample;
758 : Bool first = GF_TRUE;
759 : GF_TrunEntry *ent;
760 : GF_TrackFragmentBox *traf=NULL;
761 : GF_TrackFragmentRunBox *trun;
762 : sap_type = 0;
763 2442 : *sap_delta = 0;
764 2442 : *starts_with_sap = GF_FALSE;
765 2442 : for (i=0; i<gf_list_count(moof->TrackList); i++) {
766 2442 : traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
767 2442 : if (traf->tfhd->trackID==refTrackID) break;
768 : traf=NULL;
769 : }
770 2442 : if (!traf) return sap_type;
771 : earliest_cts = 0;
772 :
773 : /*first check if we have a roll/rap sample in this traf, and mark its sample count*/
774 : sap_type = 0;
775 : sap_sample_num = 0;
776 : /*check RAP and ROLL*/
777 2442 : count = traf->sampleGroups ? gf_list_count(traf->sampleGroups) : 0;
778 2625 : for (i=0; i<count; i++) {
779 : GF_SampleGroupBox *sg;
780 : u32 first_sample;
781 : Bool rap_type = GF_FALSE;
782 183 : sg = (GF_SampleGroupBox*)gf_list_get(traf->sampleGroups, i);
783 :
784 183 : switch (sg->grouping_type) {
785 110 : case GF_ISOM_SAMPLE_GROUP_RAP:
786 : case GF_ISOM_SAMPLE_GROUP_SYNC:
787 : rap_type = GF_TRUE;
788 : break;
789 : case GF_ISOM_SAMPLE_GROUP_ROLL:
790 : break;
791 58 : default:
792 58 : continue;
793 : }
794 : /*first entry is SAP*/
795 : first_sample = 1;
796 233 : for (j=0; j<sg->entry_count; j++) {
797 156 : if (! sg->sample_entries[j].group_description_index) {
798 54 : first_sample += sg->sample_entries[j].sample_count;
799 54 : continue;
800 : }
801 102 : if (!j) {
802 71 : *starts_with_sap = GF_TRUE;
803 : sap_sample_num = 0;
804 : }
805 102 : if (!sap_sample_num || (sap_sample_num>first_sample)) {
806 102 : sap_type = rap_type ? 3 : 4;
807 : sap_sample_num = first_sample;
808 : }
809 : break;
810 : }
811 : }
812 :
813 : /*then browse all samples, looking for SYNC flag or sap_sample_num*/
814 : cur_sample = 1;
815 : delta = 0;
816 2442 : i=0;
817 2456 : while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
818 2442 : if (trun->flags & GF_ISOM_TRUN_FIRST_FLAG) {
819 1749 : if (GF_ISOM_GET_FRAG_SYNC(trun->flags)) {
820 1749 : ent = &trun->samples[0];
821 : // if (!delta) earliest_cts = ent->CTS_Offset;
822 1749 : *sap_delta = delta + ent->CTS_Offset - ent->CTS_Offset;
823 1749 : *starts_with_sap = first;
824 1749 : sap_type = ent->SAP_type;
825 : return sap_type;
826 : }
827 : }
828 457 : for (j=0; j<trun->nb_samples; j++) {
829 1136 : ent = &trun->samples[j];
830 1136 : if (!delta) earliest_cts = ent->CTS_Offset;
831 :
832 1136 : if (GF_ISOM_GET_FRAG_SYNC(ent->flags)) {
833 599 : *sap_delta = delta + ent->CTS_Offset - earliest_cts;
834 599 : *starts_with_sap = first;
835 599 : sap_type = ent->SAP_type;
836 : return sap_type;
837 : }
838 : /*we found our roll or rap sample*/
839 537 : if (cur_sample==sap_sample_num) {
840 80 : *sap_delta = delta + ent->CTS_Offset - earliest_cts;
841 : return sap_type;
842 : }
843 457 : delta += ent->Duration;
844 : first = GF_FALSE;
845 457 : cur_sample++;
846 : }
847 : }
848 : /*not found*/
849 : return 0;
850 : }
851 :
852 2442 : u32 moof_get_duration(GF_MovieFragmentBox *moof, GF_ISOTrackID refTrackID)
853 : {
854 : u32 i, j, duration;
855 : GF_TrackFragmentBox *traf = NULL;
856 : GF_TrackFragmentRunBox *trun;
857 2442 : for (i=0; i<gf_list_count(moof->TrackList); i++) {
858 2442 : traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
859 2442 : if (traf->tfhd->trackID==refTrackID) break;
860 : traf=NULL;
861 : }
862 2442 : if (!traf) return 0;
863 :
864 : duration = 0;
865 2442 : i=0;
866 7326 : while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
867 85655 : for (j=0; j<trun->nb_samples; j++) {
868 85655 : GF_TrunEntry *ent = &trun->samples[j];
869 85655 : if (ent->flags & GF_ISOM_TRAF_SAMPLE_DUR)
870 0 : duration += ent->Duration;
871 : else
872 85655 : duration += traf->trex->def_sample_duration;
873 : }
874 : }
875 : return duration;
876 : }
877 :
878 4516 : static u64 moof_get_earliest_cts(GF_MovieFragmentBox *moof, GF_ISOTrackID refTrackID)
879 : {
880 : u32 i, j;
881 : u64 cts, duration;
882 : GF_TrackFragmentBox *traf=NULL;
883 : GF_TrackFragmentRunBox *trun;
884 4516 : for (i=0; i<gf_list_count(moof->TrackList); i++) {
885 4516 : traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
886 4516 : if (traf->tfhd->trackID==refTrackID) break;
887 : traf=NULL;
888 : }
889 4516 : if (!traf) return 0;
890 :
891 : duration = 0;
892 : cts = (u64) -1;
893 4516 : i=0;
894 9032 : while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
895 158806 : for (j=0; j<trun->nb_samples; j++) {
896 158806 : GF_TrunEntry *ent = &trun->samples[j];
897 158806 : if (duration + ent->CTS_Offset < cts)
898 : cts = duration + ent->CTS_Offset;
899 158806 : duration += ent->Duration;
900 : }
901 : }
902 : return cts;
903 : }
904 :
905 :
906 : GF_Err gf_isom_write_compressed_box(GF_ISOFile *mov, GF_Box *root_box, u32 repl_type, GF_BitStream *bs, u32 *box_csize);
907 :
908 7155 : static GF_Err StoreFragment(GF_ISOFile *movie, Bool load_mdat_only, s32 data_offset_diff, u32 *moof_size, Bool reassign_bs)
909 : {
910 : GF_Err e;
911 : u64 moof_start, pos;
912 : u32 size, i, s_count, mdat_size;
913 : s32 offset;
914 : u8 *buffer;
915 : GF_TrackFragmentBox *traf;
916 : GF_TrackFragmentRunBox *trun;
917 : GF_BitStream *bs, *bs_orig;
918 7155 : if (!movie->moof) return GF_OK;
919 :
920 7142 : bs = movie->editFileMap->bs;
921 7142 : if (!movie->moof_first) load_mdat_only = GF_FALSE;
922 : mdat_size = 0;
923 : //1 - flush all caches
924 7142 : i=0;
925 23452 : while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
926 : u32 j, nb_written, last_gid, cur_sample_idx;
927 : /*do not write empty senc*/
928 9168 : if (traf->sample_encryption && !gf_list_count(traf->sample_encryption->samp_aux_info)) {
929 7 : gf_list_del_item(traf->child_boxes, traf->sample_encryption);
930 7 : gf_isom_box_del((GF_Box *) traf->sample_encryption);
931 7 : traf->sample_encryption = NULL;
932 : /*remove saiz and saio (todo, check if other saiz/saio types are used*/
933 14 : for (j=0; j<gf_list_count(traf->sai_sizes); j++) {
934 7 : GF_SampleAuxiliaryInfoSizeBox *saiz = gf_list_get(traf->sai_sizes, j);
935 7 : switch (saiz->aux_info_type) {
936 7 : case GF_ISOM_CENC_SCHEME:
937 : case GF_ISOM_CBC_SCHEME:
938 : case GF_ISOM_CENS_SCHEME:
939 : case GF_ISOM_CBCS_SCHEME:
940 : case 0:
941 7 : gf_list_rem(traf->sai_sizes, j);
942 7 : gf_list_del_item(traf->child_boxes, saiz);
943 7 : gf_isom_box_del((GF_Box *)saiz);
944 7 : j--;
945 7 : break;
946 : }
947 : }
948 7 : for (j=0; j<gf_list_count(traf->sai_offsets); j++) {
949 7 : GF_SampleAuxiliaryInfoOffsetBox *saio = gf_list_get(traf->sai_offsets, j);
950 7 : switch (saio->aux_info_type) {
951 7 : case GF_ISOM_CENC_SCHEME:
952 : case GF_ISOM_CBC_SCHEME:
953 : case GF_ISOM_CENS_SCHEME:
954 : case GF_ISOM_CBCS_SCHEME:
955 : case 0:
956 7 : gf_list_rem(traf->sai_offsets, j);
957 7 : gf_list_del_item(traf->child_boxes, saio);
958 7 : gf_isom_box_del((GF_Box *)saio);
959 7 : j--;
960 7 : break;
961 : }
962 : }
963 : }
964 9168 : if (!traf->DataCache) continue;
965 0 : s_count = gf_list_count(traf->TrackRuns);
966 0 : if (!s_count) continue;
967 :
968 : //store all cached truns - there may be more than one when using sample interleaving in truns
969 : nb_written = 0;
970 : last_gid = 0;
971 : cur_sample_idx = 0;
972 0 : while (nb_written<s_count) {
973 : u32 min_next_gid = 0xFFFFFFFF;
974 :
975 0 : for (j=0; j<s_count; j++) {
976 0 : trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, j);
977 : //done
978 0 : if (!trun->cache || !trun->sample_count) continue;
979 :
980 0 : if (!traf->use_sample_interleave || (last_gid!=trun->interleave_id)) {
981 0 : if (trun->interleave_id < min_next_gid)
982 : min_next_gid = trun->interleave_id;
983 0 : continue;
984 : }
985 :
986 : //update offset
987 0 : trun->data_offset = (u32) (gf_bs_get_position(bs) - movie->moof->fragment_offset - 8);
988 : //write cache
989 0 : gf_bs_get_content(trun->cache, &buffer, &size);
990 0 : gf_bs_write_data(bs, buffer, size);
991 0 : gf_bs_del(trun->cache);
992 0 : gf_free(buffer);
993 0 : trun->cache = NULL;
994 0 : trun->first_sample_idx = cur_sample_idx;
995 0 : cur_sample_idx += trun->sample_count;
996 :
997 0 : nb_written++;
998 : }
999 : last_gid = min_next_gid;
1000 : }
1001 :
1002 0 : traf->DataCache=0;
1003 :
1004 : /*merge all truns*/
1005 0 : if (traf->merge_sample_interleave) {
1006 : u32 k, cur_idx = 0;
1007 0 : trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, 0);
1008 0 : trun->sample_order = gf_malloc(sizeof(u32) * cur_sample_idx);
1009 :
1010 0 : for (k=0; k<trun->sample_count; k++) {
1011 0 : trun->sample_order[cur_idx] = trun->first_sample_idx + k;
1012 0 : cur_idx ++;
1013 : }
1014 :
1015 0 : while (s_count>1) {
1016 0 : GF_TrackFragmentRunBox *atrun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, 1);
1017 0 : trun->sample_count += atrun->sample_count;
1018 :
1019 0 : trun->sample_alloc = trun->nb_samples + atrun->nb_samples;
1020 0 : trun->samples = gf_realloc(trun->samples, sizeof(GF_TrunEntry) * trun->sample_alloc);
1021 0 : if (!trun->samples) return GF_OUT_OF_MEM;
1022 :
1023 0 : memcpy(&trun->samples[trun->nb_samples], atrun->samples, sizeof(GF_TrunEntry)*atrun->nb_samples);
1024 0 : trun->nb_samples += atrun->nb_samples;
1025 :
1026 0 : for (k=0; k<atrun->sample_count; k++) {
1027 0 : trun->sample_order[cur_idx] = atrun->first_sample_idx + k;
1028 0 : cur_idx ++;
1029 : }
1030 0 : gf_list_rem(traf->TrackRuns, 1);
1031 0 : gf_list_del_item(traf->child_boxes, atrun);
1032 0 : gf_isom_box_del((GF_Box*)atrun);
1033 0 : s_count--;
1034 : }
1035 :
1036 : }
1037 : }
1038 :
1039 7142 : if (load_mdat_only) {
1040 3571 : pos = gf_bs_get_position(bs);
1041 3571 : if (movie->moof->fragment_offset > pos)
1042 : return GF_CORRUPTED_DATA;
1043 :
1044 : //we assume we never write large MDATs in fragment mode which should always be true
1045 3571 : movie->moof->mdat_size = (u32) (pos - movie->moof->fragment_offset);
1046 :
1047 3571 : if (movie->segment_bs) {
1048 0 : e = gf_bs_seek(bs, 0);
1049 0 : if (e) return e;
1050 : /*write mdat size*/
1051 0 : gf_bs_write_u32(bs, (u32) movie->moof->mdat_size);
1052 : /*and get internal buffer*/
1053 0 : e = gf_bs_seek(bs, movie->moof->mdat_size);
1054 0 : if (e) return e;
1055 0 : gf_bs_get_content(bs, &movie->moof->mdat, &movie->moof->mdat_size);
1056 :
1057 0 : gf_bs_del(bs);
1058 0 : movie->editFileMap->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
1059 : } else {
1060 3571 : u64 frag_offset = movie->segment_start;
1061 3571 : e = gf_bs_seek(bs, frag_offset);
1062 3571 : if (e) return e;
1063 : /*write mdat size*/
1064 3571 : gf_bs_write_u32(bs, (u32) movie->moof->mdat_size);
1065 :
1066 3571 : movie->moof->mdat = (char*)gf_malloc(sizeof(char) * movie->moof->mdat_size);
1067 3571 : if (!movie->moof->mdat) return GF_OUT_OF_MEM;
1068 :
1069 3571 : e = gf_bs_seek(bs, frag_offset);
1070 3571 : if (e) return e;
1071 3571 : gf_bs_read_data(bs, movie->moof->mdat, movie->moof->mdat_size);
1072 :
1073 3571 : e = gf_bs_seek(bs, frag_offset);
1074 3571 : if (e) return e;
1075 3571 : gf_bs_truncate(bs);
1076 : }
1077 :
1078 : return GF_OK;
1079 : }
1080 :
1081 3571 : moof_start = gf_bs_get_position(bs);
1082 :
1083 3571 : if (movie->moof->ntp) {
1084 0 : moof_start += 8*4;
1085 : }
1086 :
1087 : //2- update MOOF MDAT header
1088 3571 : if (!movie->moof->mdat) {
1089 0 : e = gf_bs_seek(bs, movie->moof->fragment_offset);
1090 0 : if (e) return e;
1091 : //we assume we never write large MDATs in fragment mode which should always be true
1092 0 : mdat_size = (u32) (moof_start - movie->moof->fragment_offset);
1093 0 : gf_bs_write_u32(bs, (u32) mdat_size);
1094 0 : gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
1095 0 : e = gf_bs_seek(bs, moof_start);
1096 0 : if (e) return e;
1097 : }
1098 :
1099 : /*estimate moof size and shift trun offsets*/
1100 : #ifndef USE_BASE_DATA_OFFSET
1101 : offset = 0;
1102 3571 : if (movie->use_segments || movie->force_moof_base_offset) {
1103 3514 : e = gf_isom_box_size((GF_Box *) movie->moof);
1104 3514 : if (e) return e;
1105 3514 : offset = (s32) movie->moof->size;
1106 : /*mdat size & type*/
1107 3514 : offset += 8;
1108 3514 : update_trun_offsets(movie, offset);
1109 : }
1110 : #endif
1111 :
1112 : //3- clean our traf's
1113 3571 : i=0;
1114 11726 : while ((traf = (GF_TrackFragmentBox*) gf_list_enum(movie->moof->TrackList, &i))) {
1115 : //compute default settings for the TRAF
1116 4584 : ComputeFragmentDefaults(traf);
1117 : //updates all trun and set all flags, INCLUDING TRAF FLAGS (durations, ...)
1118 4584 : s_count = UpdateRuns(movie, traf);
1119 : //empty fragment destroy it
1120 4584 : if (!traf->tfhd->EmptyDuration && !s_count) {
1121 820 : i--;
1122 820 : gf_list_rem(movie->moof->TrackList, i);
1123 820 : gf_isom_box_del_parent(&movie->moof->child_boxes, (GF_Box *) traf);
1124 820 : continue;
1125 : }
1126 : }
1127 :
1128 3571 : buffer = NULL;
1129 : /*rewind bitstream and load mdat in memory */
1130 3571 : if (movie->moof_first && !movie->moof->mdat) {
1131 0 : buffer = (char*)gf_malloc(sizeof(char)*mdat_size);
1132 0 : if (!buffer) return GF_OUT_OF_MEM;
1133 0 : e = gf_bs_seek(bs, movie->moof->fragment_offset);
1134 0 : if (e) return e;
1135 0 : gf_bs_read_data(bs, buffer, mdat_size);
1136 : /*back to mdat start and erase with moov*/
1137 0 : e = gf_bs_seek(bs, movie->moof->fragment_offset);
1138 0 : if (e) return e;
1139 0 : gf_bs_truncate(bs);
1140 : }
1141 :
1142 : //4- Write moof
1143 3571 : e = gf_isom_box_size((GF_Box *) movie->moof);
1144 3571 : if (e) return e;
1145 : /*moof first, update traf headers - THIS WILL IMPACT THE MOOF SIZE IF WE
1146 : DECIDE NOT TO USE THE DATA-OFFSET FLAG*/
1147 3571 : if (movie->moof_first
1148 : #ifndef USE_BASE_DATA_OFFSET
1149 3571 : && !(movie->use_segments || movie->force_moof_base_offset)
1150 : #endif
1151 : ) {
1152 57 : i=0;
1153 211 : while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
1154 : /*offset increases by moof size*/
1155 97 : traf->tfhd->base_data_offset += movie->moof->size;
1156 97 : traf->tfhd->base_data_offset += data_offset_diff;
1157 97 : if (movie->on_block_out) {
1158 97 : traf->tfhd->base_data_offset += movie->fragmented_file_pos;
1159 : }
1160 : }
1161 : }
1162 : #ifndef USE_BASE_DATA_OFFSET
1163 3514 : else if (movie->use_segments || movie->force_moof_base_offset) {
1164 3514 : if (offset != (movie->moof->size+8)) {
1165 3514 : offset = (s32) (movie->moof->size + 8 - offset);
1166 3514 : update_trun_offsets(movie, offset);
1167 3514 : e = gf_isom_box_size((GF_Box *) movie->moof);
1168 3514 : if (e) return e;
1169 : }
1170 : }
1171 : #endif
1172 :
1173 : bs_orig = bs;
1174 3571 : if (reassign_bs && movie->on_block_out) {
1175 0 : bs = gf_bs_new_cbk(movie->on_block_out, movie->on_block_out_usr_data, movie->on_block_out_block_size);
1176 : }
1177 :
1178 3571 : if (movie->moof->ntp) {
1179 0 : gf_bs_write_u32(bs, 8*4);
1180 0 : gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_PRFT );
1181 0 : gf_bs_write_u8(bs, 1);
1182 0 : gf_bs_write_u24(bs, 0);
1183 0 : gf_bs_write_u32(bs, movie->moof->reference_track_ID);
1184 0 : gf_bs_write_u64(bs, movie->moof->ntp);
1185 0 : gf_bs_write_u64(bs, movie->moof->timestamp);
1186 : }
1187 :
1188 3571 : if (moof_size) *moof_size = (u32) movie->moof->size;
1189 :
1190 3571 : pos = gf_bs_get_position(bs);
1191 :
1192 3571 : i=0;
1193 10906 : while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) {
1194 3764 : traf->moof_start_in_bs = pos;
1195 : }
1196 :
1197 : /*we don't want to dispatch any block until done writing the moof*/
1198 3571 : if (movie->on_block_out)
1199 3571 : gf_bs_prevent_dispatch(bs, GF_TRUE);
1200 :
1201 3571 : if (movie->compress_mode>GF_ISOM_COMP_MOOV) {
1202 10 : e = gf_isom_write_compressed_box(movie, (GF_Box *) movie->moof, GF_4CC('!', 'm', 'o', 'f'), bs, moof_size);
1203 : } else {
1204 3561 : e = gf_isom_box_write((GF_Box *) movie->moof, bs);
1205 : }
1206 :
1207 3571 : if (movie->on_block_out)
1208 3571 : gf_bs_prevent_dispatch(bs, GF_FALSE);
1209 :
1210 3571 : if (e) return e;
1211 :
1212 : //rewrite mdat after moof
1213 3571 : if (movie->moof->mdat) {
1214 3571 : gf_bs_write_data(bs, movie->moof->mdat, movie->moof->mdat_size);
1215 3571 : gf_free(movie->moof->mdat);
1216 3571 : movie->moof->mdat = NULL;
1217 0 : } else if (buffer) {
1218 0 : gf_bs_write_data(bs, buffer, mdat_size);
1219 0 : gf_free(buffer);
1220 : }
1221 :
1222 3571 : if (bs != bs_orig) {
1223 0 : u64 frag_size = gf_bs_get_position(bs);
1224 0 : gf_bs_del(bs);
1225 0 : movie->fragmented_file_pos += frag_size;
1226 0 : gf_bs_seek(bs_orig, 0);
1227 0 : gf_bs_truncate(bs_orig);
1228 : }
1229 3571 : else if (movie->on_block_out) {
1230 3571 : u64 frag_size = gf_bs_get_position(bs);
1231 3571 : movie->fragmented_file_pos += frag_size;
1232 : }
1233 :
1234 3571 : if (!movie->use_segments) {
1235 80 : gf_isom_box_del((GF_Box *) movie->moof);
1236 80 : movie->moof = NULL;
1237 : }
1238 : return GF_OK;
1239 : }
1240 :
1241 2074 : static GF_Err sidx_rewrite(GF_SegmentIndexBox *sidx, GF_BitStream *bs, u64 start_pos, GF_SubsegmentIndexBox *ssix)
1242 : {
1243 : GF_Err e = GF_OK;
1244 2074 : u64 pos = gf_bs_get_position(bs);
1245 2074 : if (ssix) {
1246 0 : e = gf_isom_box_size((GF_Box *)ssix);
1247 0 : sidx->first_offset = ssix->size;
1248 : }
1249 : /*write sidx*/
1250 2074 : gf_bs_seek(bs, start_pos);
1251 2074 : if (!e) e = gf_isom_box_write((GF_Box *) sidx, bs);
1252 2074 : if (!e && ssix) {
1253 0 : e = gf_isom_box_write((GF_Box *) ssix, bs);
1254 : }
1255 2074 : gf_bs_seek(bs, pos);
1256 2074 : return e;
1257 : }
1258 :
1259 17 : 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, Bool use_ssix)
1260 : {
1261 : GF_BitStream *bs;
1262 : GF_Err e;
1263 : u32 i;
1264 :
1265 : //and only at setup
1266 17 : if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
1267 17 : if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
1268 17 : if (movie->root_sidx) return GF_BAD_PARAM;
1269 17 : if (movie->root_ssix) return GF_BAD_PARAM;
1270 17 : if (movie->moof) return GF_BAD_PARAM;
1271 17 : if (gf_list_count(movie->moof_list)) return GF_BAD_PARAM;
1272 :
1273 17 : movie->root_sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX);
1274 17 : if (!movie->root_sidx) return GF_OUT_OF_MEM;
1275 : /*we don't write anything between sidx and following moov*/
1276 17 : movie->root_sidx->first_offset = 0;
1277 :
1278 : /*for now we only store one ref per subsegment and don't support daisy-chaining*/
1279 17 : movie->root_sidx->nb_refs = nb_segs;
1280 :
1281 17 : if (use_ssix) {
1282 1 : movie->root_ssix = (GF_SubsegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SSIX);
1283 1 : movie->root_ssix->subsegment_count = nb_segs;
1284 1 : movie->root_ssix->subsegment_alloc = movie->root_ssix->subsegment_count;
1285 : }
1286 :
1287 : //dynamic mode
1288 17 : if (!nb_segs) {
1289 17 : movie->dyn_root_sidx = GF_TRUE;
1290 17 : return GF_OK;
1291 : }
1292 :
1293 0 : movie->root_sidx->refs = (GF_SIDXReference*)gf_malloc(sizeof(GF_SIDXReference) * movie->root_sidx->nb_refs);
1294 0 : if (!movie->root_sidx->refs) return GF_OUT_OF_MEM;
1295 0 : memset(movie->root_sidx->refs, 0, sizeof(GF_SIDXReference) * movie->root_sidx->nb_refs);
1296 :
1297 0 : movie->root_sidx_index = 0;
1298 :
1299 0 : if (use_ssix) {
1300 0 : movie->root_ssix->subsegments = gf_malloc(sizeof(GF_SubsegmentInfo) * nb_segs);
1301 0 : if (!movie->root_ssix->subsegments) return GF_OUT_OF_MEM;
1302 0 : for (i=0; i<nb_segs; i++) {
1303 0 : movie->root_ssix->subsegments[i].range_count = 2;
1304 0 : movie->root_ssix->subsegments[i].ranges = gf_malloc(sizeof(GF_SubsegmentRangeInfo)*2);
1305 0 : if (!movie->root_ssix->subsegments[i].ranges) return GF_OUT_OF_MEM;
1306 0 : movie->root_ssix->subsegments[i].ranges[0].level = 0;
1307 0 : movie->root_ssix->subsegments[i].ranges[0].range_size = 0;
1308 0 : movie->root_ssix->subsegments[i].ranges[1].level = 1;
1309 0 : movie->root_ssix->subsegments[i].ranges[1].range_size = 0;
1310 : }
1311 : }
1312 :
1313 : /*remember start of sidx*/
1314 0 : movie->root_sidx_offset = gf_bs_get_position(movie->editFileMap->bs);
1315 :
1316 0 : bs = movie->editFileMap->bs;
1317 :
1318 0 : e = gf_isom_box_size((GF_Box *) movie->root_sidx);
1319 0 : if (e) return e;
1320 0 : e = gf_isom_box_write((GF_Box *) movie->root_sidx, bs);
1321 0 : if (e) return e;
1322 :
1323 0 : if (movie->root_ssix) {
1324 0 : e = gf_isom_box_size((GF_Box *) movie->root_ssix);
1325 0 : if (e) return e;
1326 0 : e = gf_isom_box_write((GF_Box *) movie->root_ssix, bs);
1327 0 : if (e) return e;
1328 : }
1329 :
1330 : //include ssix in index range - spec is not clear whether this is forbidden
1331 0 : if (start_range) *start_range = (u32) movie->root_sidx_offset;
1332 0 : if (end_range) *end_range = (u32) gf_bs_get_position(bs)-1;
1333 :
1334 : return GF_OK;
1335 : }
1336 :
1337 :
1338 3479 : static GF_Err gf_isom_write_styp(GF_ISOFile *movie, Bool last_segment)
1339 : {
1340 : /*write STYP if we write to a different file or if we write the last segment*/
1341 3479 : if (movie->use_segments && !movie->append_segment && !movie->segment_start && !movie->styp_written) {
1342 : GF_Err e;
1343 :
1344 : /*modify brands STYP*/
1345 :
1346 : /*"msix" brand: this is a DASH Initialization Segment*/
1347 2487 : gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_MSIX, GF_TRUE);
1348 2487 : if (last_segment) {
1349 : /*"lmsg" brand: this is the last DASH Segment*/
1350 248 : gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_LMSG, GF_TRUE);
1351 : }
1352 :
1353 2487 : movie->brand->type = GF_ISOM_BOX_TYPE_STYP;
1354 2487 : e = gf_isom_box_size((GF_Box *) movie->brand);
1355 2487 : if (e) return e;
1356 2487 : e = gf_isom_box_write((GF_Box *) movie->brand, movie->editFileMap->bs);
1357 2487 : if (e) return e;
1358 :
1359 2487 : movie->styp_written = GF_TRUE;
1360 : }
1361 : return GF_OK;
1362 : }
1363 :
1364 : GF_EXPORT
1365 453 : GF_Err gf_isom_flush_fragments(GF_ISOFile *movie, Bool last_segment)
1366 : {
1367 : GF_BitStream *temp_bs = NULL, *orig_bs;
1368 : GF_Err e;
1369 :
1370 453 : if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
1371 453 : if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
1372 :
1373 : /*flush our fragment (store in mem)*/
1374 453 : if (movie->moof) {
1375 453 : e = StoreFragment(movie, GF_TRUE, 0, NULL, GF_FALSE);
1376 453 : if (e) return e;
1377 : }
1378 :
1379 453 : if (movie->segment_bs) {
1380 0 : temp_bs = movie->editFileMap->bs;
1381 0 : movie->editFileMap->bs = movie->segment_bs;
1382 : }
1383 :
1384 453 : gf_bs_seek(movie->editFileMap->bs, movie->segment_start);
1385 453 : gf_bs_truncate(movie->editFileMap->bs);
1386 453 : orig_bs = movie->editFileMap->bs;
1387 453 : if (movie->on_block_out) {
1388 453 : if (!movie->block_buffer) movie->block_buffer_size = movie->on_block_out_block_size;
1389 453 : movie->editFileMap->bs = gf_bs_new_cbk_buffer(movie->on_block_out, movie->on_block_out_usr_data, movie->block_buffer, movie->block_buffer_size);
1390 : }
1391 :
1392 : /*write styp to file if needed*/
1393 453 : e = gf_isom_write_styp(movie, last_segment);
1394 453 : if (e) goto exit;
1395 :
1396 : /*write all pending fragments to file*/
1397 906 : while (gf_list_count(movie->moof_list)) {
1398 : s32 offset_diff;
1399 : u32 moof_size;
1400 :
1401 453 : movie->moof = (GF_MovieFragmentBox*)gf_list_get(movie->moof_list, 0);
1402 453 : gf_list_rem(movie->moof_list, 0);
1403 :
1404 453 : offset_diff = (s32) (gf_bs_get_position(movie->editFileMap->bs) - movie->moof->fragment_offset);
1405 453 : movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs);
1406 :
1407 453 : e = StoreFragment(movie, GF_FALSE, offset_diff, &moof_size, GF_FALSE);
1408 453 : if (e) goto exit;
1409 :
1410 453 : gf_isom_box_del((GF_Box *) movie->moof);
1411 453 : movie->moof = NULL;
1412 : }
1413 :
1414 : /*append mode: store fragment at the end of the regular movie bitstream, and delete the temp bitstream*/
1415 453 : if (movie->append_segment) {
1416 : char bloc[1024];
1417 0 : u32 seg_size = (u32) gf_bs_get_size(movie->editFileMap->bs);
1418 0 : gf_bs_seek(movie->editFileMap->bs, 0);
1419 0 : while (seg_size) {
1420 0 : u32 size = gf_bs_read_data(movie->editFileMap->bs, bloc, (seg_size>1024) ? 1024 : seg_size);
1421 0 : gf_bs_write_data(movie->movieFileMap->bs, bloc, size);
1422 0 : seg_size -= size;
1423 : }
1424 0 : gf_isom_datamap_flush(movie->movieFileMap);
1425 :
1426 0 : gf_isom_datamap_del(movie->editFileMap);
1427 0 : movie->editFileMap = gf_isom_fdm_new_temp(NULL);
1428 : } else {
1429 453 : gf_isom_datamap_flush(movie->editFileMap);
1430 : }
1431 453 : movie->segment_start = gf_bs_get_position(movie->editFileMap->bs);
1432 :
1433 453 : if (temp_bs) {
1434 0 : movie->segment_bs = movie->editFileMap->bs;
1435 0 : movie->editFileMap->bs = temp_bs;
1436 : }
1437 :
1438 453 : if (orig_bs != movie->editFileMap->bs) {
1439 : u32 tmpsize;
1440 453 : gf_bs_get_content_no_truncate(movie->editFileMap->bs, &movie->block_buffer, &tmpsize, &movie->block_buffer_size);
1441 453 : gf_bs_del(movie->editFileMap->bs);
1442 453 : movie->editFileMap->bs = orig_bs;
1443 : //we are dispatching through callbacks, the movie segment start is always 0
1444 453 : movie->segment_start = 0;
1445 : }
1446 0 : exit:
1447 : return e;
1448 : }
1449 :
1450 : typedef struct
1451 : {
1452 : GF_SegmentIndexBox *sidx;
1453 : u64 start_offset, end_offset;
1454 : } SIDXEntry;
1455 :
1456 : static u64 get_presentation_time(u64 media_time, s32 ts_shift)
1457 : {
1458 175828 : if ((ts_shift<0) && (media_time < -ts_shift)) {
1459 : media_time = 0;
1460 : } else {
1461 175828 : media_time += ts_shift;
1462 : }
1463 : return media_time ;
1464 : }
1465 :
1466 :
1467 : #if 0 //unused
1468 : /*! gets name of current segment (or last segment if called between close_segment and start_segment)
1469 : \param isom_file the target ISO file
1470 : \return associated file name of the segment
1471 : */
1472 : GF_EXPORT
1473 : const char *gf_isom_get_segment_name(GF_ISOFile *movie)
1474 : {
1475 : if (!movie) return NULL;
1476 : if (movie->append_segment) return movie->movieFileMap->szName;
1477 : return movie->editFileMap->szName;
1478 : }
1479 : #endif
1480 :
1481 3043 : static void compute_seg_size(GF_ISOFile *movie, u64 *out_seg_size)
1482 : {
1483 : u64 final_size = 0;
1484 3043 : if (out_seg_size) {
1485 3026 : if (movie->append_segment) {
1486 0 : final_size = gf_bs_get_position(movie->movieFileMap->bs);
1487 0 : final_size -= movie->segment_start;
1488 3026 : } else if (movie->editFileMap) {
1489 3026 : final_size = gf_bs_get_position(movie->editFileMap->bs);
1490 : }
1491 3026 : *out_seg_size = final_size;
1492 : }
1493 3043 : }
1494 :
1495 30 : static u32 moof_get_first_sap_end(GF_MovieFragmentBox *moof)
1496 : {
1497 30 : u32 i, count = gf_list_count(moof->TrackList);
1498 0 : for (i=0; i<count; i++) {
1499 : u32 j, nb_trun;
1500 30 : GF_TrackFragmentBox *traf = gf_list_get(moof->TrackList, i);
1501 30 : u32 base_offset = (u32) traf->tfhd->base_data_offset;
1502 :
1503 30 : nb_trun = gf_list_count(traf->TrackRuns);
1504 0 : for (j=0; j<nb_trun; j++) {
1505 : u32 k;
1506 30 : GF_TrackFragmentRunBox *trun = gf_list_get(traf->TrackRuns, j);
1507 30 : u32 offset = base_offset + trun->data_offset;
1508 0 : for (k=0; k<trun->nb_samples; k++) {
1509 30 : GF_TrunEntry *ent = &trun->samples[k];
1510 30 : if (ent->SAP_type) return offset + ent->size;
1511 :
1512 0 : offset += ent->size;
1513 : }
1514 : }
1515 : }
1516 : return 0;
1517 : }
1518 :
1519 2350 : static u64 estimate_next_moof_earliest_presentation_time(u64 ref_track_decode_time, s32 ts_shift, u32 refTrackID, GF_ISOFile *movie)
1520 : {
1521 : u32 i, j, nb_aus, nb_ctso, nb_moof;
1522 : u64 duration;
1523 : GF_TrunEntry *ent;
1524 : GF_TrackFragmentBox *traf=NULL;
1525 : GF_TrackFragmentRunBox *trun;
1526 : u32 timescale;
1527 : u64 min_next_cts = -1;
1528 :
1529 2350 : GF_MovieFragmentBox *moof = gf_list_get(movie->moof_list, 0);
1530 :
1531 2350 : for (i=0; i<gf_list_count(moof->TrackList); i++) {
1532 2350 : traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
1533 2350 : if (traf->tfhd->trackID==refTrackID) break;
1534 : traf = NULL;
1535 : }
1536 : //no ref track, nothing to estimate
1537 2350 : if (!traf) return -1;
1538 2350 : timescale = traf->trex->track->Media->mediaHeader->timeScale;
1539 :
1540 : nb_aus = 0;
1541 : duration = 0;
1542 : nb_ctso = 0;
1543 2350 : nb_moof = 0;
1544 :
1545 7142 : while ((moof = (GF_MovieFragmentBox*)gf_list_enum(movie->moof_list, &nb_moof))) {
1546 :
1547 2442 : for (i=0; i<gf_list_count(moof->TrackList); i++) {
1548 2442 : traf = (GF_TrackFragmentBox*)gf_list_get(moof->TrackList, i);
1549 2442 : if (traf->tfhd->trackID==refTrackID) break;
1550 : traf = NULL;
1551 : }
1552 2442 : if (!traf) continue;
1553 :
1554 2442 : i=0;
1555 7326 : while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &i))) {
1556 85655 : for (j=0; j<trun->nb_samples; j++) {
1557 85655 : ent = &trun->samples[j];
1558 85655 : if (nb_aus + 1 + movie->sidx_pts_store_count > movie->sidx_pts_store_alloc) {
1559 19596 : movie->sidx_pts_store_alloc = movie->sidx_pts_store_count+nb_aus+1;
1560 19596 : movie->sidx_pts_store = gf_realloc(movie->sidx_pts_store, sizeof(u64) * movie->sidx_pts_store_alloc);
1561 19596 : movie->sidx_pts_next_store = gf_realloc(movie->sidx_pts_next_store, sizeof(u64) * movie->sidx_pts_store_alloc);
1562 : }
1563 : //get PTS for this AU, push to regular list
1564 171310 : movie->sidx_pts_store[movie->sidx_pts_store_count + nb_aus] = get_presentation_time( ref_track_decode_time + duration + ent->CTS_Offset, ts_shift);
1565 : //get PTS for this AU shifted by its presentation duration, push to shifted list
1566 171310 : movie->sidx_pts_next_store[movie->sidx_pts_store_count + nb_aus] = get_presentation_time( ref_track_decode_time + duration + ent->CTS_Offset + ent->Duration, ts_shift);
1567 85655 : duration += ent->Duration;
1568 85655 : if (ent->CTS_Offset)
1569 8753 : nb_ctso++;
1570 :
1571 85655 : nb_aus++;
1572 : }
1573 : }
1574 : }
1575 :
1576 2350 : movie->sidx_pts_store_count += nb_aus;
1577 :
1578 : //no AUs, nothing to estimate
1579 2350 : if (!nb_aus) {
1580 0 : movie->sidx_pts_store_count = 0;
1581 0 : return -1;
1582 : }
1583 : //no cts offset, assume earliest PTS in next segment is last PTS in this segment + duration
1584 2350 : if (!nb_ctso) {
1585 2151 : min_next_cts = movie->sidx_pts_next_store[movie->sidx_pts_store_count - 1];
1586 2151 : movie->sidx_pts_store_count = 0;
1587 2151 : return min_next_cts;
1588 : }
1589 :
1590 : //look for all shifted PTS of this segment in the regular list. If found in the shifted list, the AU is in this segment
1591 : //remove from both list
1592 13190 : for (i=0; i<movie->sidx_pts_store_count; i++) {
1593 31107 : for (j=i; j<movie->sidx_pts_store_count; j++) {
1594 : /*
1595 :
1596 : if (movie->sidx_pts_next_store[i] == movie->sidx_pts_store[j]) {
1597 :
1598 : take care of misaligned timescale eg 24fps but 10000 timescale), we may not find exactly
1599 : the same sample - if diff is below one ms consider it a match
1600 : not doing so would accumulate PTSs in the list, slowing down the muxing*/
1601 43867 : s64 diff = movie->sidx_pts_next_store[i];
1602 43867 : diff -= (s64) movie->sidx_pts_store[j];
1603 43867 : if (timescale>1000) {
1604 23614 : if (ABS(diff) * 1000 < 1 * timescale)
1605 : diff = 0;
1606 : }
1607 37295 : if (diff==0) {
1608 12760 : if (movie->sidx_pts_store_count >= i + 1)
1609 12760 : memmove(&movie->sidx_pts_next_store[i], &movie->sidx_pts_next_store[i+1], sizeof(u64) * (movie->sidx_pts_store_count - i - 1) );
1610 12760 : if (movie->sidx_pts_store_count >= j + 1)
1611 12760 : memmove(&movie->sidx_pts_store[j], &movie->sidx_pts_store[j+1], sizeof(u64) * (movie->sidx_pts_store_count - j - 1) );
1612 12760 : movie->sidx_pts_store_count--;
1613 12760 : i--;
1614 12760 : break;
1615 : }
1616 : }
1617 : }
1618 : //the shifted list contain all AUs not yet in this segment, keep the smallest to compute the earliest PTS in next seg
1619 : //note that we assume the durations were correctly set
1620 430 : for (i=0; i<movie->sidx_pts_store_count; i++) {
1621 231 : if (min_next_cts > movie->sidx_pts_next_store[i])
1622 : min_next_cts = movie->sidx_pts_next_store[i];
1623 : }
1624 : return min_next_cts;
1625 : }
1626 :
1627 :
1628 : GF_EXPORT
1629 3043 : GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 subsegments_per_sidx, GF_ISOTrackID referenceTrackID, u64 ref_track_decode_time, s32 ts_shift, u64 ref_track_next_cts, Bool daisy_chain_sidx, Bool use_ssix, Bool last_segment, Bool close_segment_handle, u32 segment_marker_4cc, u64 *index_start_range, u64 *index_end_range, u64 *out_seg_size)
1630 : {
1631 : GF_SegmentIndexBox *sidx=NULL;
1632 : GF_SegmentIndexBox *root_sidx=NULL;
1633 : GF_SubsegmentIndexBox *ssix=NULL;
1634 : GF_List *daisy_sidx = NULL;
1635 : GF_BitStream *orig_bs;
1636 : u64 sidx_start, sidx_end;
1637 : Bool first_frag_in_subseg;
1638 : Bool no_sidx = GF_FALSE;
1639 : u32 count, cur_idx, cur_dur, sidx_dur, sidx_idx, idx_offset, frag_count;
1640 : u64 last_top_box_pos, root_prev_offset, local_sidx_start, local_sidx_end, prev_earliest_cts, next_earliest_cts;
1641 : GF_TrackBox *trak = NULL;
1642 : GF_Err e;
1643 : /*number of subsegment in this segment (eg nb references in the first SIDX found)*/
1644 : u32 nb_subsegs=0;
1645 : /*number of subsegment per sidx (eg number of references of any sub-SIDX*/
1646 : u32 subseg_per_sidx;
1647 : /*number of fragments per subsegment*/
1648 : u32 frags_per_subseg;
1649 : /*number of fragments per subsidx*/
1650 : u32 frags_per_subsidx;
1651 :
1652 : sidx_start = sidx_end = 0;
1653 :
1654 3043 : if (index_start_range) *index_start_range = 0;
1655 3043 : if (index_end_range) *index_end_range = 0;
1656 :
1657 : //and only at setup
1658 3043 : if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
1659 3043 : if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
1660 :
1661 3043 : count = gf_list_count(movie->moov->mvex->TrackExList);
1662 3043 : if (!count) return GF_BAD_PARAM;
1663 :
1664 : /*store fragment*/
1665 3043 : if (movie->moof) {
1666 3026 : e = StoreFragment(movie, GF_TRUE, 0, NULL, GF_FALSE);
1667 3026 : if (e) return e;
1668 : }
1669 : /*restore final bitstream*/
1670 3043 : if (movie->segment_bs) {
1671 0 : gf_bs_del(movie->editFileMap->bs);
1672 0 : movie->editFileMap->bs = movie->segment_bs;
1673 0 : movie->segment_bs = NULL;
1674 : }
1675 :
1676 3043 : count = gf_list_count(movie->moof_list);
1677 3043 : if (!count) {
1678 : /*append segment marker box*/
1679 17 : if (segment_marker_4cc) {
1680 0 : if (movie->append_segment) {
1681 0 : gf_bs_write_u32(movie->movieFileMap->bs, 8); //write size field
1682 0 : gf_bs_write_u32(movie->movieFileMap->bs, segment_marker_4cc); //write box type field
1683 : } else {
1684 0 : gf_bs_write_u32(movie->editFileMap->bs, 8); //write size field
1685 0 : gf_bs_write_u32(movie->editFileMap->bs, segment_marker_4cc); //write box type field
1686 : }
1687 : }
1688 :
1689 17 : compute_seg_size(movie, out_seg_size);
1690 :
1691 17 : if (close_segment_handle) {
1692 17 : gf_isom_datamap_del(movie->editFileMap);
1693 17 : movie->editFileMap = NULL;
1694 : }
1695 :
1696 : return GF_OK;
1697 : }
1698 :
1699 3026 : gf_bs_seek(movie->editFileMap->bs, movie->segment_start);
1700 3026 : gf_bs_truncate(movie->editFileMap->bs);
1701 :
1702 : idx_offset = 0;
1703 :
1704 3026 : if (referenceTrackID) {
1705 2350 : trak = gf_isom_get_track_from_id(movie->moov, referenceTrackID);
1706 2350 : if (!trak) return GF_BAD_PARAM;
1707 : }
1708 :
1709 3026 : if (subsegments_per_sidx < 0) {
1710 : referenceTrackID = 0;
1711 : subsegments_per_sidx = 0;
1712 : }
1713 3026 : if (!subsegments_per_sidx && !referenceTrackID) {
1714 : no_sidx = GF_TRUE;
1715 : }
1716 :
1717 3026 : orig_bs = movie->editFileMap->bs;
1718 3026 : if (movie->on_block_out) {
1719 3026 : if (!movie->block_buffer) movie->block_buffer_size = movie->on_block_out_block_size;
1720 3026 : movie->editFileMap->bs = gf_bs_new_cbk_buffer(movie->on_block_out, movie->on_block_out_usr_data, movie->block_buffer, movie->block_buffer_size);
1721 3026 : if (referenceTrackID) gf_bs_prevent_dispatch(movie->editFileMap->bs, GF_TRUE);
1722 : }
1723 :
1724 3026 : e = gf_isom_write_styp(movie, last_segment);
1725 3026 : if (e) goto exit;
1726 :
1727 : frags_per_subseg = 0;
1728 : subseg_per_sidx = 0;
1729 : frags_per_subsidx = 0;
1730 :
1731 : prev_earliest_cts = 0;
1732 : next_earliest_cts = 0;
1733 :
1734 3026 : if (daisy_chain_sidx)
1735 0 : daisy_sidx = gf_list_new();
1736 :
1737 : /*prepare SIDX: we write a blank SIDX box with the right number of entries, and will rewrite it later on*/
1738 3026 : if (referenceTrackID) {
1739 : Bool is_root_sidx = GF_FALSE;
1740 :
1741 2350 : 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);
1742 :
1743 : //we don't trust ref_track_next_cts to be the earliest in the following segment
1744 2350 : next_earliest_cts = estimate_next_moof_earliest_presentation_time(ref_track_decode_time, ts_shift, referenceTrackID, movie);
1745 :
1746 2350 : if (movie->root_sidx) {
1747 : sidx = movie->root_sidx;
1748 : } else {
1749 2074 : sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX);
1750 2074 : if (!sidx) return GF_OUT_OF_MEM;
1751 2074 : if (movie->force_sidx_v1)
1752 0 : sidx->version = 1;
1753 : }
1754 2350 : sidx->reference_ID = referenceTrackID;
1755 2350 : sidx->timescale = trak->Media->mediaHeader->timeScale;
1756 : /*we don't write anything between sidx and following moov*/
1757 2350 : sidx->first_offset = 0;
1758 :
1759 : /*we allocated our sidx to have one ref per "segment" (eg per call to close_segment)*/
1760 2350 : if (movie->root_sidx) {
1761 276 : if (!movie->root_sidx_index) {
1762 17 : sidx->earliest_presentation_time = prev_earliest_cts;
1763 : }
1764 : nb_subsegs = 1;
1765 : frags_per_subseg = count;
1766 : frags_per_subsidx = count;
1767 : subseg_per_sidx = 1;
1768 : daisy_chain_sidx = GF_FALSE;
1769 :
1770 276 : idx_offset = movie->root_sidx_index;
1771 276 : sidx_end = gf_bs_get_position(movie->editFileMap->bs);
1772 : } else {
1773 2074 : sidx->earliest_presentation_time = prev_earliest_cts;
1774 :
1775 : /*if more subsegments requested than fragments available, make a single sidx*/
1776 2074 : if ((s32) count <= subsegments_per_sidx)
1777 : subsegments_per_sidx = 0;
1778 :
1779 2074 : if (daisy_chain_sidx && (subsegments_per_sidx<2))
1780 : subsegments_per_sidx = 2;
1781 :
1782 : /*single SIDX, each fragment is a subsegment and we reference all subsegments*/
1783 2074 : if (!subsegments_per_sidx) {
1784 : nb_subsegs = count;
1785 : /*we consider that each fragment is a subsegment - this could be controled by another parameter*/
1786 : frags_per_subseg = 1;
1787 : frags_per_subsidx = count;
1788 : subseg_per_sidx = count;
1789 :
1790 2074 : sidx->nb_refs = nb_subsegs;
1791 : daisy_chain_sidx = GF_FALSE;
1792 : }
1793 : /*daisy-chain SIDX: each SIDX describes a subsegment made of frags_per_subseg fragments plus next */
1794 0 : else if (daisy_chain_sidx) {
1795 0 : frags_per_subsidx = count/subsegments_per_sidx;
1796 0 : if (frags_per_subsidx * subsegments_per_sidx < count) frags_per_subsidx++;
1797 :
1798 : nb_subsegs = subsegments_per_sidx;
1799 :
1800 : /*we consider that each fragment is a subsegment - this could be controled by another parameter*/
1801 : frags_per_subseg = 1;
1802 : subseg_per_sidx = frags_per_subsidx / frags_per_subseg;
1803 : if (subseg_per_sidx * frags_per_subseg < frags_per_subsidx) subseg_per_sidx++;
1804 :
1805 0 : sidx->nb_refs = subseg_per_sidx + 1;
1806 : }
1807 : /*hierarchical SIDX*/
1808 : else {
1809 0 : frags_per_subsidx = count/subsegments_per_sidx;
1810 0 : if (frags_per_subsidx * subsegments_per_sidx < count) frags_per_subsidx++;
1811 :
1812 : nb_subsegs = subsegments_per_sidx;
1813 :
1814 : /*we consider that each fragment is a subsegment - this could be controled by another parameter*/
1815 : frags_per_subseg = 1;
1816 : subseg_per_sidx = frags_per_subsidx / frags_per_subseg;
1817 : if (subseg_per_sidx * frags_per_subseg < frags_per_subsidx) subseg_per_sidx++;
1818 :
1819 0 : sidx->nb_refs = nb_subsegs;
1820 : is_root_sidx = GF_TRUE;
1821 : }
1822 :
1823 2074 : sidx->refs = (GF_SIDXReference*)gf_malloc(sizeof(GF_SIDXReference)*sidx->nb_refs);
1824 2074 : if (!sidx->refs) return GF_OUT_OF_MEM;
1825 2074 : memset(sidx->refs, 0, sizeof(GF_SIDXReference)*sidx->nb_refs);
1826 :
1827 : /*remember start of sidx*/
1828 2074 : sidx_start = gf_bs_get_position(movie->editFileMap->bs);
1829 :
1830 2074 : e = gf_isom_box_size((GF_Box *) sidx);
1831 2074 : if (e) goto exit;
1832 2074 : e = gf_isom_box_write((GF_Box *) sidx, movie->editFileMap->bs);
1833 2074 : if (e) goto exit;
1834 :
1835 2074 : if (use_ssix && !ssix && !movie->root_ssix) {
1836 : u32 k;
1837 0 : ssix = (GF_SubsegmentIndexBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SSIX);
1838 0 : if (!ssix) return GF_OUT_OF_MEM;
1839 0 : ssix->subsegments = gf_malloc(sizeof(GF_SubsegmentInfo) * sidx->nb_refs);
1840 0 : if (!ssix->subsegments) return GF_OUT_OF_MEM;
1841 0 : ssix->subsegment_count = sidx->nb_refs;
1842 0 : ssix->subsegment_alloc = ssix->subsegment_count;
1843 0 : for (k=0; k<sidx->nb_refs; k++) {
1844 0 : GF_SubsegmentInfo *subs = &ssix->subsegments[k];
1845 0 : subs->range_count = 2;
1846 0 : subs->ranges = gf_malloc(sizeof(GF_SubsegmentRangeInfo)*2);
1847 0 : if (!subs->ranges) return GF_OUT_OF_MEM;
1848 0 : subs->ranges[0].level = 1;
1849 0 : subs->ranges[1].level = 2;
1850 0 : subs->ranges[0].range_size = subs->ranges[1].range_size = 0;
1851 : }
1852 :
1853 0 : e = gf_isom_box_size((GF_Box *) ssix);
1854 0 : if (e) return e;
1855 0 : e = gf_isom_box_write((GF_Box *) ssix, movie->editFileMap->bs);
1856 0 : if (e) return e;
1857 : }
1858 :
1859 2074 : sidx_end = gf_bs_get_position(movie->editFileMap->bs);
1860 :
1861 2074 : if (daisy_sidx) {
1862 : SIDXEntry *entry;
1863 0 : GF_SAFEALLOC(entry, SIDXEntry);
1864 0 : if (!entry) {
1865 : e = GF_OUT_OF_MEM;
1866 : goto exit;
1867 : }
1868 0 : entry->sidx = sidx;
1869 0 : entry->start_offset = sidx_start;
1870 0 : gf_list_add(daisy_sidx, entry);
1871 : }
1872 : }
1873 :
1874 2350 : if (is_root_sidx) {
1875 : root_sidx = sidx;
1876 : sidx = NULL;
1877 : }
1878 : count = cur_idx = 0;
1879 : }
1880 :
1881 :
1882 : last_top_box_pos = root_prev_offset = sidx_end;
1883 : sidx_idx = 0;
1884 : sidx_dur = 0;
1885 : local_sidx_start = local_sidx_end = 0;
1886 :
1887 : /*cumulated segments duration since start of the sidx */
1888 : frag_count = frags_per_subsidx;
1889 : cur_dur = 0;
1890 : cur_idx = 0;
1891 : first_frag_in_subseg = GF_TRUE;
1892 : e = GF_OK;
1893 9170 : while (gf_list_count(movie->moof_list)) {
1894 : s32 offset_diff;
1895 : u32 moof_size;
1896 :
1897 3118 : movie->moof = (GF_MovieFragmentBox*)gf_list_get(movie->moof_list, 0);
1898 3118 : gf_list_rem(movie->moof_list, 0);
1899 :
1900 : /*hierarchical or daisy-chain SIDXs*/
1901 3118 : if (!no_sidx && !sidx && (root_sidx || daisy_chain_sidx) ) {
1902 : u32 subsegments_remaining;
1903 0 : sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX);
1904 0 : if (!sidx) return GF_OUT_OF_MEM;
1905 0 : sidx->reference_ID = referenceTrackID;
1906 0 : sidx->timescale = trak ? trak->Media->mediaHeader->timeScale : 1000;
1907 0 : sidx->earliest_presentation_time = get_presentation_time( ref_track_decode_time + sidx_dur + moof_get_earliest_cts(movie->moof, referenceTrackID), ts_shift);
1908 :
1909 : frag_count = frags_per_subsidx;
1910 :
1911 : /*last segment, add only one ref*/
1912 0 : subsegments_remaining = 1 + gf_list_count(movie->moof_list);
1913 0 : if (subseg_per_sidx*frags_per_subseg > subsegments_remaining) {
1914 0 : subseg_per_sidx = subsegments_remaining / frags_per_subseg;
1915 0 : if (subseg_per_sidx * frags_per_subseg < subsegments_remaining) subseg_per_sidx++;
1916 : }
1917 : /*we don't write anything between sidx and following moov*/
1918 0 : sidx->first_offset = 0;
1919 0 : sidx->nb_refs = subseg_per_sidx;
1920 0 : if (daisy_chain_sidx && (nb_subsegs>1)) {
1921 0 : sidx->nb_refs += 1;
1922 : }
1923 0 : sidx->refs = (GF_SIDXReference*)gf_malloc(sizeof(GF_SIDXReference)*sidx->nb_refs);
1924 0 : if (!sidx->refs) return GF_OUT_OF_MEM;
1925 0 : memset(sidx->refs, 0, sizeof(GF_SIDXReference)*sidx->nb_refs);
1926 :
1927 0 : if (root_sidx)
1928 0 : root_sidx->refs[sidx_idx].reference_type = GF_TRUE;
1929 :
1930 : /*remember start of sidx*/
1931 0 : local_sidx_start = gf_bs_get_position(movie->editFileMap->bs);
1932 :
1933 : /*write it*/
1934 0 : e = gf_isom_box_size((GF_Box *) sidx);
1935 0 : if (e) goto exit;
1936 0 : e = gf_isom_box_write((GF_Box *) sidx, movie->editFileMap->bs);
1937 0 : if (e) goto exit;
1938 :
1939 0 : local_sidx_end = gf_bs_get_position(movie->editFileMap->bs);
1940 :
1941 : /*adjust prev offset*/
1942 : last_top_box_pos = local_sidx_end;
1943 :
1944 0 : if (daisy_sidx) {
1945 : SIDXEntry *entry;
1946 0 : GF_SAFEALLOC(entry, SIDXEntry);
1947 0 : if (!entry) {
1948 : e = GF_OUT_OF_MEM;
1949 : goto exit;
1950 : }
1951 0 : entry->sidx = sidx;
1952 0 : entry->start_offset = local_sidx_start;
1953 0 : gf_list_add(daisy_sidx, entry);
1954 : }
1955 : }
1956 :
1957 3118 : offset_diff = (s32) (gf_bs_get_position(movie->editFileMap->bs) - movie->moof->fragment_offset);
1958 3118 : movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs);
1959 :
1960 3118 : if (!e) {
1961 : Bool generate_ssix = GF_FALSE;
1962 3118 : if (movie->root_ssix) generate_ssix = GF_TRUE;
1963 3088 : else if (use_ssix && ssix) generate_ssix = GF_TRUE;
1964 :
1965 3118 : e = StoreFragment(movie, GF_FALSE, offset_diff, &moof_size, GF_FALSE);
1966 3118 : if (e) {
1967 : e = GF_OUT_OF_MEM;
1968 : goto exit;
1969 : }
1970 :
1971 3118 : if (sidx) {
1972 2442 : u32 cur_index = idx_offset + cur_idx;
1973 :
1974 : /*do not compute earliest CTS if single segment sidx since we only have set the info for one subsegment*/
1975 2442 : if (!movie->root_sidx && first_frag_in_subseg) {
1976 2166 : u64 first_cts = get_presentation_time( ref_track_decode_time + sidx_dur + cur_dur + moof_get_earliest_cts(movie->moof, referenceTrackID), ts_shift);
1977 2166 : if (cur_index) {
1978 92 : u32 subseg_dur = (u32) (first_cts - prev_earliest_cts);
1979 92 : sidx->refs[cur_index-1].subsegment_duration = subseg_dur;
1980 92 : if (root_sidx) root_sidx->refs[sidx_idx].subsegment_duration += subseg_dur;
1981 : }
1982 : prev_earliest_cts = first_cts;
1983 : first_frag_in_subseg = GF_FALSE;
1984 : }
1985 :
1986 2442 : if (sidx->nb_refs<=cur_index) {
1987 276 : sidx->nb_refs = cur_index+1;
1988 276 : sidx->refs = gf_realloc(sidx->refs, sizeof(GF_SIDXReference)*sidx->nb_refs);
1989 276 : memset(&sidx->refs[cur_index], 0, sizeof(GF_SIDXReference));
1990 : }
1991 :
1992 : /*we refer to next moof*/
1993 2442 : sidx->refs[cur_index].reference_type = GF_FALSE;
1994 2442 : if (!sidx->refs[cur_index].SAP_type) {
1995 2442 : 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);
1996 2442 : if (sidx->refs[cur_index].SAP_type) {
1997 2428 : if (root_sidx && !root_sidx->refs[sidx_idx].SAP_type) {
1998 0 : root_sidx->refs[sidx_idx].SAP_type = sidx->refs[cur_index].SAP_type;
1999 0 : root_sidx->refs[sidx_idx].SAP_delta_time = sidx->refs[cur_index].SAP_delta_time;
2000 0 : root_sidx->refs[sidx_idx].starts_with_SAP = sidx->refs[cur_index].starts_with_SAP;
2001 : }
2002 : }
2003 : }
2004 2442 : cur_dur += moof_get_duration(movie->moof, referenceTrackID);
2005 :
2006 : /*reference size is end of the moof we just wrote minus last_box_pos*/
2007 2442 : sidx->refs[cur_index].reference_size += (u32) ( gf_bs_get_position(movie->editFileMap->bs) - last_top_box_pos) ;
2008 2442 : last_top_box_pos = gf_bs_get_position(movie->editFileMap->bs);
2009 :
2010 2442 : count++;
2011 :
2012 2442 : if (generate_ssix) {
2013 : u32 last_sseg_range0_size, remain_size;
2014 30 : if (movie->root_ssix) {
2015 : ssix = movie->root_ssix;
2016 30 : if (ssix->subsegment_count <= cur_index) {
2017 : assert(ssix->subsegment_count == cur_index);
2018 30 : ssix->subsegment_count = cur_index+1;
2019 30 : ssix->subsegment_alloc = ssix->subsegment_count;
2020 30 : ssix->subsegments = gf_realloc(ssix->subsegments, ssix->subsegment_count * sizeof(GF_SubsegmentInfo));
2021 30 : ssix->subsegments[cur_index].range_count = 2;
2022 30 : ssix->subsegments[cur_index].ranges = gf_malloc(sizeof(GF_SubsegmentRangeInfo)*2);
2023 : }
2024 : }
2025 : assert(ssix);
2026 30 : ssix->subsegments[cur_index].ranges[0].level = 1;
2027 30 : ssix->subsegments[cur_index].ranges[0].range_size = moof_get_first_sap_end(movie->moof);
2028 :
2029 30 : last_sseg_range0_size = (cur_index < ssix->subsegment_count) ? ssix->subsegments[cur_index].ranges[0].range_size : 0;
2030 30 : ssix->subsegments[cur_index].ranges[1].level = 2;
2031 :
2032 30 : remain_size = sidx->refs[cur_index].reference_size - last_sseg_range0_size;
2033 30 : ssix->subsegments[cur_index].ranges[1].range_size = remain_size;
2034 30 : if (remain_size>0xFFFFFF) {
2035 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso fragment] Remaining subsegment size %d larger than max ssix range size 0xFFFFFF, file might be broken\n", remain_size));
2036 : }
2037 :
2038 30 : if (movie->root_ssix)
2039 : ssix = NULL;
2040 : }
2041 :
2042 : /*we are switching subsegment*/
2043 2442 : frag_count--;
2044 :
2045 2442 : if (count==frags_per_subseg) {
2046 : count = 0;
2047 : first_frag_in_subseg = GF_TRUE;
2048 2442 : cur_idx++;
2049 : }
2050 :
2051 : /*switching to next SIDX*/
2052 2442 : if ((cur_idx==subseg_per_sidx) || !frag_count) {
2053 : u32 subseg_dur;
2054 : /*update last ref duration*/
2055 :
2056 : //get next segment earliest cts - if estimation failed, use ref_track_next_cts
2057 2350 : if ((next_earliest_cts==-1) || (next_earliest_cts < prev_earliest_cts)) {
2058 : u64 next_cts;
2059 2 : if (gf_list_count(movie->moof_list)) {
2060 0 : 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);
2061 : } else {
2062 : next_cts = get_presentation_time( ref_track_next_cts, ts_shift);
2063 : }
2064 2 : subseg_dur = (u32) (next_cts - prev_earliest_cts);
2065 : } else {
2066 2348 : subseg_dur = (u32) (next_earliest_cts - prev_earliest_cts);
2067 : }
2068 :
2069 2350 : if (movie->root_sidx) {
2070 276 : sidx->refs[idx_offset].subsegment_duration = subseg_dur;
2071 : }
2072 : /*if daisy chain and not the last sidx, we have an extra entry at the end*/
2073 2074 : else if (daisy_chain_sidx && (nb_subsegs>1)) {
2074 0 : sidx->refs[sidx->nb_refs - 2].subsegment_duration = subseg_dur;
2075 : } else {
2076 2074 : sidx->refs[sidx->nb_refs-1].subsegment_duration = subseg_dur;
2077 : }
2078 :
2079 2350 : if (root_sidx) {
2080 :
2081 0 : root_sidx->refs[sidx_idx].subsegment_duration += subseg_dur;
2082 :
2083 :
2084 0 : root_sidx->refs[sidx_idx].reference_size = (u32) (gf_bs_get_position(movie->editFileMap->bs) - local_sidx_start);
2085 0 : if (!sidx_idx) {
2086 0 : root_sidx->earliest_presentation_time = sidx->earliest_presentation_time;
2087 : }
2088 0 : sidx_rewrite(sidx, movie->editFileMap->bs, local_sidx_start, ssix);
2089 0 : gf_isom_box_del((GF_Box*)sidx);
2090 : sidx = NULL;
2091 2350 : } else if (daisy_chain_sidx) {
2092 0 : SIDXEntry *entry = (SIDXEntry*)gf_list_last(daisy_sidx);
2093 0 : entry->end_offset = gf_bs_get_position(movie->editFileMap->bs);
2094 0 : nb_subsegs--;
2095 : sidx = NULL;
2096 : }
2097 2350 : sidx_dur += cur_dur;
2098 : cur_dur = 0;
2099 : count = 0;
2100 : cur_idx=0;
2101 2350 : if (movie->root_sidx)
2102 276 : movie->root_sidx_index++;
2103 2350 : sidx_idx++;
2104 : }
2105 : }
2106 : }
2107 3118 : gf_isom_box_del((GF_Box *) movie->moof);
2108 3118 : movie->moof = NULL;
2109 : }
2110 :
2111 : /*append segment marker box*/
2112 3026 : if (segment_marker_4cc) {
2113 0 : gf_bs_write_u32(movie->editFileMap->bs, 8); //write size field
2114 0 : gf_bs_write_u32(movie->editFileMap->bs, segment_marker_4cc); //write box type field
2115 : }
2116 :
2117 3026 : if (movie->root_sidx) {
2118 276 : if (last_segment && !movie->dyn_root_sidx) {
2119 : assert(movie->root_sidx_index == movie->root_sidx->nb_refs);
2120 :
2121 0 : sidx_rewrite(movie->root_sidx, movie->editFileMap->bs, movie->root_sidx_offset, movie->root_ssix);
2122 0 : gf_isom_box_del((GF_Box*) movie->root_sidx);
2123 0 : movie->root_sidx = NULL;
2124 :
2125 0 : if (movie->root_ssix) {
2126 0 : gf_isom_box_del((GF_Box*)movie->root_ssix);
2127 0 : movie->root_ssix = NULL;
2128 : }
2129 : }
2130 276 : if (ssix)
2131 0 : gf_isom_box_del((GF_Box*)ssix);
2132 :
2133 276 : compute_seg_size(movie, out_seg_size);
2134 276 : goto exit;
2135 : }
2136 :
2137 2750 : if (sidx) {
2138 : assert(!root_sidx);
2139 2074 : sidx_rewrite(sidx, movie->editFileMap->bs, sidx_start, ssix);
2140 2074 : gf_isom_box_del((GF_Box*)sidx);
2141 : }
2142 2750 : if (ssix) {
2143 0 : gf_isom_box_del((GF_Box*)ssix);
2144 : ssix = NULL;
2145 : }
2146 :
2147 2750 : if (daisy_sidx) {
2148 : u32 i, j;
2149 : u64 last_entry_end_offset = 0;
2150 :
2151 0 : count = gf_list_count(daisy_sidx);
2152 0 : for (i=count; i>1; i--) {
2153 0 : SIDXEntry *entry = (SIDXEntry*)gf_list_get(daisy_sidx, i-2);
2154 0 : SIDXEntry *next_entry = (SIDXEntry*)gf_list_get(daisy_sidx, i-1);
2155 :
2156 0 : if (!last_entry_end_offset) {
2157 0 : last_entry_end_offset = next_entry->end_offset;
2158 : /*rewrite last sidx*/
2159 0 : sidx_rewrite(next_entry->sidx, movie->editFileMap->bs, next_entry->start_offset, NULL);
2160 : }
2161 : /*copy over SAP info for last item (which points to next item !)*/
2162 0 : entry->sidx->refs[entry->sidx->nb_refs-1] = next_entry->sidx->refs[0];
2163 : /*and rewrite reference type, size and dur*/
2164 0 : entry->sidx->refs[entry->sidx->nb_refs-1].reference_type = GF_TRUE;
2165 0 : entry->sidx->refs[entry->sidx->nb_refs-1].reference_size = (u32) (last_entry_end_offset - next_entry->start_offset);
2166 0 : entry->sidx->refs[entry->sidx->nb_refs-1].subsegment_duration = 0;
2167 0 : for (j=0; j<next_entry->sidx->nb_refs; j++) {
2168 0 : entry->sidx->refs[entry->sidx->nb_refs-1].subsegment_duration += next_entry->sidx->refs[j].subsegment_duration;
2169 : }
2170 0 : sidx_rewrite(entry->sidx, movie->editFileMap->bs, entry->start_offset, NULL);
2171 : }
2172 0 : while (gf_list_count(daisy_sidx)) {
2173 0 : SIDXEntry *entry = (SIDXEntry*)gf_list_last(daisy_sidx);
2174 0 : gf_isom_box_del((GF_Box*)entry->sidx);
2175 0 : gf_free(entry);
2176 0 : gf_list_rem_last(daisy_sidx);
2177 : }
2178 0 : gf_list_del(daisy_sidx);
2179 : }
2180 2750 : if (root_sidx) {
2181 0 : sidx_rewrite(root_sidx, movie->editFileMap->bs, sidx_start, NULL);
2182 0 : gf_isom_box_del((GF_Box*)root_sidx);
2183 : }
2184 :
2185 2750 : if ((root_sidx || sidx) && !daisy_chain_sidx) {
2186 2074 : if (index_start_range) *index_start_range = sidx_start;
2187 2074 : if (index_end_range) *index_end_range = sidx_end - 1;
2188 : }
2189 :
2190 2750 : if (movie->append_segment) {
2191 : char bloc[1024];
2192 0 : u32 seg_size = (u32) gf_bs_get_size(movie->editFileMap->bs);
2193 0 : gf_bs_seek(movie->editFileMap->bs, 0);
2194 0 : while (seg_size) {
2195 0 : u32 size = gf_bs_read_data(movie->editFileMap->bs, bloc, (seg_size>1024) ? 1024 : seg_size);
2196 0 : gf_bs_write_data(movie->movieFileMap->bs, bloc, size);
2197 0 : seg_size -= size;
2198 : }
2199 0 : gf_isom_datamap_del(movie->editFileMap);
2200 0 : movie->editFileMap = gf_isom_fdm_new_temp(NULL);
2201 2750 : } else if (close_segment_handle == GF_TRUE) {
2202 0 : gf_isom_datamap_del(movie->editFileMap);
2203 0 : movie->editFileMap = NULL;
2204 : }
2205 2750 : compute_seg_size(movie, out_seg_size);
2206 :
2207 3026 : exit:
2208 3026 : if (movie->editFileMap && (orig_bs != movie->editFileMap->bs)) {
2209 : u32 tmpsize;
2210 3026 : gf_bs_get_content_no_truncate(movie->editFileMap->bs, &movie->block_buffer, &tmpsize, &movie->block_buffer_size);
2211 3026 : gf_bs_del(movie->editFileMap->bs);
2212 3026 : movie->editFileMap->bs = orig_bs;
2213 : }
2214 : return e;
2215 : }
2216 :
2217 : GF_EXPORT
2218 17 : GF_Err gf_isom_flush_sidx(GF_ISOFile *movie, u32 sidx_max_size, Bool force_v1)
2219 : {
2220 : GF_BitStream *bs;
2221 : GF_Err e;
2222 : u32 size;
2223 : //and only at setup
2224 17 : if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
2225 17 : if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
2226 :
2227 17 : if (! movie->on_block_out) return GF_BAD_PARAM;
2228 17 : if (! movie->root_sidx) return GF_BAD_PARAM;
2229 :
2230 17 : if (!movie->block_buffer_size) movie->block_buffer_size = movie->on_block_out_block_size;
2231 17 : bs = gf_bs_new_cbk_buffer(movie->on_block_out, movie->on_block_out_usr_data, movie->block_buffer, movie->block_buffer_size);
2232 17 : gf_bs_prevent_dispatch(bs, GF_TRUE);
2233 :
2234 : assert(movie->root_sidx_index == movie->root_sidx->nb_refs);
2235 :
2236 17 : if (force_v1)
2237 0 : movie->root_sidx->version = 1;
2238 :
2239 17 : e = gf_isom_box_size((GF_Box*)movie->root_sidx);
2240 17 : size = (u32) movie->root_sidx->size;
2241 17 : if (movie->root_ssix) {
2242 1 : e = gf_isom_box_size((GF_Box*)movie->root_ssix);
2243 1 : size += (u32) movie->root_ssix->size;
2244 1 : movie->root_sidx->first_offset = (u32) movie->root_ssix->size;
2245 : }
2246 :
2247 17 : if (sidx_max_size && (size > sidx_max_size) ) {
2248 : #ifndef GPAC_DISABLE_LOG
2249 0 : u32 orig_seg_count = movie->root_sidx->nb_refs;
2250 : #endif
2251 : //trash 8 bytes to be able to write a free box before
2252 0 : sidx_max_size -= 8;
2253 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso fragment] SIDX size %d is larger than allocated SIDX block %d, merging final segments\n", movie->root_sidx->size, sidx_max_size));
2254 0 : while (movie->root_sidx->nb_refs>2) {
2255 0 : movie->root_sidx->refs[movie->root_sidx->nb_refs-2].subsegment_duration += movie->root_sidx->refs[movie->root_sidx->nb_refs-1].subsegment_duration;
2256 0 : movie->root_sidx->refs[movie->root_sidx->nb_refs-2].reference_size += movie->root_sidx->refs[movie->root_sidx->nb_refs-1].reference_size;
2257 0 : movie->root_sidx->nb_refs--;
2258 0 : if (movie->root_ssix) {
2259 0 : movie->root_ssix->subsegments[movie->root_ssix->subsegment_count-2].ranges[1].range_size += movie->root_ssix->subsegments[movie->root_ssix->subsegment_count-1].ranges[0].range_size;
2260 0 : movie->root_ssix->subsegments[movie->root_ssix->subsegment_count-2].ranges[1].range_size += movie->root_ssix->subsegments[movie->root_ssix->subsegment_count-1].ranges[1].range_size;
2261 0 : movie->root_ssix->subsegment_count--;
2262 : }
2263 :
2264 0 : e = gf_isom_box_size((GF_Box*)movie->root_sidx);
2265 0 : size = (u32) movie->root_sidx->size;
2266 0 : if (movie->root_ssix) {
2267 0 : e = gf_isom_box_size((GF_Box*)movie->root_ssix);
2268 0 : size += (u32) movie->root_ssix->size;
2269 0 : movie->root_sidx->first_offset = (u32) movie->root_ssix->size;
2270 : }
2271 :
2272 0 : if (size < sidx_max_size) break;
2273 : }
2274 0 : if (size > sidx_max_size) {
2275 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso fragment] SIDX size %d is larger than allocated SIDX block and no more segments to merge\n", size, sidx_max_size));
2276 : return GF_IO_ERR;
2277 : } else {
2278 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso fragment] Merged %d segments in SIDX to fit allocated block, remaining segments %d\n", orig_seg_count - movie->root_sidx->nb_refs, movie->root_sidx->nb_refs));
2279 : }
2280 : }
2281 17 : if (!e) {
2282 17 : if (movie->root_ssix) {
2283 1 : gf_isom_box_size((GF_Box *) movie->root_ssix);
2284 :
2285 1 : if (movie->compress_mode>=GF_ISOM_COMP_MOOF_SSIX) {
2286 : u32 ssix_comp_size;
2287 : //compute ssix compressed size by using NULL destination bitstream
2288 : //not really optimum since we compress twice the ssix, to optimize ...
2289 0 : e = gf_isom_write_compressed_box(movie, (GF_Box *) movie->root_ssix, GF_4CC('!', 's', 's', 'x'), NULL, &ssix_comp_size);
2290 0 : movie->root_sidx->first_offset = ssix_comp_size;
2291 : } else {
2292 1 : movie->root_sidx->first_offset = (u32) movie->root_ssix->size;
2293 : }
2294 : }
2295 17 : if (!e) {
2296 17 : if (movie->compress_mode>=GF_ISOM_COMP_MOOF_SIDX) {
2297 1 : e = gf_isom_write_compressed_box(movie, (GF_Box *) movie->root_sidx, GF_4CC('!', 's', 'i', 'x'), bs, NULL);
2298 : } else {
2299 16 : e = gf_isom_box_write((GF_Box *) movie->root_sidx, bs);
2300 : }
2301 : }
2302 :
2303 17 : if (!e && movie->root_ssix) {
2304 1 : if (movie->compress_mode>=GF_ISOM_COMP_MOOF_SSIX) {
2305 0 : e = gf_isom_write_compressed_box(movie, (GF_Box *) movie->root_ssix, GF_4CC('!', 's', 's', 'x'), bs, NULL);
2306 : } else {
2307 1 : e = gf_isom_box_write((GF_Box *) movie->root_ssix, bs);
2308 : }
2309 : }
2310 : }
2311 :
2312 17 : gf_isom_box_del((GF_Box*) movie->root_sidx);
2313 17 : movie->root_sidx = NULL;
2314 17 : if (movie->root_ssix) {
2315 1 : gf_isom_box_del((GF_Box*) movie->root_ssix);
2316 1 : movie->root_ssix = NULL;
2317 : }
2318 :
2319 17 : gf_bs_get_content_no_truncate(bs, &movie->block_buffer, &size, &movie->block_buffer_size);
2320 17 : gf_bs_del(bs);
2321 17 : return e;
2322 : }
2323 :
2324 : GF_EXPORT
2325 30 : GF_Err gf_isom_close_fragments(GF_ISOFile *movie)
2326 : {
2327 30 : if (movie->use_segments) {
2328 17 : return gf_isom_close_segment(movie, 0, 0, 0, 0, 0, 0, GF_FALSE, GF_FALSE, 1, 0, NULL, NULL, NULL);
2329 : } else {
2330 13 : return StoreFragment(movie, GF_FALSE, 0, NULL, GF_TRUE);
2331 : }
2332 : }
2333 :
2334 : GF_EXPORT
2335 3044 : GF_Err gf_isom_start_segment(GF_ISOFile *movie, const char *SegName, Bool memory_mode)
2336 : {
2337 : GF_Err e;
2338 : //and only at setup
2339 3044 : if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
2340 3044 : if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
2341 :
2342 3044 : if (gf_list_count(movie->moof_list))
2343 : return GF_BAD_PARAM;
2344 :
2345 3044 : movie->segment_bs = NULL;
2346 3044 : movie->append_segment = GF_FALSE;
2347 : /*update segment file*/
2348 3044 : if (SegName || !gf_isom_get_filename(movie)) {
2349 2503 : if (movie->editFileMap) gf_isom_datamap_del(movie->editFileMap);
2350 2503 : e = gf_isom_datamap_new(SegName, NULL, GF_ISOM_DATA_MAP_WRITE, &movie->editFileMap);
2351 2503 : movie->segment_start = 0;
2352 2503 : movie->styp_written = GF_FALSE;
2353 2503 : if (e) return e;
2354 : } else {
2355 : assert(gf_list_count(movie->moof_list) == 0);
2356 541 : movie->segment_start = gf_bs_get_position(movie->editFileMap->bs);
2357 : /*if movieFileMap is not null, we are concatenating segments to the original movie, force a copy*/
2358 541 : if (movie->movieFileMap)
2359 0 : movie->append_segment = GF_TRUE;
2360 541 : movie->styp_written = GF_TRUE;
2361 : }
2362 :
2363 : /*create a memory bitstream for all file IO until final flush*/
2364 3044 : if (memory_mode) {
2365 0 : movie->segment_bs = movie->editFileMap->bs;
2366 0 : movie->editFileMap->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
2367 : }
2368 : return GF_OK;
2369 : }
2370 :
2371 : GF_EXPORT
2372 1 : GF_Err gf_isom_set_fragment_reference_time(GF_ISOFile *movie, GF_ISOTrackID reference_track_ID, u64 ntp, u64 timestamp)
2373 : {
2374 1 : if (!movie || !movie->moof) return GF_BAD_PARAM;
2375 0 : movie->moof->reference_track_ID = reference_track_ID;
2376 0 : movie->moof->ntp = ntp;
2377 0 : movie->moof->timestamp = timestamp;
2378 0 : return GF_OK;
2379 : }
2380 :
2381 : GF_EXPORT
2382 1 : GF_Err gf_isom_set_traf_mss_timeext(GF_ISOFile *movie, GF_ISOTrackID reference_track_ID, u64 ntp_in_track_timescale, u64 traf_duration_in_track_timescale)
2383 : {
2384 : u32 i;
2385 1 : if (!movie || !movie->moof)
2386 : return GF_BAD_PARAM;
2387 0 : for (i=0; i<gf_list_count(movie->moof->TrackList); i++) {
2388 0 : GF_TrackFragmentBox *traf = (GF_TrackFragmentBox*)gf_list_get(movie->moof->TrackList, i);
2389 0 : if (!traf)
2390 : return GF_BAD_PARAM;
2391 0 : if (traf->tfxd)
2392 0 : gf_isom_box_del_parent(&traf->child_boxes, (GF_Box*)traf->tfxd);
2393 0 : traf->tfxd = (GF_MSSTimeExtBox *)gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_UUID_TFXD);
2394 0 : if (!traf->tfxd) return GF_OUT_OF_MEM;
2395 0 : traf->tfxd->absolute_time_in_track_timescale = ntp_in_track_timescale;
2396 0 : traf->tfxd->fragment_duration_in_track_timescale = traf_duration_in_track_timescale;
2397 : }
2398 : return GF_OK;
2399 : }
2400 :
2401 : GF_EXPORT
2402 3571 : GF_Err gf_isom_start_fragment(GF_ISOFile *movie, GF_ISOStartFragmentFlags flags)
2403 : {
2404 : u32 i, count;
2405 : GF_TrackExtendsBox *trex;
2406 : GF_TrackFragmentBox *traf;
2407 : GF_Err e;
2408 3571 : Bool moof_first = (flags & GF_ISOM_FRAG_MOOF_FIRST) ? GF_TRUE : GF_FALSE;
2409 : #ifdef GF_ENABLE_CTRN
2410 : Bool use_ctrn = (flags & GF_ISOM_FRAG_USE_COMPACT) ? GF_TRUE : GF_FALSE;
2411 : #endif
2412 :
2413 : //and only at setup
2414 3571 : if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) )
2415 : return GF_BAD_PARAM;
2416 3571 : if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE;
2417 :
2418 3571 : count = gf_list_count(movie->moov->mvex->TrackExList);
2419 3571 : if (!count)
2420 : return GF_BAD_PARAM;
2421 :
2422 : /*always force cached mode when writing movie segments*/
2423 3571 : if (movie->use_segments) moof_first = GF_TRUE;
2424 3571 : movie->moof_first = moof_first;
2425 :
2426 : //store existing fragment
2427 3571 : if (movie->moof) {
2428 92 : e = StoreFragment(movie, movie->use_segments ? GF_TRUE : GF_FALSE, 0, NULL, movie->use_segments ? GF_TRUE : GF_FALSE);
2429 92 : if (e) return e;
2430 : }
2431 :
2432 : //create new fragment
2433 3571 : movie->moof = (GF_MovieFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MOOF);
2434 3571 : if (!movie->moof) return GF_OUT_OF_MEM;
2435 3571 : movie->moof->mfhd = (GF_MovieFragmentHeaderBox *) gf_isom_box_new_parent(&movie->moof->child_boxes, GF_ISOM_BOX_TYPE_MFHD);
2436 3571 : if (!movie->moof->mfhd) return GF_OUT_OF_MEM;
2437 3571 : movie->moof->mfhd->sequence_number = movie->NextMoofNumber;
2438 3571 : movie->NextMoofNumber ++;
2439 3571 : if (movie->use_segments || movie->on_block_out)
2440 3571 : gf_list_add(movie->moof_list, movie->moof);
2441 :
2442 :
2443 : /*remember segment offset*/
2444 3571 : movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs);
2445 :
2446 : /*prepare MDAT*/
2447 3571 : gf_bs_write_u32(movie->editFileMap->bs, 0);
2448 3571 : gf_bs_write_u32(movie->editFileMap->bs, GF_ISOM_BOX_TYPE_MDAT);
2449 :
2450 : //we create a TRAF for each setup track, unused ones will be removed at store time
2451 8153 : for (i=0; i<count; i++) {
2452 4582 : trex = (GF_TrackExtendsBox*)gf_list_get(movie->moov->mvex->TrackExList, i);
2453 4582 : traf = (GF_TrackFragmentBox *) gf_isom_box_new_parent(&movie->moof->child_boxes, GF_ISOM_BOX_TYPE_TRAF);
2454 4582 : if (!traf) return GF_OUT_OF_MEM;
2455 4582 : traf->trex = trex;
2456 4582 : traf->tfhd = (GF_TrackFragmentHeaderBox *) gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_TYPE_TFHD);
2457 4582 : if (!traf->tfhd) return GF_OUT_OF_MEM;
2458 4582 : traf->tfhd->trackID = trex->trackID;
2459 : //add 8 bytes (MDAT size+type) to avoid the data_offset in the first trun
2460 4582 : traf->tfhd->base_data_offset = movie->moof->fragment_offset + 8;
2461 : #ifdef GF_ENABLE_CTRN
2462 : traf->use_ctrn = use_ctrn;
2463 : if (trex->inherit_from_traf_id)
2464 : traf->use_inherit = GF_TRUE;
2465 : #endif
2466 4582 : gf_list_add(movie->moof->TrackList, traf);
2467 :
2468 4582 : if (movie->mfra) {
2469 : GF_TrackFragmentRandomAccessBox *tfra;
2470 : GF_RandomAccessEntry *raf;
2471 24 : if (!traf->trex->tfra) {
2472 3 : tfra = (GF_TrackFragmentRandomAccessBox *)gf_isom_box_new_parent(&movie->mfra->child_boxes, GF_ISOM_BOX_TYPE_TFRA);
2473 3 : if (!tfra) return GF_OUT_OF_MEM;
2474 3 : tfra->track_id = traf->trex->trackID;
2475 3 : tfra->traf_bits = 8;
2476 3 : tfra->trun_bits = 8;
2477 3 : tfra->sample_bits = 8;
2478 3 : gf_list_add(movie->mfra->tfra_list, tfra);
2479 3 : traf->trex->tfra = tfra;
2480 : }
2481 24 : tfra = traf->trex->tfra;
2482 24 : tfra->entries = (GF_RandomAccessEntry *)gf_realloc(tfra->entries, (tfra->nb_entries+1)*sizeof(GF_RandomAccessEntry));
2483 24 : tfra->nb_entries++;
2484 24 : raf = &tfra->entries[tfra->nb_entries-1];
2485 24 : raf->sample_number = 1;
2486 24 : raf->time = 0;
2487 24 : raf->traf_number = i+1;
2488 : //trun number is set once we fond a sync
2489 24 : raf->trun_number = 0;
2490 24 : if (!strcmp(movie->fileName, "_gpac_isobmff_redirect")) {
2491 24 : raf->moof_offset = movie->fragmented_file_pos;
2492 : } else {
2493 0 : raf->moof_offset = movie->moof->fragment_offset;
2494 : }
2495 : }
2496 : }
2497 : return GF_OK;
2498 : }
2499 :
2500 :
2501 57 : GF_Err gf_isom_set_fragment_template(GF_ISOFile *movie, u8 *tpl_data, u32 tpl_size, Bool *has_tfdt, GF_SegmentIndexBox **out_sidx)
2502 : {
2503 : GF_BitStream *bs;
2504 : GF_Err e=GF_OK;
2505 57 : if (out_sidx) *out_sidx = NULL;
2506 57 : if (!movie->moof) return GF_BAD_PARAM;
2507 :
2508 57 : bs = gf_bs_new(tpl_data, tpl_size, GF_BITSTREAM_READ);
2509 285 : while (gf_bs_available(bs)) {
2510 : GF_Box *a;
2511 171 : e = gf_isom_box_parse(&a, bs);
2512 171 : if (e) break;
2513 171 : if (a->type==GF_ISOM_BOX_TYPE_STYP) {
2514 57 : if (movie->brand) {
2515 57 : gf_list_del_item(movie->TopBoxes, movie->brand);
2516 57 : gf_isom_box_del((GF_Box *)movie->brand);
2517 : }
2518 57 : movie->brand = (GF_FileTypeBox *) a;
2519 57 : gf_list_add(movie->TopBoxes, movie->brand);
2520 228 : continue;
2521 : }
2522 114 : if (a->type==GF_ISOM_BOX_TYPE_OTYP) {
2523 0 : if (movie->otyp) {
2524 0 : gf_list_del_item(movie->TopBoxes, movie->otyp);
2525 0 : gf_isom_box_del(movie->otyp);
2526 : }
2527 0 : movie->otyp = (GF_Box *) a;
2528 0 : gf_list_add(movie->TopBoxes, movie->otyp);
2529 0 : continue;
2530 : }
2531 114 : if (a->type==GF_ISOM_BOX_TYPE_MOOF) {
2532 : u32 i, count, j, nb_trex;
2533 : s32 idx;
2534 : Bool single_track=GF_FALSE;
2535 : GF_MovieFragmentBox *moof = (GF_MovieFragmentBox *)a;
2536 :
2537 57 : moof->fragment_offset = movie->moof->fragment_offset;
2538 57 : nb_trex = gf_list_count(movie->moov->mvex->TrackExList);
2539 57 : count = gf_list_count(moof->TrackList);
2540 114 : for (i=0; i<count; i++) {
2541 57 : GF_TrackFragmentBox *traf = gf_list_get(moof->TrackList, i);
2542 57 : GF_TrackBox *trak = traf->tfhd ? gf_isom_get_track_from_id(movie->moov, traf->tfhd->trackID) : NULL;
2543 57 : if (traf->tfhd && !trak && !single_track && (gf_list_count(movie->moov->trackList)==1)) {
2544 0 : trak = gf_list_get(movie->moov->trackList, 0);
2545 : single_track = GF_TRUE;
2546 0 : traf->tfhd->trackID = trak->Header->trackID;
2547 : }
2548 57 : for (j=0; j<nb_trex && trak; j++) {
2549 57 : GF_TrackExtendsBox *trex = gf_list_get(movie->moov->mvex->TrackExList, j);
2550 57 : if (trex->trackID == traf->tfhd->trackID) {
2551 57 : traf->trex = trex;
2552 57 : break;
2553 : }
2554 : }
2555 57 : if (!trak || !traf->trex) {
2556 0 : gf_list_rem(moof->TrackList, i);
2557 0 : i--;
2558 0 : count--;
2559 0 : gf_isom_box_del((GF_Box*)traf);
2560 0 : continue;
2561 : }
2562 57 : traf->tfhd->base_data_offset = movie->moof->fragment_offset + 8;
2563 57 : if (traf->tfdt && has_tfdt)
2564 57 : *has_tfdt = GF_TRUE;
2565 : }
2566 : //remove old moof and switch with this one
2567 57 : idx = gf_list_find(movie->moof_list, movie->moof);
2568 57 : if (idx>=0) {
2569 57 : gf_list_rem(movie->moof_list, idx);
2570 57 : gf_list_add(movie->moof_list, moof);
2571 : }
2572 57 : gf_isom_box_del((GF_Box *)movie->moof);
2573 57 : movie->moof = moof;
2574 57 : continue;
2575 : }
2576 57 : if (a->type==GF_ISOM_BOX_TYPE_SIDX) {
2577 57 : if (out_sidx && !*out_sidx) {
2578 57 : *out_sidx = (GF_SegmentIndexBox *) a;
2579 57 : continue;
2580 : }
2581 : }
2582 0 : gf_isom_box_del(a);
2583 : }
2584 57 : gf_bs_del(bs);
2585 57 : return e;
2586 : }
2587 :
2588 : static
2589 : u32 GetRunSize(GF_TrackFragmentRunBox *trun)
2590 : {
2591 : u32 i, size=0;
2592 2883243 : for (i=0; i<trun->nb_samples; i++) {
2593 2883243 : size += trun->samples[i].size;
2594 : }
2595 : return size;
2596 : }
2597 :
2598 : GF_EXPORT
2599 111914 : GF_Err gf_isom_fragment_add_sample(GF_ISOFile *movie, GF_ISOTrackID TrackID, const GF_ISOSample *sample, u32 DescIndex,
2600 : u32 Duration, u8 PaddingBits, u16 DegradationPriority, Bool redundant_coding)
2601 : {
2602 : u32 count, buffer_size;
2603 : u8 *buffer;
2604 : u64 pos;
2605 111914 : GF_ISOSample *od_sample = NULL;
2606 : GF_TrunEntry ent, *prev_ent;
2607 : GF_TrackFragmentBox *traf, *traf_2;
2608 : GF_TrackFragmentRunBox *trun;
2609 :
2610 111914 : if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) || !sample)
2611 : return GF_BAD_PARAM;
2612 :
2613 111914 : traf = gf_isom_get_traf(movie, TrackID);
2614 111914 : if (!traf)
2615 : return GF_BAD_PARAM;
2616 :
2617 111914 : if (!traf->tfhd->sample_desc_index)
2618 3762 : traf->tfhd->sample_desc_index = DescIndex ? DescIndex : traf->trex->def_sample_desc_index;
2619 :
2620 111914 : pos = gf_bs_get_position(movie->editFileMap->bs);
2621 :
2622 :
2623 : //WARNING: we change stream description, create a new TRAF
2624 111914 : if ( DescIndex && (traf->tfhd->sample_desc_index != DescIndex)) {
2625 : //if we're caching flush the current run
2626 2 : if (traf->DataCache && !traf->use_sample_interleave) {
2627 0 : count = gf_list_count(traf->TrackRuns);
2628 0 : if (count) {
2629 0 : trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
2630 0 : trun->data_offset = (u32) (pos - movie->moof->fragment_offset - 8);
2631 0 : gf_bs_get_content(trun->cache, &buffer, &buffer_size);
2632 0 : gf_bs_write_data(movie->editFileMap->bs, buffer, buffer_size);
2633 0 : gf_bs_del(trun->cache);
2634 0 : trun->cache = NULL;
2635 0 : gf_free(buffer);
2636 : }
2637 : }
2638 2 : traf_2 = (GF_TrackFragmentBox *) gf_isom_box_new_parent(&movie->moof->child_boxes, GF_ISOM_BOX_TYPE_TRAF);
2639 2 : if (!traf_2) return GF_OUT_OF_MEM;
2640 2 : traf_2->trex = traf->trex;
2641 2 : traf_2->tfhd = (GF_TrackFragmentHeaderBox *) gf_isom_box_new_parent(&traf_2->child_boxes, GF_ISOM_BOX_TYPE_TFHD);
2642 2 : if (!traf_2->tfhd) return GF_OUT_OF_MEM;
2643 2 : traf_2->tfhd->trackID = traf->tfhd->trackID;
2644 : //keep the same offset
2645 2 : traf_2->tfhd->base_data_offset = movie->moof->fragment_offset + 8;
2646 2 : gf_list_add(movie->moof->TrackList, traf_2);
2647 :
2648 : //duplicate infos
2649 2 : traf_2->IFrameSwitching = traf->IFrameSwitching;
2650 2 : traf_2->use_sample_interleave = traf->use_sample_interleave;
2651 2 : traf_2->interleave_id = traf->interleave_id;
2652 2 : traf_2->truns_first = traf->truns_first;
2653 2 : traf_2->truns_v1 = traf->truns_v1;
2654 2 : traf_2->large_tfdt = traf->large_tfdt;
2655 2 : traf_2->DataCache = traf->DataCache;
2656 2 : traf_2->tfhd->sample_desc_index = DescIndex;
2657 :
2658 : //switch them ...
2659 : traf = traf_2;
2660 : }
2661 :
2662 111914 : pos = gf_bs_get_position(movie->editFileMap->bs);
2663 : //check if we need a new trun entry
2664 111914 : count = (traf->use_sample_interleave && traf->force_new_trun) ? 0 : gf_list_count(traf->TrackRuns);
2665 111914 : if (count) {
2666 108150 : trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
2667 : //check data offset when no caching as trun entries shall ALWAYS be contiguous samples
2668 216300 : if (!traf->DataCache && (movie->moof->fragment_offset + 8 + trun->data_offset + GetRunSize(trun) != pos) )
2669 : count = 0;
2670 :
2671 : //check I-frame detection
2672 108150 : if (traf->IFrameSwitching && sample->IsRAP)
2673 : count = 0;
2674 :
2675 108150 : if (traf->DataCache && (traf->DataCache==trun->sample_count) && !traf->use_sample_interleave)
2676 : count = 0;
2677 :
2678 108150 : if (traf->force_new_trun)
2679 : count = 0;
2680 :
2681 : //if data cache is on and we're changing TRUN, store the cache and update data offset
2682 108150 : if (!count && traf->DataCache && !traf->use_sample_interleave) {
2683 0 : trun->data_offset = (u32) (pos - movie->moof->fragment_offset - 8);
2684 0 : gf_bs_get_content(trun->cache, &buffer, &buffer_size);
2685 0 : gf_bs_write_data(movie->editFileMap->bs, buffer, buffer_size);
2686 0 : gf_bs_del(trun->cache);
2687 0 : trun->cache = NULL;
2688 0 : gf_free(buffer);
2689 : }
2690 : }
2691 111914 : traf->force_new_trun = 0;
2692 :
2693 : //new run
2694 111914 : if (!count) {
2695 3775 : trun = (GF_TrackFragmentRunBox *) gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_TYPE_TRUN);
2696 3775 : if (!trun) return GF_OUT_OF_MEM;
2697 : //store data offset (we have the 8 btyes offset of the MDAT)
2698 3775 : trun->data_offset = (u32) (pos - movie->moof->fragment_offset - 8);
2699 3775 : gf_list_add(traf->TrackRuns, trun);
2700 : #ifdef GF_ENABLE_CTRN
2701 : trun->use_ctrn = traf->use_ctrn;
2702 : trun->use_inherit = traf->use_inherit;
2703 : trun->ctso_multiplier = traf->trex->def_sample_duration;
2704 : #endif
2705 3775 : trun->interleave_id = traf->interleave_id;
2706 3775 : if (traf->truns_v1)
2707 0 : trun->version = 1;
2708 :
2709 : //if we use data caching, create a bitstream
2710 3775 : if (traf->DataCache)
2711 0 : trun->cache = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
2712 : }
2713 :
2714 : memset(&ent, 0, sizeof(GF_TrunEntry));
2715 111914 : ent.CTS_Offset = sample->CTS_Offset;
2716 111914 : ent.Duration = Duration;
2717 111914 : ent.dts = sample->DTS;
2718 111914 : ent.size = sample->dataLength;
2719 111914 : ent.nb_pack = sample->nb_pack;
2720 111914 : ent.flags = GF_ISOM_FORMAT_FRAG_FLAGS(PaddingBits, sample->IsRAP, DegradationPriority);
2721 111914 : if (sample->IsRAP) {
2722 33220 : ent.flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(0, 2, 0, (redundant_coding ? 1 : 0) );
2723 33220 : ent.SAP_type = sample->IsRAP;
2724 : }
2725 111914 : if (trun->nb_samples) {
2726 108139 : prev_ent = &trun->samples[trun->nb_samples-1];
2727 : } else {
2728 : prev_ent = NULL;
2729 : }
2730 :
2731 108139 : if (prev_ent && prev_ent->dts && sample->DTS) {
2732 107786 : u32 nsamp = prev_ent->nb_pack ? prev_ent->nb_pack : 1;
2733 107786 : if (nsamp*prev_ent->Duration != sample->DTS - prev_ent->dts)
2734 2533 : prev_ent->Duration = (u32) (sample->DTS - prev_ent->dts) / nsamp;
2735 : }
2736 111914 : if (trun->nb_samples >= trun->sample_alloc) {
2737 4149 : trun->sample_alloc += 50;
2738 4149 : if (trun->nb_samples >= trun->sample_alloc) trun->sample_alloc = trun->nb_samples+1;
2739 4149 : trun->samples = gf_realloc(trun->samples, sizeof(GF_TrunEntry)*trun->sample_alloc);
2740 4149 : if (!trun->samples) return GF_OUT_OF_MEM;
2741 : }
2742 111914 : trun->samples[trun->nb_samples] = ent;
2743 111914 : trun->nb_samples ++;
2744 :
2745 111914 : if (sample->CTS_Offset<0) {
2746 1488 : trun->version = 1;
2747 : }
2748 111914 : trun->sample_count += sample->nb_pack ? sample->nb_pack : 1;
2749 :
2750 : //rewrite OD frames
2751 111914 : if (traf->trex->track->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
2752 : //this may fail if dependencies are not well done ...
2753 2 : Media_ParseODFrame(traf->trex->track->Media, sample, &od_sample);
2754 2 : sample = od_sample;
2755 : }
2756 :
2757 : //finally write the data
2758 111914 : if (sample->dataLength) {
2759 111914 : if (!traf->DataCache) {
2760 111914 : if (!gf_bs_write_data(movie->editFileMap->bs, sample->data, sample->dataLength)) {
2761 0 : 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));
2762 : return GF_OUT_OF_MEM;
2763 : }
2764 0 : } else if (trun->cache) {
2765 0 : if (!gf_bs_write_data(trun->cache, sample->data, sample->dataLength)) {
2766 0 : 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));
2767 : return GF_OUT_OF_MEM;
2768 : }
2769 : } else {
2770 : return GF_BAD_PARAM;
2771 : }
2772 : }
2773 111914 : if (od_sample) gf_isom_sample_del(&od_sample);
2774 :
2775 111914 : if (traf->trex->tfra) {
2776 : GF_RandomAccessEntry *raf;
2777 514 : raf = &traf->trex->tfra->entries[traf->trex->tfra->nb_entries-1];
2778 : //if sample is sync, store its time and trun number
2779 514 : if (!raf->trun_number && sample->IsRAP) {
2780 18 : raf->time = sample->DTS + sample->CTS_Offset;
2781 18 : raf->trun_number = gf_list_count(traf->TrackRuns);
2782 18 : raf->sample_number = trun->sample_count;
2783 : }
2784 : }
2785 : return GF_OK;
2786 : }
2787 :
2788 : GF_EXPORT
2789 20396 : GF_Err gf_isom_fragment_set_cenc_sai(GF_ISOFile *output, GF_ISOTrackID TrackID, u8 *sai_b, u32 sai_b_size, Bool use_subsamples, Bool use_saio_32bit, Bool use_multikey)
2790 : {
2791 : GF_CENCSampleAuxInfo *sai;
2792 20396 : GF_TrackFragmentBox *traf = gf_isom_get_traf(output, TrackID);
2793 : GF_SampleEncryptionBox *senc;
2794 :
2795 20396 : if (!traf) return GF_BAD_PARAM;
2796 :
2797 20396 : if (!traf->sample_encryption) {
2798 344 : if (!traf->trex->track->sample_encryption) {
2799 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isofile] trying to add CENC SAI without storage box allocated\n" ));
2800 : return GF_BAD_PARAM;
2801 : }
2802 344 : if (traf->trex->track->sample_encryption->type == GF_ISOM_BOX_TYPE_SENC) {
2803 332 : traf->sample_encryption = gf_isom_create_samp_enc_box(0, 0);
2804 : } else {
2805 : GF_SampleEncryptionBox *psec = (GF_SampleEncryptionBox *) traf->trex->track->sample_encryption;
2806 : if (!psec) return GF_ISOM_INVALID_FILE;
2807 12 : traf->sample_encryption = gf_isom_create_piff_psec_box(1, 0, psec->AlgorithmID, psec->IV_size, psec->KID);
2808 : }
2809 344 : if (!traf->sample_encryption) return GF_OUT_OF_MEM;
2810 344 : traf->sample_encryption->traf = traf;
2811 :
2812 344 : if (!traf->child_boxes) traf->child_boxes = gf_list_new();
2813 344 : gf_list_add(traf->child_boxes, traf->sample_encryption);
2814 : }
2815 20396 : senc = (GF_SampleEncryptionBox *) traf->sample_encryption;
2816 :
2817 20396 : if (!sai_b_size && !sai_b) {
2818 2737 : gf_isom_cenc_set_saiz_saio(senc, NULL, traf, 0, use_saio_32bit, use_multikey);
2819 2737 : return GF_OK;
2820 : }
2821 :
2822 17659 : GF_SAFEALLOC(sai, GF_CENCSampleAuxInfo);
2823 17659 : if (!sai) return GF_OUT_OF_MEM;
2824 :
2825 17659 : if (sai_b && sai_b_size) {
2826 17659 : sai->cenc_data_size = sai_b_size;
2827 17659 : sai->cenc_data = gf_malloc(sizeof(u8) * sai_b_size);
2828 17659 : if (!sai->cenc_data) {
2829 0 : gf_free(sai);
2830 0 : return GF_OUT_OF_MEM;
2831 : }
2832 : memcpy(sai->cenc_data, sai_b, sai_b_size);
2833 : } else {
2834 0 : sai->isNotProtected = 1;
2835 : }
2836 :
2837 17659 : gf_list_add(senc->samp_aux_info, sai);
2838 17659 : if (use_subsamples)
2839 12151 : senc->flags = 0x00000002;
2840 17659 : if (use_multikey)
2841 0 : senc->version = 1;
2842 :
2843 17659 : gf_isom_cenc_set_saiz_saio(senc, NULL, traf, sai->cenc_data_size, use_saio_32bit, use_multikey);
2844 17659 : return GF_OK;
2845 : }
2846 :
2847 : GF_EXPORT
2848 16 : GF_Err gf_isom_fragment_append_data(GF_ISOFile *movie, GF_ISOTrackID TrackID, u8 *data, u32 data_size, u8 PaddingBits)
2849 : {
2850 : u32 count;
2851 : u8 rap;
2852 : u16 degp;
2853 : GF_TrunEntry *ent;
2854 : GF_TrackFragmentBox *traf;
2855 : GF_TrackFragmentRunBox *trun;
2856 16 : if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
2857 :
2858 16 : traf = gf_isom_get_traf(movie, TrackID);
2859 16 : if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
2860 :
2861 : //add TRUN entry
2862 16 : count = gf_list_count(traf->TrackRuns);
2863 16 : if (!count) return GF_BAD_PARAM;
2864 :
2865 16 : trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
2866 16 : if (!trun->nb_samples) return GF_BAD_PARAM;
2867 16 : ent = &trun->samples[trun->nb_samples-1];
2868 16 : ent->size += data_size;
2869 :
2870 16 : rap = GF_ISOM_GET_FRAG_SYNC(ent->flags);
2871 16 : degp = GF_ISOM_GET_FRAG_DEG(ent->flags);
2872 16 : ent->flags = GF_ISOM_FORMAT_FRAG_FLAGS(PaddingBits, rap, degp);
2873 :
2874 : //finally write the data
2875 16 : if (!traf->DataCache) {
2876 16 : gf_bs_write_data(movie->editFileMap->bs, data, data_size);
2877 0 : } else if (trun->cache) {
2878 0 : gf_bs_write_data(trun->cache, data, data_size);
2879 : } else {
2880 : return GF_BAD_PARAM;
2881 : }
2882 : return GF_OK;
2883 : }
2884 :
2885 1523 : GF_Err gf_isom_fragment_add_subsample(GF_ISOFile *movie, GF_ISOTrackID TrackID, u32 flags, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable)
2886 : {
2887 : u32 i, count, last_sample;
2888 : GF_TrackFragmentBox *traf;
2889 : GF_SubSampleInformationBox *subs = NULL;
2890 1523 : if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
2891 :
2892 1523 : traf = gf_isom_get_traf(movie, TrackID);
2893 1523 : if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
2894 :
2895 : /*compute last sample number in traf*/
2896 : last_sample = 0;
2897 1523 : count = gf_list_count(traf->TrackRuns);
2898 3046 : for (i=0; i<count; i++) {
2899 1523 : GF_TrackFragmentRunBox *trun = (GF_TrackFragmentRunBox*)gf_list_get(traf->TrackRuns, i);
2900 1523 : last_sample += trun->sample_count;
2901 : }
2902 :
2903 1523 : if (!traf->sub_samples) {
2904 15 : traf->sub_samples = gf_list_new();
2905 : }
2906 1523 : count = gf_list_count(traf->sub_samples);
2907 1523 : for (i=0; i<count;i++) {
2908 1508 : subs = gf_list_get(traf->sub_samples, i);
2909 1508 : if (subs->flags==flags) break;
2910 : subs=NULL;
2911 : }
2912 1523 : if (!subs) {
2913 15 : subs = (GF_SubSampleInformationBox *) gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_TYPE_SUBS);
2914 15 : if (!subs) return GF_OUT_OF_MEM;
2915 15 : subs->version = (subSampleSize>0xFFFF) ? 1 : 0;
2916 15 : subs->flags = flags;
2917 15 : gf_list_add(traf->sub_samples, subs);
2918 : }
2919 1523 : return gf_isom_add_subsample_info(subs, last_sample, subSampleSize, priority, reserved, discardable);
2920 : }
2921 :
2922 : #if 0 //unused
2923 : static GF_Err gf_isom_copy_sample_group_entry_to_traf(GF_TrackFragmentBox *traf, GF_SampleTableBox *stbl, u32 grouping_type, u32 grouping_type_parameter, u32 sampleGroupDescriptionIndex, Bool sgpd_in_traf)
2924 : {
2925 : if (sgpd_in_traf) {
2926 : void *entry = NULL;
2927 : u32 i, count;
2928 : GF_SampleGroupDescriptionBox *sgdesc = NULL;
2929 : GF_BitStream *bs;
2930 :
2931 : count = gf_list_count(stbl->sampleGroupsDescription);
2932 : for (i = 0; i < count; i++) {
2933 : sgdesc = (GF_SampleGroupDescriptionBox *)gf_list_get(stbl->sampleGroupsDescription, i);
2934 : if (sgdesc->grouping_type == grouping_type)
2935 : break;
2936 : sgdesc = NULL;
2937 : }
2938 : if (!sgdesc)
2939 : return GF_BAD_PARAM;
2940 :
2941 : entry = gf_list_get(sgdesc->group_descriptions, sampleGroupDescriptionIndex-1);
2942 : if (!entry)
2943 : return GF_BAD_PARAM;
2944 :
2945 : switch (grouping_type) {
2946 : case GF_ISOM_SAMPLE_GROUP_RAP:
2947 : {
2948 : char udta[2];
2949 : bs = gf_bs_new(udta, 2*sizeof(char), GF_BITSTREAM_WRITE);
2950 : gf_bs_write_u8(bs, ((GF_VisualRandomAccessEntry *)entry)->num_leading_samples_known);
2951 : gf_bs_write_u8(bs, ((GF_VisualRandomAccessEntry *)entry)->num_leading_samples);
2952 : gf_bs_del(bs);
2953 : return gf_isom_set_sample_group_info_ex(NULL, traf, 0, grouping_type, 0, udta, sg_rap_create_entry, sg_rap_compare_entry);
2954 : }
2955 : case GF_ISOM_SAMPLE_GROUP_SYNC:
2956 : {
2957 : char udta[1];
2958 : bs = gf_bs_new(udta, 1*sizeof(char), GF_BITSTREAM_WRITE);
2959 : gf_bs_write_int(bs, 0, 2);
2960 : gf_bs_write_int(bs, ((GF_SYNCEntry *)entry)->NALU_type, 6);
2961 : gf_bs_del(bs);
2962 : return gf_isom_set_sample_group_info_ex(NULL, traf, 0, grouping_type, 0, udta, sg_rap_create_entry, sg_rap_compare_entry);
2963 : }
2964 : case GF_ISOM_SAMPLE_GROUP_ROLL:
2965 : {
2966 : char udta[2];
2967 : bs = gf_bs_new(udta, 2*sizeof(char), GF_BITSTREAM_WRITE);
2968 : gf_bs_write_u16(bs, ((GF_RollRecoveryEntry *)entry)->roll_distance);
2969 : gf_bs_del(bs);
2970 : return gf_isom_set_sample_group_info_ex(NULL, traf, 0, grouping_type, 0, udta, sg_roll_create_entry, sg_roll_compare_entry);
2971 : }
2972 : case GF_ISOM_SAMPLE_GROUP_SEIG:
2973 : {
2974 : return gf_isom_set_sample_group_info_ex(NULL, traf, 0, grouping_type, 0, entry, sg_encryption_create_entry, sg_encryption_compare_entry);
2975 : }
2976 : default:
2977 : return GF_BAD_PARAM;
2978 : }
2979 : }
2980 :
2981 : return gf_isom_add_sample_group_entry(traf->sampleGroups, 0, grouping_type, grouping_type_parameter, sampleGroupDescriptionIndex, NULL);
2982 : }
2983 : /*copy over the subsample and sampleToGroup information of the given sample from the source track/file to the last sample added to the current track fragment of the destination file*/
2984 : GF_Err gf_isom_fragment_copy_subsample(GF_ISOFile *dest, GF_ISOTrackID TrackID, GF_ISOFile *orig, u32 track, u32 sampleNumber, Bool sgpd_in_traf)
2985 : {
2986 : u32 i, count, last_sample, idx, subs_flags;
2987 : GF_SubSampleInfoEntry *sub_sample;
2988 : GF_Err e;
2989 : GF_TrackBox *trak;
2990 : GF_TrackFragmentBox *traf;
2991 : GF_TrunEntry *ent;
2992 : GF_TrackFragmentRunBox *trun;
2993 : if (!dest->moof || !(dest->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
2994 :
2995 : traf = gf_isom_get_traf(dest, TrackID);
2996 : if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
2997 :
2998 : trak = gf_isom_get_track_from_file(orig, track);
2999 : if (!trak) return GF_BAD_PARAM;
3000 :
3001 : /*modify depends flags*/
3002 : if (trak->Media->information->sampleTable->SampleDep) {
3003 : u32 isLeading, dependsOn, dependedOn, redundant;
3004 :
3005 : isLeading = dependsOn = dependedOn = redundant = 0;
3006 : count = gf_list_count(traf->TrackRuns);
3007 : if (!count) return GF_BAD_PARAM;
3008 : trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
3009 : count = gf_list_count(trun->entries);
3010 : if (!count) return GF_BAD_PARAM;
3011 :
3012 : ent = (GF_TrunEntry *)gf_list_get(trun->entries, count-1);
3013 : e = stbl_GetSampleDepType(trak->Media->information->sampleTable->SampleDep, sampleNumber, &isLeading, &dependsOn, &dependedOn, &redundant);
3014 : if (e) return e;
3015 :
3016 : GF_ISOM_RESET_FRAG_DEPEND_FLAGS(ent->flags);
3017 :
3018 : if (traf->use_sdtp) {
3019 : u8 sflags=0;
3020 : if (!traf->sdtp) {
3021 : traf->sdtp = (GF_SampleDependencyTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SDTP);
3022 : if (!traf->sdtp) return GF_OUT_OF_MEM;
3023 : }
3024 : sflags |= isLeading << 6;
3025 : sflags |= dependsOn << 4;
3026 : sflags |= dependedOn << 2;
3027 : sflags |= redundant;
3028 :
3029 : traf->sdtp->sample_info = gf_realloc(traf->sdtp->sample_info, sizeof(u8)*(traf->sdtp->sampleCount+1));
3030 : traf->sdtp->sample_info[traf->sdtp->sampleCount] = (u8) sflags;
3031 : traf->sdtp->sampleCount++;
3032 : traf->sdtp->sample_alloc = traf->sdtp->sampleCount+1;
3033 :
3034 :
3035 : if (traf->use_sdtp==2) {
3036 : ent->flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(isLeading, dependsOn, dependedOn, redundant);
3037 : }
3038 : } else {
3039 : ent->flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(isLeading, dependsOn, dependedOn, redundant);
3040 : }
3041 : }
3042 :
3043 : /*copy subsample info if any*/
3044 : idx=1;
3045 : while (gf_isom_get_subsample_types(orig, track, idx, &subs_flags)) {
3046 : GF_SubSampleInformationBox *subs_traf=NULL;
3047 : idx++;
3048 : if (! gf_isom_sample_get_subsample_entry(orig, track, sampleNumber, subs_flags, &sub_sample))
3049 : continue;
3050 :
3051 : if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
3052 :
3053 : /*compute last sample number in traf*/
3054 : last_sample = 0;
3055 : count = gf_list_count(traf->TrackRuns);
3056 : for (i=0; i<count; i++) {
3057 : GF_TrackFragmentRunBox *trun = (GF_TrackFragmentRunBox*)gf_list_get(traf->TrackRuns, i);
3058 : last_sample += trun->sample_count;
3059 : }
3060 :
3061 : /*create subsample if needed*/
3062 : if (!traf->sub_samples) {
3063 : traf->sub_samples = gf_list_new();
3064 : }
3065 : count = gf_list_count(traf->sub_samples);
3066 : for (i=0; i<count; i++) {
3067 : subs_traf = gf_list_get(traf->sub_samples, i);
3068 : if (subs_traf->flags==subs_flags) break;
3069 : subs_traf = NULL;
3070 : }
3071 : if (!subs_traf) {
3072 : subs_traf = (GF_SubSampleInformationBox *) gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_TYPE_SUBS);
3073 : if (!subs_traf) return GF_OUT_OF_MEM;
3074 : subs_traf->version = 0;
3075 : subs_traf->flags = subs_flags;
3076 : gf_list_add(traf->sub_samples, subs_traf);
3077 : }
3078 :
3079 : count = gf_list_count(sub_sample->SubSamples);
3080 : for (i=0; i<count; i++) {
3081 : GF_SubSampleEntry *entry = (GF_SubSampleEntry*)gf_list_get(sub_sample->SubSamples, i);
3082 : e = gf_isom_add_subsample_info(subs_traf, last_sample, entry->subsample_size, entry->subsample_priority, entry->reserved, entry->discardable);
3083 : if (e) return e;
3084 : }
3085 : }
3086 : /*copy sampleToGroup info if any*/
3087 : if (trak->Media->information->sampleTable->sampleGroups) {
3088 : count = gf_list_count(trak->Media->information->sampleTable->sampleGroups);
3089 : for (i=0; i<count; i++) {
3090 : GF_SampleGroupBox *sg;
3091 : Bool found = GF_FALSE;
3092 : u32 j;
3093 : u32 first_sample_in_entry, last_sample_in_entry;
3094 : first_sample_in_entry = 1;
3095 :
3096 : sg = (GF_SampleGroupBox*)gf_list_get(trak->Media->information->sampleTable->sampleGroups, i);
3097 : for (j=0; j<sg->entry_count; j++) {
3098 : last_sample_in_entry = first_sample_in_entry + sg->sample_entries[j].sample_count - 1;
3099 : if ((sampleNumber<first_sample_in_entry) || (sampleNumber>last_sample_in_entry)) {
3100 : first_sample_in_entry = last_sample_in_entry+1;
3101 : continue;
3102 : }
3103 :
3104 : if (!traf->sampleGroups)
3105 : traf->sampleGroups = gf_list_new();
3106 :
3107 : /*found our sample, add it to trak->sampleGroups*/
3108 : 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);
3109 : if (e) return e;
3110 :
3111 : found = GF_TRUE;
3112 : break;
3113 : }
3114 : //unmapped sample
3115 : if (!found) {
3116 : if (!traf->sampleGroups)
3117 : traf->sampleGroups = gf_list_new();
3118 :
3119 : e = gf_isom_copy_sample_group_entry_to_traf(traf, trak->Media->information->sampleTable, sg->grouping_type, sg->grouping_type_parameter, 0, sgpd_in_traf);
3120 : if (e) return e;
3121 : }
3122 : }
3123 : }
3124 : return GF_OK;
3125 : }
3126 : #endif
3127 :
3128 :
3129 1575 : GF_Err gf_isom_fragment_set_sample_flags(GF_ISOFile *movie, GF_ISOTrackID trackID, u32 is_leading, u32 dependsOn, u32 dependedOn, u32 redundant)
3130 : {
3131 : u32 count;
3132 : GF_TrackFragmentBox *traf;
3133 : GF_TrunEntry *ent;
3134 : GF_TrackFragmentRunBox *trun;
3135 1575 : if (!movie || !movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
3136 :
3137 1575 : traf = gf_isom_get_traf(movie, trackID);
3138 1575 : if (!traf->tfhd->sample_desc_index) return GF_BAD_PARAM;
3139 :
3140 1575 : count = gf_list_count(traf->TrackRuns);
3141 1575 : if (!count) return GF_BAD_PARAM;
3142 1575 : trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1);
3143 1575 : if (!trun->nb_samples) return GF_BAD_PARAM;
3144 1575 : ent = &trun->samples[trun->nb_samples-1];
3145 :
3146 1575 : GF_ISOM_RESET_FRAG_DEPEND_FLAGS(ent->flags);
3147 :
3148 1575 : if (traf->use_sdtp) {
3149 : u8 sflags=0;
3150 0 : if (!traf->sdtp) {
3151 0 : traf->sdtp = (GF_SampleDependencyTypeBox *) gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_TYPE_SDTP);
3152 0 : if (!traf->sdtp) return GF_OUT_OF_MEM;
3153 : }
3154 0 : sflags |= is_leading << 6;
3155 0 : sflags |= dependsOn << 4;
3156 0 : sflags |= dependedOn << 2;
3157 0 : sflags |= redundant;
3158 :
3159 0 : traf->sdtp->sample_info = gf_realloc(traf->sdtp->sample_info, sizeof(u8)*(traf->sdtp->sampleCount+1));
3160 0 : traf->sdtp->sample_info[traf->sdtp->sampleCount] = (u8) sflags;
3161 0 : traf->sdtp->sampleCount++;
3162 0 : traf->sdtp->sample_alloc = traf->sdtp->sampleCount;
3163 0 : if (traf->use_sdtp==2) {
3164 0 : ent->flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(is_leading, dependsOn, dependedOn, redundant);
3165 : }
3166 : } else {
3167 1575 : ent->flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(is_leading, dependsOn, dependedOn, redundant);
3168 : }
3169 : return GF_OK;
3170 : }
3171 :
3172 :
3173 : #endif /*GPAC_DISABLE_ISOM_WRITE*/
3174 :
3175 : GF_EXPORT
3176 219 : Bool gf_isom_is_track_fragmented(GF_ISOFile *movie, GF_ISOTrackID TrackID)
3177 : {
3178 219 : if (!movie || !movie->moov || !movie->moov->mvex) return GF_FALSE;
3179 3 : return (GetTrex(movie->moov, TrackID) != NULL) ? GF_TRUE : GF_FALSE;
3180 : }
3181 :
3182 : GF_EXPORT
3183 1196 : Bool gf_isom_is_fragmented(GF_ISOFile *movie)
3184 : {
3185 1196 : if (!movie || !movie->moov) return GF_FALSE;
3186 : /* By default if the Moov has an mvex, the file is fragmented */
3187 1166 : if (movie->moov->mvex) return GF_TRUE;
3188 970 : return GF_FALSE;
3189 : }
3190 :
3191 : GF_EXPORT
3192 3438 : GF_Err gf_isom_set_traf_base_media_decode_time(GF_ISOFile *movie, GF_ISOTrackID TrackID, u64 decode_time)
3193 : {
3194 : GF_TrackFragmentBox *traf;
3195 3438 : if (!movie || !movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
3196 :
3197 3438 : traf = gf_isom_get_traf(movie, TrackID);
3198 3438 : if (!traf) return GF_BAD_PARAM;
3199 :
3200 3438 : if (!traf->tfdt) {
3201 3381 : traf->tfdt = (GF_TFBaseMediaDecodeTimeBox *) gf_isom_box_new_parent(&traf->child_boxes, GF_ISOM_BOX_TYPE_TFDT);
3202 3381 : if (!traf->tfdt) return GF_OUT_OF_MEM;
3203 : }
3204 3438 : traf->tfdt->baseMediaDecodeTime = decode_time;
3205 3438 : if (traf->large_tfdt)
3206 0 : traf->tfdt->version = 1;
3207 : return GF_OK;
3208 : }
3209 :
3210 1 : GF_Err gf_isom_enable_mfra(GF_ISOFile *file)
3211 : {
3212 1 : if (!file) return GF_BAD_PARAM;
3213 1 : file->mfra = (GF_MovieFragmentRandomAccessBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_MFRA);
3214 1 : if (!file->mfra) return GF_OUT_OF_MEM;
3215 1 : return GF_OK;
3216 : }
3217 :
3218 : #endif /*GPAC_DISABLE_ISOM_FRAGMENTS)*/
3219 :
3220 :
3221 : GF_EXPORT
3222 3571 : void gf_isom_set_next_moof_number(GF_ISOFile *movie, u32 value)
3223 : {
3224 : #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
3225 3571 : if (movie) movie->NextMoofNumber = value;
3226 : #endif
3227 3571 : }
3228 :
3229 : GF_EXPORT
3230 1 : u32 gf_isom_get_next_moof_number(GF_ISOFile *movie)
3231 : {
3232 : #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
3233 1 : if (movie) return movie->NextMoofNumber;
3234 : #endif
3235 : return 0;
3236 : }
3237 :
3238 : #endif /*GPAC_DISABLE_ISOM*/
|