Coverage Report

Created: 2025-12-05 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/isomedia/isom_intern.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
#include <gpac/thread.h>
29
30
#ifndef GPAC_DISABLE_ISOM
31
32
/**************************************************************
33
    Some Local functions for movie creation
34
**************************************************************/
35
36
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
37
GF_Err MergeFragment(GF_MovieFragmentBox *moof, GF_ISOFile *mov)
38
0
{
39
0
  GF_Err e;
40
0
  u32 i, j;
41
0
  u64 MaxDur;
42
0
  GF_TrackFragmentBox *traf;
43
0
  GF_TrackBox *trak;
44
0
  u64 base_data_offset;
45
46
0
  MaxDur = 0;
47
48
  //we shall have a MOOV and its MVEX BEFORE any MOOF
49
0
  if (!mov->moov || !mov->moov->mvex) {
50
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Error: %s not received before merging fragment\n", mov->moov ? "mvex" : "moov" ));
51
0
    return GF_ISOM_INVALID_FILE;
52
0
  }
53
  //and all fragments should be continous but:
54
  //- dash with dependent representations may likely give R1(moofSN 1, 3, 5, 7) plus R2(moofSN 2, 4, 6, 8)
55
  //- smooth muxed in a single file may end up with V(1),A(1), V(2),A(2) ...
56
  //we do not throw an error if not as we may still want to be able to concatenate dependent representations in DASH and
57
0
  if (mov->NextMoofNumber && moof->mfhd && (mov->NextMoofNumber >= moof->mfhd->sequence_number)) {
58
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] wrong sequence number: got %d but last one was %d\n", moof->mfhd->sequence_number, mov->NextMoofNumber));
59
0
  }
60
61
0
  base_data_offset = mov->current_top_box_start;
62
0
  if (moof->compressed_diff)
63
0
    base_data_offset -= moof->compressed_diff;
64
65
0
  i=0;
66
0
  while ((traf = (GF_TrackFragmentBox*)gf_list_enum(moof->TrackList, &i))) {
67
0
    u32 prev_sample_count;
68
0
    if (!traf->tfhd) {
69
0
      trak = NULL;
70
0
      traf->trex = NULL;
71
0
    } else if (mov->is_smooth) {
72
0
      trak = gf_list_get(mov->moov->trackList, 0);
73
0
      traf->trex = (GF_TrackExtendsBox*)gf_list_get(mov->moov->mvex->TrackExList, 0);
74
0
      gf_fatal_assert(traf->trex);
75
0
      traf->trex->trackID = trak->Header->trackID = traf->tfhd->trackID;
76
0
    } else {
77
0
      trak = gf_isom_get_track_from_id(mov->moov, traf->tfhd->trackID);
78
0
      j=0;
79
0
      while ((traf->trex = (GF_TrackExtendsBox*)gf_list_enum(mov->moov->mvex->TrackExList, &j))) {
80
0
        if (traf->trex->trackID == traf->tfhd->trackID) break;
81
0
        traf->trex = NULL;
82
0
      }
83
0
    }
84
85
0
    if (!trak || !traf->trex) {
86
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Error: Cannot find fragment track with ID %d\n", traf->tfhd ? traf->tfhd->trackID : 0));
87
0
      return GF_ISOM_INVALID_FILE;
88
0
    }
89
90
0
    prev_sample_count = trak->Media->information->sampleTable->SampleSize ? trak->Media->information->sampleTable->SampleSize->sampleCount : 0;
91
0
    e = MergeTrack(trak, traf, moof, mov->current_top_box_start, moof->compressed_diff, &base_data_offset);
92
0
    if (e) return e;
93
0
    trak->first_traf_merged = GF_TRUE;
94
95
0
    trak->present_in_scalable_segment = 1;
96
97
    //update trak duration
98
0
    SetTrackDuration(trak);
99
0
    if (trak->Header->duration > MaxDur)
100
0
      MaxDur = trak->Header->duration;
101
102
    //we have PSSH per moov, internally remap as a sample group of type PSSH
103
0
    if (gf_list_count(moof->PSSHs)) {
104
0
#ifndef GPAC_DISABLE_ISOM_WRITE
105
0
      u8 *pssh_data;
106
0
      u32 pssh_len;
107
0
      u32 k, nb_pssh = gf_list_count(moof->PSSHs);
108
0
      GF_BitStream *pssh_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
109
0
      gf_bs_write_u32(pssh_bs, nb_pssh);
110
0
      for (j=0; j<nb_pssh; j++) {
111
0
        GF_ProtectionSystemHeaderBox *pssh = gf_list_get(moof->PSSHs, j);
112
0
        gf_bs_write_data(pssh_bs, pssh->SystemID, 16);
113
0
        gf_bs_write_u32(pssh_bs, pssh->version);
114
0
        if (pssh->version) {
115
0
          gf_bs_write_u32(pssh_bs, pssh->KID_count);
116
0
          for (k=0; k<pssh->KID_count; k++) {
117
0
            gf_bs_write_data(pssh_bs, pssh->KIDs[k], 16);
118
0
          }
119
0
        }
120
0
        gf_bs_write_u32(pssh_bs, pssh->private_data_size);
121
0
        gf_bs_write_data(pssh_bs, pssh->private_data, pssh->private_data_size);
122
0
      }
123
0
      gf_bs_get_content(pssh_bs, &pssh_data, &pssh_len);
124
0
      gf_bs_del(pssh_bs);
125
126
0
      gf_isom_set_sample_group_description_internal(mov, gf_list_find(mov->moov->trackList, trak)+1, 1+prev_sample_count, GF_4CC('P','S','S','H'), 0, pssh_data, pssh_len, GF_FALSE, 0);
127
0
      gf_free(pssh_data);
128
0
#endif
129
0
    }
130
131
132
0
#ifndef GPAC_DISABLE_ISOM_WRITE
133
    //we have emsg, internally remap as a sample group of type EMSG
134
0
    if (gf_list_count(mov->emsgs)) {
135
0
      u8 *emsg_data;
136
0
      u32 emsg_len;
137
0
      u32 nb_emsg = gf_list_count(mov->emsgs);
138
0
      GF_BitStream *emsg_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
139
0
      for (j=0; j<nb_emsg; j++) {
140
0
        GF_Box *emsg = gf_list_get(mov->emsgs, j);
141
0
        gf_isom_box_write(emsg, emsg_bs);
142
0
      }
143
0
      gf_bs_get_content(emsg_bs, &emsg_data, &emsg_len);
144
0
      gf_bs_del(emsg_bs);
145
146
0
      gf_isom_set_sample_group_description_internal(mov, gf_list_find(mov->moov->trackList, trak)+1, 1+prev_sample_count, GF_4CC('E','M','S','G'), 0, emsg_data, emsg_len, GF_FALSE, 0);
147
0
      gf_free(emsg_data);
148
0
    }
149
0
#endif
150
0
  }
151
0
  if (mov->emsgs) {
152
0
    gf_isom_box_array_del(mov->emsgs);
153
0
    mov->emsgs = NULL;
154
0
  }
155
156
0
  if (moof->child_boxes) {
157
0
    GF_Box *a;
158
0
    i = 0;
159
0
    while ((a = (GF_Box *)gf_list_enum(moof->child_boxes, &i))) {
160
0
      if (a->type == GF_ISOM_BOX_TYPE_PSSH) {
161
0
        GF_ProtectionSystemHeaderBox *pssh = (GF_ProtectionSystemHeaderBox *)gf_isom_box_new_parent(&mov->moov->child_boxes, GF_ISOM_BOX_TYPE_PSSH);
162
0
        if (!pssh) return GF_OUT_OF_MEM;
163
0
        memmove(pssh->SystemID, ((GF_ProtectionSystemHeaderBox *)a)->SystemID, 16);
164
0
        if (((GF_ProtectionSystemHeaderBox *)a)->KIDs && ((GF_ProtectionSystemHeaderBox *)a)->KID_count > 0) {
165
0
          pssh->KID_count = ((GF_ProtectionSystemHeaderBox *)a)->KID_count;
166
0
          pssh->KIDs = (bin128 *)gf_malloc(pssh->KID_count*sizeof(bin128));
167
0
          if (!pssh->KIDs) return GF_OUT_OF_MEM;
168
169
0
          memmove(pssh->KIDs, ((GF_ProtectionSystemHeaderBox *)a)->KIDs, pssh->KID_count*sizeof(bin128));
170
0
        }
171
0
        pssh->private_data_size = ((GF_ProtectionSystemHeaderBox *)a)->private_data_size;
172
0
        pssh->private_data = (u8 *)gf_malloc(pssh->private_data_size*sizeof(char));
173
0
        if (!pssh->private_data) return GF_OUT_OF_MEM;
174
0
        memmove(pssh->private_data, ((GF_ProtectionSystemHeaderBox *)a)->private_data, pssh->private_data_size);
175
0
        pssh->moof_defined = 1;
176
0
        mov->has_pssh_moof = GF_TRUE;
177
0
      }
178
0
    }
179
0
  }
180
181
0
  mov->NextMoofNumber = moof->mfhd ? moof->mfhd->sequence_number : 0;
182
  //update movie duration
183
0
  if (mov->moov->mvhd->duration < MaxDur) mov->moov->mvhd->duration = MaxDur;
184
0
  return GF_OK;
185
0
}
186
187
static void FixTrackID(GF_ISOFile *mov)
188
0
{
189
0
  if (!mov->moov) return;
190
191
0
  if (gf_list_count(mov->moov->trackList) == 1 && gf_list_count(mov->moof->TrackList) == 1) {
192
0
    GF_TrackFragmentBox *traf = (GF_TrackFragmentBox*)gf_list_get(mov->moof->TrackList, 0);
193
0
    GF_TrackBox *trak = (GF_TrackBox*)gf_list_get(mov->moov->trackList, 0);
194
0
    if (!traf->tfhd || !trak->Header) return;
195
0
    if ((traf->tfhd->trackID != trak->Header->trackID)) {
196
0
      if (!mov->is_smooth) {
197
0
        GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Warning: trackID of MOOF/TRAF(%u) is not the same as MOOV/TRAK(%u). Trying to fix.\n", traf->tfhd->trackID, trak->Header->trackID));
198
0
      } else {
199
0
        trak->Header->trackID = traf->tfhd->trackID;
200
0
      }
201
0
      traf->tfhd->trackID = trak->Header->trackID;
202
0
    }
203
0
  }
204
0
}
205
206
static void FixSDTPInTRAF(GF_MovieFragmentBox *moof)
207
0
{
208
0
  u32 k;
209
0
  if (!moof)
210
0
    return;
211
212
0
  for (k = 0; k < gf_list_count(moof->TrackList); k++) {
213
0
    GF_TrackFragmentBox *traf = gf_list_get(moof->TrackList, k);
214
0
    if (traf->sdtp) {
215
0
      GF_TrackFragmentRunBox *trun;
216
0
      u32 j = 0, sample_index = 0;
217
218
0
      if (traf->sdtp->sampleCount == gf_list_count(traf->TrackRuns)) {
219
0
        GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Warning: TRAF box of track id=%u contains a SDTP. Converting to TRUN sample flags.\n", traf->tfhd->trackID));
220
0
      }
221
222
0
      while ((trun = (GF_TrackFragmentRunBox*)gf_list_enum(traf->TrackRuns, &j))) {
223
0
        u32 i;
224
0
        Bool aborted=GF_FALSE;
225
        //use sample flags
226
0
        trun->flags |= GF_ISOM_TRUN_FLAGS;
227
        //remove first sample flag (cannot be used with sample flags)
228
0
        trun->flags &= ~GF_ISOM_TRUN_FIRST_FLAG;
229
0
        for (i=0; i<trun->nb_samples; i++) {
230
0
          GF_TrunEntry *entry = &trun->samples[i];
231
0
          if (sample_index >= traf->sdtp->sampleCount) {
232
0
            aborted = GF_TRUE;
233
0
            break;
234
0
          }
235
0
          const u8 info = traf->sdtp->sample_info[sample_index];
236
0
          entry->flags |= GF_ISOM_GET_FRAG_DEPEND_FLAGS(info >> 6, info >> 4, info >> 2, info);
237
0
          sample_index++;
238
0
        }
239
0
        if (aborted) break;
240
0
      }
241
0
      if (sample_index != traf->sdtp->sampleCount) {
242
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Error: TRAF box of track id=%u list %d samples but SDTP %d.\n", traf->tfhd->trackID, sample_index, traf->sdtp->sampleCount));
243
0
      }
244
0
      gf_isom_box_del_parent(&traf->child_boxes, (GF_Box*)traf->sdtp);
245
0
      traf->sdtp = NULL;
246
0
    }
247
0
  }
248
0
}
249
#endif //GPAC_DISABLE_ISOM_FRAGMENTS
250
251
void gf_isom_push_mdat_end(GF_ISOFile *mov, u64 mdat_end, Bool is_pred)
252
0
{
253
0
  u32 i, count;
254
0
  if (!mov || !mov->moov) return;
255
256
0
  count = gf_list_count(mov->moov->trackList);
257
0
  for (i=0; i<count; i++) {
258
0
    u32 j;
259
0
    GF_TrafToSampleMap *traf_map;
260
0
    GF_TrackBox *trak = gf_list_get(mov->moov->trackList, i);
261
0
    if (!trak->Media->information->sampleTable->traf_map) continue;
262
263
0
    traf_map = trak->Media->information->sampleTable->traf_map;
264
0
    for (j=traf_map->nb_entries; j>0; j--) {
265
0
      GF_TrafMapEntry *finfo = &traf_map->frag_starts[j-1];
266
0
      if (finfo->is_predicted_offset) {
267
0
        finfo->is_predicted_offset = is_pred ? 1 : 0;
268
0
        if (mdat_end > finfo->mdat_end)
269
0
          finfo->mdat_end = 0;
270
0
      }
271
272
0
      if (!finfo->mdat_end) {
273
0
        finfo->mdat_end = mdat_end;
274
0
        finfo->is_predicted_offset = is_pred ? 1 : 0;
275
0
        break;
276
0
      }
277
0
    }
278
0
  }
279
0
}
280
281
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
282
283
#ifdef GF_ENABLE_CTRN
284
static void gf_isom_setup_traf_inheritance(GF_ISOFile *mov)
285
{
286
  u32 i, count;
287
  if (!mov->moov->mvex)
288
    return;
289
  count = gf_list_count(mov->moov->trackList);
290
291
  for (i=0; i<count; i++) {
292
    u32 refTrackNum=0;
293
    gf_isom_get_reference(mov, i+1, GF_ISOM_REF_TRIN, 1, &refTrackNum);
294
    if (refTrackNum) {
295
      GF_ISOTrackID tkid = gf_isom_get_track_id(mov, i+1);
296
      GF_ISOTrackID reftkid = gf_isom_get_track_id(mov, refTrackNum);
297
      GF_TrackExtendsBox *trex = GetTrex(mov->moov, tkid);
298
      if (trex) trex->inherit_from_traf_id = reftkid;
299
    }
300
  }
301
}
302
#endif
303
304
#endif
305
306
//for now we only use regular sample to group internally (except when dumping), not the pattern version
307
//we unrill the pattern and replace the compact version with a regular one
308
static void convert_compact_sample_groups(u32 all_samples, GF_List *child_boxes, GF_List *sampleGroups)
309
0
{
310
0
  u32 i;
311
0
  for (i=0; i<gf_list_count(sampleGroups); i++) {
312
0
    u32 j;
313
0
    GF_SampleGroupBox *sbgp;
314
0
    GF_CompactSampleGroupBox *csgp = gf_list_get(sampleGroups, i);
315
0
    if (csgp->type != GF_ISOM_BOX_TYPE_CSGP) continue;
316
317
0
    if (!all_samples) {
318
0
      u32 j=0;
319
0
      for (j=0; j<gf_list_count(child_boxes); j++) {
320
0
        GF_TrackFragmentRunBox *trun = (GF_TrackFragmentRunBox *)gf_list_get(child_boxes, j);
321
0
        if (trun->type != GF_ISOM_BOX_TYPE_TRUN) continue;
322
0
        all_samples += trun->sample_count;
323
0
      }
324
0
    }
325
0
    u32 max_samples = all_samples;
326
327
0
    gf_list_rem(sampleGroups, i);
328
0
    gf_list_del_item(child_boxes, csgp);
329
330
0
    sbgp = (GF_SampleGroupBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SBGP);
331
0
    gf_list_insert(sampleGroups, sbgp, i);
332
0
    gf_list_add(child_boxes, sbgp);
333
0
    i--;
334
335
0
    sbgp->grouping_type = csgp->grouping_type;
336
0
    if (csgp->grouping_type_parameter) {
337
0
      sbgp->grouping_type_parameter = csgp->grouping_type_parameter;
338
0
      sbgp->version = 1;
339
0
    }
340
0
    sbgp->entry_count = 0;
341
0
    for (j=0; j<csgp->pattern_count; j++) {
342
0
      u32 k=0;
343
0
      u32 nb_samples = csgp->patterns[j].sample_count;
344
0
      if (nb_samples > max_samples) {
345
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Invalid compact sample to group box, %u samples in pattern but %u remaining\n", nb_samples, max_samples));
346
0
        break;
347
0
      }
348
0
      max_samples -= nb_samples;
349
350
      //unroll the pattern
351
0
      while (nb_samples) {
352
0
        u32 nb_same_index=1;
353
0
        if (csgp->patterns[j].length<=k)
354
0
          break;
355
0
        u32 sg_idx = csgp->patterns[j].sample_group_description_indices[k];
356
0
        while (nb_same_index+k<csgp->patterns[j].length) {
357
0
          if (csgp->patterns[j].sample_group_description_indices[k+nb_same_index] != sg_idx)
358
0
            break;
359
0
          nb_same_index++;
360
0
        }
361
0
        sbgp->sample_entries = gf_realloc(sbgp->sample_entries, sizeof(GF_SampleGroupEntry) * (sbgp->entry_count+1));
362
0
        if (nb_same_index>nb_samples)
363
0
          nb_same_index = nb_samples;
364
365
0
        sbgp->sample_entries[sbgp->entry_count].sample_count = nb_same_index;
366
0
        sbgp->sample_entries[sbgp->entry_count].group_description_index = sg_idx;
367
0
        nb_samples -= nb_same_index;
368
0
        sbgp->entry_count++;
369
0
        k+= nb_same_index;
370
0
        if (k==csgp->patterns[j].length)
371
0
          k = 0;
372
0
      }
373
0
    }
374
0
    gf_isom_box_del((GF_Box*)csgp);
375
0
  }
376
0
}
377
378
static GF_Err gf_isom_parse_movie_boxes_internal(GF_ISOFile *mov, u32 *boxType, u64 *bytesMissing, Bool progressive_mode)
379
0
{
380
0
  GF_Box *a;
381
0
  u64 top_start, mdat_end=0;
382
0
  GF_Err e = GF_OK;
383
0
  u32 btype;
384
385
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
386
0
  if (mov->single_moof_mode && mov->single_moof_state == 2) {
387
0
    return e;
388
0
  }
389
390
  /*restart from where we stopped last*/
391
0
  top_start = mov->current_top_box_start;
392
0
  if (mov->bytes_removed) {
393
0
    gf_assert(top_start >= mov->bytes_removed);
394
0
    top_start -= mov->bytes_removed;
395
0
  }
396
0
  gf_bs_seek(mov->movieFileMap->bs, top_start);
397
398
0
  if (gf_opts_get_bool("core", "no-mabr-patch")) {
399
0
    gf_bs_set_cookie(mov->movieFileMap->bs, gf_bs_get_cookie(mov->movieFileMap->bs) | GF_ISOM_BS_COOKIE_NO_MABR_PATCH);
400
0
  }
401
0
#endif
402
403
404
  /*while we have some data, parse our boxes*/
405
0
  while (gf_isom_datamap_top_level_box_avail(mov->movieFileMap)) {
406
0
    *bytesMissing = 0;
407
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
408
0
    mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs) + mov->bytes_removed;
409
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Parsing a top-level box at position %d\n", mov->current_top_box_start));
410
0
#endif
411
412
0
    e = gf_isom_parse_root_box(&a, mov->movieFileMap->bs, &btype, bytesMissing, progressive_mode);
413
0
    if (boxType) *boxType = btype;
414
415
0
    if (e >= 0) {
416
      //safety check, should never happen
417
0
      if (!a) return GF_ISOM_INVALID_FILE;
418
0
    } else if (e == GF_ISOM_INCOMPLETE_FILE) {
419
      /*our mdat is uncomplete, only valid for READ ONLY files...*/
420
0
      if (mov->openMode != GF_ISOM_OPEN_READ) {
421
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Incomplete MDAT while file is not read-only\n"));
422
0
        return GF_ISOM_INVALID_FILE;
423
0
      }
424
425
0
      if ((btype == GF_ISOM_BOX_TYPE_MDAT)
426
0
        && mov->signal_frag_bounds
427
0
        && !(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)
428
0
      ) {
429
        //signal mdat end - note that if multiple mdats are used per fragment and hidden data is present at the end of
430
        //N(>1) mdats, the prediction may not be true and we will expose offsets that could be incomplete when
431
        //processing non-local files (http, pipes...)
432
0
        u64 mdat_end = gf_bs_get_size(mov->movieFileMap->bs) + *bytesMissing;
433
0
        gf_isom_push_mdat_end(mov, mdat_end, GF_TRUE);
434
0
      }
435
436
0
      if ((mov->openMode == GF_ISOM_OPEN_READ) && !progressive_mode) {
437
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Incomplete file while reading for dump - aborting parsing\n"));
438
0
        break;
439
0
      }
440
0
      return e;
441
0
    } else {
442
0
      return e;
443
0
    }
444
445
0
    switch (a->type) {
446
    /*MOOV box*/
447
0
    case GF_ISOM_BOX_TYPE_MOOV:
448
0
      if (mov->moov) {
449
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate MOOV detected!\n"));
450
0
        gf_isom_box_del(a);
451
0
        return GF_ISOM_INVALID_FILE;
452
0
      }
453
0
      mov->moov = (GF_MovieBox *)a;
454
0
      if (mov->moov->has_cmvd==1) {
455
0
        GF_Box *cmvd = gf_isom_box_find_child(a->child_boxes, GF_QT_BOX_TYPE_CMVD);
456
0
        mov->moov = (GF_MovieBox *) (cmvd ? gf_isom_box_find_child(cmvd->child_boxes, GF_ISOM_BOX_TYPE_MOOV) : NULL);
457
0
        if (!mov->moov) {
458
0
          gf_isom_box_del(a);
459
0
          return GF_ISOM_INVALID_FILE;
460
0
        }
461
0
        gf_list_del_item(cmvd->child_boxes, mov->moov);
462
0
        gf_isom_box_del(a);
463
0
        a = (GF_Box*)mov->moov;
464
0
        mov->moov->type = GF_ISOM_BOX_TYPE_MOOV;
465
0
      }
466
0
      mov->original_moov_offset = mov->current_top_box_start;
467
      /*set our pointer to the movie*/
468
0
      mov->moov->mov = mov;
469
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
470
0
      if (mov->moov->mvex) mov->moov->mvex->mov = mov;
471
472
#ifdef GF_ENABLE_CTRN
473
      if (! (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) {
474
        gf_isom_setup_traf_inheritance(mov);
475
      }
476
#endif
477
478
0
#endif
479
0
      e = gf_list_add(mov->TopBoxes, a);
480
0
      if (e) return e;
481
482
0
      if (!mov->moov->mvhd) {
483
0
        if (mov->moov->has_cmvd!=2) {
484
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing MovieHeaderBox\n"));
485
0
          return GF_ISOM_INVALID_FILE;
486
0
        }
487
0
      }
488
489
0
      if (mov->meta) {
490
0
        gf_isom_meta_restore_items_ref(mov, mov->meta);
491
0
      }
492
493
      //dump senc info in dump mode
494
0
      if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
495
0
        u32 k;
496
0
        for (k=0; k<gf_list_count(mov->moov->trackList); k++) {
497
0
          GF_TrackBox *trak = (GF_TrackBox *)gf_list_get(mov->moov->trackList, k);
498
499
0
          if (trak->sample_encryption) {
500
0
            e = senc_Parse(mov->movieFileMap->bs, trak, NULL, trak->sample_encryption, 0);
501
0
            if (e) return e;
502
0
          }
503
0
        }
504
0
      } else {
505
0
        u32 k;
506
0
        for (k=0; k<gf_list_count(mov->moov->trackList); k++) {
507
0
          GF_TrackBox *trak = (GF_TrackBox *)gf_list_get(mov->moov->trackList, k);
508
0
          if (trak->extl) continue;
509
0
          if (trak->Media->information->sampleTable->sampleGroups) {
510
0
            convert_compact_sample_groups(trak->Media->information->sampleTable->SampleSize->sampleCount, trak->Media->information->sampleTable->child_boxes, trak->Media->information->sampleTable->sampleGroups);
511
0
          }
512
0
        }
513
0
      }
514
515
0
      if (mdat_end && mov->signal_frag_bounds && !(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) ) {
516
0
        gf_isom_push_mdat_end(mov, mdat_end, GF_FALSE);
517
0
        mdat_end=0;
518
0
      }
519
0
      break;
520
521
    /*META box*/
522
0
    case GF_ISOM_BOX_TYPE_META:
523
0
      if (mov->meta) {
524
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate META detected!\n"));
525
0
        gf_isom_box_del(a);
526
0
        return GF_ISOM_INVALID_FILE;
527
0
      }
528
0
      mov->meta = (GF_MetaBox *)a;
529
0
      mov->original_meta_offset = mov->current_top_box_start;
530
0
      e = gf_list_add(mov->TopBoxes, a);
531
0
      if (e) {
532
0
        return e;
533
0
      }
534
0
      gf_isom_meta_restore_items_ref(mov, mov->meta);
535
0
      break;
536
537
    /*we only keep the MDAT in READ for dump purposes*/
538
0
    case GF_ISOM_BOX_TYPE_MDAT:
539
0
      if (!mov->first_data_toplevel_offset) {
540
0
        mov->first_data_toplevel_offset = mov->current_top_box_start;
541
0
        mov->first_data_toplevel_size = a->size;
542
0
      }
543
544
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
545
0
      if (mov->emsgs) {
546
0
        gf_isom_box_array_del(mov->emsgs);
547
0
        mov->emsgs = NULL;
548
0
      }
549
0
#endif
550
551
0
      if (mov->openMode == GF_ISOM_OPEN_READ) {
552
0
        if (!mov->mdat) {
553
0
          mov->mdat = (GF_MediaDataBox *) a;
554
0
          e = gf_list_add(mov->TopBoxes, mov->mdat);
555
0
          if (e) {
556
0
            return e;
557
0
          }
558
0
        }
559
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
560
0
        else if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) gf_list_add(mov->TopBoxes, a);
561
0
#endif
562
0
        else gf_isom_box_del(a); //in other modes we don't care
563
564
565
0
        if (mov->signal_frag_bounds && !(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) ) {
566
0
          mdat_end = gf_bs_get_position(mov->movieFileMap->bs);
567
0
          if (mov->moov) {
568
0
            gf_isom_push_mdat_end(mov, mdat_end, GF_FALSE);
569
0
            mdat_end=0;
570
0
          }
571
0
        }
572
0
      }
573
      //keep all imda boxes for later rewrite
574
0
      else if (((GF_MediaDataBox *)a)->is_imda) {
575
0
        e = gf_list_add(mov->TopBoxes, a);
576
0
        if (e) return e;
577
0
      }
578
      /*if we don't have any MDAT yet, create one (edit-write mode)
579
      We only work with one mdat, but we're puting it at the place
580
      of the first mdat found when opening a file for editing*/
581
0
      else if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_KEEP_FRAGMENTS)) {
582
0
        gf_isom_box_del(a);
583
0
        mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT);
584
0
        if (!mov->mdat) return GF_OUT_OF_MEM;
585
0
        e = gf_list_add(mov->TopBoxes, mov->mdat);
586
0
        if (e) {
587
0
          return e;
588
0
        }
589
0
      } else {
590
0
        gf_isom_box_del(a);
591
0
      }
592
0
      break;
593
0
    case GF_ISOM_BOX_TYPE_FTYP:
594
      /*ONE AND ONLY ONE FTYP*/
595
0
      if (mov->brand) {
596
0
        gf_isom_box_del(a);
597
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate 'ftyp' detected!\n"));
598
0
        return GF_ISOM_INVALID_FILE;
599
0
      }
600
0
      mov->brand = (GF_FileTypeBox *)a;
601
602
0
      e = gf_list_add(mov->TopBoxes, a);
603
0
      if (e) return e;
604
0
      break;
605
606
0
    case GF_ISOM_BOX_TYPE_OTYP:
607
      /*ONE AND ONLY ONE FTYP*/
608
0
      if (mov->otyp) {
609
0
        gf_isom_box_del(a);
610
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate 'otyp' detected!\n"));
611
0
        return GF_ISOM_INVALID_FILE;
612
0
      }
613
614
0
      if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
615
0
        mov->otyp = (GF_Box *)a;
616
0
        e = gf_list_add(mov->TopBoxes, a);
617
0
        if (e) return e;
618
0
      } else {
619
0
        GF_FileTypeBox *brand = (GF_FileTypeBox *) gf_isom_box_find_child(a->child_boxes, GF_ISOM_BOX_TYPE_FTYP);
620
0
        if (brand) {
621
0
          s32 pos;
622
0
          gf_list_del_item(a->child_boxes, brand);
623
0
          pos = gf_list_del_item(mov->TopBoxes, mov->brand);
624
0
          gf_isom_box_del((GF_Box *) mov->brand);
625
0
          mov->brand = brand;
626
0
          if (pos<0) pos=0;
627
0
          gf_list_insert(mov->TopBoxes, brand, pos);
628
0
        }
629
0
        gf_isom_box_del(a);
630
0
      }
631
0
      break;
632
633
0
    case GF_ISOM_BOX_TYPE_PDIN:
634
      /*ONE AND ONLY ONE PDIN*/
635
0
      if (mov->pdin) {
636
0
        gf_isom_box_del(a);
637
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate 'pdin'' detected!\n"));
638
0
        return GF_ISOM_INVALID_FILE;
639
0
      }
640
0
      mov->pdin = (GF_ProgressiveDownloadBox *) a;
641
642
0
      e = gf_list_add(mov->TopBoxes, a);
643
0
      if (e) return e;
644
0
      break;
645
646
647
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
648
0
    case GF_ISOM_BOX_TYPE_STYP:
649
0
    {
650
0
      u32 brand = ((GF_FileTypeBox *)a)->majorBrand;
651
0
      switch (brand) {
652
0
      case GF_ISOM_BRAND_SISX:
653
0
      case GF_ISOM_BRAND_RISX:
654
0
      case GF_ISOM_BRAND_SSSS:
655
0
        mov->is_index_segment = GF_TRUE;
656
0
        break;
657
0
      default:
658
0
        break;
659
0
      }
660
0
    }
661
    /*fall-through*/
662
663
0
    case GF_ISOM_BOX_TYPE_SIDX:
664
0
    case GF_ISOM_BOX_TYPE_SSIX:
665
0
      if (mov->moov && !mov->first_data_toplevel_offset) {
666
0
        mov->first_data_toplevel_offset = mov->current_top_box_start;
667
0
        mov->first_data_toplevel_size = a->size;
668
0
      }
669
0
      if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
670
0
        e = gf_list_add(mov->TopBoxes, a);
671
0
        if (e) return e;
672
0
      } else if (mov->signal_frag_bounds && !(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)  && (mov->openMode!=GF_ISOM_OPEN_KEEP_FRAGMENTS)
673
0
      ) {
674
0
        if (a->type==GF_ISOM_BOX_TYPE_SIDX) {
675
0
          if (mov->root_sidx) gf_isom_box_del( (GF_Box *) mov->root_sidx);
676
0
          mov->root_sidx = (GF_SegmentIndexBox *) a;
677
0
          mov->sidx_start_offset = mov->current_top_box_start;
678
0
          mov->sidx_end_offset = gf_bs_get_position(mov->movieFileMap->bs);
679
0
          if (!mov->root_sidx_end_offset) {
680
0
            mov->root_sidx_end_offset = mov->sidx_end_offset;
681
0
            mov->root_sidx_start_offset = mov->sidx_start_offset;
682
0
          }
683
0
        }
684
0
        else if (a->type==GF_ISOM_BOX_TYPE_STYP) {
685
0
          mov->styp_start_offset = mov->current_top_box_start;
686
687
0
          if (mov->seg_styp) gf_isom_box_del(mov->seg_styp);
688
0
          mov->seg_styp = a;
689
0
        } else if (a->type==GF_ISOM_BOX_TYPE_SSIX) {
690
0
          if (mov->seg_ssix) gf_isom_box_del(mov->seg_ssix);
691
0
          mov->seg_ssix = a;
692
0
        } else {
693
0
          gf_isom_box_del(a);
694
0
        }
695
0
        gf_isom_push_mdat_end(mov, mov->current_top_box_start, GF_FALSE);
696
0
      } else if (!mov->NextMoofNumber && (a->type==GF_ISOM_BOX_TYPE_SIDX)) {
697
0
        if (mov->main_sidx) gf_isom_box_del( (GF_Box *) mov->main_sidx);
698
0
        mov->main_sidx = (GF_SegmentIndexBox *) a;
699
0
        mov->main_sidx_end_pos = mov->current_top_box_start + a->size;
700
0
      } else {
701
0
        gf_isom_box_del(a);
702
0
      }
703
0
      break;
704
705
0
    case GF_ISOM_BOX_TYPE_MOOF:
706
      //no support for inplace rewrite for fragmented files
707
0
#ifndef GPAC_DISABLE_ISOM_WRITE
708
0
      gf_isom_disable_inplace_rewrite(mov);
709
0
#endif
710
0
      if (!mov->moov) {
711
0
        GF_LOG(mov->moof ? GF_LOG_DEBUG : GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Movie fragment but no moov (yet) - possibly broken parsing!\n"));
712
0
      }
713
0
      if (mov->single_moof_mode) {
714
0
        mov->single_moof_state++;
715
0
        if (mov->single_moof_state > 1) {
716
0
          gf_isom_box_del(a);
717
0
          return GF_OK;
718
0
        }
719
0
      }
720
0
      ((GF_MovieFragmentBox *)a)->mov = mov;
721
722
0
      mov->moof = (GF_MovieFragmentBox *) a;
723
724
      /*some smooth streaming streams contain a SDTP under the TRAF: this is incorrect, convert it*/
725
0
      FixTrackID(mov);
726
0
      if (! (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) {
727
0
        FixSDTPInTRAF(mov->moof);
728
0
      } else {
729
0
        u32 k;
730
0
        for (k=0; k<gf_list_count(mov->moof->TrackList); k++) {
731
0
          GF_TrackFragmentBox *traf = (GF_TrackFragmentBox *)gf_list_get(mov->moof->TrackList, k);
732
0
          if (traf->sampleGroups) {
733
0
            convert_compact_sample_groups(0, traf->child_boxes, traf->sampleGroups);
734
0
          }
735
0
        }
736
0
      }
737
738
      /*read & debug: store at root level*/
739
0
      if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
740
0
        u32 k;
741
0
        gf_list_add(mov->TopBoxes, a);
742
        /*also update pointers to trex for debug*/
743
0
        if (mov->moov) {
744
0
          for (k=0; k<gf_list_count(mov->moof->TrackList); k++) {
745
0
            GF_TrackFragmentBox *traf = gf_list_get(mov->moof->TrackList, k);
746
0
            if (traf->tfhd && mov->moov->mvex && mov->moov->mvex->TrackExList) {
747
0
              GF_TrackBox *trak = gf_isom_get_track_from_id(mov->moov, traf->tfhd->trackID);
748
0
              u32 j=0;
749
0
              while ((traf->trex = (GF_TrackExtendsBox*)gf_list_enum(mov->moov->mvex->TrackExList, &j))) {
750
0
                if (traf->trex->trackID == traf->tfhd->trackID) {
751
0
                  if (!traf->trex->track) traf->trex->track = trak;
752
0
                  break;
753
0
                }
754
0
                traf->trex = NULL;
755
0
              }
756
0
            }
757
            //we should only parse senc/psec when no saiz/saio is present, otherwise we fetch the info directly
758
0
            if (traf->trex && traf->tfhd && traf->trex->track && traf->sample_encryption) {
759
0
              GF_TrackBox *trak = GetTrackbyID(mov->moov, traf->tfhd->trackID);
760
0
              if (trak) {
761
0
                trak->current_traf_stsd_idx = traf->tfhd->sample_desc_index ? traf->tfhd->sample_desc_index : traf->trex->def_sample_desc_index;
762
0
                e = senc_Parse(mov->movieFileMap->bs, trak, traf, traf->sample_encryption, 0);
763
0
                if (e) return e;
764
0
                trak->current_traf_stsd_idx = 0;
765
0
              }
766
0
            }
767
0
          }
768
0
        } else {
769
0
          for (k=0; k<gf_list_count(mov->moof->TrackList); k++) {
770
0
            GF_TrackFragmentBox *traf = gf_list_get(mov->moof->TrackList, k);
771
0
            if (traf->sample_encryption) {
772
0
              e = senc_Parse(mov->movieFileMap->bs, NULL, traf, traf->sample_encryption, 0);
773
0
              if (e) return e;
774
0
            }
775
0
          }
776
777
0
        }
778
0
      } else if (mov->openMode==GF_ISOM_OPEN_KEEP_FRAGMENTS && mov->moof->mfhd) {
779
0
        mov->NextMoofNumber = mov->moof->mfhd->sequence_number+1;
780
0
        mov->moof = NULL;
781
0
        gf_isom_box_del(a);
782
0
      } else {
783
        /*merge all info*/
784
0
        e = MergeFragment((GF_MovieFragmentBox *)a, mov);
785
0
        gf_isom_box_del(a);
786
0
        if (e) return e;
787
0
      }
788
789
      //done with moov
790
0
      if (mov->root_sidx) {
791
0
        gf_isom_box_del((GF_Box *) mov->root_sidx);
792
0
        mov->root_sidx = NULL;
793
0
      }
794
0
      if (mov->root_ssix) {
795
0
        gf_isom_box_del(mov->seg_ssix);
796
0
        mov->root_ssix = NULL;
797
0
      }
798
0
      if (mov->seg_styp) {
799
0
        gf_isom_box_del(mov->seg_styp);
800
0
        mov->seg_styp = NULL;
801
0
      }
802
0
      mov->sidx_start_offset = 0;
803
0
      mov->sidx_end_offset = 0;
804
0
      mov->styp_start_offset = 0;
805
0
      break;
806
0
#endif
807
0
    case GF_ISOM_BOX_TYPE_UNKNOWN:
808
0
    {
809
0
      GF_UnknownBox *box = (GF_UnknownBox*)a;
810
0
      if (box->original_4cc == GF_ISOM_BOX_TYPE_JP) {
811
0
        u8 *c = (u8 *) box->data;
812
0
        if ((box->dataSize==4) && (GF_4CC(c[0],c[1],c[2],c[3])==(u32)0x0D0A870A))
813
0
          mov->is_jp2 = 1;
814
0
        gf_isom_box_del(a);
815
0
      } else if ((mov->openMode == GF_ISOM_OPEN_READ_DUMP)
816
0
        || (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)
817
0
        || !mov->mdat || !mov->moov
818
0
      ) {
819
0
        e = gf_list_add(mov->TopBoxes, a);
820
0
        if (e) return e;
821
0
      } else {
822
        //trash unknown boxes after moov/mdat if not debug
823
0
        gf_isom_box_del(a);
824
0
      }
825
0
    }
826
0
    break;
827
828
0
    case GF_ISOM_BOX_TYPE_PRFT:
829
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
830
0
      if (!(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) {
831
        //keep the last one read
832
0
        if (mov->last_producer_ref_time)
833
0
          gf_isom_box_del(a);
834
0
        else
835
0
          mov->last_producer_ref_time = (GF_ProducerReferenceTimeBox *)a;
836
0
      } else {
837
0
        e = gf_list_add(mov->TopBoxes, a);
838
0
        if (e) return e;
839
0
      }
840
0
#endif
841
0
      break;
842
843
0
    case GF_ISOM_BOX_TYPE_EMSG:
844
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
845
0
      if (! (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) {
846
0
        if (!mov->emsgs) mov->emsgs = gf_list_new();
847
0
        gf_list_add(mov->emsgs, a);
848
0
      } else {
849
0
        e = gf_list_add(mov->TopBoxes, a);
850
0
        if (e) return e;
851
0
      }
852
0
#endif
853
0
      break;
854
855
0
    case GF_ISOM_BOX_TYPE_MFRA:
856
0
    case GF_ISOM_BOX_TYPE_MFRO:
857
      //only keep for dump mode, otherwise we ignore these boxes and we don't want to carry them over in non-fragmented file
858
0
      if (! (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) {
859
0
        gf_isom_box_del(a);
860
0
      } else {
861
0
        e = gf_list_add(mov->TopBoxes, a);
862
0
        if (e) return e;
863
0
      }
864
0
      break;
865
866
0
    default:
867
0
      e = gf_list_add(mov->TopBoxes, a);
868
0
      if (e) return e;
869
0
      break;
870
0
    }
871
872
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
873
    /*remember where we left, in case we append an entire number of movie fragments*/
874
0
    mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs) + mov->bytes_removed;
875
0
#endif
876
0
  }
877
0
  if (!mov->first_data_toplevel_offset)
878
0
    mov->first_data_toplevel_offset = mov->current_top_box_start;
879
880
  /*we need at least moov or meta*/
881
0
  if (!mov->moov && !mov->meta
882
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
883
0
          && !mov->moof && !mov->is_index_segment
884
0
#endif
885
0
     ) {
886
0
    return GF_ISOM_INCOMPLETE_FILE;
887
0
  }
888
  /*we MUST have movie header*/
889
0
  if (!gf_opts_get_bool("core", "no-check")) {
890
0
    if (mov->moov && !mov->moov->mvhd && (mov->moov->has_cmvd!=2)) {
891
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing MVHD in MOOV!\n"));
892
0
      return GF_ISOM_INVALID_FILE;
893
0
    }
894
895
    /*we MUST have meta handler*/
896
0
    if (mov->meta && !mov->meta->handler) {
897
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing handler in META!\n"));
898
0
      return GF_ISOM_INVALID_FILE;
899
0
    }
900
0
  }
901
902
0
#ifndef GPAC_DISABLE_ISOM_WRITE
903
904
0
  if (mov->moov) {
905
    /*set the default interleaving time*/
906
0
    mov->interleavingTime = mov->moov->mvhd ? mov->moov->mvhd->timeScale : 0;
907
908
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
909
    /*in edit mode with successfully loaded fragments, delete all fragment signaling since
910
    file is no longer fragmented*/
911
0
    if ((mov->openMode > GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_KEEP_FRAGMENTS) && mov->moov->mvex) {
912
0
      gf_isom_box_del_parent(&mov->moov->child_boxes, (GF_Box *)mov->moov->mvex);
913
0
      mov->moov->mvex = NULL;
914
0
    }
915
0
#endif
916
917
0
  }
918
919
  //create a default mdat if none was found
920
0
  if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_KEEP_FRAGMENTS)) {
921
0
    mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT);
922
0
    if (!mov->mdat) return GF_OUT_OF_MEM;
923
0
    e = gf_list_add(mov->TopBoxes, mov->mdat);
924
0
    if (e) return e;
925
0
  }
926
0
#endif /*GPAC_DISABLE_ISOM_WRITE*/
927
928
0
  return GF_OK;
929
0
}
930
931
extern u64 unused_bytes;
932
933
GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u32 *boxType, u64 *bytesMissing, Bool progressive_mode)
934
0
{
935
0
  GF_Err e;
936
0
  GF_Blob *blob = NULL;
937
938
  //if associated file is a blob, lock blob before parsing !
939
0
  if (mov->movieFileMap && ((mov->movieFileMap->type == GF_ISOM_DATA_MEM) || (mov->movieFileMap->type == GF_ISOM_DATA_FILE))) {
940
0
    blob = ((GF_FileDataMap *)mov->movieFileMap)->blob;
941
0
  }
942
943
0
  if (blob)
944
0
    gf_mx_p(blob->mx);
945
946
0
  unused_bytes = 0;
947
0
  e = gf_isom_parse_movie_boxes_internal(mov, boxType, bytesMissing, progressive_mode);
948
949
0
  if (blob)
950
0
    gf_mx_v(blob->mx);
951
0
  return e;
952
953
0
}
954
955
GF_ISOFile *gf_isom_new_movie()
956
0
{
957
0
  GF_ISOFile *mov = (GF_ISOFile*)gf_malloc(sizeof(GF_ISOFile));
958
0
  if (mov == NULL) {
959
0
    gf_isom_set_last_error(NULL, GF_OUT_OF_MEM);
960
0
    return NULL;
961
0
  }
962
0
  memset(mov, 0, sizeof(GF_ISOFile));
963
964
  /*init the boxes*/
965
0
  mov->TopBoxes = gf_list_new();
966
0
  if (!mov->TopBoxes) {
967
0
    gf_isom_set_last_error(NULL, GF_OUT_OF_MEM);
968
0
    gf_free(mov);
969
0
    return NULL;
970
0
  }
971
972
  /*default storage mode is flat*/
973
0
#ifndef GPAC_DISABLE_ISOM_WRITE
974
0
  mov->storageMode = GF_ISOM_STORE_FLAT;
975
0
#endif
976
0
  mov->es_id_default_sync = -1;
977
0
  return mov;
978
0
}
979
980
//Create and parse the movie for READ - EDIT only
981
GF_ISOFile *gf_isom_open_file(const char *fileName, GF_ISOOpenMode OpenMode, const char *tmp_dir)
982
0
{
983
0
  GF_Err e;
984
0
  u64 bytes;
985
0
  GF_ISOFile *mov = gf_isom_new_movie();
986
0
  if (!mov || !fileName) return NULL;
987
988
0
  mov->fileName = gf_strdup(fileName);
989
0
  mov->openMode = OpenMode;
990
991
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
992
0
  if (OpenMode==GF_ISOM_OPEN_READ_DUMP)
993
0
    mov->store_traf_map = GF_TRUE;
994
0
#endif
995
996
0
  if ( (OpenMode == GF_ISOM_OPEN_READ)
997
0
    || (OpenMode == GF_ISOM_OPEN_READ_DUMP)
998
0
    || (OpenMode == GF_ISOM_OPEN_READ_DUMP_NO_COMP)
999
0
    || (OpenMode == GF_ISOM_OPEN_READ_EDIT)
1000
0
  ) {
1001
0
    if (OpenMode == GF_ISOM_OPEN_READ_EDIT) {
1002
0
      mov->openMode = GF_ISOM_OPEN_READ_EDIT;
1003
1004
0
#ifndef GPAC_DISABLE_ISOM_WRITE
1005
      // create a memory edit map in case we add samples, typically during import
1006
0
      e = gf_isom_datamap_new(NULL, tmp_dir, GF_ISOM_DATA_MAP_WRITE, & mov->editFileMap);
1007
#else
1008
      e = GF_NOT_SUPPORTED;
1009
#endif
1010
0
      if (e) {
1011
0
        gf_isom_set_last_error(NULL, e);
1012
0
        gf_isom_delete_movie(mov);
1013
0
        return NULL;
1014
0
      }
1015
0
    } else {
1016
0
      mov->openMode = GF_ISOM_OPEN_READ;
1017
0
    }
1018
0
    mov->es_id_default_sync = -1;
1019
    //for open, we do it the regular way and let the GF_DataMap assign the appropriate struct
1020
    //this can be FILE (the only one supported...) as well as remote
1021
    //(HTTP, ...),not suported yet
1022
    //the bitstream IS PART OF the GF_DataMap
1023
    //as this is read-only, use a FileMapping. this is the only place where
1024
    //we use file mapping
1025
0
    e = gf_isom_datamap_new(fileName, NULL, GF_ISOM_DATA_MAP_READ_ONLY, &mov->movieFileMap);
1026
0
    if (e) {
1027
0
      gf_isom_set_last_error(NULL, e);
1028
0
      gf_isom_delete_movie(mov);
1029
0
      return NULL;
1030
0
    }
1031
0
    if (OpenMode == GF_ISOM_OPEN_READ_DUMP_NO_COMP) {
1032
0
      OpenMode = GF_ISOM_OPEN_READ_DUMP;
1033
0
      gf_bs_set_cookie(mov->movieFileMap->bs, GF_ISOM_BS_COOKIE_NO_DECOMP);
1034
0
    }
1035
1036
0
    if (OpenMode == GF_ISOM_OPEN_READ_DUMP) {
1037
0
      mov->FragmentsFlags |= GF_ISOM_FRAG_READ_DEBUG;
1038
0
    }
1039
0
  } else {
1040
1041
#ifdef GPAC_DISABLE_ISOM_WRITE
1042
    //not allowed for READ_ONLY lib
1043
    gf_isom_delete_movie(mov);
1044
    gf_isom_set_last_error(NULL, GF_ISOM_INVALID_MODE);
1045
    return NULL;
1046
1047
#else
1048
1049
    //set a default output name for edited file
1050
0
    mov->finalName = (char*)gf_malloc(strlen(fileName) + 5);
1051
0
    if (!mov->finalName) {
1052
0
      gf_isom_set_last_error(NULL, GF_OUT_OF_MEM);
1053
0
      gf_isom_delete_movie(mov);
1054
0
      return NULL;
1055
0
    }
1056
0
    strcpy(mov->finalName, "out_");
1057
0
    strcat(mov->finalName, fileName);
1058
1059
    //open the original file with edit tag
1060
0
    e = gf_isom_datamap_new(fileName, NULL, GF_ISOM_DATA_MAP_EDIT, &mov->movieFileMap);
1061
    //if the file doesn't exist, we assume it's wanted and create one from scratch
1062
0
    if (e) {
1063
0
      gf_isom_set_last_error(NULL, e);
1064
0
      gf_isom_delete_movie(mov);
1065
0
      return NULL;
1066
0
    }
1067
    //and create a temp fileName for the edit
1068
0
    e = gf_isom_datamap_new("_gpac_isobmff_tmp_edit", tmp_dir, GF_ISOM_DATA_MAP_WRITE, & mov->editFileMap);
1069
0
    if (e) {
1070
0
      gf_isom_set_last_error(NULL, e);
1071
0
      gf_isom_delete_movie(mov);
1072
0
      return NULL;
1073
0
    }
1074
1075
0
    mov->es_id_default_sync = -1;
1076
1077
0
#endif
1078
0
  }
1079
1080
  //OK, let's parse the movie...
1081
0
  mov->LastError = gf_isom_parse_movie_boxes(mov, NULL, &bytes, 0);
1082
0
  if (((OpenMode & 0xFF) == GF_ISOM_OPEN_READ_DUMP) && (mov->LastError==GF_ISOM_INCOMPLETE_FILE))
1083
0
    mov->LastError = GF_OK;
1084
1085
#if 0
1086
  if (!mov->LastError && (OpenMode == GF_ISOM_OPEN_CAT_FRAGMENTS)) {
1087
    gf_isom_datamap_del(mov->movieFileMap);
1088
    /*reopen the movie file map in cat mode*/
1089
    mov->LastError = gf_isom_datamap_new(fileName, tmp_dir, GF_ISOM_DATA_MAP_CAT, & mov->movieFileMap);
1090
  }
1091
#endif
1092
1093
0
  if (mov->LastError) {
1094
0
    gf_isom_set_last_error(NULL, mov->LastError);
1095
0
    gf_isom_delete_movie(mov);
1096
0
    return NULL;
1097
0
  }
1098
1099
0
  mov->nb_box_init_seg = gf_list_count(mov->TopBoxes);
1100
0
  return mov;
1101
0
}
1102
1103
GF_EXPORT
1104
GF_Err gf_isom_set_write_callback(GF_ISOFile *mov,
1105
      gf_isom_on_block_out on_block_out,
1106
      gf_isom_on_block_patch on_block_patch,
1107
      gf_isom_on_last_block_start on_last_block_start,
1108
      void *usr_data,
1109
      u32 block_size)
1110
0
{
1111
0
#ifndef GPAC_DISABLE_ISOM_WRITE
1112
0
  if (mov->finalName && !strcmp(mov->finalName, "_gpac_isobmff_redirect")) {}
1113
0
  else if (mov->fileName && !strcmp(mov->fileName, "_gpac_isobmff_redirect")) {}
1114
0
  else return GF_BAD_PARAM;
1115
0
  if (!on_block_out) return GF_BAD_PARAM;
1116
0
  mov->on_block_out = on_block_out;
1117
0
  mov->on_block_patch = on_block_patch;
1118
0
  mov->on_last_block_start = on_last_block_start;
1119
0
  mov->on_block_out_usr_data = usr_data;
1120
0
  mov->on_block_out_block_size = block_size;
1121
0
  return GF_OK;
1122
#else
1123
  return GF_NOT_SUPPORTED;
1124
#endif
1125
0
}
1126
1127
1128
u64 gf_isom_get_mp4time()
1129
0
{
1130
0
  u32 calctime, msec;
1131
0
  u64 ret;
1132
0
  gf_utc_time_since_1970(&calctime, &msec);
1133
0
  calctime += GF_ISOM_MAC_TIME_OFFSET;
1134
0
  ret = calctime;
1135
0
  return ret;
1136
0
}
1137
1138
void gf_isom_delete_movie(GF_ISOFile *mov)
1139
0
{
1140
0
  if (!mov) return;
1141
1142
  // track datahandlers might point to the movieFileMap that we are about to delete
1143
  // remove the association to avoid double frees
1144
0
  if (mov->moov && mov->moov->trackList) {
1145
0
    u32 i;
1146
0
    for (i=0; i<gf_list_count(mov->moov->trackList); i++) {
1147
0
      GF_TrackBox *trak = (GF_TrackBox*)gf_list_get(mov->moov->trackList, i);
1148
1149
0
      if (trak && trak->Media && trak->Media->information) {
1150
0
        if (trak->Media->information->dataHandler == mov->movieFileMap)
1151
0
          trak->Media->information->dataHandler = NULL;
1152
1153
0
        if (trak->Media->information->scalableDataHandler == mov->movieFileMap)
1154
0
          trak->Media->information->scalableDataHandler = NULL;
1155
0
      }
1156
0
    }
1157
0
  }
1158
1159
  //these are our two main files
1160
0
  if (mov->movieFileMap) gf_isom_datamap_del(mov->movieFileMap);
1161
1162
0
#ifndef GPAC_DISABLE_ISOM_WRITE
1163
0
  if (mov->editFileMap) {
1164
0
    gf_isom_datamap_del(mov->editFileMap);
1165
0
  }
1166
0
  if (mov->finalName) gf_free(mov->finalName);
1167
0
#endif
1168
1169
0
  gf_isom_box_array_del(mov->TopBoxes);
1170
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
1171
0
  gf_isom_box_array_del(mov->moof_list);
1172
0
  if (mov->mfra)
1173
0
    gf_isom_box_del((GF_Box*)mov->mfra);
1174
0
  if (mov->sidx_pts_store)
1175
0
    gf_free(mov->sidx_pts_store);
1176
0
  if (mov->sidx_pts_next_store)
1177
0
    gf_free(mov->sidx_pts_next_store);
1178
1179
0
  if (mov->main_sidx)
1180
0
    gf_isom_box_del((GF_Box*)mov->main_sidx);
1181
1182
0
  if (mov->block_buffer)
1183
0
    gf_free(mov->block_buffer);
1184
1185
0
  if (mov->emsgs)
1186
0
    gf_isom_box_array_del(mov->emsgs);
1187
0
#endif
1188
0
  if (mov->last_producer_ref_time)
1189
0
    gf_isom_box_del((GF_Box *) mov->last_producer_ref_time);
1190
0
  if (mov->fileName) gf_free(mov->fileName);
1191
0
  gf_free(mov);
1192
0
}
1193
1194
GF_TrackBox *gf_isom_get_track_from_id(GF_MovieBox *moov, GF_ISOTrackID trackID)
1195
0
{
1196
0
  u32 i, count;
1197
0
  if (!moov || !trackID) return NULL;
1198
1199
0
  count = gf_list_count(moov->trackList);
1200
0
  for (i = 0; i<count; i++) {
1201
0
    GF_TrackBox *trak = (GF_TrackBox*)gf_list_get(moov->trackList, i);
1202
0
    if (trak->Header->trackID == trackID) return trak;
1203
0
  }
1204
0
  return NULL;
1205
0
}
1206
1207
GF_TrackBox *gf_isom_get_track_from_original_id(GF_MovieBox *moov, u32 originalID, u32 originalFile)
1208
0
{
1209
0
  u32 i, count;
1210
0
  if (!moov || !originalID) return NULL;
1211
1212
0
  count = gf_list_count(moov->trackList);
1213
0
  for (i = 0; i<count; i++) {
1214
0
    GF_TrackBox *trak = (GF_TrackBox*)gf_list_get(moov->trackList, i);
1215
0
    if ((trak->originalFile == originalFile) && (trak->originalID == originalID)) return trak;
1216
0
  }
1217
0
  return NULL;
1218
0
}
1219
1220
GF_EXPORT
1221
GF_TrackBox *gf_isom_get_track_box(GF_ISOFile *movie, u32 trackNumber)
1222
0
{
1223
0
  GF_TrackBox *trak;
1224
0
  if (!movie) return NULL;
1225
0
  trak = gf_isom_get_track(movie->moov, trackNumber);
1226
0
  if (!trak) movie->LastError = GF_BAD_PARAM;
1227
0
  return trak;
1228
0
}
1229
1230
1231
//WARNING: MOVIETIME IS EXPRESSED IN MEDIA TS
1232
GF_Err GetMediaTime(GF_TrackBox *trak, Bool force_non_empty, u64 movieTime, u64 *MediaTime, s64 *SegmentStartTime, s64 *MediaOffset, u8 *useEdit, u64 *next_edit_start_plus_one)
1233
0
{
1234
#if 0
1235
  GF_Err e;
1236
  u32 sampleNumber, prevSampleNumber;
1237
  u64 firstDTS;
1238
#endif
1239
0
  u32 i, count;
1240
0
  Bool last_is_empty = 0;
1241
0
  u64 time, lastSampleTime;
1242
0
  s64 mtime;
1243
0
  GF_EdtsEntry *ent;
1244
0
  Double scale_ts;
1245
0
  GF_SampleTableBox *stbl = trak->Media->information->sampleTable;
1246
1247
0
  if (next_edit_start_plus_one) *next_edit_start_plus_one = 0;
1248
0
  *useEdit = 1;
1249
0
  *MediaTime = 0;
1250
  //no segment yet...
1251
0
  *SegmentStartTime = -1;
1252
0
  *MediaOffset = -1;
1253
0
  if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale || !stbl->SampleSize) {
1254
0
    return GF_ISOM_INVALID_FILE;
1255
0
  }
1256
1257
  //no samples...
1258
0
  if (!stbl->SampleSize->sampleCount) {
1259
0
    lastSampleTime = 0;
1260
0
  } else {
1261
0
    lastSampleTime = trak->Media->mediaHeader->duration;
1262
0
  }
1263
1264
  //No edits, 1 to 1 mapping
1265
0
  if (! trak->editBox || !trak->editBox->editList) {
1266
0
    *MediaTime = movieTime;
1267
    //check this is in our media time line
1268
0
    if ((*MediaTime > lastSampleTime)
1269
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
1270
0
            && !trak->moov->mov->moof
1271
0
#endif
1272
0
       ) {
1273
0
      *MediaTime = lastSampleTime;
1274
0
    }
1275
0
    *useEdit = 0;
1276
0
    return GF_OK;
1277
0
  }
1278
  //browse the edit list and get the time
1279
0
  scale_ts = trak->Media->mediaHeader->timeScale;
1280
0
  scale_ts /= trak->moov->mvhd->timeScale;
1281
1282
0
  time = 0;
1283
0
  ent = NULL;
1284
0
  count=gf_list_count(trak->editBox->editList->entryList);
1285
0
  for (i=0; i<count; i++) {
1286
0
    ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, i);
1287
0
    if ( (time + ent->segmentDuration) * scale_ts > movieTime) {
1288
0
      if (!force_non_empty || (ent->mediaTime >= 0)) {
1289
0
        if (next_edit_start_plus_one) *next_edit_start_plus_one = 1 + (u64) ((time + ent->segmentDuration) * scale_ts);
1290
0
        goto ent_found;
1291
0
      }
1292
0
    }
1293
0
    time += ent->segmentDuration;
1294
0
    last_is_empty = ent->segmentDuration ? 0 : 1;
1295
0
  }
1296
1297
0
  if (last_is_empty) {
1298
0
    ent = (GF_EdtsEntry *)gf_list_last(trak->editBox->editList->entryList);
1299
0
    if (ent->mediaRate == 0x10000) {
1300
0
      *MediaTime = movieTime + ent->mediaTime;
1301
0
    } else {
1302
0
      ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, 0);
1303
0
      if (ent->mediaRate == -0x10000) {
1304
0
        u64 dur = (u64) (ent->segmentDuration * scale_ts);
1305
0
        *MediaTime = (movieTime > dur) ? (movieTime-dur) : 0;
1306
0
      }
1307
0
    }
1308
0
    *useEdit = 0;
1309
0
    return GF_OK;
1310
0
  }
1311
1312
1313
  //we had nothing in the list (strange file but compliant...)
1314
  //return the 1 to 1 mapped vale of the last media sample
1315
0
  if (!ent) {
1316
0
    *MediaTime = movieTime;
1317
    //check this is in our media time line
1318
0
    if (*MediaTime > lastSampleTime) *MediaTime = lastSampleTime;
1319
0
    *useEdit = 0;
1320
0
    return GF_OK;
1321
0
  }
1322
  //request for a bigger time that what we can give: return the last sample (undefined behavior...)
1323
0
  *MediaTime = lastSampleTime;
1324
0
  return GF_OK;
1325
1326
0
ent_found:
1327
  //OK, we found our entry, set the SegmentTime
1328
0
  *SegmentStartTime = time;
1329
1330
  //we request an empty list, there's no media here...
1331
0
  if (ent->mediaTime < 0) {
1332
0
    *MediaTime = 0;
1333
0
    return GF_OK;
1334
0
  }
1335
  //we request a dwell edit
1336
0
  if (! ent->mediaRate) {
1337
0
    *MediaTime = ent->mediaTime;
1338
    //no media offset
1339
0
    *MediaOffset = 0;
1340
0
    *useEdit = 2;
1341
0
    return GF_OK;
1342
0
  }
1343
1344
  /*WARNING: this can be "-1" when doing searchForward mode (to prevent jumping to next entry)*/
1345
0
  mtime = ent->mediaTime + movieTime - (time * trak->Media->mediaHeader->timeScale / trak->moov->mvhd->timeScale);
1346
0
  if (mtime<0) mtime = 0;
1347
0
  *MediaTime = (u64) mtime;
1348
0
  *MediaOffset = ent->mediaTime;
1349
1350
#if 0
1351
  //
1352
  //Sanity check: is the requested time valid ? This is to cope with wrong EditLists
1353
  //we have the translated time, but we need to make sure we have a sample at this time ...
1354
  //we have to find a COMPOSITION time
1355
  e = stbl_findEntryForTime(stbl, (u32) *MediaTime, 1, &sampleNumber, &prevSampleNumber);
1356
  if (e) return e;
1357
1358
  //first case: our time is after the last sample DTS (it's a broken editList somehow)
1359
  //set the media time to the last sample
1360
  if (!sampleNumber && !prevSampleNumber) {
1361
    *MediaTime = lastSampleTime;
1362
    return GF_OK;
1363
  }
1364
  //get the appropriated sample
1365
  if (!sampleNumber) sampleNumber = prevSampleNumber;
1366
1367
  stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &DTS);
1368
  CTS = 0;
1369
  if (stbl->CompositionOffset) stbl_GetSampleCTS(stbl->CompositionOffset, sampleNumber, &CTS);
1370
1371
  //now get the entry sample (the entry time gives the CTS, and we need the DTS
1372
  e = stbl_findEntryForTime(stbl, (u32) ent->mediaTime, 0, &sampleNumber, &prevSampleNumber);
1373
  if (e) return e;
1374
1375
  //oops, the mediaTime indicates a sample that is not in our media !
1376
  if (!sampleNumber && !prevSampleNumber) {
1377
    *MediaTime = lastSampleTime;
1378
    return GF_ISOM_INVALID_FILE;
1379
  }
1380
  if (!sampleNumber) sampleNumber = prevSampleNumber;
1381
1382
  stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &firstDTS);
1383
1384
  //and store the "time offset" of the desired sample in this segment
1385
  //this is weird, used to rebuild the timeStamp when reading from the track, not the
1386
  //media ...
1387
  *MediaOffset = firstDTS;
1388
#endif
1389
0
  return GF_OK;
1390
0
}
1391
1392
GF_Err GetNextMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *OutMovieTime)
1393
0
{
1394
0
  u32 i;
1395
0
  u64 time;
1396
0
  GF_EdtsEntry *ent;
1397
1398
0
  *OutMovieTime = 0;
1399
0
  if (! trak->editBox || !trak->editBox->editList) return GF_BAD_PARAM;
1400
1401
0
  time = 0;
1402
0
  ent = NULL;
1403
0
  i=0;
1404
0
  while ((ent = (GF_EdtsEntry *)gf_list_enum(trak->editBox->editList->entryList, &i))) {
1405
0
    if (gf_timestamp_greater_or_equal(time, trak->moov->mvhd->timeScale, movieTime, trak->Media->mediaHeader->timeScale)) {
1406
      /*skip empty edits*/
1407
0
      if (ent->mediaTime >= 0) {
1408
0
        *OutMovieTime = time * trak->Media->mediaHeader->timeScale / trak->moov->mvhd->timeScale;
1409
0
        if (*OutMovieTime>0) *OutMovieTime -= 1;
1410
0
        return GF_OK;
1411
0
      }
1412
0
    }
1413
0
    time += ent->segmentDuration;
1414
0
  }
1415
  //request for a bigger time that what we can give: return the last sample (undefined behavior...)
1416
0
  *OutMovieTime = trak->moov->mvhd->duration;
1417
0
  return GF_EOS;
1418
0
}
1419
1420
GF_Err GetPrevMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *OutMovieTime)
1421
0
{
1422
0
  u32 i;
1423
0
  u64 time;
1424
0
  GF_EdtsEntry *ent;
1425
1426
0
  *OutMovieTime = 0;
1427
0
  if (! trak->editBox || !trak->editBox->editList) return GF_BAD_PARAM;
1428
1429
0
  time = 0;
1430
0
  ent = NULL;
1431
0
  i=0;
1432
0
  while ((ent = (GF_EdtsEntry *)gf_list_enum(trak->editBox->editList->entryList, &i))) {
1433
0
    if (ent->mediaTime == -1) {
1434
0
      if ( gf_timestamp_greater_or_equal(time + ent->segmentDuration, trak->moov->mvhd->timeScale, movieTime, trak->Media->mediaHeader->timeScale)) {
1435
0
        *OutMovieTime = gf_timestamp_rescale(time, trak->moov->mvhd->timeScale, trak->Media->mediaHeader->timeScale);
1436
0
        return GF_OK;
1437
0
      }
1438
0
      continue;
1439
0
    }
1440
    /*get the first entry whose end is greater than or equal to the desired time*/
1441
0
    time += ent->segmentDuration;
1442
0
    if (gf_timestamp_greater_or_equal(time, trak->moov->mvhd->timeScale, movieTime, trak->Media->mediaHeader->timeScale)) {
1443
0
      *OutMovieTime = time * trak->Media->mediaHeader->timeScale / trak->moov->mvhd->timeScale;
1444
0
      return GF_OK;
1445
0
    }
1446
0
  }
1447
0
  *OutMovieTime = 0;
1448
0
  return GF_OK;
1449
0
}
1450
1451
#ifndef GPAC_DISABLE_ISOM_WRITE
1452
1453
GF_Err gf_isom_insert_moov(GF_ISOFile *file)
1454
0
{
1455
0
  GF_MovieHeaderBox *mvhd;
1456
0
  if (file->moov) return GF_OK;
1457
1458
  //OK, create our boxes (mvhd, iods, ...)
1459
0
  file->moov = (GF_MovieBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MOOV);
1460
0
  if (!file->moov) return GF_OUT_OF_MEM;
1461
0
  file->moov->mov = file;
1462
  //Header SetUp
1463
0
  mvhd = (GF_MovieHeaderBox *) gf_isom_box_new_parent(&file->moov->child_boxes, GF_ISOM_BOX_TYPE_MVHD);
1464
0
  if (!mvhd) return GF_OUT_OF_MEM;
1465
1466
0
  if (gf_sys_is_test_mode() ) {
1467
0
    mvhd->creationTime = mvhd->modificationTime = 0;
1468
0
  } else {
1469
0
    u64 now = gf_isom_get_mp4time();
1470
0
    mvhd->creationTime = now;
1471
0
    if (!file->keep_utc)
1472
0
      mvhd->modificationTime = now;
1473
0
  }
1474
1475
0
  mvhd->nextTrackID = 1;
1476
  //600 is our default movie TimeScale
1477
0
  mvhd->timeScale = 600;
1478
1479
0
  file->interleavingTime = mvhd->timeScale;
1480
0
  moov_on_child_box((GF_Box*)file->moov, (GF_Box *)mvhd, GF_FALSE);
1481
0
  gf_list_add(file->TopBoxes, file->moov);
1482
0
  return GF_OK;
1483
0
}
1484
1485
//Create the movie for WRITE only
1486
GF_ISOFile *gf_isom_create_movie(const char *fileName, GF_ISOOpenMode OpenMode, const char *tmp_dir)
1487
0
{
1488
0
  GF_Err e;
1489
1490
0
  GF_ISOFile *mov = gf_isom_new_movie();
1491
0
  if (!mov || !fileName) return NULL;
1492
0
  mov->openMode = OpenMode;
1493
  //then set up our movie
1494
1495
  //in WRITE, the input dataMap is ALWAYS NULL
1496
0
  mov->movieFileMap = NULL;
1497
1498
  //but we have the edit one
1499
0
  if (OpenMode == GF_ISOM_OPEN_WRITE) {
1500
0
    const char *ext;
1501
    //THIS IS NOT A TEMP FILE, WRITE mode is used for "live capture"
1502
    //this file will be the final file...
1503
0
    mov->fileName = gf_strdup(fileName);
1504
0
    e = gf_isom_datamap_new(fileName, NULL, GF_ISOM_DATA_MAP_WRITE, &mov->editFileMap);
1505
0
    if (e) goto err_exit;
1506
1507
    /*brand is set to ISOM or QT by default - it may be touched until sample data is added to track*/
1508
0
    ext = gf_file_ext_start(fileName);
1509
0
    if (ext && (!strnicmp(ext, ".mov", 4) || !strnicmp(ext, ".qt", 3))) {
1510
0
      gf_isom_set_brand_info((GF_ISOFile *) mov, GF_ISOM_BRAND_QT, 512);
1511
0
    } else {
1512
0
      gf_isom_set_brand_info((GF_ISOFile *) mov, GF_ISOM_BRAND_ISOM, 1);
1513
0
    }
1514
0
  } else {
1515
    //we are in EDIT mode but we are creating the file -> temp file
1516
0
    mov->finalName = gf_strdup(fileName);
1517
0
    e = gf_isom_datamap_new("_gpac_isobmff_tmp_edit", tmp_dir, GF_ISOM_DATA_MAP_WRITE, &mov->editFileMap);
1518
0
    if (e) {
1519
0
      gf_isom_set_last_error(NULL, e);
1520
0
      gf_isom_delete_movie(mov);
1521
0
      return NULL;
1522
0
    }
1523
    //brand is set to ISOM by default
1524
0
    gf_isom_set_brand_info( (GF_ISOFile *) mov, GF_ISOM_BRAND_ISOM, 1);
1525
0
  }
1526
1527
  //create an MDAT
1528
0
  mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT);
1529
0
  if (!mov->mdat) {
1530
0
    gf_isom_set_last_error(NULL, GF_OUT_OF_MEM);
1531
0
    gf_isom_delete_movie(mov);
1532
0
    return NULL;
1533
0
  }
1534
0
  gf_list_add(mov->TopBoxes, mov->mdat);
1535
1536
  //default behavior is capture mode, no interleaving (eg, no rewrite of mdat)
1537
0
  mov->storageMode = GF_ISOM_STORE_FLAT;
1538
0
  return mov;
1539
1540
0
err_exit:
1541
0
  gf_isom_set_last_error(NULL, e);
1542
0
  if (mov) gf_isom_delete_movie(mov);
1543
0
  return NULL;
1544
0
}
1545
1546
GF_EdtsEntry *CreateEditEntry(u64 EditDuration, u64 MediaTime, u32 MediaRate, u8 EditMode)
1547
0
{
1548
0
  GF_EdtsEntry *ent;
1549
1550
0
  ent = (GF_EdtsEntry*)gf_malloc(sizeof(GF_EdtsEntry));
1551
0
  if (!ent) return NULL;
1552
1553
0
  switch (EditMode) {
1554
0
  case GF_ISOM_EDIT_EMPTY:
1555
0
    ent->mediaRate = 0x10000;
1556
0
    ent->mediaTime = -1;
1557
0
    break;
1558
1559
0
  case GF_ISOM_EDIT_DWELL:
1560
0
    ent->mediaRate = 0;
1561
0
    ent->mediaTime = MediaTime;
1562
0
    break;
1563
0
  default:
1564
0
    ent->mediaRate = MediaRate;
1565
0
    ent->mediaTime = MediaTime;
1566
0
    break;
1567
0
  }
1568
0
  ent->segmentDuration = EditDuration;
1569
0
  return ent;
1570
0
}
1571
1572
GF_Err gf_isom_add_subsample_info(GF_SubSampleInformationBox *sub_samples, u32 sampleNumber, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable)
1573
0
{
1574
0
  u32 i, count, last_sample;
1575
0
  GF_SubSampleInfoEntry *pSamp;
1576
0
  GF_SubSampleEntry *pSubSamp;
1577
1578
0
  pSamp = NULL;
1579
0
  last_sample = 0;
1580
0
  count = gf_list_count(sub_samples->Samples);
1581
0
  for (i=0; i<count; i++) {
1582
0
    pSamp = (GF_SubSampleInfoEntry*) gf_list_get(sub_samples->Samples, i);
1583
    /*TODO - do we need to support insertion of subsample info ?*/
1584
0
    if (last_sample + pSamp->sample_delta > sampleNumber) return GF_NOT_SUPPORTED;
1585
0
    if (last_sample + pSamp->sample_delta == sampleNumber) break;
1586
0
    last_sample += pSamp->sample_delta;
1587
0
    pSamp = NULL;
1588
0
  }
1589
1590
0
  if (!pSamp) {
1591
0
    GF_SAFEALLOC(pSamp, GF_SubSampleInfoEntry);
1592
0
    if (!pSamp) return GF_OUT_OF_MEM;
1593
0
    pSamp->SubSamples = gf_list_new();
1594
0
    if (!pSamp->SubSamples ) {
1595
0
      gf_free(pSamp);
1596
0
      return GF_OUT_OF_MEM;
1597
0
    }
1598
0
    pSamp->sample_delta = sampleNumber - last_sample;
1599
0
    gf_list_add(sub_samples->Samples, pSamp);
1600
0
  }
1601
1602
0
  if ((subSampleSize>0xFFFF) && !sub_samples->version) {
1603
0
    sub_samples->version = 1;
1604
0
  }
1605
  /*remove last subsample info*/
1606
0
  if (!subSampleSize) {
1607
0
    pSubSamp = gf_list_last(pSamp->SubSamples);
1608
0
    gf_list_rem_last(pSamp->SubSamples);
1609
0
    gf_free(pSubSamp);
1610
0
    if (!gf_list_count(pSamp->SubSamples)) {
1611
0
      gf_list_del_item(sub_samples->Samples, pSamp);
1612
0
      gf_list_del(pSamp->SubSamples);
1613
0
      gf_free(pSamp);
1614
0
    }
1615
0
    return GF_OK;
1616
0
  }
1617
  /*add subsample*/
1618
0
  GF_SAFEALLOC(pSubSamp, GF_SubSampleEntry);
1619
0
  if (!pSubSamp) return GF_OUT_OF_MEM;
1620
0
  pSubSamp->subsample_size = subSampleSize;
1621
0
  pSubSamp->subsample_priority = priority;
1622
0
  pSubSamp->reserved = reserved;
1623
0
  pSubSamp->discardable = discardable;
1624
0
  return gf_list_add(pSamp->SubSamples, pSubSamp);
1625
0
}
1626
1627
#endif /*GPAC_DISABLE_ISOM_WRITE*/
1628
1629
#if 0 //unused
1630
u32 gf_isom_sample_get_subsamples_count(GF_ISOFile *movie, u32 track)
1631
{
1632
  GF_TrackBox *trak = gf_isom_get_track_box(movie, track);
1633
  if (!track) return 0;
1634
  if (!trak->Media || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->sub_samples) return 0;
1635
  return gf_list_count(trak->Media->information->sampleTable->sub_samples);
1636
}
1637
#endif
1638
1639
Bool gf_isom_get_subsample_types(GF_ISOFile *movie, u32 track, u32 subs_index, u32 *flags)
1640
0
{
1641
0
  GF_SubSampleInformationBox *sub_samples=NULL;
1642
0
  GF_TrackBox *trak = gf_isom_get_track_box(movie, track);
1643
1644
0
  if (!track || !subs_index) return GF_FALSE;
1645
0
  if (!trak->Media || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->sub_samples) return GF_FALSE;
1646
0
  sub_samples = gf_list_get(trak->Media->information->sampleTable->sub_samples, subs_index-1);
1647
0
  if (!sub_samples) return GF_FALSE;
1648
0
  *flags = sub_samples->flags;
1649
0
  return GF_TRUE;
1650
0
}
1651
1652
u32 gf_isom_sample_get_subsample_entry(GF_ISOFile *movie, u32 track, u32 sampleNumber, u32 flags, GF_SubSampleInfoEntry **sub_sample)
1653
0
{
1654
0
  u32 i, count, last_sample;
1655
0
  GF_SubSampleInformationBox *sub_samples=NULL;
1656
0
  GF_TrackBox *trak = gf_isom_get_track_box(movie, track);
1657
0
  if (sub_sample) *sub_sample = NULL;
1658
0
  if (!track) return 0;
1659
0
  if (!trak->Media || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->sub_samples) return 0;
1660
0
  count = gf_list_count(trak->Media->information->sampleTable->sub_samples);
1661
0
  for (i=0; i<count; i++) {
1662
0
    sub_samples = gf_list_get(trak->Media->information->sampleTable->sub_samples, i);
1663
0
    if (sub_samples->flags==flags) break;
1664
0
    sub_samples = NULL;
1665
0
  }
1666
0
  if (!sub_samples) return 0;
1667
1668
0
  last_sample = 0;
1669
0
  count = gf_list_count(sub_samples->Samples);
1670
0
  for (i=0; i<count; i++) {
1671
0
    GF_SubSampleInfoEntry *pSamp = (GF_SubSampleInfoEntry *) gf_list_get(sub_samples->Samples, i);
1672
0
    if (last_sample + pSamp->sample_delta == sampleNumber) {
1673
0
      if (sub_sample) *sub_sample = pSamp;
1674
0
      return gf_list_count(pSamp->SubSamples);
1675
0
    }
1676
0
    last_sample += pSamp->sample_delta;
1677
0
  }
1678
0
  return 0;
1679
0
}
1680
#endif /*GPAC_DISABLE_ISOM*/