Coverage Report

Created: 2026-10-16 08:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/isomedia/track.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2000-2026
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/constants.h>
28
29
#ifndef GPAC_DISABLE_ISOM
30
31
GF_TrackBox *GetTrackbyID(GF_MovieBox *moov, GF_ISOTrackID TrackID)
32
0
{
33
0
  GF_TrackBox *trak;
34
0
  u32 i;
35
0
  if (!moov) return NULL;
36
0
  i=0;
37
0
  while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) {
38
0
    if (trak->Header->trackID == TrackID) return trak;
39
0
  }
40
0
  return NULL;
41
0
}
42
43
GF_TrackBox *gf_isom_get_track(GF_MovieBox *moov, u32 trackNumber)
44
1.68M
{
45
1.68M
  if (!moov) return NULL;
46
  //no need to check for these, they will anyway result in NULL returned from gf_list_get
47
//  if (!moov || !trackNumber || (trackNumber > gf_list_count(moov->trackList))) return NULL;
48
1.68M
  return (GF_TrackBox*)gf_list_get(moov->trackList, trackNumber - 1);
49
1.68M
}
50
51
//get the number of a track given its ID
52
//return 0 if not found error
53
u32 gf_isom_get_tracknum_from_id(GF_MovieBox *moov, GF_ISOTrackID trackID)
54
125
{
55
125
  u32 i;
56
125
  GF_TrackBox *trak;
57
125
  i=0;
58
136
  while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) {
59
136
    if (trak->Header->trackID == trackID) return i;
60
136
  }
61
0
  return 0;
62
125
}
63
64
//extraction of the ESD from the track
65
GF_Err GetESD(GF_MovieBox *moov, GF_ISOTrackID trackID, u32 StreamDescIndex, GF_ESD **outESD)
66
28
{
67
28
  GF_Err e;
68
28
  GF_ESD *esd;
69
28
  u32 track_num = 0;
70
28
  u32 k;
71
28
  GF_SampleTableBox *stbl;
72
28
  GF_TrackBox *trak, *OCRTrack;
73
28
  GF_TrackReferenceTypeBox *dpnd;
74
28
  GF_SLConfig *slc;
75
28
  GF_MPEGSampleEntryBox *entry;
76
77
28
  if (!moov) return GF_ISOM_INVALID_FILE;
78
79
28
  track_num = gf_isom_get_tracknum_from_id(moov, trackID);
80
28
  dpnd = NULL;
81
28
  *outESD = NULL;
82
83
28
  trak = gf_isom_get_track(moov, track_num);
84
28
  if (!trak) return GF_ISOM_INVALID_FILE;
85
86
28
  e = Media_GetESD(trak->Media, StreamDescIndex, &esd, 0);
87
28
  if (e) return e;
88
19
  if (!esd) return GF_NON_COMPLIANT_BITSTREAM;
89
90
19
  e = Media_GetSampleDesc(trak->Media, StreamDescIndex, (GF_SampleEntryBox **) &entry, NULL);
91
19
  if (e) return e;
92
  //set the ID
93
19
  esd->ESID = trackID;
94
95
  //find stream dependencies: dpnd, sbas and scal
96
76
  for (k=0; k<3; k++) {
97
57
    u32 ref = GF_ISOM_BOX_TYPE_DPND;
98
57
    if (k==1) ref = GF_ISOM_REF_BASE;
99
38
    else if (k==2) ref = GF_ISOM_REF_SCAL;
100
101
57
    e = Track_FindRef(trak, ref , &dpnd);
102
57
    if (e) return e;
103
57
    if (dpnd && dpnd->trackIDCount) {
104
      //ONLY ONE STREAM DEPENDENCY IS ALLOWED
105
0
      if (!k && (dpnd->trackIDCount != 1)) return GF_ISOM_INVALID_MEDIA;
106
      //fix the spec: where is the index located ??
107
0
      esd->dependsOnESID = dpnd->trackIDs[0];
108
0
      break;
109
57
    } else {
110
57
      esd->dependsOnESID = 0;
111
57
    }
112
57
  }
113
114
19
  if (trak->udta) {
115
0
    GF_UserDataMap *map;
116
0
    u32 i = 0;
117
0
    while ((map = (GF_UserDataMap*)gf_list_enum(trak->udta->recordList, &i))) {
118
0
      if (map->boxType == GF_ISOM_BOX_TYPE_AUXV) {
119
0
        GF_Descriptor *d = gf_odf_desc_new(GF_ODF_AUX_VIDEO_DATA);
120
0
        gf_list_add(esd->extensionDescriptors, d);
121
0
        break;
122
0
      }
123
0
    }
124
0
  }
125
126
  //OK, get the OCR (in a REAL MP4File, OCR is 0 in ESD and is specified through track reference
127
19
  dpnd = NULL;
128
19
  OCRTrack = NULL;
129
  //find OCR dependencies
130
19
  e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_SYNC, &dpnd);
131
19
  if (e) return e;
132
19
  if (dpnd) {
133
0
    if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA;
134
0
    esd->OCRESID = dpnd->trackIDs[0];
135
0
    OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]);
136
137
0
    while (OCRTrack) {
138
      /*if we have a dependency on a track that doesn't have OCR dep, remove that dependency*/
139
0
      e = Track_FindRef(OCRTrack, GF_ISOM_BOX_TYPE_SYNC, &dpnd);
140
0
      if (e || !dpnd || !dpnd->trackIDCount) {
141
0
        OCRTrack = NULL;
142
0
        goto default_sync;
143
0
      }
144
      /*this is explicit desync*/
145
0
      if ((dpnd->trackIDs[0]==0) || (dpnd->trackIDs[0]==OCRTrack->Header->trackID))
146
0
        break;
147
      /*loop in OCRs, break it*/
148
0
      if (esd->ESID == (u16) OCRTrack->Header->trackID) {
149
0
        OCRTrack = NULL;
150
0
        goto default_sync;
151
0
      }
152
      /*check next*/
153
0
      OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]);
154
0
    }
155
0
    if (!OCRTrack) goto default_sync;
156
19
  } else {
157
19
default_sync:
158
    /*all tracks are sync'ed by default*/
159
19
    if (trak->moov->mov->es_id_default_sync<0) {
160
15
      if (esd->OCRESID)
161
0
        trak->moov->mov->es_id_default_sync = esd->OCRESID;
162
15
      else
163
15
        trak->moov->mov->es_id_default_sync = esd->ESID;
164
15
    }
165
19
    if (trak->moov->mov->es_id_default_sync) esd->OCRESID = (u16) trak->moov->mov->es_id_default_sync;
166
    /*cf ESD writer*/
167
19
    if (esd->OCRESID == esd->ESID) esd->OCRESID = 0;
168
19
  }
169
170
171
172
  //update the IPI stuff if needed
173
19
  if (esd->ipiPtr != NULL) {
174
0
    dpnd = NULL;
175
0
    e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_IPIR, &dpnd);
176
0
    if (e) return e;
177
0
    if (dpnd) {
178
0
      if (esd->ipiPtr->tag != GF_ODF_ISOM_IPI_PTR_TAG) return GF_ISOM_INVALID_FILE;
179
      //OK, retrieve the ID: the IPI_ES_Id is currently the ref track
180
0
      esd->ipiPtr->IPI_ES_Id = dpnd->trackIDs[esd->ipiPtr->IPI_ES_Id - 1];
181
      //and change the tag
182
0
      esd->ipiPtr->tag = GF_ODF_IPI_PTR_TAG;
183
0
    } else {
184
0
      return GF_ISOM_INVALID_FILE;
185
0
    }
186
0
  }
187
188
19
  if ((trak->Media->mediaHeader->packedLanguage[0] != 'u')
189
9
          || (trak->Media->mediaHeader->packedLanguage[1] != 'n')
190
11
          || (trak->Media->mediaHeader->packedLanguage[2] != 'd') ) {
191
11
    if (!esd->langDesc) esd->langDesc = (GF_Language *) gf_odf_desc_new(GF_ODF_LANG_TAG);
192
193
11
    esd->langDesc->langCode = trak->Media->mediaHeader->packedLanguage[0];
194
11
    esd->langDesc->langCode <<= 8;
195
11
    esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[1];
196
11
    esd->langDesc->langCode <<= 8;
197
11
    esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[2];
198
11
  }
199
200
201
19
  {
202
19
    u16 rvc_predefined;
203
19
    u8 *rvc_cfg_data;
204
19
    const char *mime_type;
205
19
    u32 rvc_cfg_size;
206
19
    e = gf_isom_get_rvc_config(moov->mov, track_num, 1, &rvc_predefined, &rvc_cfg_data, &rvc_cfg_size, &mime_type);
207
19
    if (e==GF_OK) {
208
0
      if (rvc_predefined) {
209
0
        esd->decoderConfig->predefined_rvc_config = rvc_predefined;
210
0
      } else {
211
0
        if (mime_type && !strcmp(mime_type, "application/rvc-config+xml+gz") ) {
212
0
          esd->decoderConfig->rvc_config = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);
213
0
#if !defined(GPAC_DISABLE_ZLIB)
214
0
          gf_gz_decompress_payload_ex(rvc_cfg_data, rvc_cfg_size, &esd->decoderConfig->rvc_config->data, &esd->decoderConfig->rvc_config->dataLength, GF_TRUE);
215
0
          gf_free(rvc_cfg_data);
216
0
#endif
217
0
        } else if (rvc_cfg_data && rvc_cfg_size) {
218
0
          esd->decoderConfig->rvc_config = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);
219
0
          esd->decoderConfig->rvc_config->data = rvc_cfg_data;
220
0
          esd->decoderConfig->rvc_config->dataLength = rvc_cfg_size;
221
0
        }
222
0
      }
223
0
    }
224
19
  }
225
226
227
  /*normally all files shall be stored with predefined=SLPredef_MP4, but of course some are broken (philips)
228
  so we just check the ESD_URL. If set, use the given cfg, otherwise always rewrite it*/
229
19
  if (esd->URLString != NULL) {
230
0
    *outESD = esd;
231
0
    return GF_OK;
232
0
  }
233
234
  //if we are in publishing mode and we have an SLConfig specified, use it as is
235
19
  switch (entry->type) {
236
0
  case GF_ISOM_BOX_TYPE_MP4V:
237
0
    slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc;
238
0
    break;
239
4
  case GF_ISOM_BOX_TYPE_MP4A:
240
4
    slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc;
241
4
    break;
242
0
  case GF_ISOM_BOX_TYPE_MP4S:
243
0
    slc = entry->slc;
244
0
    break;
245
15
  default:
246
15
    slc = NULL;
247
15
    break;
248
19
  }
249
19
  if (slc) {
250
0
    gf_odf_desc_del((GF_Descriptor *)esd->slConfig);
251
0
    gf_odf_desc_copy((GF_Descriptor *)slc, (GF_Descriptor **)&esd->slConfig);
252
0
    *outESD = esd;
253
0
    return GF_OK;
254
0
  }
255
  //otherwise use the regular mapping
256
257
19
  if (!esd->slConfig)
258
0
    esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG);
259
260
  //this is a desc for a media in the file, let's rewrite some param
261
19
  esd->slConfig->timestampLength = 32;
262
19
  esd->slConfig->timestampResolution = trak->Media->mediaHeader->timeScale;
263
  //NO OCR from MP4File streams (eg, constant OC Res one)
264
19
  esd->slConfig->OCRLength = 0;
265
19
  esd->slConfig->OCRResolution = 0;
266
//  if (OCRTrack) esd->slConfig->OCRResolution = OCRTrack->Media->mediaHeader->timeScale;
267
268
19
  stbl = trak->Media->information->sampleTable;
269
  // a little optimization here: if all our samples are sync,
270
  //set the RAPOnly to true... for external users...
271
19
  if (! stbl->SyncSample) {
272
6
    if (
273
6
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
274
6
        moov->mvex &&
275
2
#endif
276
2
        esd->decoderConfig && esd->decoderConfig->streamType &&
277
2
        (esd->decoderConfig->streamType==GF_STREAM_VISUAL)
278
6
    ) {
279
2
      esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0;
280
2
      esd->slConfig->useRandomAccessPointFlag = 1;
281
2
      if (trak->moov->mov->openMode!=GF_ISOM_OPEN_READ) {
282
0
        stbl->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSS);
283
0
        if (!stbl->SyncSample) return GF_OUT_OF_MEM;
284
0
      }
285
4
    } else {
286
4
      esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1;
287
4
      esd->slConfig->useRandomAccessPointFlag = 0;
288
4
    }
289
13
  } else {
290
13
    esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0;
291
    //signal we are NOT using sync points if no info is present in the table
292
13
    esd->slConfig->useRandomAccessPointFlag = stbl->SyncSample->nb_entries ? 1 : 0;
293
13
  }
294
  //change to support reflecting OD streams
295
19
  esd->slConfig->useAccessUnitEndFlag = 1;
296
19
  esd->slConfig->useAccessUnitStartFlag = 1;
297
298
  //signal we do have padding flag (since we only use logical SL packet
299
  //the user can decide whether to use the info or not
300
19
  esd->slConfig->usePaddingFlag = stbl->PaddingBits ? 1 : 0;
301
302
  //same with degradation priority
303
19
  esd->slConfig->degradationPriorityLength = stbl->DegradationPriority ? 32 : 0;
304
305
  //this new SL will be OUT OF THE FILE. Let's set its predefined to 0
306
19
  esd->slConfig->predefined = 0;
307
308
309
19
  *outESD = esd;
310
19
  return GF_OK;
311
19
}
312
313
314
//extraction of the ESD from the track for the given time
315
GF_Err GetESDForTime(GF_MovieBox *moov, GF_ISOTrackID trackID, u64 CTS, GF_ESD **outESD)
316
5
{
317
5
  GF_Err e;
318
5
  u32 sampleDescIndex;
319
5
  GF_TrackBox *trak;
320
321
5
  trak = gf_isom_get_track(moov, gf_isom_get_tracknum_from_id(moov, trackID));
322
5
  if (!trak) return GF_ISOM_INVALID_FILE;
323
324
5
  e = Media_GetSampleDescIndex(trak->Media, CTS, &sampleDescIndex );
325
5
  if (e) return e;
326
5
  return GetESD(moov, trackID, sampleDescIndex, outESD);
327
5
}
328
329
330
GF_Err Track_FindRef(GF_TrackBox *trak, u32 ReferenceType, GF_TrackReferenceTypeBox **dpnd)
331
2.02k
{
332
2.02k
  GF_TrackReferenceBox *ref;
333
2.02k
  GF_TrackReferenceTypeBox *a;
334
2.02k
  u32 i;
335
2.02k
  if (! trak) return GF_BAD_PARAM;
336
2.02k
  if (! trak->References) {
337
1.31k
    *dpnd = NULL;
338
1.31k
    return GF_OK;
339
1.31k
  }
340
713
  ref = trak->References;
341
713
  i=0;
342
1.42k
  while ((a = (GF_TrackReferenceTypeBox *)gf_list_enum(ref->child_boxes, &i))) {
343
713
    if (a->reference_type == ReferenceType) {
344
0
      *dpnd = a;
345
0
      return GF_OK;
346
0
    }
347
713
  }
348
713
  *dpnd = NULL;
349
713
  return GF_OK;
350
713
}
351
352
Bool Track_IsMPEG4Stream(u32 HandlerType)
353
45
{
354
45
  switch (HandlerType) {
355
38
  case GF_ISOM_MEDIA_VISUAL:
356
38
    case GF_ISOM_MEDIA_AUXV:
357
38
    case GF_ISOM_MEDIA_PICT:
358
45
  case GF_ISOM_MEDIA_AUDIO:
359
45
  case GF_ISOM_MEDIA_SUBPIC:
360
45
  case GF_ISOM_MEDIA_OD:
361
45
  case GF_ISOM_MEDIA_OCR:
362
45
  case GF_ISOM_MEDIA_SCENE:
363
45
  case GF_ISOM_MEDIA_MPEG7:
364
45
  case GF_ISOM_MEDIA_OCI:
365
45
  case GF_ISOM_MEDIA_IPMP:
366
45
  case GF_ISOM_MEDIA_MPEGJ:
367
45
  case GF_ISOM_MEDIA_ESM:
368
45
    return 1;
369
  /*Timedtext is NOT an MPEG-4 stream*/
370
0
  default:
371
    /*consider xxsm as MPEG-4 handlers*/
372
0
    if ( (((HandlerType>>8) & 0xFF)== 's') && ((HandlerType& 0xFF)== 'm'))
373
0
      return 1;
374
0
    return 0;
375
45
  }
376
45
}
377
378
379
GF_Err SetTrackDurationEx(GF_TrackBox *trak, Bool keep_utc)
380
905
{
381
905
  u64 trackDuration=0xFFFFFFFF;
382
905
  u32 i;
383
905
  GF_Err e = GF_OK;
384
385
  //the total duration is the media duration: adjust it in case...
386
905
  if (!trak->extl) {
387
905
    e = Media_SetDuration(trak);
388
905
    if (e) return e;
389
390
    //assert the timeScales are non-NULL
391
895
    if (!trak->moov->mvhd || !trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) return GF_ISOM_INVALID_FILE;
392
895
    trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
393
895
  }
394
395
  //if we have an edit list, the duration is the sum of all the editList
396
  //entries' duration (always expressed in MovieTimeScale)
397
895
  if (trak->editBox && trak->editBox->editList) {
398
691
    GF_EdtsEntry *ent;
399
691
    GF_EditListBox *elst = trak->editBox->editList;
400
691
    trackDuration = 0;
401
691
    i=0;
402
1.45k
    while ((ent = (GF_EdtsEntry*)gf_list_enum(elst->entryList, &i))) {
403
768
      trackDuration += ent->segmentDuration;
404
768
    }
405
691
  }
406
895
  if (!trackDuration && trak->Media) {
407
45
    trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
408
45
  }
409
895
  if (!trak->Header || (trak->extl && (trackDuration==0xFFFFFFFF))) {
410
0
    return GF_OK;
411
0
  }
412
895
  trak->Header->duration = trackDuration;
413
895
  if (!keep_utc && !trak->moov->mov->keep_utc && !gf_sys_is_test_mode() )
414
747
    trak->Header->modificationTime = gf_isom_get_mp4time();
415
895
  return GF_OK;
416
895
}
417
418
GF_Err SetTrackDuration(GF_TrackBox *trak)
419
757
{
420
757
  return SetTrackDurationEx(trak, GF_FALSE);
421
757
}
422
423
424
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
425
426
#ifdef GF_ENABLE_CTRN
427
GF_TrunEntry *traf_get_sample_entry(GF_TrackFragmentBox *traf, u32 sample_index)
428
{
429
  u32 i, idx;
430
  GF_TrackFragmentRunBox *trun;
431
  idx=0;
432
  i=0;
433
  while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
434
    u32 j;
435
    for (j=0; j<trun->sample_count; j++) {
436
      GF_TrunEntry *ent = gf_list_get(trun->entries, j);
437
      if (idx==sample_index) return ent;
438
      if (ent->nb_pack>1) {
439
        if (idx < sample_index + ent->nb_pack)
440
          return ent;
441
        idx += ent->nb_pack;
442
      } else {
443
        idx++;
444
      }
445
    }
446
  }
447
  return NULL;
448
}
449
#endif
450
451
static u32 saio_get_index(GF_TrackFragmentBox *traf, u32 sample_idx)
452
0
{
453
0
  u32 k, all_samples=0, saio_idx=0;
454
0
  for (k=0; k<gf_list_count(traf->TrackRuns); k++) {
455
0
    GF_TrackFragmentRunBox *trun = gf_list_get(traf->TrackRuns, k);
456
0
    all_samples+=trun->nb_samples;
457
0
    if (sample_idx<all_samples) break;
458
0
    saio_idx++;
459
0
  }
460
0
  return saio_idx;
461
0
}
462
463
GF_Err MergeTrack(GF_TrackBox *trak, GF_TrackFragmentBox *traf, GF_MovieFragmentBox *moof_box, u64 moof_offset, s32 compressed_diff, u64 *cumulated_offset)
464
92
{
465
92
  u32 i, j, chunk_size, track_num;
466
92
  u64 base_offset, data_offset, traf_duration, tfdt;
467
92
  u32 def_duration, DescIndex, def_size, def_flags;
468
92
  u32 duration, size, flags, prev_trun_data_offset, num_first_sample_in_traf;
469
92
  u8 pad, sync;
470
92
  u16 degr;
471
92
  Bool first_samp_in_traf=GF_TRUE;
472
92
  Bool store_traf_map=GF_FALSE;
473
92
  u8 *moof_template=NULL;
474
92
  u32 moof_template_size=0;
475
92
  Bool is_seg_start = GF_FALSE;
476
92
  u64 seg_start=0, sidx_start=0, sidx_end=0, frag_start=0, last_dts=0;
477
92
  GF_TrackFragmentRunBox *trun;
478
92
  GF_TrunEntry *ent;
479
#ifdef GF_ENABLE_CTRN
480
  GF_TrackFragmentBox *traf_ref = NULL;
481
  u32 sample_index;
482
#endif
483
92
  Bool is_first_merge = !trak->first_traf_merged;
484
92
  Bool patch_no_dur;
485
486
92
  GF_Err stbl_AppendTime(GF_SampleTableBox *stbl, u32 duration, u32 nb_pack);
487
92
  GF_Err stbl_AppendSize(GF_SampleTableBox *stbl, u32 size, u32 nb_pack);
488
92
  GF_Err stbl_AppendChunk(GF_SampleTableBox *stbl, u64 offset);
489
92
  GF_Err stbl_AppendSampleToChunk(GF_SampleTableBox *stbl, u32 DescIndex, u32 samplesInChunk);
490
92
  GF_Err stbl_AppendCTSOffset(GF_SampleTableBox *stbl, s32 CTSOffset);
491
92
  GF_Err stbl_AppendRAP(GF_SampleTableBox *stbl, u8 isRap);
492
92
  GF_Err stbl_AppendPadding(GF_SampleTableBox *stbl, u8 padding);
493
92
  GF_Err stbl_AppendDegradation(GF_SampleTableBox *stbl, u16 DegradationPriority);
494
495
92
  if (trak->Header->trackID != traf->tfhd->trackID) return GF_OK;
496
92
  if (!trak->Media->information->sampleTable
497
92
    || !trak->Media->information->sampleTable->SampleSize
498
92
    || !trak->Media->information->sampleTable->TimeToSample
499
92
    || !trak->Media->information->sampleTable->SampleToChunk
500
92
    || !trak->Media->information->sampleTable->ChunkOffset
501
92
  ) {
502
0
    return GF_ISOM_INVALID_FILE;
503
0
  }
504
505
92
  if (!traf->trex->track)
506
38
    traf->trex->track = trak;
507
508
  //setup all our defaults
509
92
  DescIndex = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DESC) ? traf->tfhd->sample_desc_index : traf->trex->def_sample_desc_index;
510
92
  if (!DescIndex) {
511
6
    GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] default sample description set to 0, likely broken ! Fixing to 1\n" ));
512
6
    DescIndex = 1;
513
86
  } else if (DescIndex > gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes)) {
514
3
    GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] default sample description set to %d but only %d sample description(s), likely broken ! Fixing to 1\n", DescIndex, gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes)));
515
3
    DescIndex = 1;
516
3
  }
517
#ifdef GF_ENABLE_CTRN
518
  if (traf->trex->inherit_from_traf_id) {
519
    u32 traf_count = gf_list_count(moof_box->TrackList);
520
    for (i=0; i<traf_count; i++) {
521
      GF_TrackFragmentBox *atraf = gf_list_get(moof_box->TrackList, i);
522
      if (atraf->tfhd && atraf->tfhd->trackID==traf->trex->inherit_from_traf_id) {
523
        traf_ref = atraf;
524
        break;
525
      }
526
    }
527
  }
528
#endif
529
530
92
  def_duration = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DUR) ? traf->tfhd->def_sample_duration : traf->trex->def_sample_duration;
531
92
  def_size = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_SIZE) ? traf->tfhd->def_sample_size : traf->trex->def_sample_size;
532
92
  def_flags = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) ? traf->tfhd->def_sample_flags : traf->trex->def_sample_flags;
533
534
92
  patch_no_dur = gf_isom_is_video_handler_type(trak->Media->handler->handlerType);
535
536
  //locate base offset, by default use moof (dash-like)
537
92
  base_offset = moof_offset;
538
  //explicit base offset, use it
539
92
  if (traf->tfhd->flags & GF_ISOM_TRAF_BASE_OFFSET)
540
0
    base_offset = traf->tfhd->base_data_offset;
541
  //no moof offset and no explicit offset, the offset is the end of the last written chunk of
542
  //the previous traf. For the first traf, *cumulated_offset is actually moof offset
543
92
  else if (!(traf->tfhd->flags & GF_ISOM_MOOF_BASE_OFFSET))
544
47
    base_offset = *cumulated_offset;
545
546
92
  chunk_size = 0;
547
92
  prev_trun_data_offset = 0;
548
92
  data_offset = 0;
549
92
  traf_duration = 0;
550
551
92
  num_first_sample_in_traf = trak->Media->information->sampleTable->SampleSize->sampleCount;
552
553
92
  if (traf->tfdt)
554
45
    tfdt = 1+traf->tfdt->baseMediaDecodeTime;
555
47
  else if (traf->tfxd)
556
0
    tfdt = 1+traf->tfxd->absolute_time_in_track_timescale;
557
47
  else
558
47
    tfdt = 0;
559
560
92
  if (tfdt) {
561
45
    tfdt -= 1;
562
    //do this test for each fragment merged as soon as we have a tfdt, so that we detect samples with extended duration
563
    //if trak->moov->mov->NextMoofNumber is 0 we initialize or seek so skip test
564
45
    if (trak->moov->mov->NextMoofNumber && trak->dts_at_next_frag_start) {
565
33
      s32 diff = (s32) ((s64) tfdt - (s64) trak->dts_at_next_frag_start);
566
33
      if (diff < 0) {
567
0
        GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Warning: TFDT timing "LLD" less than cumulated timing "LLD" - using tfdt\n", tfdt, trak->dts_at_next_frag_start ));
568
0
      }
569
      //sample dur was extended, adjust track duration
570
33
      else if (diff > 0) {
571
11
        GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[iso file] TFDT timing "LLD" higher than cumulated timing "LLD" (last sample got extended in duration)\n", tfdt, trak->dts_at_next_frag_start ));
572
11
        traf_duration += diff;
573
574
11
        if (!is_first_merge) {
575
11
          u64 last_DTS;
576
11
          u32 last_duration;
577
11
          stbl_GetSampleDTS_and_Duration(trak->Media->information->sampleTable->TimeToSample, trak->Media->information->sampleTable->SampleSize->sampleCount, &last_DTS, &last_duration);
578
11
          stbl_RemoveDTS(trak->Media->information->sampleTable, trak->Media->information->sampleTable->SampleSize->sampleCount, 1, 0);
579
11
          stbl_AppendTime(trak->Media->information->sampleTable, last_duration+diff, 1);
580
11
        }
581
11
      }
582
33
    }
583
    //remember dts if this is the first fragment we merge (either after a table reset or at first segment start)
584
45
    if (is_first_merge) {
585
12
      trak->dts_at_seg_start = tfdt;
586
12
      trak->dts_at_next_frag_start = tfdt;
587
12
    }
588
47
  } else if (is_first_merge && trak->moov->mov->is_smooth) {
589
0
    trak->dts_at_seg_start = trak->dts_at_next_frag_start;
590
0
  }
591
592
92
  if (traf->tfxd) {
593
0
    trak->last_tfxd_value = traf->tfxd->absolute_time_in_track_timescale;
594
0
    trak->last_tfxd_value += traf->tfxd->fragment_duration_in_track_timescale;
595
0
  }
596
92
  if (traf->tfrf) {
597
0
    if (trak->tfrf) gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->tfrf);
598
0
    trak->tfrf = traf->tfrf;
599
0
    gf_list_del_item(traf->child_boxes, traf->tfrf);
600
0
    gf_list_add(trak->child_boxes, trak->tfrf);
601
0
  }
602
92
  if (traf->SampleRefs) {
603
0
    if (!trak->Media->information->sampleTable->SampleRefs) {
604
0
      trak->Media->information->sampleTable->SampleRefs = traf->SampleRefs;
605
0
      gf_list_add(trak->Media->information->sampleTable->child_boxes, traf->SampleRefs);
606
0
      gf_list_del_item(traf->child_boxes, traf->SampleRefs);
607
0
      traf->SampleRefs = NULL;
608
0
    } else {
609
0
      gf_list_transfer(trak->Media->information->sampleTable->SampleRefs->entries, traf->SampleRefs->entries);
610
0
    }
611
0
  }
612
613
92
  if (trak->moov->mov->signal_frag_bounds) {
614
0
    store_traf_map = GF_TRUE;
615
0
    if (is_first_merge) {
616
0
      GF_MovieFragmentBox *moof_clone = NULL;
617
0
      gf_isom_box_freeze_order((GF_Box *)moof_box);
618
0
#ifndef GPAC_DISABLE_ISOM_WRITE
619
0
      gf_isom_clone_box((GF_Box *)moof_box, (GF_Box **)&moof_clone);
620
621
0
      if (moof_clone) {
622
0
        GF_BitStream *bs;
623
0
        for (i=0; i<gf_list_count(moof_clone->TrackList); i++) {
624
0
          GF_TrackFragmentBox *traf_clone = gf_list_get(moof_clone->TrackList, i);
625
0
          gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->TrackRuns);
626
0
          gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sampleGroups);
627
0
          gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sampleGroupsDescription);
628
0
          gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sub_samples);
629
0
          gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sai_offsets);
630
0
          gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sai_sizes);
631
0
          if (traf_clone->sample_encryption) {
632
0
            gf_isom_box_del_parent(&traf_clone->child_boxes, (GF_Box *) traf_clone->sample_encryption);
633
0
            traf_clone->sample_encryption = NULL;
634
0
          }
635
0
          if (traf_clone->sdtp) {
636
0
            gf_isom_box_del_parent(&traf_clone->child_boxes, (GF_Box *) traf_clone->sdtp);
637
0
            traf_clone->sdtp = NULL;
638
0
          }
639
0
          if (traf_clone->SampleRefs) {
640
0
            gf_isom_box_del_parent(&traf_clone->child_boxes, (GF_Box *) traf_clone->SampleRefs);
641
0
            traf_clone->SampleRefs = NULL;
642
0
          }
643
0
        }
644
0
        gf_isom_box_size((GF_Box *)moof_clone);
645
0
        bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
646
647
0
        if (trak->moov->mov->seg_styp) {
648
0
          gf_isom_box_size(trak->moov->mov->seg_styp);
649
0
          gf_isom_box_write(trak->moov->mov->seg_styp, bs);
650
0
        }
651
0
        if (trak->moov->mov->root_sidx) {
652
0
          gf_isom_box_size((GF_Box *)trak->moov->mov->root_sidx);
653
0
          gf_isom_box_write((GF_Box *)trak->moov->mov->root_sidx, bs);
654
0
        }
655
0
        if (trak->moov->mov->seg_ssix) {
656
0
          gf_isom_box_size(trak->moov->mov->seg_ssix);
657
0
          gf_isom_box_write(trak->moov->mov->seg_ssix, bs);
658
0
        }
659
0
        gf_isom_box_write((GF_Box *)moof_clone, bs);
660
0
        gf_isom_box_del((GF_Box*)moof_clone);
661
662
0
        gf_bs_get_content(bs, &moof_template, &moof_template_size);
663
0
        gf_bs_del(bs);
664
0
      }
665
0
#endif
666
0
    }
667
0
    if (trak->moov->mov->seg_styp) {
668
0
      is_seg_start = GF_TRUE;
669
0
      seg_start = trak->moov->mov->styp_start_offset;
670
0
    }
671
0
    if (trak->moov->mov->root_sidx) {
672
0
      is_seg_start = GF_TRUE;
673
0
      sidx_start = trak->moov->mov->sidx_start_offset;
674
0
      sidx_end = trak->moov->mov->sidx_end_offset;
675
0
      if (! seg_start || (sidx_start<seg_start))
676
0
        seg_start = sidx_start;
677
0
    }
678
0
    frag_start = trak->moov->mov->current_top_box_start;
679
0
  }
680
92
  else if (trak->moov->mov->store_traf_map) {
681
0
    store_traf_map = GF_TRUE;
682
0
  }
683
684
92
  u64 max_end = 0;
685
#ifdef GF_ENABLE_CTRN
686
  sample_index = 0;
687
#endif
688
92
  i=0;
689
183
  while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
690
91
    if (! (trun->flags & (GF_ISOM_TRUN_DURATION | GF_ISOM_TRUN_SIZE | GF_ISOM_TRUN_FLAGS | GF_ISOM_TRUN_CTS_OFFSET) ) ) {
691
0
      if (!def_size || (trun->sample_count>0x10000)) {
692
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Invalid track run for track %d - default size %d num samples %d\n", traf->trex->trackID, def_size, trun->sample_count));
693
0
        return GF_ISOM_INVALID_FILE;
694
0
      }
695
0
    }
696
697
    //merge the run
698
1.28k
    for (j=0; j<trun->sample_count; j++) {
699
1.19k
      GF_Err e;
700
1.19k
      s32 cts_offset=0;
701
1.19k
      if (j<trun->nb_samples) {
702
1.19k
        ent = &trun->samples[j];
703
1.19k
      } else {
704
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Track %d doesn't have enough trun entries (%d) compared to sample count (%d) in run\n", traf->trex->trackID, trun->nb_samples, trun->sample_count ));
705
0
        break;
706
0
      }
707
1.19k
      size = def_size;
708
1.19k
      duration = def_duration;
709
1.19k
      flags = def_flags;
710
711
      //CTS - if flag not set (trun or ctrn) defaults to 0 which is the base value after alloc
712
      //we just need to overwrite its value if inherited
713
1.19k
      cts_offset = ent->CTS_Offset;
714
715
#ifdef GF_ENABLE_CTRN
716
      if (trun->use_ctrn) {
717
        if (!j && (trun->ctrn_flags & GF_ISOM_CTRN_FIRST_SAMPLE) ) {
718
          if (trun->ctrn_first_dur) duration = ent->Duration;
719
          if (trun->ctrn_first_size) size = ent->size;
720
          if (trun->ctrn_first_ctts) flags = ent->flags;
721
        } else {
722
          if (trun->ctrn_dur) duration = ent->Duration;
723
          if (trun->ctrn_size) size = ent->size;
724
          if (trun->ctrn_sample_flags) flags = ent->flags;
725
        }
726
        /*re-override*/
727
        if (trun->ctrn_flags & 0xF0) {
728
          GF_TrunEntry *ref_entry;
729
          if (!traf_ref) {
730
            GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Track %d use traf inheritance to track ID %d but reference traf not found\n", traf->trex->trackID, traf->trex->inherit_from_traf_id ));
731
            break;
732
          }
733
          ref_entry = traf_get_sample_entry(traf_ref, sample_index);
734
          if (!ref_entry) {
735
            GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Track %d use traf inheritance but sample %d not found in reference traf\n", traf->trex->trackID, sample_index+1 ));
736
            break;
737
          }
738
          if (trun->ctrn_flags & GF_ISOM_CTRN_INHERIT_DUR)
739
            duration = ref_entry->Duration;
740
          if (trun->ctrn_flags & GF_ISOM_CTRN_INHERIT_SIZE)
741
            size = ref_entry->size;
742
          if (trun->ctrn_flags & GF_ISOM_CTRN_INHERIT_FLAGS)
743
            flags = ref_entry->flags;
744
          if (trun->ctrn_flags & GF_ISOM_CTRN_INHERIT_CTSO)
745
            cts_offset = ref_entry->CTS_Offset;
746
        }
747
748
      } else
749
#endif
750
1.19k
      {
751
1.19k
        if (trun->flags & GF_ISOM_TRUN_DURATION) duration = ent->Duration;
752
1.19k
        if (trun->flags & GF_ISOM_TRUN_SIZE) size = ent->size;
753
1.19k
        if (trun->flags & GF_ISOM_TRUN_FLAGS) {
754
498
          flags = ent->flags;
755
699
        } else if (!j && (trun->flags & GF_ISOM_TRUN_FIRST_FLAG)) {
756
10
          flags = trun->first_sample_flags;
757
10
        }
758
1.19k
      }
759
#ifdef GF_ENABLE_CTRN
760
      sample_index++;
761
#endif
762
      //fix for broken truns with empty duration for frame
763
1.19k
      if (patch_no_dur && !duration && (trun->flags & GF_ISOM_TRUN_DURATION)) {
764
5
        duration = trun->min_duration;
765
5
      }
766
767
      /*store the resolved value in case we have inheritance*/
768
1.19k
      ent->size = size;
769
1.19k
      ent->Duration = duration;
770
1.19k
      ent->flags = flags;
771
1.19k
      ent->CTS_Offset = cts_offset;
772
773
1.19k
      last_dts += duration;
774
775
      //add size first
776
1.19k
      if (!trak->Media->information->sampleTable->SampleSize) {
777
0
        trak->Media->information->sampleTable->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STSZ);
778
0
        if (!trak->Media->information->sampleTable->SampleSize)
779
0
          return GF_OUT_OF_MEM;
780
0
      }
781
1.19k
      e = stbl_AppendSize(trak->Media->information->sampleTable, size, ent->nb_pack);
782
1.19k
      if (e) return e;
783
784
      //then TS
785
1.19k
      if (!trak->Media->information->sampleTable->TimeToSample) {
786
0
        trak->Media->information->sampleTable->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STTS);
787
0
        if (!trak->Media->information->sampleTable->TimeToSample)
788
0
          return GF_OUT_OF_MEM;
789
0
      }
790
1.19k
      e = stbl_AppendTime(trak->Media->information->sampleTable, duration, ent->nb_pack);
791
1.19k
      if (e) return e;
792
793
      //add chunk on first sample
794
1.19k
      if (!j) {
795
91
        u64 final_offset;
796
91
        data_offset = base_offset;
797
        //we have an explicit data offset for this trun
798
91
        if (trun->flags & GF_ISOM_TRUN_DATA_OFFSET) {
799
89
          data_offset += trun->data_offset;
800
          /*reset chunk size since data is now relative to this trun*/
801
89
          chunk_size = 0;
802
          /*remember this data offset for following trun*/
803
89
          prev_trun_data_offset = trun->data_offset;
804
          /*if mdat is located after the moof, and the moof was compressed, adjust offset
805
          otherwise the offset does not need adjustment*/
806
89
          if (trun->data_offset>=0) {
807
84
            data_offset -= compressed_diff;
808
84
            prev_trun_data_offset -= compressed_diff;
809
84
          }
810
89
        }
811
        //we had an explicit data offset for the previous trun, use it + chunk size
812
2
        else if (prev_trun_data_offset) {
813
          /*data offset is previous chunk size plus previous offset of the trun*/
814
0
          data_offset += prev_trun_data_offset + chunk_size;
815
0
        }
816
        //no explicit data offset, continuous data after last data in previous chunk
817
2
        else {
818
2
          data_offset += chunk_size;
819
          //data offset of first trun in first traf, adjust if compressed moof
820
2
          if ((i==1) && (trun->data_offset>=0)) {
821
2
            data_offset -= compressed_diff;
822
2
          }
823
2
        }
824
825
91
        final_offset = data_offset;
826
        //adjust offset if moov was also compressed and we are still in the same file
827
        //so that later call to gf_isom_get_sample properly adjust back the offset
828
91
        if (trak->moov->compressed_diff) {
829
0
          final_offset += trak->moov->compressed_diff;
830
0
        }
831
832
91
        if (!trak->Media->information->sampleTable->ChunkOffset) {
833
0
          trak->Media->information->sampleTable->ChunkOffset = gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STCO);
834
0
          if (!trak->Media->information->sampleTable->ChunkOffset)
835
0
            return GF_OUT_OF_MEM;
836
0
        }
837
91
        e = stbl_AppendChunk(trak->Media->information->sampleTable, final_offset);
838
91
        if (e) return e;
839
        //then sampleToChunk
840
91
        if (!trak->Media->information->sampleTable->SampleToChunk) {
841
0
          trak->Media->information->sampleTable->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STSC);
842
0
          if (!trak->Media->information->sampleTable->SampleToChunk)
843
0
            return GF_OUT_OF_MEM;
844
0
        }
845
91
        e = stbl_AppendSampleToChunk(trak->Media->information->sampleTable,
846
91
                                 DescIndex, trun->sample_count);
847
91
        if (e) return e;
848
91
      }
849
1.19k
      chunk_size += size;
850
1.19k
      if (store_traf_map && first_samp_in_traf) {
851
0
        first_samp_in_traf = GF_FALSE;
852
0
        e = stbl_AppendTrafMap(trak->moov->mov, trak->Media->information->sampleTable, is_seg_start, seg_start, frag_start, tfdt ? tfdt : trak->dts_at_next_frag_start, moof_template, moof_template_size, sidx_start, sidx_end, ent->nb_pack);
853
0
        if (e) return e;
854
        //do not deallocate, the memory is now owned by traf map
855
0
        moof_template = NULL;
856
0
        moof_template_size = 0;
857
0
      }
858
1.19k
      if (ent->nb_pack>1) {
859
15
        j+= ent->nb_pack-1;
860
15
        traf_duration += ent->nb_pack*duration;
861
15
        last_dts += (ent->nb_pack-1)*duration;
862
15
        continue;
863
15
      }
864
865
1.18k
      traf_duration += duration;
866
867
1.18k
      e = stbl_AppendCTSOffset(trak->Media->information->sampleTable, cts_offset);
868
1.18k
      if (e) return e;
869
      //flags
870
1.18k
      sync = GF_ISOM_GET_FRAG_SYNC(flags);
871
1.18k
      if (trak->Media->information->sampleTable->no_sync_found && sync) {
872
35
        trak->Media->information->sampleTable->no_sync_found = 0;
873
35
      }
874
1.18k
      e = stbl_AppendRAP(trak->Media->information->sampleTable, sync);
875
1.18k
      if (e) return e;
876
1.18k
      pad = GF_ISOM_GET_FRAG_PAD(flags);
877
1.18k
      if (pad) {
878
59
        e = stbl_AppendPadding(trak->Media->information->sampleTable, pad);
879
59
        if (e) return e;
880
59
      }
881
1.18k
      degr = GF_ISOM_GET_FRAG_DEG(flags);
882
1.18k
      if (degr) {
883
10
        e = stbl_AppendDegradation(trak->Media->information->sampleTable, degr);
884
10
        if (e) return e;
885
10
      }
886
1.18k
      e = stbl_AppendDependencyType(trak->Media->information->sampleTable, GF_ISOM_GET_FRAG_LEAD(flags), GF_ISOM_GET_FRAG_DEPENDS(flags), GF_ISOM_GET_FRAG_DEPENDED(flags), GF_ISOM_GET_FRAG_REDUNDANT(flags));
887
1.18k
      if (e) return e;
888
1.18k
    }
889
890
91
    u64 data_offset_end = data_offset + chunk_size;
891
91
    if (!max_end || (max_end<data_offset_end))
892
91
      max_end = data_offset_end;
893
91
  }
894
895
  //remember target next dts - last_dts is the duration in media timescale, does not include tfdt
896
92
  trak->dts_at_next_frag_start += last_dts;
897
898
92
  if (traf_duration && trak->editBox && trak->editBox->editList) {
899
    //append to last edit only, adding edits on the fly is not possible in isobmff
900
42
    GF_EdtsEntry *edts_e = gf_list_last(trak->editBox->editList->entryList);
901
42
    if (edts_e && (edts_e->was_empty_dur || !edts_e->segmentDuration)) {
902
      //extend last edit duration by the amount of media received in fragment (traf duration)
903
      //regardless of the mediaTime offset of the edit (cf #2985)
904
28
      u64 extend_dur = traf_duration;
905
28
      extend_dur *= trak->moov->mvhd->timeScale;
906
28
      extend_dur /= trak->Media->mediaHeader->timeScale;
907
28
      edts_e->segmentDuration += extend_dur;
908
28
      edts_e->was_empty_dur = GF_TRUE;
909
28
    }
910
42
  }
911
912
  //in any case, update the cumulated offset
913
  //this will handle hypothetical files mixing MOOF offset and implicit non-moof offset
914
92
  *cumulated_offset = data_offset + chunk_size;
915
916
  /*merge sample groups*/
917
92
  if (traf->sampleGroups) {
918
0
    GF_List *groups;
919
0
    GF_List *groupDescs;
920
0
    Bool is_identical_sgpd = GF_TRUE;
921
0
    u32 *new_idx = NULL, new_idx_count=0;
922
923
0
    if (!trak->Media->information->sampleTable->sampleGroups)
924
0
      trak->Media->information->sampleTable->sampleGroups = gf_list_new();
925
926
0
    if (!trak->Media->information->sampleTable->sampleGroupsDescription)
927
0
      trak->Media->information->sampleTable->sampleGroupsDescription = gf_list_new();
928
929
0
    groupDescs = trak->Media->information->sampleTable->sampleGroupsDescription;
930
0
    for (i=0; i<gf_list_count(traf->sampleGroupsDescription); i++) {
931
0
      GF_SampleGroupDescriptionBox *new_sgdesc = NULL;
932
0
      GF_SampleGroupDescriptionBox *sgdesc = gf_list_get(traf->sampleGroupsDescription, i);
933
0
      for (j=0; j<gf_list_count(groupDescs); j++) {
934
0
        new_sgdesc = gf_list_get(groupDescs, j);
935
0
        if (new_sgdesc->grouping_type==sgdesc->grouping_type) break;
936
0
        new_sgdesc = NULL;
937
0
      }
938
      /*new description, move it to our sample table*/
939
0
      if (!new_sgdesc) {
940
0
        gf_list_add(groupDescs, sgdesc);
941
0
        gf_list_add(trak->Media->information->sampleTable->child_boxes, sgdesc);
942
0
        gf_list_rem(traf->sampleGroupsDescription, i);
943
0
        gf_list_del_item(traf->child_boxes, sgdesc);
944
0
        i--;
945
0
      }
946
      /*merge descriptions*/
947
0
      else {
948
0
        u32 count;
949
950
0
        is_identical_sgpd = gf_isom_is_identical_sgpd(new_sgdesc, sgdesc, 0);
951
0
        if (is_identical_sgpd)
952
0
          continue;
953
954
0
        new_idx_count = gf_list_count(sgdesc->group_descriptions);
955
0
        new_idx = (u32 *)gf_malloc(new_idx_count * sizeof(u32));
956
0
        if (!new_idx) return GF_OUT_OF_MEM;
957
958
0
        count = 0;
959
0
        while (gf_list_count(sgdesc->group_descriptions)) {
960
0
          void *sgpd_entry = gf_list_get(sgdesc->group_descriptions, 0);
961
0
          Bool new_entry = GF_TRUE;
962
963
0
          for (j = 0; j < gf_list_count(new_sgdesc->group_descriptions); j++) {
964
0
            void *ptr = gf_list_get(new_sgdesc->group_descriptions, j);
965
0
            if (gf_isom_is_identical_sgpd(sgpd_entry, ptr, new_sgdesc->grouping_type)) {
966
0
              new_idx[count] = j + 1;
967
0
              count ++;
968
0
              new_entry = GF_FALSE;
969
970
0
              sgpd_del_entry(new_sgdesc->grouping_type, sgpd_entry, new_sgdesc->is_opaque);
971
0
              break;
972
0
            }
973
0
          }
974
975
0
          if (new_entry) {
976
0
            gf_list_add(new_sgdesc->group_descriptions, sgpd_entry);
977
0
            new_idx[count] = gf_list_count(new_sgdesc->group_descriptions);
978
0
            count ++;
979
0
          }
980
981
0
          gf_list_rem(sgdesc->group_descriptions, 0);
982
0
        }
983
0
      }
984
0
    }
985
986
0
    groups = trak->Media->information->sampleTable->sampleGroups;
987
0
    for (i=0; i<gf_list_count(traf->sampleGroups); i++) {
988
0
      GF_SampleGroupBox *stbl_group = NULL;
989
0
      GF_SampleGroupBox *frag_group = gf_list_get(traf->sampleGroups, i);
990
991
992
0
      for (j=0; j<gf_list_count(groups); j++) {
993
0
        stbl_group = gf_list_get(groups, j);
994
0
        if ((frag_group->grouping_type==stbl_group->grouping_type) && (frag_group->grouping_type_parameter==stbl_group->grouping_type_parameter))
995
0
          break;
996
0
        stbl_group = NULL;
997
0
      }
998
0
      if (!stbl_group) {
999
0
        stbl_group = (GF_SampleGroupBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_SBGP);
1000
0
        if (!stbl_group) return GF_OUT_OF_MEM;
1001
0
        stbl_group->grouping_type = frag_group->grouping_type;
1002
0
        stbl_group->grouping_type_parameter = frag_group->grouping_type_parameter;
1003
0
        stbl_group->version = frag_group->version;
1004
0
        gf_list_add(groups, stbl_group);
1005
        //we created a new sample to group, the first num_first_sample_in_traf are not mapped to any description
1006
0
        if (num_first_sample_in_traf) {
1007
0
          stbl_group->entry_count = 1;
1008
0
          stbl_group->sample_entries = gf_malloc(sizeof(GF_SampleGroupEntry));
1009
0
          if (!stbl_group->sample_entries) return GF_OUT_OF_MEM;
1010
0
          stbl_group->sample_entries[0].group_description_index = 0;
1011
0
          stbl_group->sample_entries[0].sample_count = num_first_sample_in_traf;
1012
0
        }
1013
0
      }
1014
1015
0
      if (is_identical_sgpd) {
1016
        //adjust sgpd index: in traf index start at 0x1001
1017
0
        for (j = 0; j < frag_group->entry_count; j++)
1018
0
          frag_group->sample_entries[j].group_description_index &= 0x0FFFF;
1019
0
        if (frag_group->entry_count && stbl_group->entry_count &&
1020
0
                (frag_group->sample_entries[0].group_description_index==stbl_group->sample_entries[stbl_group->entry_count-1].group_description_index)
1021
0
           ) {
1022
0
          stbl_group->sample_entries[stbl_group->entry_count - 1].sample_count += frag_group->sample_entries[0].sample_count;
1023
0
          if (frag_group->entry_count>1) {
1024
0
            stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count - 1));
1025
0
            memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[1], sizeof(GF_SampleGroupEntry) * (frag_group->entry_count - 1));
1026
0
            stbl_group->entry_count += frag_group->entry_count - 1;
1027
0
          }
1028
0
        } else {
1029
0
          stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count));
1030
0
          memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[0], sizeof(GF_SampleGroupEntry) * frag_group->entry_count);
1031
0
          stbl_group->entry_count += frag_group->entry_count;
1032
0
        }
1033
0
      } else {
1034
0
        u32 samples_in_stbl_group = 0;
1035
0
        for (j=0; j<stbl_group->entry_count; j++) {
1036
0
          samples_in_stbl_group += stbl_group->sample_entries[j].sample_count;
1037
0
        }
1038
0
        u32 num_entries = stbl_group->entry_count + frag_group->entry_count;
1039
0
        if (samples_in_stbl_group < num_first_sample_in_traf) num_entries++;
1040
1041
0
        stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * num_entries);
1042
        //set unmapped entries to 0
1043
0
        if (samples_in_stbl_group < num_first_sample_in_traf) {
1044
0
          stbl_group->sample_entries[stbl_group->entry_count].sample_count = num_first_sample_in_traf - samples_in_stbl_group;
1045
0
          stbl_group->sample_entries[stbl_group->entry_count].group_description_index = 0;
1046
0
          stbl_group->entry_count++;
1047
0
        }
1048
1049
        //adjust sgpd index
1050
0
        for (j = 0; j < frag_group->entry_count; j++) {
1051
0
          u32 sgidx = frag_group->sample_entries[j].group_description_index;
1052
0
          if (sgidx > 0x10000) {
1053
0
            sgidx -= 0x10001;
1054
0
            if (sgidx>=new_idx_count) {
1055
0
              GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[isobmf] corrupted sample group index in fragment %d but only %d group descriptions in fragment\n", sgidx, new_idx_count));
1056
0
            } else {
1057
0
              frag_group->sample_entries[j].group_description_index = new_idx[sgidx];
1058
0
            }
1059
0
          }
1060
0
        }
1061
0
        memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[0], sizeof(GF_SampleGroupEntry) * frag_group->entry_count);
1062
0
        stbl_group->entry_count += frag_group->entry_count;
1063
0
      }
1064
0
    }
1065
1066
0
    if (new_idx) gf_free(new_idx);
1067
0
  }
1068
1069
92
  u32 samples_in_traf = trak->Media->information->sampleTable->SampleSize->sampleCount - num_first_sample_in_traf;
1070
1071
  /*content is encrypted*/
1072
92
  track_num = gf_isom_get_tracknum_from_id(trak->moov, trak->Header->trackID);
1073
92
  if (gf_isom_is_cenc_media(trak->moov->mov, track_num, DescIndex)
1074
91
    || traf->sample_encryption) {
1075
    /*Merge sample auxiliary encryption information*/
1076
1
    GF_SampleEncryptionBox *senc = NULL;
1077
1
    u32 scheme_type=0;
1078
1
    gf_isom_get_cenc_info(trak->moov->mov, track_num, DescIndex, NULL, &scheme_type, NULL);
1079
1080
1
    if (traf->sample_encryption) {
1081
0
      for (i = 0; i < gf_list_count(trak->Media->information->sampleTable->child_boxes); i++) {
1082
0
        GF_Box *a = (GF_Box *)gf_list_get(trak->Media->information->sampleTable->child_boxes, i);
1083
0
        if (a->type != traf->sample_encryption->type) continue;
1084
1085
0
        if ((a->type ==GF_ISOM_BOX_TYPE_UUID) && (((GF_UUIDBox *)a)->internal_4cc == GF_ISOM_BOX_UUID_PSEC)) {
1086
0
          senc = (GF_SampleEncryptionBox *)a;
1087
0
          break;
1088
0
        }
1089
0
        else if (a->type ==GF_ISOM_BOX_TYPE_SENC) {
1090
0
          senc = (GF_SampleEncryptionBox *)a;
1091
0
          break;
1092
0
        }
1093
0
      }
1094
0
      if (!senc && trak->sample_encryption)
1095
0
        senc = trak->sample_encryption;
1096
1097
0
      if (!senc) {
1098
0
        if (traf->sample_encryption->piff_type==1) {
1099
0
          senc = (GF_SampleEncryptionBox *)gf_isom_create_piff_psec_box(1, 0x2, 0, 0, NULL);
1100
0
        } else {
1101
0
          senc = gf_isom_create_samp_enc_box(1, 0x2);
1102
0
        }
1103
1104
0
        if (!trak->Media->information->sampleTable->child_boxes) trak->Media->information->sampleTable->child_boxes = gf_list_new();
1105
1106
0
        trak->sample_encryption = senc;
1107
0
        if (!trak->child_boxes) trak->child_boxes = gf_list_new();
1108
0
        gf_list_add(trak->child_boxes, senc);
1109
0
      }
1110
0
    }
1111
1112
    /*get sample auxiliary information by saiz/saio rather than by parsing senc box*/
1113
1
    if (gf_isom_cenc_has_saiz_saio_traf(traf, scheme_type)) {
1114
0
      u32 nb_saio;
1115
0
      u32 aux_info_type;
1116
0
      u64 offset;
1117
0
      GF_Err e;
1118
0
      Bool is_encrypted;
1119
0
      GF_SampleAuxiliaryInfoOffsetBox *saio = NULL;
1120
0
      GF_SampleAuxiliaryInfoSizeBox *saiz = NULL;
1121
1122
0
      offset = nb_saio = 0;
1123
1124
0
      for (i = 0; i < gf_list_count(traf->sai_offsets); i++) {
1125
0
        saio = (GF_SampleAuxiliaryInfoOffsetBox *)gf_list_get(traf->sai_offsets, i);
1126
0
        aux_info_type = saio->aux_info_type;
1127
0
        if (!aux_info_type) aux_info_type = scheme_type;
1128
1129
        /*if we have only 1 sai_offsets, assume that its type is cenc*/
1130
0
        if ((aux_info_type == GF_ISOM_CENC_SCHEME) || (aux_info_type == GF_ISOM_CBC_SCHEME) ||
1131
0
          (aux_info_type == GF_ISOM_CENS_SCHEME) || (aux_info_type == GF_ISOM_CBCS_SCHEME) ||
1132
0
          (gf_list_count(traf->sai_offsets) == 1)) {
1133
0
          if (saio->offsets && saio->entry_count) {
1134
0
            offset = saio->offsets[0] + moof_offset;
1135
0
            nb_saio = saio->entry_count;
1136
0
            break;
1137
0
          }
1138
0
        }
1139
0
        saio = NULL;
1140
0
      }
1141
0
      for (i = 0; i < gf_list_count(traf->sai_sizes); i++) {
1142
0
        saiz = (GF_SampleAuxiliaryInfoSizeBox *)gf_list_get(traf->sai_sizes, i);
1143
0
        aux_info_type = saiz->aux_info_type;
1144
0
        if (!aux_info_type) aux_info_type = scheme_type;
1145
        /*if we have only 1 sai_sizes, assume that its type is cenc*/
1146
0
        if ((aux_info_type == GF_ISOM_CENC_SCHEME) || (aux_info_type == GF_ISOM_CBC_SCHEME) ||
1147
0
          (aux_info_type == GF_ISOM_CENS_SCHEME) || (aux_info_type == GF_ISOM_CBCS_SCHEME) ||
1148
0
          (gf_list_count(traf->sai_sizes) == 1)) {
1149
0
          break;
1150
0
        }
1151
0
        saiz = NULL;
1152
0
      }
1153
0
      if (saiz && saio && senc) {
1154
0
        u32 saiz_count = saiz->sample_count;
1155
0
        if (saiz_count > samples_in_traf) {
1156
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] Number of CENC SAI samples %d greater than samples in traf %d, skipping merge\n", saiz_count, samples_in_traf));
1157
0
          saiz_count = 0;
1158
0
        }
1159
1160
0
        for (i = 0; i < saiz_count; i++) {
1161
0
          const u8 *key_info=NULL;
1162
0
          u32 key_info_size, samp_num;
1163
0
          if (nb_saio != 1) {
1164
0
            u32 saio_idx = saio_get_index(traf, i);
1165
0
            if (saio_idx>=saio->entry_count)
1166
0
              return GF_ISOM_INVALID_FILE;
1167
1168
0
            offset = saio->offsets[saio_idx] + moof_offset;
1169
0
          }
1170
0
          size = saiz->default_sample_info_size ? saiz->default_sample_info_size : saiz->sample_info_size[i];
1171
1172
0
          samp_num = num_first_sample_in_traf + i + 1;
1173
1174
0
          trak->current_traf_stsd_idx = DescIndex;
1175
          //add sample_count_at_seg_start since gf_isom_get_sample_cenc_info_internal removes them
1176
0
          e = gf_isom_get_sample_cenc_info_internal(trak, traf, senc, samp_num + trak->sample_count_at_seg_start, &is_encrypted, NULL, NULL, &key_info, &key_info_size);
1177
0
          trak->current_traf_stsd_idx = 0;
1178
0
          if (e) {
1179
0
            GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] could not get cenc info for sample %d: %s\n", i+1, gf_error_to_string(e) ));
1180
0
            return e;
1181
0
          }
1182
1183
0
          if (key_info) {
1184
            //not multikey
1185
0
            if (!key_info[0]) {
1186
              //size greater than IV
1187
0
              if (size > key_info[3])
1188
0
                senc->flags = 0x00000002;
1189
0
            }
1190
            //multikey, always use subsamples
1191
0
            else {
1192
0
              senc->flags = 0x00000002;
1193
0
            }
1194
0
          }
1195
          //merge into table using true sample num since start of fragment
1196
0
          e = gf_isom_cenc_merge_saiz_saio(senc, trak->Media->information->sampleTable, samp_num, offset, size);
1197
0
          if (e) return e;
1198
1199
0
          if (offset + size > max_end)
1200
0
            max_end = offset + size;
1201
1202
          //we no longer load sai, this will be loaded through saio/saiz when fecthing it
1203
          //this avoids too high mem usage
1204
          //we do keep it if edit mode to rewrite senc
1205
0
          if (trak->moov->mov->openMode>=GF_ISOM_OPEN_EDIT) {
1206
0
            GF_CENCSampleAuxInfo *sai;
1207
0
            GF_SAFEALLOC(sai, GF_CENCSampleAuxInfo);
1208
0
            if (!sai) return GF_OUT_OF_MEM;
1209
0
            if (is_encrypted) {
1210
0
              sai->cenc_data_size = size;
1211
0
              sai->cenc_data = gf_malloc(sizeof(u8)*size);
1212
0
              if (!sai->cenc_data) return GF_OUT_OF_MEM;
1213
0
              u64 cur_position = gf_bs_get_position(trak->moov->mov->movieFileMap->bs);
1214
0
              gf_bs_seek(trak->moov->mov->movieFileMap->bs, offset - trak->moov->mov->bytes_removed);
1215
1216
0
              gf_bs_read_data(trak->moov->mov->movieFileMap->bs, sai->cenc_data, sai->cenc_data_size);
1217
1218
0
              gf_bs_seek(trak->moov->mov->movieFileMap->bs, cur_position);
1219
0
            } else {
1220
0
              sai->isNotProtected=1;
1221
0
            }
1222
0
            gf_list_add(senc->samp_aux_info, sai);
1223
0
          }
1224
1225
          //always increment offset (in case we need saio.nb_entries>1)
1226
0
          offset += size;
1227
0
        }
1228
0
      }
1229
1
    } else if (traf->sample_encryption) {
1230
0
      senc_Parse(trak->moov->mov->movieFileMap->bs, trak, traf, traf->sample_encryption, samples_in_traf);
1231
0
      trak->sample_encryption->AlgorithmID = traf->sample_encryption->AlgorithmID;
1232
0
      if (!trak->sample_encryption->IV_size)
1233
0
        trak->sample_encryption->IV_size = traf->sample_encryption->IV_size;
1234
0
      if (!trak->sample_encryption->samp_aux_info) trak->sample_encryption->samp_aux_info = gf_list_new();
1235
0
      gf_list_transfer(trak->sample_encryption->samp_aux_info, traf->sample_encryption->samp_aux_info);
1236
0
      if (traf->sample_encryption->flags & 0x00000002)
1237
0
        trak->sample_encryption->flags |= 0x00000002;
1238
0
    }
1239
1
  }
1240
1241
  /*merge other saio*/
1242
92
  for (i=0; i<gf_list_count(traf->sai_sizes); i++) {
1243
0
    GF_SampleAuxiliaryInfoOffsetBox *saio = NULL;
1244
0
    GF_SampleAuxiliaryInfoSizeBox *saiz = gf_list_get(traf->sai_sizes, i);
1245
0
    switch (saiz->aux_info_type) {
1246
0
    case GF_ISOM_CENC_SCHEME:
1247
0
    case GF_ISOM_CBC_SCHEME:
1248
0
    case GF_ISOM_CENS_SCHEME:
1249
0
    case GF_ISOM_CBCS_SCHEME:
1250
0
    case GF_ISOM_PIFF_SCHEME:
1251
0
    case 0:
1252
0
      continue;
1253
0
    default:
1254
0
      break;
1255
0
    }
1256
0
    for (j=0; j<gf_list_count(traf->sai_offsets); j++) {
1257
0
      saio = gf_list_get(traf->sai_offsets, j);
1258
0
      if ((saio->aux_info_type==saiz->aux_info_type) && (saio->aux_info_type_parameter==saiz->aux_info_type_parameter)) break;
1259
0
      saio=NULL;
1260
0
    }
1261
0
    if (!saio) {
1262
0
      GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[isobmf] No saio for saiz type %s aux info type %d, cannot merge SAI\n", gf_4cc_to_str(saiz->aux_info_type), saiz->aux_info_type_parameter));
1263
0
      continue;
1264
0
    }
1265
0
    if (!saio->offsets) {
1266
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] No offset in saio type %s aux info type %d, cannot merge SAI\n", gf_4cc_to_str(saiz->aux_info_type), saiz->aux_info_type_parameter));
1267
0
      continue;
1268
0
    }
1269
0
    u64 offset = saio->offsets[0] + moof_offset;
1270
0
    u32 nb_saio = saio->entry_count;
1271
0
    if ((nb_saio>1) && (saio->entry_count != gf_list_count(traf->TrackRuns))) {
1272
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] Number of SAI offset does not match number of fragments, cannot merge SAI %s aux info type %d\n", gf_4cc_to_str(saiz->aux_info_type), saiz->aux_info_type_parameter));
1273
0
      continue;
1274
0
    }
1275
1276
0
    if (saiz->sample_count > samples_in_traf) {
1277
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] Number of SAI samples %d greater than samples in traf %d, skipping merge SAI %s aux info type %d\n", saiz->sample_count, samples_in_traf, gf_4cc_to_str(saiz->aux_info_type), saiz->aux_info_type_parameter));
1278
0
      continue;
1279
0
    }
1280
1281
0
    u32 sai_max_size=0;
1282
0
    u8 *sai = NULL;
1283
0
    for (j=0; j < saiz->sample_count; j++) {
1284
0
      if (nb_saio != 1) {
1285
0
        u32 saio_idx = saio_get_index(traf, i);
1286
0
        if (saio_idx>=saio->entry_count) {
1287
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] Number of offset less than number of fragments, cannot merge SAI %s aux info type %d\n", gf_4cc_to_str(saiz->aux_info_type), saiz->aux_info_type_parameter));
1288
0
          break;
1289
0
        }
1290
0
        offset = saio->offsets[j] + moof_offset;
1291
0
      }
1292
0
      size = saiz->default_sample_info_size ? saiz->default_sample_info_size : saiz->sample_info_size[j];
1293
0
      if (!size) {
1294
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] SAI of size 0 cannot be merged\n"));
1295
0
        continue;
1296
0
      }
1297
1298
0
      u64 cur_position = gf_bs_get_position(trak->moov->mov->movieFileMap->bs);
1299
0
      gf_bs_seek(trak->moov->mov->movieFileMap->bs, offset);
1300
1301
0
      u32 samp_num = num_first_sample_in_traf + j + 1;
1302
1303
0
      if (sai_max_size<size) {
1304
0
        sai_max_size = size;
1305
0
        sai = gf_realloc(sai, sai_max_size);
1306
0
      }
1307
0
      gf_bs_read_data(trak->moov->mov->movieFileMap->bs, sai, size);
1308
0
      gf_bs_seek(trak->moov->mov->movieFileMap->bs, cur_position);
1309
1310
0
      GF_Err e = gf_isom_add_sample_aux_info_internal(trak, NULL, samp_num, saiz->aux_info_type, saiz->aux_info_type_parameter, sai, size);
1311
0
      if (e) {
1312
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] Failed to merge sai data: %s\n", gf_error_to_string(e) ));
1313
0
      }
1314
1315
      //always increment size even for saio.nb_entries>1
1316
0
      offset += size;
1317
1318
0
    if (offset > max_end)
1319
0
      max_end = offset;
1320
1321
0
    }
1322
0
    if (sai) gf_free(sai);
1323
0
  }
1324
  //signal max offset from what we could gather - this is just an estimation, as there could be hidden data at the end of
1325
  //the containing mdat
1326
92
  if (trak->moov->mov->signal_frag_bounds && !(trak->moov->mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) ) {
1327
0
    gf_isom_push_mdat_end(trak->moov->mov, max_end, GF_TRUE);
1328
0
  }
1329
92
  if (moof_template)
1330
0
    gf_free(moof_template);
1331
92
  return GF_OK;
1332
92
}
1333
1334
#endif
1335
1336
1337
#ifndef GPAC_DISABLE_ISOM_WRITE
1338
1339
//used to check if a TrackID is available
1340
u8 RequestTrack(GF_MovieBox *moov, GF_ISOTrackID TrackID)
1341
0
{
1342
0
  u32 i;
1343
0
  GF_TrackBox *trak;
1344
1345
0
  i=0;
1346
0
  while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) {
1347
0
    if (trak->Header->trackID == TrackID) {
1348
0
      gf_isom_set_last_error(moov->mov, GF_BAD_PARAM);
1349
0
      return 0;
1350
0
    }
1351
0
  }
1352
0
  return 1;
1353
0
}
1354
1355
GF_Err Track_RemoveRef(GF_TrackBox *trak, u32 ReferenceType)
1356
0
{
1357
0
  GF_TrackReferenceBox *ref;
1358
0
  GF_Box *a;
1359
0
  u32 i;
1360
0
  if (! trak) return GF_BAD_PARAM;
1361
0
  if (! trak->References) return GF_OK;
1362
0
  ref = trak->References;
1363
0
  i=0;
1364
0
  while ((a = (GF_Box *)gf_list_enum(ref->child_boxes, &i))) {
1365
0
    if (a->type == ReferenceType) {
1366
0
      gf_isom_box_del_parent(&ref->child_boxes, a);
1367
0
      return GF_OK;
1368
0
    }
1369
0
  }
1370
0
  return GF_OK;
1371
0
}
1372
1373
GF_Err NewMedia(GF_MediaBox **mdia, u32 MediaType, u32 TimeScale)
1374
0
{
1375
0
  GF_MediaHeaderBox *mdhd;
1376
0
  GF_Box *mediaInfo;
1377
0
  GF_HandlerBox *hdlr;
1378
0
  GF_MediaInformationBox *minf;
1379
0
  GF_DataInformationBox *dinf;
1380
0
  GF_SampleTableBox *stbl;
1381
0
  GF_DataReferenceBox *dref;
1382
0
  char *str="";
1383
1384
0
  GF_Err e;
1385
1386
0
  if (!mdia) return GF_BAD_PARAM;
1387
1388
0
  minf = *mdia ? (*mdia)->information : NULL;
1389
0
  mdhd = *mdia ? (*mdia)->mediaHeader : NULL;
1390
0
  hdlr = *mdia ? (*mdia)->handler : NULL;
1391
0
  dinf =  minf ? minf->dataInformation : NULL;
1392
0
  stbl = minf ? minf->sampleTable : NULL;
1393
0
  dref = dinf ? dinf->dref : NULL;
1394
0
  mediaInfo = minf ? minf->InfoHeader : NULL;
1395
1396
  //first create the media
1397
0
  if (!*mdia) {
1398
0
    *mdia = (GF_MediaBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDIA);
1399
0
    if (! *mdia) { e = GF_OUT_OF_MEM; goto err_exit; }
1400
0
  }
1401
0
  if (!mdhd) {
1402
0
    mdhd = (GF_MediaHeaderBox *) gf_isom_box_new_parent( & ((*mdia)->child_boxes), GF_ISOM_BOX_TYPE_MDHD);
1403
0
    if (! mdhd) { e = GF_OUT_OF_MEM; goto err_exit; }
1404
0
    e = mdia_on_child_box((GF_Box*)*mdia, (GF_Box *) mdhd, GF_FALSE);
1405
0
    if (e) goto err_exit;
1406
0
  }
1407
0
  if (!hdlr) {
1408
0
    hdlr = (GF_HandlerBox *) gf_isom_box_new_parent(& ((*mdia)->child_boxes), GF_ISOM_BOX_TYPE_HDLR);
1409
0
    if (! hdlr) { e = GF_OUT_OF_MEM; goto err_exit; }
1410
0
    e = mdia_on_child_box((GF_Box*)*mdia, (GF_Box *) hdlr, GF_FALSE);
1411
0
    if (e) goto err_exit;
1412
0
  }
1413
0
  if (!minf) {
1414
0
    minf = (GF_MediaInformationBox *) gf_isom_box_new_parent(& ((*mdia)->child_boxes), GF_ISOM_BOX_TYPE_MINF);
1415
0
    if (! minf) { e = GF_OUT_OF_MEM; goto err_exit; }
1416
0
    e = mdia_on_child_box((GF_Box*)*mdia, (GF_Box *) minf, GF_FALSE);
1417
0
    if (e) goto err_exit;
1418
0
  }
1419
0
  if (!dinf) {
1420
0
    dinf = (GF_DataInformationBox *) gf_isom_box_new_parent(&minf->child_boxes, GF_ISOM_BOX_TYPE_DINF);
1421
0
    if (! dinf) { e = GF_OUT_OF_MEM; goto err_exit; }
1422
0
    e = minf_on_child_box((GF_Box*)minf, (GF_Box *) dinf, GF_FALSE);
1423
0
    if (e) goto err_exit;
1424
0
  }
1425
1426
0
  if (!mediaInfo) {
1427
    //"handler name" is for debugging purposes. Let's stick our name here ;)
1428
0
    switch (MediaType) {
1429
0
    case GF_ISOM_MEDIA_VISUAL:
1430
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
1431
0
      str = "GPAC ISO Video Handler";
1432
0
      break;
1433
0
    case GF_ISOM_MEDIA_AUXV:
1434
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
1435
0
      str = "GPAC ISO Auxiliary Video Handler";
1436
0
      break;
1437
0
    case GF_ISOM_MEDIA_PICT:
1438
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
1439
0
      str = "GPAC ISO Picture Sequence Handler";
1440
0
      break;
1441
0
    case GF_ISOM_MEDIA_AUDIO:
1442
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_SMHD);
1443
0
      str = "GPAC ISO Audio Handler";
1444
0
      break;
1445
0
    case GF_ISOM_MEDIA_HINT:
1446
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_HMHD);
1447
0
      str = "GPAC ISO Hint Handler";
1448
0
      break;
1449
0
    case GF_ISOM_MEDIA_META:
1450
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1451
0
      str = "GPAC Timed MetaData Handler";
1452
0
      break;
1453
0
    case GF_ISOM_MEDIA_OD:
1454
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1455
0
      str = "GPAC MPEG-4 OD Handler";
1456
0
      break;
1457
0
    case GF_ISOM_MEDIA_OCR:
1458
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1459
0
      str = "GPAC MPEG-4 OCR Handler";
1460
0
      break;
1461
0
    case GF_ISOM_MEDIA_SCENE:
1462
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1463
0
      str = "GPAC MPEG-4 Scene Description Handler";
1464
0
      break;
1465
0
    case GF_ISOM_MEDIA_MPEG7:
1466
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1467
0
      str = "GPAC MPEG-4 MPEG-7 Handler";
1468
0
      break;
1469
0
    case GF_ISOM_MEDIA_OCI:
1470
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1471
0
      str = "GPAC MPEG-4 OCI Handler";
1472
0
      break;
1473
0
    case GF_ISOM_MEDIA_IPMP:
1474
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1475
0
      str = "GPAC MPEG-4 IPMP Handler";
1476
0
      break;
1477
0
    case GF_ISOM_MEDIA_MPEGJ:
1478
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1479
0
      str = "GPAC MPEG-4 MPEG-J Handler";
1480
0
      break;
1481
0
    case GF_ISOM_MEDIA_TEXT:
1482
0
    case GF_ISOM_MEDIA_SUBT:
1483
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1484
0
      str = "GPAC Streaming Text Handler";
1485
0
      break;
1486
0
    case GF_ISOM_MEDIA_MPEG_SUBT:
1487
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_STHD);
1488
0
      str = "GPAC MPEG Subtitle Handler";
1489
0
      break;
1490
0
    case GF_ISOM_MEDIA_DIMS:
1491
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
1492
0
      MediaType = GF_ISOM_MEDIA_SCENE;
1493
0
      str = "GPAC DIMS Handler";
1494
0
      break;
1495
0
    case GF_ISOM_MEDIA_TIMECODE:
1496
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_GMHD);
1497
0
      str = "GPAC TMCD Handler";
1498
0
      break;
1499
0
    default:
1500
0
      mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1501
0
      str = "GPAC IsoMedia Handler";
1502
0
      break;
1503
0
    }
1504
0
    if (! mediaInfo) { e = GF_OUT_OF_MEM; goto err_exit; }
1505
0
    if (!minf->child_boxes) minf->child_boxes = gf_list_new();
1506
0
    gf_list_add(minf->child_boxes, mediaInfo);
1507
1508
0
    e = minf_on_child_box((GF_Box*)minf, (GF_Box *) mediaInfo, GF_FALSE);
1509
0
    if (e) goto err_exit;
1510
0
  }
1511
1512
0
  mdhd->timeScale = TimeScale;
1513
0
  hdlr->handlerType = MediaType;
1514
0
  if (!hdlr->nameUTF8)
1515
0
    hdlr->nameUTF8 = gf_strdup(str);
1516
1517
0
  if (!dref) {
1518
    //Create a data reference WITHOUT DATA ENTRY (we don't know anything yet about the media Data)
1519
0
    dref = (GF_DataReferenceBox *) gf_isom_box_new_parent(&dinf->child_boxes, GF_ISOM_BOX_TYPE_DREF);
1520
0
    if (! dref) { e = GF_OUT_OF_MEM; goto err_exit; }
1521
0
    e = dinf_on_child_box((GF_Box*)dinf, (GF_Box *)dref, GF_FALSE);
1522
0
    if (e) goto err_exit;
1523
0
  }
1524
1525
0
  if (!stbl) {
1526
    //first set-up the sample table...
1527
0
    stbl = (GF_SampleTableBox *) gf_isom_box_new_parent(&minf->child_boxes, GF_ISOM_BOX_TYPE_STBL);
1528
0
    if (! stbl) { e = GF_OUT_OF_MEM; goto err_exit; }
1529
1530
0
    e = minf_on_child_box((GF_Box*)minf, (GF_Box *) stbl, GF_FALSE);
1531
0
    if (e) goto err_exit;
1532
0
  }
1533
0
  if (!stbl->SampleDescription) {
1534
0
    stbl->SampleDescription = (GF_SampleDescriptionBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSD);
1535
0
    if (! stbl->SampleDescription) { e = GF_OUT_OF_MEM; goto err_exit; }
1536
0
  }
1537
1538
  //by default create a regular table, 32 but offset and normal sample size
1539
0
  if (!stbl->ChunkOffset) {
1540
0
    stbl->ChunkOffset = gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STCO);
1541
0
    if (! stbl->ChunkOffset) { e = GF_OUT_OF_MEM; goto err_exit; }
1542
0
  }
1543
0
  if (!stbl->SampleSize) {
1544
0
    stbl->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSZ);
1545
0
    if (! stbl->SampleSize) { e = GF_OUT_OF_MEM; goto err_exit; }
1546
0
  }
1547
0
  if (!stbl->SampleToChunk) {
1548
0
    stbl->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSC);
1549
0
    if (! stbl->SampleToChunk) { e = GF_OUT_OF_MEM; goto err_exit; }
1550
0
  }
1551
0
  if (!stbl->TimeToSample) {
1552
0
    stbl->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STTS);
1553
0
    if (! stbl->TimeToSample) { e = GF_OUT_OF_MEM; goto err_exit; }
1554
0
  }
1555
0
  if (!stbl->SampleDescription) {
1556
0
    stbl->SampleDescription = (GF_SampleDescriptionBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSD);
1557
0
    if (! stbl->SampleDescription) { e = GF_OUT_OF_MEM; goto err_exit; }
1558
0
  }
1559
0
  return GF_OK;
1560
1561
0
err_exit:
1562
0
  if (mdhd) gf_isom_box_del_parent(& ((*mdia)->child_boxes), (GF_Box *)mdhd);
1563
0
  if (minf) gf_isom_box_del_parent(& ((*mdia)->child_boxes), (GF_Box *)minf);
1564
0
  if (hdlr) {
1565
0
    gf_isom_box_del_parent(& ((*mdia)->child_boxes) , (GF_Box *)hdlr);
1566
0
  }
1567
0
  return e;
1568
1569
0
}
1570
1571
GF_Err Track_SetStreamDescriptor(GF_TrackBox *trak, u32 StreamDescriptionIndex, u32 DataReferenceIndex, GF_ESD *esd, u32 *outStreamIndex)
1572
0
{
1573
0
  GF_Err e;
1574
0
  GF_MPEGSampleEntryBox *entry;
1575
0
  GF_MPEGVisualSampleEntryBox *entry_v;
1576
0
  GF_MPEGAudioSampleEntryBox *entry_a;
1577
0
  GF_TrackReferenceBox *tref;
1578
0
  GF_TrackReferenceTypeBox *dpnd;
1579
0
  u16 tmpRef;
1580
1581
0
  entry = NULL;
1582
0
  tref = NULL;
1583
1584
0
  if (!trak || !esd || (!outStreamIndex && !DataReferenceIndex) ) return GF_BAD_PARAM;
1585
0
  if (!Track_IsMPEG4Stream(trak->Media->handler->handlerType)) return GF_ISOM_INVALID_MEDIA;
1586
1587
1588
0
  esd->ESID = 0;
1589
  //set SL to predefined if no url
1590
0
  if (esd->URLString == NULL) {
1591
0
    if (!esd->slConfig) esd->slConfig = (GF_SLConfig*) gf_odf_desc_new(GF_ODF_SLC_TAG);
1592
0
    esd->slConfig->predefined = SLPredef_MP4;
1593
0
    esd->slConfig->durationFlag = 0;
1594
0
    esd->slConfig->useTimestampsFlag = 1;
1595
0
  }
1596
1597
  //get the REF box if needed
1598
0
  if (esd->dependsOnESID || (esd->OCRESID  && (esd->OCRESID != trak->moov->mov->es_id_default_sync)) ) {
1599
0
    if (!trak->References) {
1600
0
      tref = (GF_TrackReferenceBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TREF);
1601
0
      if (!tref) return GF_OUT_OF_MEM;
1602
0
      e = trak_on_child_box((GF_Box*)trak, (GF_Box *)tref, GF_FALSE);
1603
0
      if (e) return e;
1604
0
    }
1605
0
    tref = trak->References;
1606
0
  }
1607
1608
  //Update Stream dependencies
1609
0
  e = Track_FindRef(trak, GF_ISOM_REF_DECODE, &dpnd);
1610
0
  if (e) return e;
1611
1612
0
  if (!dpnd && esd->dependsOnESID) {
1613
0
    e = Track_FindRef(trak, GF_ISOM_REF_BASE, &dpnd);
1614
0
    if (e) return e;
1615
0
  }
1616
1617
0
  if (!dpnd && esd->dependsOnESID) {
1618
0
    dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT);
1619
0
    dpnd->reference_type = GF_ISOM_BOX_TYPE_DPND;
1620
0
    e = reftype_AddRefTrack(dpnd, esd->dependsOnESID, NULL);
1621
0
    if (e) return e;
1622
0
  } else if (dpnd && !esd->dependsOnESID) {
1623
0
    Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_DPND);
1624
0
  }
1625
0
  esd->dependsOnESID = 0;
1626
1627
  //Update GF_Clock dependencies
1628
0
  e = Track_FindRef(trak, GF_ISOM_REF_OCR, &dpnd);
1629
0
  if (e) return e;
1630
0
  if (!dpnd && esd->OCRESID && (esd->OCRESID != trak->moov->mov->es_id_default_sync)) {
1631
0
    dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT);
1632
0
    if (!dpnd) return GF_OUT_OF_MEM;
1633
0
    dpnd->reference_type = GF_ISOM_BOX_TYPE_SYNC;
1634
0
    e = reftype_AddRefTrack(dpnd, esd->OCRESID, NULL);
1635
0
    if (e) return e;
1636
0
  } else if (dpnd && !esd->OCRESID) {
1637
0
    Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_SYNC);
1638
0
  } else if (dpnd && esd->OCRESID) {
1639
0
    if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA;
1640
0
    dpnd->trackIDs[0] = esd->OCRESID;
1641
0
  }
1642
0
  esd->OCRESID = 0;
1643
1644
  //brand new case: we have to change the IPI desc
1645
0
  if (esd->ipiPtr) {
1646
0
    e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd);
1647
0
    if (e) return e;
1648
0
    if (!dpnd) {
1649
0
      tmpRef = 0;
1650
0
      dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT);
1651
0
      if (!dpnd) return GF_OUT_OF_MEM;
1652
0
      dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR;
1653
0
      e = reftype_AddRefTrack(dpnd, esd->ipiPtr->IPI_ES_Id, &tmpRef);
1654
0
      if (e) return e;
1655
      //and replace the tag and value...
1656
0
      esd->ipiPtr->IPI_ES_Id = tmpRef;
1657
0
      esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1658
0
    } else {
1659
      //Watch out! ONLY ONE IPI dependency is allowed per stream
1660
0
      if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA;
1661
      //if an existing one is there, what shall we do ???
1662
      //donno, erase it
1663
0
      dpnd->trackIDs[0] = esd->ipiPtr->IPI_ES_Id;
1664
      //and replace the tag and value...
1665
0
      esd->ipiPtr->IPI_ES_Id = 1;
1666
0
      esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1667
0
    }
1668
0
  }
1669
1670
  /*don't store the lang desc in ESD, use the media header language info*/
1671
0
  if (esd->langDesc) {
1672
0
    trak->Media->mediaHeader->packedLanguage[0] = (esd->langDesc->langCode>>16)&0xFF;
1673
0
    trak->Media->mediaHeader->packedLanguage[1] = (esd->langDesc->langCode>>8)&0xFF;
1674
0
    trak->Media->mediaHeader->packedLanguage[2] = (esd->langDesc->langCode)&0xFF;
1675
0
    gf_odf_desc_del((GF_Descriptor *)esd->langDesc);
1676
0
    esd->langDesc = NULL;
1677
0
  }
1678
1679
  //we have a streamDescriptionIndex, use it
1680
0
  if (StreamDescriptionIndex) {
1681
0
    u32 entry_type;
1682
0
    entry = (GF_MPEGSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1);
1683
0
    if (!entry) return GF_ISOM_INVALID_FILE;
1684
1685
0
    entry_type = entry->type;
1686
0
    GF_ProtectionSchemeInfoBox *sinf = (GF_ProtectionSchemeInfoBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_SINF);
1687
0
    if (sinf && sinf->original_format) entry_type = sinf->original_format->data_format;
1688
1689
0
    GF_MPEGSampleEntryBox *mpgs_entry = NULL;
1690
0
    entry_v = NULL;
1691
0
    entry_a = NULL;
1692
0
    switch (entry->internal_type) {
1693
0
    case GF_ISOM_SAMPLE_ENTRY_GENERIC:
1694
0
      break;
1695
0
    case GF_ISOM_SAMPLE_ENTRY_VIDEO:
1696
0
      entry_v = (GF_MPEGVisualSampleEntryBox*) entry;
1697
0
      break;
1698
0
    case GF_ISOM_SAMPLE_ENTRY_AUDIO:
1699
0
      entry_a = (GF_MPEGAudioSampleEntryBox*) entry;
1700
0
      break;
1701
0
    case GF_ISOM_SAMPLE_ENTRY_MP4S:
1702
0
      mpgs_entry = (GF_MPEGSampleEntryBox *) entry;
1703
0
      break;
1704
0
    }
1705
1706
0
    switch (entry_type) {
1707
0
    case GF_ISOM_BOX_TYPE_MP4S:
1708
0
      if (!mpgs_entry) return GF_ISOM_INVALID_FILE;
1709
      //OK, delete the previous ESD
1710
0
      gf_odf_desc_del((GF_Descriptor *) entry->esd->desc);
1711
0
      entry->esd->desc = esd;
1712
0
      break;
1713
0
    case GF_ISOM_BOX_TYPE_MP4V:
1714
0
      if (!entry_v) return GF_ISOM_INVALID_MEDIA;
1715
0
      if (entry_v->esd) {
1716
0
        gf_odf_desc_del((GF_Descriptor *) entry_v->esd->desc);
1717
0
        entry_v->esd->desc = esd;
1718
0
      } else {
1719
0
        return GF_ISOM_INVALID_MEDIA;
1720
0
      }
1721
0
      break;
1722
0
    case GF_ISOM_BOX_TYPE_MP4A:
1723
0
      if (!entry_a) return GF_ISOM_INVALID_MEDIA;
1724
0
            if (entry_a->esd) { // some non-conformant files may not have an ESD ...
1725
                //OK, delete the previous ESD
1726
0
                gf_odf_desc_del((GF_Descriptor *) entry_a->esd->desc);
1727
0
                entry_a->esd->desc = esd;
1728
0
            } else {
1729
        // can't return OK here otherwise we can't know if esd hasn't been used
1730
        // and need to be freed
1731
0
        return GF_ISOM_INVALID_MEDIA;
1732
0
      }
1733
0
      break;
1734
0
    case GF_ISOM_BOX_TYPE_AVC1:
1735
0
    case GF_ISOM_BOX_TYPE_AVC2:
1736
0
    case GF_ISOM_BOX_TYPE_AVC3:
1737
0
    case GF_ISOM_BOX_TYPE_AVC4:
1738
0
    case GF_ISOM_BOX_TYPE_SVC1:
1739
0
    case GF_ISOM_BOX_TYPE_MVC1:
1740
0
    case GF_ISOM_BOX_TYPE_HVC1:
1741
0
    case GF_ISOM_BOX_TYPE_HEV1:
1742
0
    case GF_ISOM_BOX_TYPE_HVC2:
1743
0
    case GF_ISOM_BOX_TYPE_HEV2:
1744
0
    case GF_ISOM_BOX_TYPE_LHE1:
1745
0
    case GF_ISOM_BOX_TYPE_LHV1:
1746
0
    case GF_ISOM_BOX_TYPE_HVT1:
1747
0
    case GF_ISOM_BOX_TYPE_VVC1:
1748
0
    case GF_ISOM_BOX_TYPE_VVI1:
1749
0
      if (!entry_v) return GF_ISOM_INVALID_MEDIA;
1750
0
      e = AVC_HEVC_UpdateESD(entry_v, esd);
1751
0
      if (e) return e;
1752
0
      break;
1753
0
    case GF_ISOM_BOX_TYPE_LSR1:
1754
0
      e = LSR_UpdateESD((GF_LASeRSampleEntryBox*)entry, esd);
1755
0
      if (e) return e;
1756
0
      break;
1757
0
    case GF_ISOM_BOX_TYPE_AV01:
1758
0
    case GF_ISOM_BOX_TYPE_DAV1:
1759
0
    case GF_ISOM_BOX_TYPE_AV1C:
1760
0
    case GF_ISOM_BOX_TYPE_AVS3:
1761
0
    case GF_ISOM_BOX_TYPE_AV3C:
1762
0
    case GF_ISOM_BOX_TYPE_OPUS:
1763
0
    case GF_ISOM_BOX_TYPE_DOPS:
1764
0
    case GF_ISOM_BOX_TYPE_STXT:
1765
0
    case GF_ISOM_BOX_TYPE_WVTT:
1766
0
    case GF_ISOM_BOX_TYPE_STPP:
1767
0
      if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
1768
0
      break;
1769
1770
0
    default:
1771
      //silently fail, not an MPEG-4 esd
1772
0
      gf_odf_desc_del((GF_Descriptor *) esd);
1773
0
      return GF_OK;
1774
0
    }
1775
0
  } else {
1776
    //need to check we're not in URL mode where only ONE description is allowed...
1777
0
    StreamDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes);
1778
0
    if (StreamDescriptionIndex) {
1779
0
      GF_ESD *old_esd=NULL;
1780
0
      entry = (GF_MPEGSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1);
1781
0
      if (!entry) return GF_ISOM_INVALID_FILE;
1782
      //get ESD (only if present, do not emulate)
1783
0
      Media_GetESD(trak->Media, StreamDescriptionIndex, &old_esd, GF_TRUE);
1784
0
      if (old_esd && old_esd->URLString) return GF_BAD_PARAM;
1785
0
    }
1786
1787
    //OK, check the handler and create the entry
1788
0
    switch (trak->Media->handler->handlerType) {
1789
0
        case GF_ISOM_MEDIA_AUXV:
1790
0
        case GF_ISOM_MEDIA_PICT:
1791
0
    case GF_ISOM_MEDIA_VISUAL:
1792
0
      if ((esd->decoderConfig->objectTypeIndication==GF_CODECID_AVC) || (esd->decoderConfig->objectTypeIndication==GF_CODECID_SVC) || (esd->decoderConfig->objectTypeIndication==GF_CODECID_MVC)) {
1793
0
        entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AVC1);
1794
0
        if (!entry_v) return GF_OUT_OF_MEM;
1795
0
        e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd);
1796
0
        if (e) return  e;
1797
0
      } else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_HEVC) {
1798
0
        entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HVC1);
1799
0
        if (!entry_v) return GF_OUT_OF_MEM;
1800
0
        e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd);
1801
0
        if (e) return  e;
1802
0
      } else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_VVC) {
1803
0
        entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_VVC1);
1804
0
        if (!entry_v) return GF_OUT_OF_MEM;
1805
0
        e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd);
1806
0
        if (e) return  e;
1807
0
      } else {
1808
0
        entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4V);
1809
0
        if (!entry_v) return GF_OUT_OF_MEM;
1810
0
        entry_v->esd = (GF_ESDBox *) gf_isom_box_new_parent(&entry_v->child_boxes, GF_ISOM_BOX_TYPE_ESDS);
1811
0
        if (!entry_v->esd) return GF_OUT_OF_MEM;
1812
0
        entry_v->esd->desc = esd;
1813
0
      }
1814
1815
      //type cast possible now
1816
0
      entry = (GF_MPEGSampleEntryBox*) entry_v;
1817
0
      break;
1818
0
    case GF_ISOM_MEDIA_AUDIO:
1819
0
      if (esd->decoderConfig->objectTypeIndication == GF_CODECID_OPUS) {
1820
0
        GF_MPEGAudioSampleEntryBox *opus = (GF_MPEGAudioSampleEntryBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_OPUS);
1821
0
        if (!opus) return GF_OUT_OF_MEM;
1822
0
        opus->cfg_opus = (GF_OpusSpecificBox *)gf_isom_box_new_parent(&opus->child_boxes, GF_ISOM_BOX_TYPE_DOPS);
1823
0
        if (!opus->cfg_opus) return GF_OUT_OF_MEM;
1824
0
        entry = (GF_MPEGSampleEntryBox*)opus;
1825
0
        gf_odf_desc_del((GF_Descriptor *) esd);
1826
0
      } else if (esd->decoderConfig->objectTypeIndication == GF_CODECID_IAMF) {
1827
0
        GF_MPEGAudioSampleEntryBox *iamf = (GF_MPEGAudioSampleEntryBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_IAMF);
1828
0
        if (!iamf) return GF_OUT_OF_MEM;
1829
0
        iamf->cfg_iamf = (GF_IAConfigurationBox *)gf_isom_box_new_parent(&iamf->child_boxes, GF_ISOM_BOX_TYPE_IACB);
1830
0
        if (!iamf->cfg_iamf) return GF_OUT_OF_MEM;
1831
0
        entry = (GF_MPEGSampleEntryBox*)iamf;
1832
0
        gf_odf_desc_del((GF_Descriptor *) esd);
1833
0
      } else if (esd->decoderConfig->objectTypeIndication == GF_CODECID_AC3) {
1834
0
        GF_MPEGAudioSampleEntryBox *ac3 = (GF_MPEGAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AC3);
1835
0
        if (!ac3) return GF_OUT_OF_MEM;
1836
0
        ac3->cfg_ac3 = (GF_AC3ConfigBox *) gf_isom_box_new_parent(&ac3->child_boxes, GF_ISOM_BOX_TYPE_DAC3);
1837
0
        if (!ac3->cfg_ac3) return GF_OUT_OF_MEM;
1838
0
        entry = (GF_MPEGSampleEntryBox*) ac3;
1839
0
        gf_odf_desc_del((GF_Descriptor *) esd);
1840
0
      } else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_EAC3) {
1841
0
        GF_MPEGAudioSampleEntryBox *eac3 = (GF_MPEGAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_EC3);
1842
0
        if (!eac3) return GF_OUT_OF_MEM;
1843
0
        eac3->cfg_ac3 = (GF_AC3ConfigBox *) gf_isom_box_new_parent(&eac3->child_boxes, GF_ISOM_BOX_TYPE_DEC3);
1844
0
        if (!eac3->cfg_ac3) return GF_OUT_OF_MEM;
1845
0
        entry = (GF_MPEGSampleEntryBox*) eac3;
1846
0
        gf_odf_desc_del((GF_Descriptor *) esd);
1847
0
      } else if (esd->decoderConfig->objectTypeIndication == GF_CODECID_AC4) {
1848
0
        GF_MPEGAudioSampleEntryBox *ac4 = (GF_MPEGAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AC4);
1849
0
        if (!ac4) return GF_OUT_OF_MEM;
1850
0
        ac4->cfg_ac4 = (GF_AC4ConfigBox *) gf_isom_box_new_parent(&ac4->child_boxes, GF_ISOM_BOX_TYPE_DAC4);
1851
0
        if (!ac4->cfg_ac4) return GF_OUT_OF_MEM;
1852
0
        entry = (GF_MPEGSampleEntryBox*) ac4;
1853
0
        gf_odf_desc_del((GF_Descriptor *) esd);
1854
0
      } else {
1855
0
        entry_a = (GF_MPEGAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4A);
1856
0
        if (!entry_a) return GF_OUT_OF_MEM;
1857
0
        entry_a->samplerate_hi = trak->Media->mediaHeader->timeScale;
1858
0
        entry_a->esd = (GF_ESDBox *) gf_isom_box_new_parent(&entry_a->child_boxes, GF_ISOM_BOX_TYPE_ESDS);
1859
0
        if (!entry_a->esd) return GF_OUT_OF_MEM;
1860
0
        entry_a->esd->desc = esd;
1861
        //type cast possible now
1862
0
        entry = (GF_MPEGSampleEntryBox*) entry_a;
1863
0
      }
1864
0
      break;
1865
0
    default:
1866
0
      if ((esd->decoderConfig->streamType==0x03) && (esd->decoderConfig->objectTypeIndication==0x09)) {
1867
0
        entry = (GF_MPEGSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_LSR1);
1868
0
        if (!entry) return GF_OUT_OF_MEM;
1869
0
        e = LSR_UpdateESD((GF_LASeRSampleEntryBox*)entry, esd);
1870
0
        if (e) return  e;
1871
0
      } else {
1872
0
        entry = (GF_MPEGSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4S);
1873
0
        entry->esd = (GF_ESDBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_ESDS);
1874
0
        if (!entry->esd) return GF_OUT_OF_MEM;
1875
0
        entry->esd->desc = esd;
1876
0
      }
1877
0
      break;
1878
0
    }
1879
0
    entry->dataReferenceIndex = DataReferenceIndex;
1880
1881
0
    if (!trak->Media->information->sampleTable->SampleDescription->child_boxes)
1882
0
      trak->Media->information->sampleTable->SampleDescription->child_boxes = gf_list_new();
1883
0
    gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, entry);
1884
1885
0
    e = stsd_on_child_box((GF_Box*)trak->Media->information->sampleTable->SampleDescription, (GF_Box *) entry, GF_FALSE);
1886
0
    if (e) return e;
1887
0
    if(outStreamIndex) *outStreamIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes);
1888
0
  }
1889
0
  return GF_OK;
1890
0
}
1891
1892
#endif  /*GPAC_DISABLE_ISOM_WRITE*/
1893
1894
#endif /*GPAC_DISABLE_ISOM*/