Coverage Report

Created: 2025-11-24 07:00

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