Coverage Report

Created: 2025-11-09 07:05

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