Coverage Report

Created: 2026-10-26 08:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/isomedia/isom_write.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
#include <gpac/iso639.h>
29
30
31
#if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)
32
33
GF_EXPORT
34
GF_Err gf_isom_can_access_movie(GF_ISOFile *movie, GF_ISOOpenMode Mode)
35
0
{
36
0
  if (!movie) return GF_BAD_PARAM;
37
0
  if (movie->openMode < Mode) return GF_ISOM_INVALID_MODE;
38
39
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
40
0
  if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_ISOM_INVALID_MODE;
41
0
#endif
42
0
  return GF_OK;
43
0
}
44
45
static GF_Err unpack_track(GF_TrackBox *trak)
46
0
{
47
0
  GF_Err e = GF_OK;
48
0
  if (!trak->is_unpacked) {
49
0
    e = stbl_UnpackOffsets(trak->Media->information->sampleTable);
50
0
    if (e) return e;
51
0
    e = stbl_unpackCTS(trak->Media->information->sampleTable);
52
0
    trak->is_unpacked = GF_TRUE;
53
0
  }
54
0
  return e;
55
0
}
56
57
58
GF_Err FlushCaptureMode(GF_ISOFile *movie)
59
0
{
60
0
  GF_Err e;
61
0
  if (movie->openMode != GF_ISOM_OPEN_WRITE) {
62
0
    if (!movie->editFileMap) return GF_ISOM_INVALID_MODE;
63
0
    return GF_OK;
64
0
  }
65
  /*make sure nothing was added*/
66
0
  if (gf_bs_get_position(movie->editFileMap->bs)) return GF_OK;
67
68
0
  if (movie->fileName && !strcmp(movie->fileName, "_gpac_isobmff_redirect")) {
69
0
    if (!movie->on_block_out) {
70
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block callback, cannot write\n"));
71
0
      return GF_BAD_PARAM;
72
0
    }
73
74
0
    gf_bs_del(movie->editFileMap->bs);
75
0
    movie->editFileMap->bs = gf_bs_new_cbk(isom_on_block_out, movie, movie->on_block_out_block_size);
76
0
  }
77
78
  /*add all first boxes*/
79
0
  if (movie->brand) {
80
0
    e = gf_isom_box_size((GF_Box *)movie->brand);
81
0
    if (e) return e;
82
0
    e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs);
83
0
    if (e) return e;
84
0
  }
85
0
  if (movie->pdin) {
86
0
    e = gf_isom_box_size((GF_Box *)movie->pdin);
87
0
    if (e) return e;
88
0
    e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs);
89
0
    if (e) return e;
90
0
  }
91
0
  movie->mdat->bsOffset = gf_bs_get_position(movie->editFileMap->bs);
92
93
  /*we have a trick here: the data will be stored on the fly, so the first
94
  thing in the file is the MDAT. As we don't know if we have a large file (>4 GB) or not
95
  do as if we had one and write 16 bytes: 4 (type) + 4 (size) + 8 (largeSize)...*/
96
0
  gf_bs_write_long_int(movie->editFileMap->bs, 0, 64);
97
0
  gf_bs_write_long_int(movie->editFileMap->bs, 0, 64);
98
0
  return GF_OK;
99
0
}
100
101
static GF_Err CheckNoData(GF_ISOFile *movie)
102
0
{
103
0
  if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_OK;
104
0
  if (gf_bs_get_position(movie->editFileMap->bs)) return GF_BAD_PARAM;
105
0
  return GF_OK;
106
0
}
107
108
/**************************************************************
109
          File Writing / Editing
110
**************************************************************/
111
//quick function to add an IOD/OD to the file if not present (iods is optional)
112
GF_Err AddMovieIOD(GF_MovieBox *moov, u8 isIOD)
113
0
{
114
0
  GF_Descriptor *od;
115
0
  GF_ObjectDescriptorBox *iods;
116
117
  //do we have an IOD ?? If not, create one.
118
0
  if (moov->iods) return GF_OK;
119
120
0
  if (isIOD) {
121
0
    od = gf_odf_desc_new(GF_ODF_ISOM_IOD_TAG);
122
0
  } else {
123
0
    od = gf_odf_desc_new(GF_ODF_ISOM_OD_TAG);
124
0
  }
125
0
  if (!od) return GF_OUT_OF_MEM;
126
0
  ((GF_IsomObjectDescriptor *)od)->objectDescriptorID = 1;
127
128
0
  iods = (GF_ObjectDescriptorBox *) gf_isom_box_new_parent(&moov->child_boxes, GF_ISOM_BOX_TYPE_IODS);
129
0
  if (!iods) return GF_OUT_OF_MEM;
130
0
  iods->descriptor = od;
131
0
  return moov_on_child_box((GF_Box*)moov, (GF_Box *)iods, GF_FALSE);
132
0
}
133
134
//add a track to the root OD
135
GF_EXPORT
136
GF_Err gf_isom_add_track_to_root_od(GF_ISOFile *movie, u32 trackNumber)
137
0
{
138
0
  GF_Err e;
139
0
  GF_ES_ID_Inc *inc;
140
141
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
142
0
  if (e) return e;
143
0
  e = gf_isom_insert_moov(movie);
144
0
  if (e) return e;
145
146
0
  if (!movie->moov->iods) AddMovieIOD(movie->moov, 0);
147
148
0
  if (gf_isom_is_track_in_root_od(movie, trackNumber) == 1) return GF_OK;
149
150
0
  inc = (GF_ES_ID_Inc *) gf_odf_desc_new(GF_ODF_ESD_INC_TAG);
151
0
  inc->trackID = gf_isom_get_track_id(movie, trackNumber);
152
0
  if (!inc->trackID) {
153
0
    gf_odf_desc_del((GF_Descriptor *)inc);
154
0
    return movie->LastError;
155
0
  }
156
0
  if ( (movie->LastError = gf_isom_add_desc_to_root_od(movie, (GF_Descriptor *)inc) ) ) {
157
0
    return movie->LastError;
158
0
  }
159
0
  gf_odf_desc_del((GF_Descriptor *)inc);
160
0
  return GF_OK;
161
0
}
162
163
//remove the root OD
164
GF_EXPORT
165
GF_Err gf_isom_remove_root_od(GF_ISOFile *movie)
166
0
{
167
0
  GF_Err e;
168
169
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
170
0
  if (e) return e;
171
0
  if (!movie->moov || !movie->moov->iods) return GF_OK;
172
0
  gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box *)movie->moov->iods);
173
0
  movie->moov->iods = NULL;
174
0
  return GF_OK;
175
0
}
176
177
//remove a track to the root OD
178
GF_EXPORT
179
GF_Err gf_isom_remove_track_from_root_od(GF_ISOFile *movie, u32 trackNumber)
180
0
{
181
0
  GF_List *esds;
182
0
  GF_ES_ID_Inc *inc;
183
0
  u32 i;
184
0
  GF_Err e;
185
186
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
187
0
  if (e) return e;
188
0
  if (!movie->moov) return GF_OK;
189
190
0
  if (!gf_isom_is_track_in_root_od(movie, trackNumber)) return GF_OK;
191
192
0
  if (!movie->moov->iods) {
193
0
    e = AddMovieIOD(movie->moov, 0);
194
0
    if (e) return e;
195
0
  }
196
0
  switch (movie->moov->iods->descriptor->tag) {
197
0
  case GF_ODF_ISOM_IOD_TAG:
198
0
    esds = ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors;
199
0
    break;
200
0
  case GF_ODF_ISOM_OD_TAG:
201
0
    esds = ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors;
202
0
    break;
203
0
  default:
204
0
    return GF_ISOM_INVALID_FILE;
205
0
  }
206
207
  //get the desc
208
0
  i=0;
209
0
  while ((inc = (GF_ES_ID_Inc*)gf_list_enum(esds, &i))) {
210
0
    if (inc->trackID == (u32) gf_isom_get_track_id(movie, trackNumber)) {
211
0
      gf_odf_desc_del((GF_Descriptor *)inc);
212
0
      gf_list_rem(esds, i-1);
213
0
      break;
214
0
    }
215
0
  }
216
  //we don't remove the iod for P&Ls and other potential info
217
0
  return GF_OK;
218
0
}
219
220
GF_EXPORT
221
GF_Err gf_isom_set_creation_time(GF_ISOFile *movie, u64 ctime, u64 mtime)
222
0
{
223
0
  if (!movie || !movie->moov) return GF_BAD_PARAM;
224
0
  movie->moov->mvhd->creationTime = ctime;
225
0
  movie->moov->mvhd->modificationTime = mtime;
226
0
  return GF_OK;
227
0
}
228
229
GF_EXPORT
230
GF_Err gf_isom_set_track_creation_time(GF_ISOFile *movie,u32 trackNumber, u64 ctime, u64 mtime)
231
0
{
232
0
  GF_TrackBox *trak;
233
0
  trak = gf_isom_get_track_box(movie, trackNumber);
234
0
  if (!trak) return GF_BAD_PARAM;
235
236
0
  trak->Header->creationTime = ctime;
237
0
  trak->Header->modificationTime = mtime;
238
0
  return GF_OK;
239
0
}
240
241
GF_EXPORT
242
GF_Err gf_isom_set_media_creation_time(GF_ISOFile *movie,u32 trackNumber, u64 ctime, u64 mtime)
243
0
{
244
0
  GF_TrackBox *trak;
245
0
  trak = gf_isom_get_track_box(movie, trackNumber);
246
0
  if (!trak) return GF_BAD_PARAM;
247
0
  if (!trak->Media || !trak->Media->mediaHeader) return GF_ISOM_INVALID_FILE;
248
249
0
  trak->Media->mediaHeader->creationTime = ctime;
250
0
  trak->Media->mediaHeader->modificationTime = mtime;
251
0
  return GF_OK;
252
0
}
253
254
//sets the enable flag of a track
255
GF_EXPORT
256
GF_Err gf_isom_set_track_enabled(GF_ISOFile *movie, u32 trackNumber, Bool enableTrack)
257
0
{
258
0
  GF_Err e;
259
0
  GF_TrackBox *trak;
260
261
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
262
0
  if (e) return e;
263
264
0
  trak = gf_isom_get_track_box(movie, trackNumber);
265
0
  if (!trak) return GF_BAD_PARAM;
266
267
0
  if (enableTrack) {
268
0
    trak->Header->flags |= 1;
269
0
  } else {
270
0
    trak->Header->flags &= ~1;
271
0
  }
272
0
  return GF_OK;
273
0
}
274
275
//sets the enable flag of a track
276
GF_EXPORT
277
GF_Err gf_isom_set_track_flags(GF_ISOFile *movie, u32 trackNumber, u32 flags, GF_ISOMTrackFlagOp op)
278
0
{
279
0
  GF_Err e;
280
0
  GF_TrackBox *trak;
281
282
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
283
0
  if (e) return e;
284
285
0
  trak = gf_isom_get_track_box(movie, trackNumber);
286
0
  if (!trak) return GF_BAD_PARAM;
287
0
  if (op==GF_ISOM_TKFLAGS_ADD)
288
0
    trak->Header->flags |= flags;
289
0
  else if (op==GF_ISOM_TKFLAGS_REM)
290
0
    trak->Header->flags &= ~flags;
291
0
  else
292
0
    trak->Header->flags = flags;
293
0
  return GF_OK;
294
0
}
295
296
GF_EXPORT
297
GF_Err gf_isom_set_media_language(GF_ISOFile *movie, u32 trackNumber, char *code)
298
0
{
299
0
  GF_Err e;
300
0
  GF_TrackBox *trak;
301
302
0
  trak = gf_isom_get_track_box(movie, trackNumber);
303
0
  if (!trak || !code) return GF_BAD_PARAM;
304
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
305
0
  if (e) return e;
306
307
0
  if (trak->extl) {
308
0
    GF_ExtendedLanguageBox *elng = (GF_ExtendedLanguageBox *) gf_isom_box_find_child(trak->child_boxes, GF_ISOM_BOX_TYPE_ELNG);
309
0
    if (!elng) {
310
0
      elng = (GF_ExtendedLanguageBox *)gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_ELNG);
311
0
      if (!elng) return GF_OUT_OF_MEM;
312
0
    }
313
0
    if (elng->extended_language) gf_free(elng->extended_language);
314
0
    elng->extended_language = gf_strdup(code);
315
316
0
    if (!movie->keep_utc)
317
0
      trak->Header->modificationTime = gf_isom_get_mp4time();
318
0
    return GF_OK;
319
0
  }
320
321
  // Old language-storage processing
322
  // if the new code is on 3 chars, we use it
323
  // otherwise, we find the associated 3 chars code and use it
324
0
  if (strlen(code) == 3) {
325
0
    memcpy(trak->Media->mediaHeader->packedLanguage, code, sizeof(char)*3);
326
0
  } else {
327
0
    s32 lang_idx;
328
0
    const char *code_3cc;
329
0
    lang_idx = gf_lang_find(code);
330
0
    if (lang_idx == -1) {
331
0
      if (code[0]!=0) {
332
0
        GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("The given code is not a valid one: %s, using 'und' as 3-letter code\n", code));
333
0
      }
334
0
      code_3cc = "und";
335
0
    } else {
336
0
      code_3cc = gf_lang_get_3cc(lang_idx);
337
0
    }
338
0
    memcpy(trak->Media->mediaHeader->packedLanguage, code_3cc, sizeof(char)*3);
339
0
  }
340
341
  // New language-storage processing
342
  // change the code in the extended language box (if any)
343
  // otherwise add an extended language box only if the given code is not 3 chars
344
0
  {
345
0
    u32 i, count;
346
0
    GF_ExtendedLanguageBox *elng;
347
0
    elng = NULL;
348
0
    count = gf_list_count(trak->Media->child_boxes);
349
0
    for (i = 0; i < count; i++) {
350
0
      GF_Box *box = (GF_Box *)gf_list_get(trak->Media->child_boxes, i);
351
0
      if (box->type == GF_ISOM_BOX_TYPE_ELNG) {
352
0
        elng = (GF_ExtendedLanguageBox *)box;
353
0
        break;
354
0
      }
355
0
    }
356
0
    if (!elng && (strlen(code) > 3)) {
357
0
      elng = (GF_ExtendedLanguageBox *)gf_isom_box_new_parent(&trak->Media->child_boxes, GF_ISOM_BOX_TYPE_ELNG);
358
0
      if (!elng) return GF_OUT_OF_MEM;
359
0
    }
360
0
    if (elng) {
361
0
      if (elng->extended_language) {
362
0
        gf_free(elng->extended_language);
363
0
      }
364
0
      elng->extended_language = gf_strdup(code);
365
0
    }
366
0
  }
367
0
  if (!movie->keep_utc)
368
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
369
0
  return GF_OK;
370
0
}
371
372
static GF_Err gf_isom_set_root_iod(GF_ISOFile *movie)
373
0
{
374
0
  GF_IsomInitialObjectDescriptor *iod;
375
0
  GF_IsomObjectDescriptor *od;
376
0
  GF_Err e;
377
378
0
  e = gf_isom_insert_moov(movie);
379
0
  if (e) return e;
380
0
  if (!movie->moov->iods) {
381
0
    AddMovieIOD(movie->moov, 1);
382
0
    return GF_OK;
383
0
  }
384
  //if OD, switch to IOD
385
0
  if (movie->moov->iods->descriptor->tag == GF_ODF_ISOM_IOD_TAG) return GF_OK;
386
0
  od = (GF_IsomObjectDescriptor *) movie->moov->iods->descriptor;
387
0
  iod = (GF_IsomInitialObjectDescriptor*)gf_malloc(sizeof(GF_IsomInitialObjectDescriptor));
388
0
  if (!iod) return GF_OUT_OF_MEM;
389
390
0
  memset(iod, 0, sizeof(GF_IsomInitialObjectDescriptor));
391
392
0
  iod->ES_ID_IncDescriptors = od->ES_ID_IncDescriptors;
393
0
  od->ES_ID_IncDescriptors = NULL;
394
  //not used in root OD
395
0
  iod->ES_ID_RefDescriptors = NULL;
396
0
  iod->extensionDescriptors = od->extensionDescriptors;
397
0
  od->extensionDescriptors = NULL;
398
0
  iod->IPMP_Descriptors = od->IPMP_Descriptors;
399
0
  od->IPMP_Descriptors = NULL;
400
0
  iod->objectDescriptorID = od->objectDescriptorID;
401
0
  iod->OCIDescriptors = od->OCIDescriptors;
402
0
  od->OCIDescriptors = NULL;
403
0
  iod->tag = GF_ODF_ISOM_IOD_TAG;
404
0
  iod->URLString = od->URLString;
405
0
  od->URLString = NULL;
406
407
0
  gf_odf_desc_del((GF_Descriptor *) od);
408
0
  movie->moov->iods->descriptor = (GF_Descriptor *)iod;
409
0
  return GF_OK;
410
0
}
411
412
GF_EXPORT
413
GF_Err gf_isom_add_desc_to_root_od(GF_ISOFile *movie, const GF_Descriptor *theDesc)
414
0
{
415
0
  GF_Err e;
416
0
  GF_Descriptor *desc, *dupDesc;
417
418
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
419
0
  if (e) return e;
420
0
  e = gf_isom_insert_moov(movie);
421
0
  if (e) return e;
422
423
0
  if (!movie->moov->iods) {
424
0
    e = AddMovieIOD(movie->moov, 0);
425
0
    if (e) return e;
426
0
  }
427
0
  if (theDesc->tag==GF_ODF_IPMP_TL_TAG) gf_isom_set_root_iod(movie);
428
429
0
  desc = movie->moov->iods->descriptor;
430
  //the type of desc is handled at the OD/IOD level, we'll be notified
431
  //if the desc is not allowed
432
0
  switch (desc->tag) {
433
0
  case GF_ODF_ISOM_IOD_TAG:
434
0
  case GF_ODF_ISOM_OD_TAG:
435
    //duplicate the desc
436
0
    e = gf_odf_desc_copy((GF_Descriptor *)theDesc, &dupDesc);
437
0
    if (e) return e;
438
    //add it (MUST BE  (I)OD level desc)
439
0
    movie->LastError = gf_odf_desc_add_desc(desc, dupDesc);
440
0
    if (movie->LastError) gf_odf_desc_del((GF_Descriptor *)dupDesc);
441
0
    break;
442
0
  default:
443
0
    movie->LastError = GF_ISOM_INVALID_FILE;
444
0
    break;
445
0
  }
446
0
  return movie->LastError;
447
0
}
448
449
450
GF_EXPORT
451
GF_Err gf_isom_set_timescale(GF_ISOFile *movie, u32 timeScale)
452
0
{
453
0
  GF_TrackBox *trak;
454
0
  u32 i;
455
0
  GF_Err e;
456
0
  if (!timeScale) return GF_BAD_PARAM;
457
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
458
0
  if (e) return e;
459
0
  e = gf_isom_insert_moov(movie);
460
0
  if (e) return e;
461
462
0
  if (movie->moov->mvhd->timeScale == timeScale) return GF_OK;
463
464
  /*rewrite all durations and edit lists*/
465
0
  movie->moov->mvhd->duration *= timeScale;
466
0
  movie->moov->mvhd->duration /= movie->moov->mvhd->timeScale;
467
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
468
0
  if (movie->moov->mvex && movie->moov->mvex->mehd) {
469
0
    movie->moov->mvex->mehd->fragment_duration *= timeScale;
470
0
    movie->moov->mvex->mehd->fragment_duration /= movie->moov->mvhd->timeScale;
471
0
  }
472
0
#endif
473
474
0
  i=0;
475
0
  while ((trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) {
476
0
    trak->Header->duration *= timeScale;
477
0
    trak->Header->duration /= movie->moov->mvhd->timeScale;
478
479
0
    if (trak->editBox && trak->editBox->editList) {
480
0
      u32 j, count = gf_list_count(trak->editBox->editList->entryList);
481
0
      for (j=0; j<count; j++) {
482
0
        GF_EdtsEntry *ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, j);
483
0
        ent->segmentDuration *= timeScale;
484
0
        ent->segmentDuration /= movie->moov->mvhd->timeScale;
485
0
      }
486
0
    }
487
0
  }
488
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
489
0
  if (movie->moov->mvex && movie->moov->mvex->mehd) {
490
0
    movie->moov->mvex->mehd->fragment_duration *= timeScale;
491
0
    movie->moov->mvex->mehd->fragment_duration /= movie->moov->mvhd->timeScale;
492
0
  }
493
0
#endif
494
0
  movie->moov->mvhd->timeScale = timeScale;
495
0
  movie->interleavingTime = timeScale;
496
0
  return GF_OK;
497
0
}
498
499
GF_EXPORT
500
GF_Err gf_isom_set_vexu(GF_ISOFile *movie, u32 hero_eye)
501
0
{
502
0
  GF_TrackBox *trak;
503
0
  GF_SampleDescriptionBox* stsd;
504
0
  GF_Box *box, *hvc1, *dvh1, *vexu;
505
0
  GF_StereoViewBox *eyes;
506
0
  GF_HeroStereoEyeDescriptionBox *hero;
507
0
  u32 i = 0;
508
0
  GF_Err e;
509
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
510
0
  if (e) return e;
511
0
  e = gf_isom_insert_moov(movie);
512
0
  if (e) return e;
513
514
0
  if (hero_eye >= 3) {
515
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Failed to insert VEXU box: hero_eye_indicator >= 3 is reserved.\n"));
516
0
    return GF_BAD_PARAM;
517
0
  }
518
519
0
  while ((trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) {
520
0
    if (!gf_isom_is_video_handler_type(trak->Media->handler->handlerType)) {
521
0
      continue;
522
0
    }
523
524
0
    stsd = trak->Media->information->sampleTable->SampleDescription;
525
0
    hvc1 = gf_isom_box_find_child(stsd->child_boxes, GF_ISOM_BOX_TYPE_HVC1);
526
0
    dvh1 = gf_isom_box_find_child(stsd->child_boxes, GF_ISOM_BOX_TYPE_DVH1);
527
0
    box = hvc1 ? hvc1 : dvh1;
528
529
0
    if(!box) {
530
0
      GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("No additional VEXU box is needed for Track %u: lack of dvh1/hvc1 boxes.\n", i));
531
0
      continue;
532
0
    }
533
0
    if (!gf_isom_box_find_child(box->child_boxes, GF_ISOM_BOX_TYPE_LHVC)) {
534
0
      GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("No additional VEXU box is needed for Track %u: the video is monoscopic.\n", i));
535
0
      continue;
536
0
    }
537
0
    if (gf_isom_box_find_child(box->child_boxes, GF_ISOM_BOX_TYPE_VEXU)) {
538
0
      GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Track %u already has VEXU box.\n", i));
539
0
      continue;
540
0
    }
541
542
    // ISO Base Media File Format and Apple HEVC Stereo Video Version 0.9
543
0
    vexu = gf_isom_box_new_parent(&box->child_boxes, GF_ISOM_BOX_TYPE_VEXU);
544
0
    if (!vexu) return GF_OUT_OF_MEM;
545
546
0
    eyes = (GF_StereoViewBox*)gf_isom_box_new_parent(&vexu->child_boxes, GF_ISOM_BOX_TYPE_EYES);
547
0
    if (!eyes) return GF_OUT_OF_MEM;
548
0
    eyes->stri.has_left_eye_view = 1;
549
0
    eyes->stri.has_right_eye_view = 1;
550
551
0
    hero = (GF_HeroStereoEyeDescriptionBox*)gf_isom_box_new_parent(&eyes->child_boxes, GF_ISOM_BOX_TYPE_HERO);
552
0
    if (!hero) return GF_OUT_OF_MEM;
553
0
    hero->hero_eye_indicator = hero_eye;
554
555
0
    GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("VEXU box is added to Track %u\n", i));
556
0
  }
557
0
  return GF_OK;
558
0
}
559
560
561
GF_EXPORT
562
GF_Err gf_isom_set_pl_indication(GF_ISOFile *movie, GF_ISOProfileLevelType PL_Code, u8 ProfileLevel)
563
0
{
564
0
  GF_IsomInitialObjectDescriptor *iod;
565
0
  GF_Err e;
566
567
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
568
0
  if (e) return e;
569
570
0
  e = gf_isom_set_root_iod(movie);
571
0
  if (e) return e;
572
573
0
  iod = (GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor;
574
575
0
  switch (PL_Code) {
576
0
  case GF_ISOM_PL_AUDIO:
577
0
    iod->audio_profileAndLevel = ProfileLevel;
578
0
    break;
579
0
  case GF_ISOM_PL_GRAPHICS:
580
0
    iod->graphics_profileAndLevel = ProfileLevel;
581
0
    break;
582
0
  case GF_ISOM_PL_OD:
583
0
    iod->OD_profileAndLevel = ProfileLevel;
584
0
    break;
585
0
  case GF_ISOM_PL_SCENE:
586
0
    iod->scene_profileAndLevel = ProfileLevel;
587
0
    break;
588
0
  case GF_ISOM_PL_VISUAL:
589
0
    iod->visual_profileAndLevel = ProfileLevel;
590
0
    break;
591
0
  case GF_ISOM_PL_INLINE:
592
0
    iod->inlineProfileFlag = ProfileLevel ? 1 : 0;
593
0
    break;
594
0
  default:
595
0
    break;
596
0
  }
597
0
  return GF_OK;
598
0
}
599
600
GF_EXPORT
601
GF_Err gf_isom_set_root_od_id(GF_ISOFile *movie, u32 OD_ID)
602
0
{
603
0
  GF_Err e;
604
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
605
0
  if (e) return e;
606
607
0
  e = gf_isom_insert_moov(movie);
608
0
  if (e) return e;
609
0
  if (!movie->moov->iods) {
610
0
    e = AddMovieIOD(movie->moov, 0);
611
0
    if (e) return e;
612
0
  }
613
614
0
  switch (movie->moov->iods->descriptor->tag) {
615
0
  case GF_ODF_ISOM_OD_TAG:
616
0
    ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID;
617
0
    break;
618
0
  case GF_ODF_ISOM_IOD_TAG:
619
0
    ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID;
620
0
    break;
621
0
  default:
622
0
    return GF_ISOM_INVALID_FILE;
623
0
  }
624
0
  return GF_OK;
625
0
}
626
627
GF_EXPORT
628
GF_Err gf_isom_set_root_od_url(GF_ISOFile *movie, const char *url_string)
629
0
{
630
0
  GF_Err e;
631
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
632
0
  if (e) return e;
633
0
  e = gf_isom_insert_moov(movie);
634
0
  if (e) return e;
635
636
0
  if (!movie->moov->iods) {
637
0
    e = AddMovieIOD(movie->moov, 0);
638
0
    if (e) return e;
639
0
  }
640
641
0
  switch (movie->moov->iods->descriptor->tag) {
642
0
  case GF_ODF_ISOM_OD_TAG:
643
0
    if (((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString) gf_free(((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString);
644
0
    ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? gf_strdup(url_string) : NULL;
645
0
    break;
646
0
  case GF_ODF_ISOM_IOD_TAG:
647
0
    if (((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString) gf_free(((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString);
648
0
    ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? gf_strdup(url_string) : NULL;
649
0
    break;
650
0
  default:
651
0
    return GF_ISOM_INVALID_FILE;
652
0
  }
653
0
  return GF_OK;
654
0
}
655
656
GF_EXPORT
657
GF_ISOTrackID gf_isom_get_last_created_track_id(GF_ISOFile *movie)
658
0
{
659
0
  return movie ? movie->last_created_track_id : 0;
660
0
}
661
662
663
GF_EXPORT
664
GF_Err gf_isom_load_extra_boxes(GF_ISOFile *movie, u8 *moov_boxes, u32 moov_boxes_size, Bool udta_only)
665
0
{
666
0
  GF_BitStream *bs;
667
668
0
  GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
669
0
  if (e) return e;
670
0
  e = gf_isom_insert_moov(movie);
671
0
  if (e) return e;
672
673
0
  bs = gf_bs_new(moov_boxes, moov_boxes_size, GF_BITSTREAM_READ);
674
675
  //we may have terminators in some QT files (4 bytes set to 0 ...)
676
0
  while (gf_bs_available(bs) >= 8) {
677
0
    GF_Box *a_box;
678
0
    e = gf_isom_box_parse_ex((GF_Box**)&a_box, bs, GF_ISOM_BOX_TYPE_MOOV, GF_FALSE, 0);
679
0
    if (e || !a_box) goto exit;
680
681
0
    if (a_box->type == GF_ISOM_BOX_TYPE_UDTA) {
682
0
      if (movie->moov->udta) gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box*)movie->moov->udta);
683
0
      movie->moov->udta = (GF_UserDataBox*) a_box;
684
685
0
      if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new();
686
0
      gf_list_add(movie->moov->child_boxes, a_box);
687
688
0
    } else if (!udta_only && (a_box->type!=GF_ISOM_BOX_TYPE_PSSH) ) {
689
0
      if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new();
690
0
      gf_list_add(movie->moov->child_boxes, a_box);
691
0
    } else {
692
0
      gf_isom_box_del(a_box);
693
0
    }
694
0
  }
695
0
exit:
696
0
  gf_bs_del(bs);
697
0
  return e;
698
0
}
699
700
GF_EXPORT
701
u32 gf_isom_new_track_from_template(GF_ISOFile *movie, GF_ISOTrackID trakID, u32 MediaType, u32 TimeScale, u8 *tk_box, u32 tk_box_size, Bool udta_only)
702
0
{
703
0
  GF_Err e;
704
0
  u64 now;
705
0
  u8 isHint;
706
0
  GF_TrackBox *trak;
707
0
  GF_TrackHeaderBox *tkhd;
708
0
  GF_MediaBox *mdia;
709
0
  GF_UserDataBox *udta = NULL;
710
711
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
712
0
  if (e) {
713
0
    gf_isom_set_last_error(movie, e);
714
0
    return 0;
715
0
  }
716
0
  e = gf_isom_insert_moov(movie);
717
0
  if (e) return e;
718
719
720
0
  isHint = 0;
721
  //we're creating a hint track... it's the same, but mode HAS TO BE EDIT
722
0
  if (MediaType == GF_ISOM_MEDIA_HINT) {
723
//    if (movie->openMode != GF_ISOM_OPEN_EDIT) return 0;
724
0
    isHint = 1;
725
0
  }
726
727
0
  mdia = NULL;
728
0
  tkhd = NULL;
729
0
  trak = NULL;
730
0
  if (trakID) {
731
    //check if we are in ES_ID boundaries
732
0
    if (!isHint && (trakID > 0xFFFF)) {
733
0
      gf_isom_set_last_error(movie, GF_BAD_PARAM);
734
0
      return 0;
735
0
    }
736
    //here we should look for available IDs ...
737
0
    if (!RequestTrack(movie->moov, trakID)) return 0;
738
0
  } else {
739
0
    trakID = movie->moov->mvhd->nextTrackID;
740
0
    if (!trakID) trakID = 1;
741
    /*ESIDs are on 16 bits*/
742
0
    if (! isHint && (trakID > 0xFFFF)) trakID = 1;
743
744
0
    while (1) {
745
0
      if (RequestTrack(movie->moov, trakID)) break;
746
0
      trakID += 1;
747
0
      if (trakID == 0xFFFFFFFF) break;
748
0
    }
749
0
    if (trakID == 0xFFFFFFFF) {
750
0
      gf_isom_set_last_error(movie, GF_BAD_PARAM);
751
0
      return 0;
752
0
    }
753
0
    if (! isHint && (trakID > 0xFFFF)) {
754
0
      gf_isom_set_last_error(movie, GF_BAD_PARAM);
755
0
      return 0;
756
0
    }
757
0
  }
758
759
0
  if (tk_box) {
760
0
    GF_BitStream *bs = gf_bs_new(tk_box, tk_box_size, GF_BITSTREAM_READ);
761
0
    gf_bs_set_cookie(bs, GF_ISOM_BS_COOKIE_NO_LOGS|GF_ISOM_BS_COOKIE_CLONE_TRACK);
762
763
0
    e = gf_isom_box_parse_ex((GF_Box**)&trak, bs, GF_ISOM_BOX_TYPE_MOOV, GF_FALSE, 0);
764
0
    gf_bs_del(bs);
765
0
    if (e) trak = NULL;
766
0
    else if (udta_only) {
767
0
      udta = trak->udta;
768
0
      trak->udta = NULL;
769
0
      gf_list_del_item(trak->child_boxes, udta);
770
0
      gf_isom_box_del((GF_Box*)trak);
771
0
      trak = NULL;
772
0
    } else {
773
0
      Bool tpl_ok = GF_TRUE;
774
0
      if (!trak->Header || !trak->Media || !trak->Media->handler || !trak->Media->mediaHeader || !trak->Media->information) tpl_ok = GF_FALSE;
775
776
0
      else {
777
0
        if (!MediaType) MediaType = trak->Media->handler->handlerType;
778
0
        e = NewMedia(&trak->Media, MediaType, TimeScale);
779
0
        if (e) tpl_ok = GF_FALSE;
780
0
      }
781
0
      if (!tpl_ok) {
782
0
        udta = trak->udta;
783
0
        trak->udta = NULL;
784
0
        gf_isom_box_del((GF_Box*)trak);
785
0
      }
786
0
    }
787
0
  }
788
0
  now = gf_isom_get_mp4time();
789
0
  if (!trak) {
790
    //OK, now create a track...
791
0
    trak = (GF_TrackBox *) gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_TRAK);
792
0
    if (!trak) {
793
0
      gf_isom_set_last_error(movie, GF_OUT_OF_MEM);
794
0
      return 0;
795
0
    }
796
0
    tkhd = (GF_TrackHeaderBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TKHD);
797
0
    if (!tkhd) {
798
0
      gf_isom_set_last_error(movie, GF_OUT_OF_MEM);
799
0
      return 0;
800
0
    }
801
802
    //OK, set up the media trak
803
0
    e = NewMedia(&mdia, MediaType, TimeScale);
804
0
    if (e) {
805
0
      gf_isom_box_del((GF_Box *)mdia);
806
0
      return 0;
807
0
    }
808
0
    gf_assert(trak->child_boxes);
809
0
    gf_list_add(trak->child_boxes, mdia);
810
811
    //OK, add this media to our track
812
0
    mdia->mediaTrack = trak;
813
814
0
    e = trak_on_child_box((GF_Box*)trak, (GF_Box *) tkhd, GF_FALSE);
815
0
    if (e) goto err_exit;
816
0
    e = trak_on_child_box((GF_Box*)trak, (GF_Box *) mdia, GF_FALSE);
817
0
    if (e) goto err_exit;
818
0
    tkhd->trackID = trakID;
819
820
0
    if (gf_sys_is_test_mode() ) {
821
0
      tkhd->creationTime = 0;
822
0
      mdia->mediaHeader->creationTime = 0;
823
0
    } else {
824
0
      tkhd->creationTime = now;
825
0
      mdia->mediaHeader->creationTime = now;
826
0
    }
827
828
0
  } else {
829
0
    tkhd = trak->Header;
830
0
    tkhd->trackID = trakID;
831
0
    mdia = trak->Media;
832
0
    mdia->mediaTrack = trak;
833
0
    mdia->mediaHeader->timeScale = TimeScale;
834
0
    if (mdia->handler->handlerType != MediaType) {
835
0
      mdia->handler->handlerType = MediaType;
836
0
      tkhd->width = 0;
837
0
      tkhd->height = 0;
838
0
      tkhd->volume = 0;
839
0
    } else {
840
0
      MediaType = 0;
841
0
    }
842
0
    trak->Header->duration = 0;
843
0
    mdia->mediaHeader->duration = 0;
844
845
0
    if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new();
846
0
    gf_list_add(movie->moov->child_boxes, trak);
847
0
  }
848
0
  if (MediaType) {
849
    //some default properties for Audio, Visual or private tracks
850
0
    switch (MediaType) {
851
0
    case GF_ISOM_MEDIA_VISUAL:
852
0
    case GF_ISOM_MEDIA_AUXV:
853
0
    case GF_ISOM_MEDIA_PICT:
854
0
    case GF_ISOM_MEDIA_SCENE:
855
0
    case GF_ISOM_MEDIA_TEXT:
856
0
    case GF_ISOM_MEDIA_SUBT:
857
      /*320-240 pix in 16.16*/
858
0
      tkhd->width = 0x01400000;
859
0
      tkhd->height = 0x00F00000;
860
0
      break;
861
0
    case GF_ISOM_MEDIA_AUDIO:
862
0
      tkhd->volume = 0x0100;
863
0
      break;
864
0
    }
865
0
  }
866
0
  movie->last_created_track_id = tkhd->trackID;
867
868
0
  if (!movie->keep_utc && !gf_sys_is_test_mode() ) {
869
0
    tkhd->modificationTime = now;
870
0
    mdia->mediaHeader->modificationTime = now;
871
0
  }
872
873
  //OK, add our trak
874
0
  e = moov_on_child_box((GF_Box*)movie->moov, (GF_Box *)trak, GF_FALSE);
875
0
  if (e) goto err_exit;
876
  //set the next track ID available
877
0
  if (trakID >= movie->moov->mvhd->nextTrackID)
878
0
    movie->moov->mvhd->nextTrackID = trakID+1;
879
880
0
  if (udta) {
881
0
    trak->udta = udta;
882
0
    gf_list_add(trak->child_boxes, udta);
883
0
  }
884
885
  //and return our track number
886
0
  return gf_isom_get_track_by_id(movie, trakID);
887
888
0
err_exit:
889
  //tkhd is registered with track and will be destroyed there
890
0
  if (trak) gf_isom_box_del((GF_Box *)trak);
891
0
  if (mdia) gf_isom_box_del((GF_Box *)mdia);
892
0
  return 0;
893
0
}
894
895
GF_EXPORT
896
GF_Err gf_isom_set_track_stsd_templates(GF_ISOFile *movie, u32 trackNumber, u8 *stsd_data, u32 stsd_data_size)
897
0
{
898
0
  GF_TrackBox *trak;
899
0
  GF_Err e;
900
0
  GF_SampleDescriptionBox *stsd=NULL;
901
0
  GF_List *tmp;
902
903
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
904
0
  if (e) return e;
905
906
0
  trak = gf_isom_get_track_box(movie, trackNumber);
907
0
  if (!trak || !trak->Media) return GF_BAD_PARAM;
908
0
  if (gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes))
909
0
    return GF_BAD_PARAM;
910
911
0
  GF_BitStream *bs = gf_bs_new(stsd_data, stsd_data_size, GF_BITSTREAM_READ);
912
0
  e = gf_isom_box_parse_ex((GF_Box **) &stsd, bs, GF_ISOM_BOX_TYPE_STBL, GF_FALSE, 0);
913
0
  gf_bs_del(bs);
914
0
  if (!e && (stsd->type==GF_ISOM_BOX_TYPE_STSD)) {
915
0
    tmp = trak->Media->information->sampleTable->SampleDescription->child_boxes;
916
0
    trak->Media->information->sampleTable->SampleDescription->child_boxes = stsd->child_boxes;
917
0
    stsd->child_boxes = tmp;
918
0
  }
919
0
  if (stsd) gf_isom_box_del((GF_Box*)stsd);
920
0
  return e;
921
0
}
922
923
GF_EXPORT
924
u32 gf_isom_new_track(GF_ISOFile *movie, GF_ISOTrackID trakID, u32 MediaType, u32 TimeScale)
925
0
{
926
0
  return gf_isom_new_track_from_template(movie, trakID, MediaType, TimeScale, NULL, 0, GF_FALSE);
927
0
}
928
929
GF_EXPORT
930
u32 gf_isom_new_external_track(GF_ISOFile *movie, GF_ISOTrackID trakID, GF_ISOTrackID refTrakID, u32 MediaType, u32 TimeScale, const char *uri)
931
0
{
932
0
  GF_TrackBox *trak;
933
0
  if (!uri) {
934
0
    gf_isom_set_last_error(movie, GF_BAD_PARAM);
935
0
    return 0;
936
0
  }
937
0
  u32 track_num = gf_isom_new_track_from_template(movie, trakID, MediaType, TimeScale, NULL, 0, GF_FALSE);
938
0
  if (!track_num) return GF_FALSE;
939
0
  trak = gf_isom_get_track_box(movie, track_num);
940
0
  if (!trak || !trak->Media) return GF_BAD_PARAM;
941
0
  gf_isom_box_del_parent(&trak->child_boxes, (GF_Box*)trak->Media);
942
0
  trak->Media = NULL;
943
944
0
  trak->type = GF_ISOM_BOX_TYPE_EXTK;
945
0
  trak->extl = (GF_ExternalTrackLocationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_EXTL);
946
0
  gf_list_add(trak->child_boxes, trak->extl);
947
0
  trak->extl->referenced_track_ID = refTrakID;
948
0
  trak->extl->referenced_handler_type = MediaType;
949
0
  trak->extl->media_timescale = TimeScale;
950
0
  trak->extl->location = gf_strdup(uri);
951
0
  trak->Header->flags = GF_ISOM_TK_IN_MOVIE | GF_ISOM_TK_ENABLED;
952
0
  trak->Header->duration = 0xFFFFFFFF;
953
0
  return track_num;
954
0
}
955
956
GF_EXPORT
957
GF_Err gf_isom_force_track_duration(GF_ISOFile *movie, u32 trackNumber, u64 dur)
958
0
{
959
0
  GF_TrackBox *trak;
960
0
  GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
961
0
  if (e) return e;
962
0
  trak = gf_isom_get_track_box(movie, trackNumber);
963
0
  if (!trak || !trak->Header) return GF_BAD_PARAM;
964
0
  trak->Header->duration = dur;
965
0
  return GF_OK;
966
0
}
967
968
969
GF_EXPORT
970
GF_Err gf_isom_remove_stream_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex)
971
0
{
972
0
  GF_TrackBox *trak;
973
0
  GF_Err e;
974
0
  GF_SampleEntryBox *entry;
975
976
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
977
0
  if (e) return e;
978
979
0
  trak = gf_isom_get_track_box(movie, trackNumber);
980
0
  if (!trak || !trak->Media) return GF_BAD_PARAM;
981
982
0
  if (!movie->keep_utc)
983
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
984
985
0
  entry = (GF_SampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1);
986
0
  if (!entry) return GF_BAD_PARAM;
987
0
  gf_list_rem(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1);
988
0
  gf_isom_box_del((GF_Box *)entry);
989
0
  return GF_OK;
990
0
}
991
992
//Create a new StreamDescription in the file. The URL and URN are used to describe external media
993
GF_EXPORT
994
GF_Err gf_isom_new_mpeg4_description(GF_ISOFile *movie,
995
                                     u32 trackNumber,
996
                                     const GF_ESD *esd,
997
                                     const char *URLname,
998
                                     const char *URNname,
999
                                     u32 *outDescriptionIndex)
1000
0
{
1001
0
  GF_TrackBox *trak;
1002
0
  GF_Err e;
1003
0
  u32 dataRefIndex;
1004
0
  GF_ESD *new_esd;
1005
1006
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
1007
0
  if (e) return e;
1008
1009
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1010
0
  if (!trak || !trak->Media ||
1011
0
          !esd || !esd->decoderConfig ||
1012
0
          !esd->slConfig) return GF_BAD_PARAM;
1013
1014
  //get or create the data ref
1015
0
  e = Media_FindDataRef(trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
1016
0
  if (e) return e;
1017
0
  if (!dataRefIndex) {
1018
0
    e = Media_CreateDataRef(movie, trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
1019
0
    if (e) return e;
1020
0
  }
1021
  //duplicate our desc
1022
0
  e = gf_odf_desc_copy((GF_Descriptor *)esd, (GF_Descriptor **)&new_esd);
1023
0
  if (e) return e;
1024
0
  if (!movie->keep_utc)
1025
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1026
0
  e = Track_SetStreamDescriptor(trak, 0, dataRefIndex, new_esd, outDescriptionIndex);
1027
0
  if (e) {
1028
0
    gf_odf_desc_del((GF_Descriptor *)new_esd);
1029
0
    return e;
1030
0
  }
1031
0
  return e;
1032
0
}
1033
1034
GF_Err gf_isom_flush_chunk(GF_TrackBox *trak, Bool is_final)
1035
0
{
1036
0
  GF_Err e;
1037
0
  u64 data_offset;
1038
0
  u32 sample_number;
1039
0
  u8 *chunk_data;
1040
0
  u32 chunk_size, chunk_alloc;
1041
0
  if (!trak->chunk_cache) return GF_OK;
1042
1043
0
  gf_bs_get_content_no_truncate(trak->chunk_cache, &chunk_data, &chunk_size, &chunk_alloc);
1044
1045
0
  data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler);
1046
1047
0
  e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, chunk_data, chunk_size);
1048
0
  if (e) return e;
1049
1050
0
  sample_number = 1 + trak->Media->information->sampleTable->SampleSize->sampleCount;
1051
0
  sample_number -= trak->nb_samples_in_cache;
1052
1053
0
  e = stbl_AddChunkOffset(trak->Media, sample_number, trak->chunk_stsd_idx, data_offset, trak->nb_samples_in_cache);
1054
1055
0
  if (is_final) {
1056
0
    gf_free(chunk_data);
1057
0
    gf_bs_del(trak->chunk_cache);
1058
0
    trak->chunk_cache = NULL;
1059
0
  } else {
1060
0
    gf_bs_reassign_buffer(trak->chunk_cache, chunk_data, chunk_alloc);
1061
0
  }
1062
0
  return e;
1063
0
}
1064
1065
static GF_Err trak_add_sample(GF_ISOFile *movie, GF_TrackBox *trak, const GF_ISOSample *sample, u32 descIndex, u64 data_offset, u32 syncShadowSampleNum)
1066
0
{
1067
0
  Bool skip_data = GF_FALSE;
1068
0
  GF_Err e;
1069
1070
  //faststart mode with interleaving time, cache data until we have a full chunk
1071
0
  if ((movie->storageMode==GF_ISOM_STORE_FASTSTART) && movie->interleavingTime) {
1072
0
    Bool flush_chunk = GF_FALSE;
1073
0
    u64 stime = sample->DTS;
1074
0
    stime *= movie->moov->mvhd->timeScale;
1075
0
    stime /= trak->Media->mediaHeader->timeScale;
1076
1077
0
    if (stime - trak->first_dts_chunk > movie->interleavingTime)
1078
0
      flush_chunk = GF_TRUE;
1079
1080
0
    if (movie->next_flush_chunk_time < stime)
1081
0
      flush_chunk = GF_TRUE;
1082
1083
0
    if (trak->chunk_stsd_idx != descIndex)
1084
0
      flush_chunk = GF_TRUE;
1085
1086
0
    if (trak->Media->information->sampleTable->MaxChunkSize && trak->Media->information->sampleTable->MaxChunkSize < trak->chunk_cache_size + sample->dataLength)
1087
0
      flush_chunk = GF_TRUE;
1088
1089
0
    if (flush_chunk) {
1090
0
      movie->next_flush_chunk_time = stime + movie->interleavingTime;
1091
0
      if (trak->chunk_cache) {
1092
0
        e = gf_isom_flush_chunk(trak, GF_FALSE);
1093
0
        if (e) return e;
1094
0
      }
1095
0
      trak->nb_samples_in_cache = 0;
1096
0
      trak->chunk_cache_size = 0;
1097
0
      trak->first_dts_chunk = stime;
1098
0
    }
1099
0
    if (!trak->chunk_cache)
1100
0
      trak->chunk_cache = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
1101
0
    gf_bs_write_data(trak->chunk_cache, sample->data, sample->dataLength);
1102
0
    trak->nb_samples_in_cache += sample->nb_pack ? sample->nb_pack : 1;
1103
0
    trak->chunk_cache_size += sample->dataLength;
1104
0
    trak->chunk_stsd_idx = descIndex;
1105
1106
0
    skip_data = GF_TRUE;
1107
0
  }
1108
1109
0
  e = Media_AddSample(trak->Media, data_offset, sample, descIndex, syncShadowSampleNum);
1110
0
  if (e) return e;
1111
1112
0
  if (!skip_data && sample->dataLength) {
1113
0
    e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, sample->data, sample->dataLength);
1114
0
    if (e) return e;
1115
0
  }
1116
1117
0
  return GF_OK;
1118
0
}
1119
1120
//Add samples to a track. Use streamDescriptionIndex to specify the desired stream (if several)
1121
GF_EXPORT
1122
GF_Err gf_isom_add_sample(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, const GF_ISOSample *sample)
1123
0
{
1124
0
  GF_Err e;
1125
0
  GF_TrackBox *trak;
1126
0
  GF_SampleEntryBox *entry;
1127
0
  u32 dataRefIndex;
1128
0
  u64 data_offset;
1129
0
  u32 descIndex;
1130
0
  GF_DataEntryURLBox *Dentry;
1131
1132
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
1133
0
  if (e) return e;
1134
1135
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1136
0
  if (!trak) return GF_BAD_PARAM;
1137
1138
0
  e = FlushCaptureMode(movie);
1139
0
  if (e) return e;
1140
1141
0
  e = unpack_track(trak);
1142
0
  if (e) return e;
1143
1144
  //OK, add the sample
1145
  //1- Get the streamDescriptionIndex and dataRefIndex
1146
  //not specified, get the latest used...
1147
0
  descIndex = StreamDescriptionIndex;
1148
0
  if (!StreamDescriptionIndex) {
1149
0
    descIndex = trak->Media->information->sampleTable->currentEntryIndex;
1150
0
  }
1151
0
  e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex);
1152
0
  if (e) return e;
1153
0
  if (!entry || !dataRefIndex) return GF_BAD_PARAM;
1154
  //set the current to this one
1155
0
  trak->Media->information->sampleTable->currentEntryIndex = descIndex;
1156
1157
1158
  //get this dataRef and return false if not self contained
1159
0
  Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1);
1160
0
  if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM;
1161
1162
  //Open our data map. We are adding stuff, so use EDIT
1163
0
  e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1);
1164
0
  if (e) return e;
1165
1166
  //Get the offset...
1167
0
  data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler);
1168
1169
  /*rewrite OD frame*/
1170
0
  if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
1171
0
    GF_ISOSample *od_sample = NULL;
1172
1173
0
    e = Media_ParseODFrame(trak->Media, sample, &od_sample);
1174
0
    if (e) return e;
1175
1176
0
    e = trak_add_sample(movie, trak, od_sample, descIndex, data_offset, 0);
1177
1178
0
    if (od_sample)
1179
0
      gf_isom_sample_del(&od_sample);
1180
0
  } else {
1181
0
    e = trak_add_sample(movie, trak, sample, descIndex, data_offset, 0);
1182
0
  }
1183
0
  if (e) return e;
1184
1185
0
  if (!movie->keep_utc)
1186
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1187
1188
  //update media duration
1189
0
  if ((s64)sample->DTS + sample->CTS_Offset>=0) {
1190
0
    GF_TimeToSampleBox *stts = trak->Media->information->sampleTable->TimeToSample;
1191
0
    u64 dur = sample->DTS + sample->CTS_Offset;
1192
0
    dur += stts->entries[stts->nb_entries-1].sampleDelta;
1193
1194
0
    if (dur > trak->Media->mediaHeader->duration) {
1195
0
      trak->Media->mediaHeader->duration = dur;
1196
0
    }
1197
0
  }
1198
  //do not update track duration yet, this is done on close
1199
0
  return GF_OK;
1200
0
}
1201
1202
GF_EXPORT
1203
GF_Err gf_isom_add_sample_shadow(GF_ISOFile *movie, u32 trackNumber, GF_ISOSample *sample)
1204
0
{
1205
0
  GF_Err e;
1206
0
  GF_TrackBox *trak;
1207
0
  GF_ISOSample *prev;
1208
0
  GF_SampleEntryBox *entry;
1209
0
  u32 dataRefIndex;
1210
0
  u64 data_offset;
1211
0
  u32 descIndex;
1212
0
  u32 sampleNum, prevSampleNum;
1213
0
  GF_DataEntryURLBox *Dentry;
1214
0
  Bool offset_times = GF_FALSE;
1215
1216
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
1217
0
  if (e) return e;
1218
1219
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1220
0
  if (!trak || !sample) return GF_BAD_PARAM;
1221
1222
0
  e = FlushCaptureMode(movie);
1223
0
  if (e) return e;
1224
1225
0
  e = unpack_track(trak);
1226
0
  if (e) return e;
1227
1228
0
  e = stbl_findEntryForTime(trak->Media->information->sampleTable, sample->DTS, 0, &sampleNum, &prevSampleNum);
1229
0
  if (e) return e;
1230
  /*we need the EXACT match*/
1231
0
  if (!sampleNum) return GF_BAD_PARAM;
1232
1233
0
  prev = gf_isom_get_sample_info(movie, trackNumber, sampleNum, &descIndex, NULL);
1234
0
  if (!prev) return gf_isom_last_error(movie);
1235
  /*for conformance*/
1236
0
  if (sample->DTS==prev->DTS) offset_times = GF_TRUE;
1237
0
  gf_isom_sample_del(&prev);
1238
1239
0
  e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex);
1240
0
  if (e) return e;
1241
0
  if (!entry || !dataRefIndex) return GF_BAD_PARAM;
1242
0
  trak->Media->information->sampleTable->currentEntryIndex = descIndex;
1243
1244
  //get this dataRef and return false if not self contained
1245
0
  Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1);
1246
0
  if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM;
1247
1248
  //Open our data map. We are adding stuff, so use EDIT
1249
0
  e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1);
1250
0
  if (e) return e;
1251
1252
0
  data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler);
1253
0
  if (offset_times) sample->DTS += 1;
1254
1255
  /*REWRITE ANY OD STUFF*/
1256
0
  if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
1257
0
    GF_ISOSample *od_sample = NULL;
1258
0
    e = Media_ParseODFrame(trak->Media, sample, &od_sample);
1259
0
    if (e) return e;
1260
1261
0
    e = trak_add_sample(movie, trak, od_sample, descIndex, data_offset, sampleNum);
1262
0
    if (od_sample)
1263
0
      gf_isom_sample_del(&od_sample);
1264
0
  } else {
1265
0
    e = trak_add_sample(movie, trak, sample, descIndex, data_offset, sampleNum);
1266
0
  }
1267
0
  if (e) return e;
1268
0
  if (offset_times) sample->DTS -= 1;
1269
1270
  //OK, update duration
1271
0
  e = Media_SetDuration(trak);
1272
0
  if (e) return e;
1273
0
  if (!movie->keep_utc)
1274
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1275
0
  return SetTrackDuration(trak);
1276
0
}
1277
1278
GF_EXPORT
1279
GF_Err gf_isom_append_sample_data(GF_ISOFile *movie, u32 trackNumber, u8 *data, u32 data_size)
1280
0
{
1281
0
  GF_Err e;
1282
0
  GF_TrackBox *trak;
1283
0
  GF_SampleEntryBox *entry;
1284
0
  u32 dataRefIndex;
1285
0
  u32 descIndex;
1286
0
  GF_DataEntryURLBox *Dentry;
1287
1288
0
  if (!data_size) return GF_OK;
1289
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
1290
0
  if (e) return e;
1291
1292
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1293
0
  if (!trak) return GF_BAD_PARAM;
1294
1295
0
  if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) return GF_BAD_PARAM;
1296
1297
  //OK, add the sample
1298
0
  descIndex = trak->Media->information->sampleTable->currentEntryIndex;
1299
1300
0
  e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex);
1301
0
  if (e) return e;
1302
0
  if (!entry || !dataRefIndex) return GF_BAD_PARAM;
1303
1304
  //get this dataRef and return false if not self contained
1305
0
  Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1);
1306
0
  if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM;
1307
1308
  //Open our data map. We are adding stuff, so use EDIT
1309
0
  e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1);
1310
0
  if (e) return e;
1311
1312
  //add the media data
1313
0
  if (trak->chunk_cache) {
1314
0
    gf_bs_write_data(trak->chunk_cache, data, data_size);
1315
0
    trak->chunk_cache_size += data_size;
1316
0
  } else {
1317
0
    e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, data, data_size);
1318
0
    if (e) return e;
1319
0
  }
1320
  //update data size
1321
0
  return stbl_SampleSizeAppend(trak->Media->information->sampleTable->SampleSize, data_size);
1322
0
}
1323
1324
1325
//Add sample reference to a track. The SampleOffset is the offset of the data in the referenced file
1326
//you must have created a StreamDescription with URL or URN specifying your referenced file
1327
//the data offset specifies the beginning of the chunk
1328
//Use streamDescriptionIndex to specify the desired stream (if several)
1329
GF_EXPORT
1330
GF_Err gf_isom_add_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_ISOSample *sample, u64 dataOffset)
1331
0
{
1332
0
  GF_TrackBox *trak;
1333
0
  GF_SampleEntryBox *entry;
1334
0
  u32 dataRefIndex;
1335
0
  u32 descIndex;
1336
0
  GF_DataEntryURLBox *Dentry;
1337
0
  GF_Err e;
1338
1339
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
1340
0
  if (e) return e;
1341
1342
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1343
0
  if (!trak) return GF_BAD_PARAM;
1344
1345
0
  e = unpack_track(trak);
1346
0
  if (e) return e;
1347
1348
  //OD is not allowed as a data ref
1349
0
  if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
1350
0
    return GF_BAD_PARAM;
1351
0
  }
1352
  //OK, add the sample
1353
  //1- Get the streamDescriptionIndex and dataRefIndex
1354
  //not specified, get the latest used...
1355
0
  descIndex = StreamDescriptionIndex;
1356
0
  if (!StreamDescriptionIndex) {
1357
0
    descIndex = trak->Media->information->sampleTable->currentEntryIndex;
1358
0
  }
1359
0
  e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex);
1360
0
  if (e) return e;
1361
0
  if (!entry || !dataRefIndex) return GF_BAD_PARAM;
1362
  //set the current to this one
1363
0
  trak->Media->information->sampleTable->currentEntryIndex = descIndex;
1364
1365
1366
  //get this dataRef and return false if self contained
1367
0
  Dentry =(GF_DataEntryURLBox*) gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1);
1368
0
  if (Dentry->flags == 1) return GF_BAD_PARAM;
1369
1370
  //add the meta data
1371
0
  e = Media_AddSample(trak->Media, dataOffset, sample, descIndex, 0);
1372
0
  if (e) return e;
1373
1374
0
  if (!movie->keep_utc)
1375
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1376
  //OK, update duration
1377
0
  e = Media_SetDuration(trak);
1378
0
  if (e) return e;
1379
0
  return SetTrackDuration(trak);
1380
1381
0
}
1382
1383
//set the duration of the last media sample. If not set, the duration of the last sample is the
1384
//duration of the previous one if any, or 1000 (default value).
1385
static GF_Err gf_isom_set_last_sample_duration_internal(GF_ISOFile *movie, u32 trackNumber, u64 dur_num, u32 dur_den, u32 mode)
1386
0
{
1387
0
  GF_TrackBox *trak;
1388
0
  GF_SttsEntry *ent;
1389
0
  GF_TimeToSampleBox *stts;
1390
0
  u64 mdur;
1391
0
  u32 duration;
1392
0
  GF_Err e;
1393
0
  Bool is_patch = GF_FALSE;
1394
1395
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
1396
0
  if (e) return e;
1397
1398
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1399
0
  if (!trak || !trak->Media || !trak->Media->mediaHeader || !trak->Media->information || !trak->Media->information->sampleTable)
1400
0
    return GF_BAD_PARAM;
1401
1402
0
  if (mode==0) {
1403
0
    duration = (u32) dur_num;
1404
0
  } else if (mode==1) {
1405
0
    duration = (u32) dur_num;
1406
0
    if (dur_den) {
1407
0
      duration *= trak->Media->mediaHeader->timeScale;
1408
0
      duration /= dur_den;
1409
0
    }
1410
0
  } else {
1411
0
    is_patch = GF_TRUE;
1412
0
  }
1413
0
  mdur = trak->Media->mediaHeader->duration;
1414
0
  stts = trak->Media->information->sampleTable->TimeToSample;
1415
0
  if (!stts->nb_entries) return GF_BAD_PARAM;
1416
1417
0
  if (is_patch) {
1418
0
    u32 i, avg_dur, nb_samp=0;
1419
0
    u64 cum_dur=0;
1420
0
    for (i=0; i<stts->nb_entries; i++) {
1421
0
      ent = (GF_SttsEntry*) &stts->entries[i];
1422
0
      cum_dur += ent->sampleCount*ent->sampleDelta;
1423
0
      nb_samp += ent->sampleCount;
1424
0
    }
1425
0
    if (cum_dur <= dur_num || !nb_samp) return GF_OK;
1426
0
    avg_dur = (u32) (dur_num / nb_samp);
1427
1428
0
    stts->entries[0].sampleDelta = avg_dur;
1429
0
    stts->entries[0].sampleCount = nb_samp;
1430
0
    stts->nb_entries = 1;
1431
0
    stts->w_LastDTS = dur_num - avg_dur;
1432
0
    return GF_OK;
1433
0
  }
1434
  //get the last entry
1435
0
  ent = (GF_SttsEntry*) &stts->entries[stts->nb_entries-1];
1436
0
  if ((mode==1) && !duration && !dur_den) {
1437
    //same as previous, nothing to adjust
1438
0
    if (ent->sampleCount>1) return GF_OK;
1439
0
    if (stts->nb_entries==1) return GF_OK;
1440
0
    duration = stts->entries[stts->nb_entries-2].sampleDelta;
1441
0
  }
1442
1443
0
  mdur -= ent->sampleDelta;
1444
0
  mdur += duration;
1445
1446
  //we only have one sample
1447
0
  if (ent->sampleCount == 1) {
1448
0
    ent->sampleDelta = (u32) duration;
1449
0
    if (mode && (stts->nb_entries>1) && (stts->entries[stts->nb_entries-2].sampleDelta==duration)) {
1450
0
      stts->entries[stts->nb_entries-2].sampleCount++;
1451
0
      stts->nb_entries--;
1452
      //and update the write cache
1453
0
      stts->w_currentSampleNum = trak->Media->information->sampleTable->SampleSize->sampleCount;
1454
0
    }
1455
0
  } else {
1456
0
    if (ent->sampleDelta == duration) return GF_OK;
1457
0
    ent->sampleCount -= 1;
1458
1459
0
    if (stts->nb_entries==stts->alloc_size) {
1460
0
      stts->alloc_size++;
1461
0
      stts->entries = (GF_SttsEntry*)gf_realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size);
1462
0
      if (!stts->entries) return GF_OUT_OF_MEM;
1463
0
    }
1464
0
    stts->entries[stts->nb_entries].sampleCount = 1;
1465
0
    stts->entries[stts->nb_entries].sampleDelta = (u32) duration;
1466
0
    stts->nb_entries++;
1467
    //and update the write cache
1468
0
    stts->w_currentSampleNum = trak->Media->information->sampleTable->SampleSize->sampleCount;
1469
0
  }
1470
0
  if (!movie->keep_utc)
1471
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1472
1473
  //update media duration if duration was set
1474
0
  if (trak->Media->mediaHeader->duration)
1475
0
    trak->Media->mediaHeader->duration = mdur;
1476
  //do not update track duration yet, this is done on close
1477
0
  return GF_OK;
1478
0
}
1479
1480
GF_EXPORT
1481
GF_Err gf_isom_set_last_sample_duration(GF_ISOFile *movie, u32 trackNumber, u32 duration)
1482
0
{
1483
0
  return gf_isom_set_last_sample_duration_internal(movie, trackNumber, duration, 0, 0);
1484
0
}
1485
1486
GF_EXPORT
1487
GF_Err gf_isom_patch_last_sample_duration(GF_ISOFile *movie, u32 trackNumber, u64 next_dts)
1488
0
{
1489
0
  return gf_isom_set_last_sample_duration_internal(movie, trackNumber, next_dts, 0, 2);
1490
0
}
1491
1492
GF_EXPORT
1493
GF_Err gf_isom_set_last_sample_duration_ex(GF_ISOFile *movie, u32 trackNumber, u32 dur_num, u32 dur_den)
1494
0
{
1495
0
  return gf_isom_set_last_sample_duration_internal(movie, trackNumber, dur_num, dur_den, 1);
1496
0
}
1497
1498
//update a sample data in the media. Note that the sample MUST exists
1499
GF_EXPORT
1500
GF_Err gf_isom_update_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, Bool data_only)
1501
0
{
1502
0
  GF_Err e;
1503
0
  GF_TrackBox *trak;
1504
1505
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_EDIT);
1506
0
  if (e) return e;
1507
1508
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1509
0
  if (!trak) return GF_BAD_PARAM;
1510
1511
0
  e = unpack_track(trak);
1512
0
  if (e) return e;
1513
1514
  //block for hint tracks
1515
0
  if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM;
1516
1517
  //REWRITE ANY OD STUFF
1518
0
  if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
1519
0
    GF_ISOSample *od_sample = NULL;
1520
0
    e = Media_ParseODFrame(trak->Media, sample, &od_sample);
1521
0
    if (!e) e = Media_UpdateSample(trak->Media, sampleNumber, od_sample, data_only);
1522
0
    if (od_sample) gf_isom_sample_del(&od_sample);
1523
0
    gf_isom_disable_inplace_rewrite(movie);
1524
0
  } else {
1525
0
    e = Media_UpdateSample(trak->Media, sampleNumber, sample, data_only);
1526
0
    if (data_only || sample->data)
1527
0
      gf_isom_disable_inplace_rewrite(movie);
1528
0
  }
1529
0
  if (e) return e;
1530
0
  if (!movie->keep_utc)
1531
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1532
1533
0
  return GF_OK;
1534
0
}
1535
1536
//update a sample data in the media. Note that the sample MUST exists,
1537
//that sample->data MUST be NULL and sample->dataLength must be NON NULL;
1538
GF_EXPORT
1539
GF_Err gf_isom_update_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset)
1540
0
{
1541
0
  GF_Err e;
1542
0
  GF_TrackBox *trak;
1543
1544
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_EDIT);
1545
0
  if (e) return e;
1546
1547
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1548
0
  if (!trak) return GF_BAD_PARAM;
1549
1550
  //block for hint tracks
1551
0
  if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM;
1552
1553
0
  if (!sampleNumber || !sample) return GF_BAD_PARAM;
1554
1555
0
  e = unpack_track(trak);
1556
0
  if (e) return e;
1557
1558
  //OD is not allowed as a data ref
1559
0
  if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) {
1560
0
    return GF_BAD_PARAM;
1561
0
  }
1562
  //OK, update it
1563
0
  e = Media_UpdateSampleReference(trak->Media, sampleNumber, sample, data_offset);
1564
0
  if (e) return e;
1565
1566
0
  if (!movie->keep_utc)
1567
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1568
0
  return GF_OK;
1569
0
}
1570
1571
//for gf_isom_remove_sample and gf_isom_remove_track: check all items sharing their data with a sample being removed
1572
//and remove sharing flag
1573
// sample_number can be 0 for complete track removal
1574
static void gf_isom_meta_track_remove(GF_ISOFile *movie, GF_TrackBox *trak, u32 sample_number)
1575
0
{
1576
0
  u32 i, count;
1577
0
  if (!movie || !movie->meta || !movie->meta->use_item_sample_sharing)
1578
0
    return;
1579
1580
0
  count = gf_list_count(movie->meta->item_locations->location_entries);
1581
0
  for (i=0; i<count; i++) {
1582
0
    u32 j;
1583
0
    GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(movie->meta->item_locations->location_entries, i);
1584
    /*get item info*/
1585
0
    GF_ItemInfoEntryBox *iinf = NULL;
1586
0
    j=0;
1587
0
    while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(movie->meta->item_infos->item_infos, &j))) {
1588
0
      if (iinf->item_ID==iloc->item_ID) break;
1589
0
    }
1590
0
    if (!iinf || !iinf->tk_id) continue;
1591
0
    if (iinf->tk_id != trak->Header->trackID) continue;
1592
1593
0
    if (sample_number && (iinf->sample_num != sample_number)) continue;
1594
0
    iinf->tk_id = 0;
1595
0
    iinf->sample_num = 0;
1596
0
  }
1597
0
}
1598
1599
1600
1601
//Remove a given sample
1602
GF_EXPORT
1603
GF_Err gf_isom_remove_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber)
1604
0
{
1605
0
  GF_Err e;
1606
0
  GF_TrackBox *trak;
1607
1608
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_EDIT);
1609
0
  if (e) return e;
1610
1611
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1612
0
  if (!trak || !sampleNumber || (sampleNumber > trak->Media->information->sampleTable->SampleSize->sampleCount) )
1613
0
    return GF_BAD_PARAM;
1614
1615
  //block for hint tracks
1616
0
  if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM;
1617
1618
0
  e = unpack_track(trak);
1619
0
  if (e) return e;
1620
  //do NOT change the order DTS, CTS, size chunk
1621
1622
  //remove DTS
1623
0
  e = stbl_RemoveDTS(trak->Media->information->sampleTable, sampleNumber, 1, trak->Media->mediaHeader->timeScale);
1624
0
  if (e) return e;
1625
  //remove CTS if any
1626
0
  if (trak->Media->information->sampleTable->CompositionOffset) {
1627
0
    e = stbl_RemoveCTS(trak->Media->information->sampleTable, sampleNumber, 1);
1628
0
    if (e) return e;
1629
0
  }
1630
  //remove size
1631
0
  e = stbl_RemoveSize(trak->Media->information->sampleTable, sampleNumber, 1);
1632
0
  if (e) return e;
1633
  //remove sampleToChunk and chunk
1634
0
  e = stbl_RemoveChunk(trak->Media->information->sampleTable, sampleNumber, 1);
1635
0
  if (e) return e;
1636
  //remove sync
1637
0
  if (trak->Media->information->sampleTable->SyncSample) {
1638
0
    e = stbl_RemoveRAP(trak->Media->information->sampleTable, sampleNumber);
1639
0
    if (e) return e;
1640
0
  }
1641
  //remove sample dep
1642
0
  if (trak->Media->information->sampleTable->SampleDep) {
1643
0
    e = stbl_RemoveRedundant(trak->Media->information->sampleTable, sampleNumber, 1);
1644
0
    if (e) return e;
1645
0
  }
1646
  //remove shadow
1647
0
  e = stbl_RemoveShadow(trak->Media->information->sampleTable, sampleNumber);
1648
0
  if (e) return e;
1649
1650
  //remove padding
1651
0
  e = stbl_RemovePaddingBits(trak->Media->information->sampleTable, sampleNumber);
1652
0
  if (e) return e;
1653
1654
0
  e = stbl_RemoveSubSample(trak->Media->information->sampleTable, sampleNumber);
1655
0
  if (e) return e;
1656
1657
0
  e = stbl_RemoveSampleGroup(trak->Media->information->sampleTable, sampleNumber);
1658
0
  if (e) return e;
1659
1660
0
  gf_isom_disable_inplace_rewrite(movie);
1661
1662
0
  gf_isom_meta_track_remove(movie, trak, sampleNumber);
1663
1664
0
  return SetTrackDuration(trak);
1665
0
}
1666
1667
1668
GF_EXPORT
1669
GF_Err gf_isom_set_final_name(GF_ISOFile *movie, char *filename)
1670
0
{
1671
0
  GF_Err e;
1672
0
  if (!movie ) return GF_BAD_PARAM;
1673
1674
  //if mode is not OPEN_EDIT file was created under the right name
1675
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_EDIT);
1676
0
  if (e) return e;
1677
1678
0
  if (filename) {
1679
    //we don't allow file overwriting
1680
0
    if ( (movie->openMode == GF_ISOM_OPEN_EDIT)
1681
0
            && movie->fileName && !strcmp(filename, movie->fileName))
1682
0
      return GF_BAD_PARAM;
1683
0
    if (movie->finalName) gf_free(movie->finalName);
1684
0
    movie->finalName = gf_strdup(filename);
1685
0
    if (!movie->finalName) return GF_OUT_OF_MEM;
1686
0
    gf_isom_disable_inplace_rewrite(movie);
1687
0
  }
1688
0
  return GF_OK;
1689
0
}
1690
1691
//Add a system descriptor to the ESD of a stream(EDIT or WRITE mode only)
1692
GF_EXPORT
1693
GF_Err gf_isom_add_desc_to_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, const GF_Descriptor *theDesc)
1694
0
{
1695
0
  GF_IPIPtr *ipiD;
1696
0
  GF_Err e;
1697
0
  u16 tmpRef;
1698
0
  GF_TrackBox *trak;
1699
0
  GF_Descriptor *desc;
1700
0
  GF_ESD *esd;
1701
0
  GF_TrackReferenceBox *tref;
1702
0
  GF_TrackReferenceTypeBox *dpnd;
1703
0
  GF_MPEGVisualSampleEntryBox *entry;
1704
0
  u32 msubtype;
1705
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
1706
0
  if (e) return e;
1707
1708
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1709
0
  if (!trak) return GF_BAD_PARAM;
1710
1711
  /*GETS NATIVE DESCRIPTOR ONLY*/
1712
0
  e = Media_GetESD(trak->Media, StreamDescriptionIndex, &esd, GF_TRUE);
1713
0
  if (e) return e;
1714
1715
0
  entry = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex-1);
1716
0
  if (!entry) return GF_BAD_PARAM;
1717
0
  msubtype = entry->type;
1718
0
  if ((msubtype==GF_ISOM_BOX_TYPE_ENCV) || (msubtype==GF_ISOM_BOX_TYPE_ENCA))
1719
0
    gf_isom_get_original_format_type(movie, trackNumber, StreamDescriptionIndex, &msubtype);
1720
1721
  //duplicate the desc
1722
0
  e = gf_odf_desc_copy((GF_Descriptor *)theDesc, &desc);
1723
0
  if (e) return e;
1724
1725
  //and add it to the ESD EXCEPT IPI PTR (we need to translate from ES_ID to TrackID!!!
1726
0
  if (!movie->keep_utc)
1727
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1728
1729
0
  switch (desc->tag) {
1730
0
  case GF_ODF_IPI_PTR_TAG:
1731
0
    goto insertIPI;
1732
0
  default:
1733
0
    break;
1734
0
  }
1735
1736
0
  if ((msubtype==GF_ISOM_BOX_TYPE_MP4S) || (msubtype==GF_ISOM_BOX_TYPE_MP4V) || (msubtype==GF_ISOM_BOX_TYPE_MP4A)) {
1737
0
    return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc);
1738
0
  }
1739
1740
0
  if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_VISUAL) {
1741
0
    gf_odf_desc_del(desc);
1742
0
    return GF_NOT_SUPPORTED;
1743
0
  }
1744
0
  GF_MPEG4ExtensionDescriptorsBox *mdesc = (GF_MPEG4ExtensionDescriptorsBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_M4DS);
1745
0
  if (!mdesc) {
1746
0
    mdesc = (GF_MPEG4ExtensionDescriptorsBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_M4DS);
1747
0
  }
1748
0
  return gf_list_add(mdesc->descriptors, desc);
1749
1750
0
insertIPI:
1751
0
  if (esd->ipiPtr) {
1752
0
    gf_odf_desc_del((GF_Descriptor *) esd->ipiPtr);
1753
0
    esd->ipiPtr = NULL;
1754
0
  }
1755
1756
0
  ipiD = (GF_IPIPtr *) desc;
1757
  //find a tref
1758
0
  if (!trak->References) {
1759
0
    tref = (GF_TrackReferenceBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TREF);
1760
0
    if (!tref) return GF_OUT_OF_MEM;
1761
0
    e = trak_on_child_box((GF_Box*)trak, (GF_Box *)tref, GF_FALSE);
1762
0
    if (e) return e;
1763
0
  }
1764
0
  tref = trak->References;
1765
1766
0
  e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd);
1767
0
  if (e) return e;
1768
0
  if (!dpnd) {
1769
0
    tmpRef = 0;
1770
0
    dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT);
1771
0
    if (!dpnd) return GF_OUT_OF_MEM;
1772
0
    dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR;
1773
0
    e = reftype_AddRefTrack(dpnd, ipiD->IPI_ES_Id, &tmpRef);
1774
0
    if (e) return e;
1775
    //and replace the tag and value...
1776
0
    ipiD->IPI_ES_Id = tmpRef;
1777
0
    ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1778
0
  } else {
1779
    //Watch out! ONLY ONE IPI dependency is allowed per stream
1780
0
    dpnd->trackIDCount = 1;
1781
0
    dpnd->trackIDs[0] = ipiD->IPI_ES_Id;
1782
    //and replace the tag and value...
1783
0
    ipiD->IPI_ES_Id = 1;
1784
0
    ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1785
0
  }
1786
  //and add the desc to the esd...
1787
0
  return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc);
1788
0
}
1789
1790
1791
//use carefully. Very useful when you made a lot of changes (IPMP, IPI, OCI, ...)
1792
//THIS WILL REPLACE THE WHOLE DESCRIPTOR ...
1793
GF_EXPORT
1794
GF_Err gf_isom_change_mpeg4_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, const GF_ESD *newESD)
1795
0
{
1796
0
  GF_Err e;
1797
0
  GF_ESD *esd;
1798
0
  GF_TrackBox *trak;
1799
0
  GF_SampleEntryBox *entry;
1800
0
  GF_SampleDescriptionBox *stsd;
1801
1802
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
1803
0
  if (e) return e;
1804
1805
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1806
0
  if (!trak) return GF_BAD_PARAM;
1807
1808
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
1809
0
  if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
1810
1811
0
  if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
1812
0
    return movie->LastError = GF_BAD_PARAM;
1813
0
  }
1814
0
  entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
1815
  //no support for generic sample entries (eg, no MPEG4 descriptor)
1816
0
  if (entry == NULL) return GF_BAD_PARAM;
1817
1818
0
  if (!movie->keep_utc)
1819
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1820
  //duplicate our desc
1821
0
  e = gf_odf_desc_copy((GF_Descriptor *)newESD, (GF_Descriptor **)&esd);
1822
0
  if (e) return e;
1823
0
  e = Track_SetStreamDescriptor(trak, StreamDescriptionIndex, entry->dataReferenceIndex, esd, NULL);
1824
0
  if (e != GF_OK) {
1825
0
    gf_odf_desc_del((GF_Descriptor *) esd);
1826
0
  }
1827
0
  return e;
1828
0
}
1829
1830
GF_EXPORT
1831
GF_Err gf_isom_set_visual_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 Width, u32 Height)
1832
0
{
1833
0
  GF_Err e;
1834
0
  GF_TrackBox *trak;
1835
0
  GF_SampleEntryBox *entry;
1836
0
  GF_SampleDescriptionBox *stsd;
1837
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
1838
0
  if (e) return e;
1839
1840
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1841
0
  if (!trak) return GF_BAD_PARAM;
1842
1843
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
1844
0
  if (!stsd) {
1845
0
    return movie->LastError = GF_ISOM_INVALID_FILE;
1846
0
  }
1847
0
  if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
1848
0
    return movie->LastError = GF_BAD_PARAM;
1849
0
  }
1850
0
  entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
1851
  //no support for generic sample entries (eg, no MPEG4 descriptor)
1852
0
  if (entry == NULL) return GF_BAD_PARAM;
1853
0
  if (!movie->keep_utc)
1854
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1855
1856
  //valid for MPEG visual, JPG and 3GPP H263
1857
0
  if (entry->internal_type == GF_ISOM_SAMPLE_ENTRY_VIDEO) {
1858
0
    ((GF_VisualSampleEntryBox*)entry)->Width = Width;
1859
0
    ((GF_VisualSampleEntryBox*)entry)->Height = Height;
1860
0
    trak->Header->width = Width<<16;
1861
0
    trak->Header->height = Height<<16;
1862
0
    return GF_OK;
1863
0
  } else if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_SCENE) {
1864
0
    trak->Header->width = Width<<16;
1865
0
    trak->Header->height = Height<<16;
1866
0
    return GF_OK;
1867
0
  } else {
1868
0
    return GF_BAD_PARAM;
1869
0
  }
1870
0
}
1871
1872
GF_EXPORT
1873
GF_Err gf_isom_set_visual_bit_depth(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u16 bitDepth)
1874
0
{
1875
0
  GF_Err e;
1876
0
  GF_TrackBox *trak;
1877
0
  GF_MPEGVisualSampleEntryBox *entry;
1878
0
  GF_SampleDescriptionBox *stsd;
1879
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
1880
0
  if (e) return e;
1881
1882
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1883
0
  if (!trak) return GF_BAD_PARAM;
1884
1885
0
  switch (trak->Media->handler->handlerType) {
1886
0
  case GF_ISOM_MEDIA_VISUAL:
1887
0
  case GF_ISOM_MEDIA_PICT:
1888
0
  case GF_ISOM_MEDIA_AUXV:
1889
0
    break;
1890
0
  default:
1891
0
    return GF_OK;
1892
0
  }
1893
1894
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
1895
0
  if (!stsd) {
1896
0
    return movie->LastError = GF_ISOM_INVALID_FILE;
1897
0
  }
1898
0
  if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
1899
0
    return movie->LastError = GF_BAD_PARAM;
1900
0
  }
1901
0
  entry = (GF_MPEGVisualSampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
1902
  //no support for generic sample entries (eg, no MPEG4 descriptor)
1903
0
  if (entry == NULL) return GF_BAD_PARAM;
1904
0
  entry->bit_depth = bitDepth;
1905
0
  return GF_OK;
1906
0
}
1907
1908
GF_EXPORT
1909
GF_Err gf_isom_set_pixel_aspect_ratio(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, s32 hSpacing, s32 vSpacing, Bool force_par)
1910
0
{
1911
0
  GF_Err e;
1912
0
  GF_TrackBox *trak;
1913
0
  GF_SampleEntryBox *entry;
1914
0
  GF_SampleDescriptionBox *stsd;
1915
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
1916
0
  if (e) return e;
1917
1918
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1919
0
  if (!trak) return GF_BAD_PARAM;
1920
1921
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
1922
0
  if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
1923
0
  if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
1924
0
    return movie->LastError = GF_BAD_PARAM;
1925
0
  }
1926
0
  entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
1927
  //no support for generic sample entries (eg, no MPEG4 descriptor)
1928
0
  if (entry == NULL) return GF_BAD_PARAM;
1929
0
  if (!movie->keep_utc)
1930
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1931
1932
0
  if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
1933
1934
0
  if (hSpacing<0) hSpacing = 1;
1935
0
  if (vSpacing<0) vSpacing = 1;
1936
1937
0
  GF_PixelAspectRatioBox *pasp = (GF_PixelAspectRatioBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_PASP);
1938
0
  if (!hSpacing || !vSpacing || ((hSpacing == vSpacing) && !force_par))  {
1939
0
    if (pasp) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *)pasp);
1940
0
    return GF_OK;
1941
0
  }
1942
0
  if (!pasp) {
1943
0
    pasp = (GF_PixelAspectRatioBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_PASP);
1944
0
    if (!pasp) return GF_OUT_OF_MEM;
1945
0
  }
1946
0
  pasp->hSpacing = (u32) hSpacing;
1947
0
  pasp->vSpacing = (u32) vSpacing;
1948
0
  return GF_OK;
1949
0
}
1950
1951
GF_EXPORT
1952
GF_Err gf_isom_set_visual_color_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 colour_type, u16 colour_primaries, u16 transfer_characteristics, u16 matrix_coefficients, Bool full_range_flag, u8 *icc_data, u32 icc_size)
1953
0
{
1954
0
  GF_Err e;
1955
0
  GF_TrackBox *trak;
1956
0
  GF_SampleEntryBox *entry;
1957
0
  GF_SampleDescriptionBox *stsd;
1958
0
  GF_ColourInformationBox *clr=NULL;
1959
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
1960
0
  if (e) return e;
1961
1962
0
  trak = gf_isom_get_track_box(movie, trackNumber);
1963
0
  if (!trak) return GF_BAD_PARAM;
1964
1965
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
1966
0
  if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
1967
0
  if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
1968
0
    return movie->LastError = GF_BAD_PARAM;
1969
0
  }
1970
0
  entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
1971
  //no support for generic sample entries (eg, no MPEG4 descriptor)
1972
0
  if (entry == NULL) return GF_BAD_PARAM;
1973
0
  if (!movie->keep_utc)
1974
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
1975
1976
0
  if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_OK;
1977
1978
0
  clr = (GF_ColourInformationBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_COLR);
1979
0
  if (!colour_type) {
1980
0
    if (clr) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *)clr);
1981
0
    return GF_OK;
1982
0
  }
1983
0
  if (clr) {
1984
    //create another color box
1985
0
    if (clr->opaque && !icc_data) clr = NULL;
1986
0
    else if (!clr->opaque && icc_data) clr = NULL;
1987
0
  }
1988
1989
0
  if (!clr) {
1990
0
    clr = (GF_ColourInformationBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_COLR);
1991
0
    if (!clr) return GF_OUT_OF_MEM;
1992
0
  }
1993
0
  clr->colour_type = colour_type;
1994
0
  clr->colour_primaries = colour_primaries;
1995
0
  clr->transfer_characteristics = transfer_characteristics;
1996
0
  clr->matrix_coefficients = matrix_coefficients;
1997
0
  clr->full_range_flag = full_range_flag;
1998
0
  if (clr->opaque) gf_free(clr->opaque);
1999
0
  clr->opaque = NULL;
2000
0
  clr->opaque_size = 0;
2001
0
  if ((colour_type==GF_ISOM_SUBTYPE_RICC) || (colour_type==GF_ISOM_SUBTYPE_PROF)) {
2002
0
    clr->opaque_size = icc_data ? icc_size : 0;
2003
0
    if (clr->opaque_size) {
2004
0
      clr->opaque = gf_malloc(sizeof(char)*clr->opaque_size);
2005
0
      if (!clr->opaque) return GF_OUT_OF_MEM;
2006
0
      memcpy(clr->opaque, icc_data, sizeof(char)*clr->opaque_size);
2007
0
    }
2008
0
  }
2009
0
  return GF_OK;
2010
0
}
2011
2012
GF_EXPORT
2013
GF_Err gf_isom_set_ambient_viewing_environment(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 ambient_illuminance, u16 ambient_light_x, u16 ambient_light_y)
2014
0
{
2015
0
  GF_Err e;
2016
0
  GF_TrackBox *trak;
2017
0
  GF_SampleEntryBox *entry;
2018
0
  GF_SampleDescriptionBox *stsd;
2019
0
  GF_AmbientViewingEnvBox *amve=NULL;
2020
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
2021
0
  if (e) return e;
2022
2023
0
  trak = gf_isom_get_track_box(movie, trackNumber);
2024
0
  if (!trak) return GF_BAD_PARAM;
2025
2026
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
2027
0
  if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
2028
0
  if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
2029
0
    return movie->LastError = GF_BAD_PARAM;
2030
0
  }
2031
0
  entry = (GF_SampleEntryBox*)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
2032
  //no support for generic sample entries (eg, no MPEG4 descriptor)
2033
0
  if (entry == NULL) return GF_BAD_PARAM;
2034
0
  if (!movie->keep_utc)
2035
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
2036
2037
0
  if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_OK;
2038
2039
0
  amve = (GF_AmbientViewingEnvBox*) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_AMVE);
2040
2041
  // ambient_illuminance shall not be equal to 0
2042
  // The values of ambient_light_x and ambient_light_y shall be in the range of 0 to 50000, inclusive
2043
0
  if (ambient_illuminance == 0 || ambient_light_x > 50000 || ambient_light_y > 50000) {
2044
0
    if (amve) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *)amve);
2045
0
    return GF_OK;
2046
0
  }
2047
2048
0
  if (!amve) {
2049
0
    amve = (GF_AmbientViewingEnvBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_AMVE);
2050
0
    if (!amve) return GF_OUT_OF_MEM;
2051
0
  }
2052
0
  amve->ambient_illuminance = ambient_illuminance;
2053
0
  amve->ambient_light_x = ambient_light_x;
2054
0
  amve->ambient_light_y = ambient_light_y;
2055
2056
0
  return GF_OK;
2057
0
}
2058
2059
GF_EXPORT
2060
GF_Err gf_isom_set_preselection_info(GF_ISOFile *file, u32 track, GF_List *cfg_list)
2061
0
{
2062
0
  GF_Err e = GF_OK;
2063
0
  GF_HandlerBox *hdlr;
2064
0
  GF_GroupListBox *grpl;
2065
0
  GF_PreselectionGroupBox *prsl;
2066
0
  GF_PreselectionConfig *cfg;
2067
2068
0
  if (!cfg_list) return GF_BAD_PARAM;
2069
2070
0
  e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
2071
0
  if (e) return e;
2072
2073
0
  if (!file->meta) {
2074
0
    file->meta = (GF_MetaBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_META);
2075
0
    if (!file->meta) return GF_OUT_OF_MEM;
2076
0
    gf_list_add(file->TopBoxes, file->meta);
2077
0
  }
2078
2079
0
  hdlr = (GF_HandlerBox*) gf_isom_box_find_child(file->meta->child_boxes, GF_ISOM_BOX_TYPE_HDLR);
2080
0
  if (!hdlr) {
2081
0
    hdlr = (GF_HandlerBox*) gf_isom_box_new_parent(&file->meta->child_boxes, GF_ISOM_BOX_TYPE_HDLR);
2082
0
    if (!hdlr) return GF_OUT_OF_MEM;
2083
0
  }
2084
0
  hdlr->handlerType = GF_4CC( 'n', 'u', 'l', 'l' );
2085
2086
0
  grpl = (GF_GroupListBox*) gf_isom_box_find_child(file->meta->child_boxes, GF_ISOM_BOX_TYPE_GRPL);
2087
0
  if (!grpl) {
2088
0
    grpl = (GF_GroupListBox*) gf_isom_box_new_parent(&file->meta->child_boxes, GF_ISOM_BOX_TYPE_GRPL);
2089
0
    if (!grpl) return GF_OUT_OF_MEM;
2090
0
  }
2091
0
  file->meta->groups_list = grpl;
2092
2093
0
  for (u32 i = 0; i < gf_list_count(cfg_list); i++) {
2094
0
    cfg = (GF_PreselectionConfig*)gf_list_get(cfg_list, i);
2095
0
    if (!cfg) continue;
2096
2097
0
    prsl = (GF_PreselectionGroupBox*) gf_isom_box_new_parent(&grpl->child_boxes, GF_ISOM_BOX_TYPE_PRSL);
2098
0
    if (!prsl) return GF_OUT_OF_MEM;
2099
2100
0
    prsl->flags = cfg->flags;
2101
0
    prsl->group_id = cfg->group_id;
2102
2103
    // use the new track number as entity ID
2104
0
    prsl->entity_id_count = 1;
2105
0
    prsl->entity_ids = gf_malloc(prsl->entity_id_count * sizeof(u32));
2106
0
    if (!prsl->entity_ids) return GF_OUT_OF_MEM;
2107
0
    prsl->entity_ids[0] = track;
2108
2109
0
    if ((cfg->flags & GF_ISOM_PRESELECTION_TAG_PRESENT) && cfg->preselection_tag) {
2110
0
      prsl->preselection_tag = gf_strdup(cfg->preselection_tag);
2111
0
    }
2112
0
    if (cfg->flags & GF_ISOM_SELECTION_PRIORITY_PRESENT) {
2113
0
      prsl->selection_priority = cfg->selection_priority;
2114
0
    }
2115
0
    if ((cfg->flags & GF_ISOM_INTERLEAVING_TAG_PRESENT) && cfg->interleaving_tag) {
2116
0
      prsl->interleaving_tag = gf_strdup(cfg->interleaving_tag);
2117
0
    }
2118
2119
0
    GF_AudioRenderingIndicationBox *ardi = (GF_AudioRenderingIndicationBox *) gf_isom_box_new_parent(&prsl->child_boxes, GF_ISOM_BOX_TYPE_ARDI);
2120
0
    if (!ardi) return GF_OUT_OF_MEM;
2121
0
    ardi->audio_rendering_indication = cfg->audio_rendering_indication;
2122
2123
0
    if (cfg->extended_language) {
2124
0
      GF_ExtendedLanguageBox *elng = (GF_ExtendedLanguageBox *) gf_isom_box_new_parent(&prsl->child_boxes, GF_ISOM_BOX_TYPE_ELNG);
2125
0
      if (!elng) return GF_OUT_OF_MEM;
2126
0
      elng->extended_language = gf_strdup(cfg->extended_language);
2127
0
    }
2128
2129
0
    if (cfg->dialog_gain_present) {
2130
0
      GF_UserDataBox *udta = (GF_UserDataBox *) gf_isom_box_new_parent(&prsl->child_boxes, GF_ISOM_BOX_TYPE_UDTA);
2131
0
      if (!udta) return GF_OUT_OF_MEM;
2132
2133
0
      GF_DialogueProcessingBox *diap = (GF_DialogueProcessingBox *) gf_isom_box_new_parent(&udta->child_boxes, GF_ISOM_BOX_TYPE_DIAP);
2134
0
      if (!diap) return GF_OUT_OF_MEM;
2135
0
      diap->dialog_gain = cfg->dialog_gain;
2136
0
    }
2137
2138
0
    if (cfg->labels) {
2139
0
      for (u32 j = 0; j < gf_list_count(cfg->labels); j++) {
2140
0
        GF_Label *label = (GF_Label*)gf_list_get(cfg->labels, j);
2141
0
        if (!label) continue;
2142
2143
0
        GF_LabelBox *labl = (GF_LabelBox *) gf_isom_box_new_parent(&prsl->child_boxes, GF_ISOM_BOX_TYPE_LABL);
2144
0
        if (!labl) return GF_OUT_OF_MEM;
2145
0
        if (label->is_group_label) labl->flags |= GF_ISOM_IS_GROUP_LABEL;
2146
0
        labl->label_id = label->label_id;
2147
0
        labl->language = gf_strdup(label->language);
2148
0
        labl->label = gf_strdup(label->label);
2149
0
      }
2150
0
    }
2151
2152
0
    if (cfg->kinds) {
2153
0
      for (u32 j = 0; j < gf_list_count(cfg->kinds); j++) {
2154
0
        GF_Kind *kind = (GF_Kind*)gf_list_get(cfg->kinds, j);
2155
0
        if (!kind) continue;
2156
2157
0
        GF_KindBox *kind_box = (GF_KindBox *) gf_isom_box_new_parent(&prsl->child_boxes, GF_ISOM_BOX_TYPE_KIND);
2158
0
        if (!kind_box) return GF_OUT_OF_MEM;
2159
0
        kind_box->value = kind->value ? gf_strdup(kind->value) : NULL;
2160
0
        kind_box->schemeURI = gf_strdup(kind->schemeURI);
2161
0
      }
2162
0
    }
2163
0
  }
2164
0
  return GF_OK;
2165
0
}
2166
2167
GF_EXPORT
2168
GF_Err gf_isom_set_dolby_vision_profile(GF_ISOFile* movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_DOVIDecoderConfigurationRecord *dvcc)
2169
0
{
2170
0
  GF_Err e;
2171
0
  GF_TrackBox* trak;
2172
0
  GF_Box *dv_cfge = NULL;
2173
0
  GF_MPEGVisualSampleEntryBox* entry;
2174
0
  Bool switch_type = GF_FALSE;
2175
0
  Bool is_avc = GF_FALSE;
2176
0
  GF_SampleDescriptionBox* stsd;
2177
0
  GF_DOVIConfigurationBox* dovi = NULL;
2178
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
2179
0
  if (e) return e;
2180
2181
0
  trak = gf_isom_get_track_box(movie, trackNumber);
2182
0
  if (!trak) return GF_BAD_PARAM;
2183
2184
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
2185
0
  if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
2186
0
  if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
2187
0
    return movie->LastError = GF_BAD_PARAM;
2188
0
  }
2189
0
  entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
2190
  //no support for generic sample entries (eg, no MPEG4 descriptor)
2191
0
  if (entry == NULL) return GF_BAD_PARAM;
2192
0
  if (!movie->keep_utc)
2193
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
2194
2195
0
  if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_OK;
2196
2197
0
  if (dvcc) {
2198
0
    switch_type = dvcc->force_dv;
2199
0
    switch (dvcc->dv_profile) {
2200
0
    case 1:
2201
0
    case 3:
2202
0
    case 5:
2203
0
      switch_type = 1;
2204
0
      break;
2205
0
    }
2206
0
  }
2207
2208
0
  switch (entry->type) {
2209
0
  case GF_ISOM_BOX_TYPE_HEV1:
2210
0
  case GF_ISOM_BOX_TYPE_HEV2:
2211
0
  case GF_ISOM_BOX_TYPE_DVHE:
2212
0
    entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVHE : GF_ISOM_BOX_TYPE_HEV1;
2213
0
    break;
2214
0
  case GF_ISOM_BOX_TYPE_HVC1:
2215
0
  case GF_ISOM_BOX_TYPE_HVC2:
2216
0
  case GF_ISOM_BOX_TYPE_DVH1:
2217
0
    entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVH1 : GF_ISOM_BOX_TYPE_HVC1;
2218
0
    break;
2219
0
  case GF_ISOM_BOX_TYPE_AVC1:
2220
0
  case GF_ISOM_BOX_TYPE_DVA1:
2221
0
    is_avc = GF_TRUE;
2222
0
    entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVA1 : GF_ISOM_BOX_TYPE_AVC1;
2223
0
    break;
2224
0
  case GF_ISOM_BOX_TYPE_AVC3:
2225
0
  case GF_ISOM_BOX_TYPE_DVAV:
2226
0
    is_avc = GF_TRUE;
2227
0
    entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVAV : GF_ISOM_BOX_TYPE_AVC3;
2228
0
    break;
2229
0
  case GF_ISOM_BOX_TYPE_AV01:
2230
0
  case GF_ISOM_BOX_TYPE_DAV1:
2231
0
    entry->type = switch_type ? GF_ISOM_BOX_TYPE_DAV1 : GF_ISOM_BOX_TYPE_AV01;
2232
0
    break;
2233
0
  default:
2234
0
    return GF_NOT_SUPPORTED;
2235
0
  }
2236
2237
0
  u32 dve_type = is_avc ? GF_ISOM_BOX_TYPE_AVCE : GF_ISOM_BOX_TYPE_HVCE;
2238
0
  dv_cfge = gf_isom_box_find_child(entry->child_boxes, dve_type);
2239
2240
0
  dovi = entry->dovi_config;
2241
0
  if (!dvcc) {
2242
0
    if (dovi) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)dovi);
2243
0
    entry->dovi_config = NULL;
2244
2245
0
    if (dv_cfge) gf_isom_box_del_parent(&entry->child_boxes, dv_cfge);
2246
    //reverse entry type
2247
0
    switch (entry->type) {
2248
0
    case GF_ISOM_BOX_TYPE_DVHE:
2249
0
      entry->type = GF_ISOM_BOX_TYPE_HEV1;
2250
0
      break;
2251
0
    case GF_ISOM_BOX_TYPE_DVH1:
2252
0
      entry->type = GF_ISOM_BOX_TYPE_HVC1;
2253
0
      break;
2254
0
    case GF_ISOM_BOX_TYPE_DVA1:
2255
0
      entry->type = GF_ISOM_BOX_TYPE_AVC1;
2256
0
      break;
2257
0
    case GF_ISOM_BOX_TYPE_DVAV:
2258
0
      entry->type = GF_ISOM_BOX_TYPE_AVC3;
2259
0
      break;
2260
0
    case GF_ISOM_BOX_TYPE_DAV1:
2261
0
      entry->type = GF_ISOM_BOX_TYPE_AV01;
2262
0
      break;
2263
0
    default:
2264
0
      break;
2265
0
    }
2266
0
    return GF_OK;
2267
0
  }
2268
0
  if (!dovi) {
2269
0
    dovi = (GF_DOVIConfigurationBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_DVCC);
2270
0
    if (!dovi) return GF_OUT_OF_MEM;
2271
0
    entry->dovi_config = dovi;
2272
0
  }
2273
0
  if (dvcc->dv_profile < 8) {
2274
0
    dovi->type = GF_ISOM_BOX_TYPE_DVCC;
2275
0
  } else {
2276
0
    dovi->type = GF_ISOM_BOX_TYPE_DVVC;
2277
0
  }
2278
0
  dovi->DOVIConfig = *dvcc;
2279
2280
0
  if (dvcc->dv_profile == 20) {
2281
0
    if (dvcc->dv_bl_signal_compatibility_id != 4) {
2282
0
      entry->type = GF_ISOM_BOX_TYPE_DVH1;
2283
0
    }
2284
0
    dovi->type = GF_ISOM_BOX_TYPE_DVCC;
2285
0
  }
2286
0
  if (dvcc->dv_profile == 10 && dvcc->dv_bl_signal_compatibility_id == 0) {
2287
0
    entry->type = GF_ISOM_BOX_TYPE_DAV1;
2288
0
    dovi->type = GF_ISOM_BOX_TYPE_DVVC;
2289
0
  }
2290
2291
0
  if (dv_cfge) gf_isom_box_del_parent(&entry->child_boxes, dv_cfge);
2292
0
  dv_cfge = NULL;
2293
2294
  //inject avcE / hvcE if enhancement layer and RPU present in single-track case
2295
  //not clear from the spec what is supposed to be in these, we just clone avcC/hvcC
2296
0
  if (dvcc->bl_present_flag && dvcc->el_present_flag && dvcc->rpu_present_flag) {
2297
0
    GF_Box *src = is_avc ? (GF_Box *)entry->avc_config : (GF_Box *)entry->hevc_config;
2298
0
    if (!src) return GF_BAD_PARAM;
2299
0
    e = gf_isom_clone_box(src, &dv_cfge);
2300
0
    if (e) return e;
2301
0
    dv_cfge->type = dve_type;
2302
0
    gf_list_add(entry->child_boxes, dv_cfge);
2303
0
  }
2304
0
  return GF_OK;
2305
0
}
2306
2307
GF_EXPORT
2308
GF_Err gf_isom_set_dolby_vision_brands(GF_ISOFile* movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_DOVIDecoderConfigurationRecord *dvcc, Bool add_cmaf_brands)
2309
0
{
2310
0
  GF_Err e;
2311
0
  GF_TrackBox* trak;
2312
0
  GF_MPEGVisualSampleEntryBox* entry;
2313
0
  GF_SampleDescriptionBox* stsd;
2314
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
2315
0
  if (e) return e;
2316
2317
0
  trak = gf_isom_get_track_box(movie, trackNumber);
2318
0
  if (!trak) return GF_BAD_PARAM;
2319
2320
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
2321
0
  if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
2322
0
  if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
2323
0
    return movie->LastError = GF_BAD_PARAM;
2324
0
  }
2325
0
  entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
2326
2327
  // Dolby Vision Streams Within the ISO Base Media File Format specification Version 2.7 section 2.6
2328
0
  gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_DBY1, GF_TRUE);
2329
0
  if (dvcc->dv_bl_signal_compatibility_id == 1) {
2330
0
    gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_DB1P, GF_TRUE);
2331
0
  } else if (dvcc->dv_bl_signal_compatibility_id == 2) {
2332
0
    gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_DB2G, GF_TRUE);
2333
0
  } else if (dvcc->dv_bl_signal_compatibility_id == 4) {
2334
0
    GF_ColourInformationBox *colr = (GF_ColourInformationBox*)gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_COLR);
2335
0
    u16 ctc = colr ? colr->transfer_characteristics : GF_COLOR_TRC_RESERVED0;
2336
2337
0
    if (ctc == GF_COLOR_TRC_BT2020_10) {
2338
0
      gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_DB4G, GF_TRUE);
2339
0
    } else if (ctc == GF_COLOR_TRC_ARIB_STD_B67) {
2340
0
      gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_DB4H, GF_TRUE);
2341
0
    }
2342
0
  }
2343
2344
  // Dolby Vision Streams Within the ISO Base Media File Format specification Version 2.8 section 6
2345
0
  if (add_cmaf_brands) {
2346
0
    if (dvcc->dv_profile == 5) {
2347
0
      gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_DV58, GF_TRUE);
2348
0
    } else if (dvcc->dv_profile == 8) {
2349
0
      gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_DV58, GF_TRUE);
2350
0
      if (dvcc->dv_bl_signal_compatibility_id == 1) {
2351
0
        gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_CHD1, GF_TRUE);
2352
0
      } else if (dvcc->dv_bl_signal_compatibility_id == 4) {
2353
0
        gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_CLG1, GF_TRUE);
2354
0
      }
2355
0
    } else if (dvcc->dv_profile == 9) {
2356
0
      gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_DV09, GF_TRUE);
2357
0
      if (dvcc->dv_bl_signal_compatibility_id == 2) {
2358
0
        gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_CHDF, GF_TRUE);
2359
0
      }
2360
0
    } else if (dvcc->dv_profile == 10) {
2361
0
      gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_DV10, GF_TRUE);
2362
0
    } else if (dvcc->dv_profile == 20) {
2363
0
      gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_DV20, GF_TRUE);
2364
0
      if (dvcc->dv_bl_signal_compatibility_id == 4) {
2365
0
        gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_CLG1, GF_TRUE);
2366
0
      }
2367
0
    }
2368
0
  }
2369
0
  return GF_OK;
2370
0
}
2371
2372
GF_EXPORT
2373
GF_Err gf_isom_set_high_dynamic_range_info(GF_ISOFile* movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_MasteringDisplayColourVolumeInfo* mdcv, GF_ContentLightLevelInfo* clli)
2374
0
{
2375
0
  GF_Err e;
2376
0
  GF_TrackBox* trak;
2377
0
  GF_SampleEntryBox* entry;
2378
0
  GF_SampleDescriptionBox* stsd;
2379
2380
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
2381
0
  if (e) return e;
2382
2383
0
  trak = gf_isom_get_track_box(movie, trackNumber);
2384
0
  if (!trak) return GF_BAD_PARAM;
2385
2386
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
2387
0
  if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
2388
0
  if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
2389
0
    return movie->LastError = GF_BAD_PARAM;
2390
0
  }
2391
0
  entry = (GF_SampleEntryBox*)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
2392
  //no support for generic sample entries (eg, no MPEG4 descriptor)
2393
0
  if (entry == NULL) return GF_BAD_PARAM;
2394
0
  if (!movie->keep_utc)
2395
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
2396
2397
0
  if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
2398
2399
0
  GF_MasteringDisplayColourVolumeBox *mdcvb = (GF_MasteringDisplayColourVolumeBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_MDCV);
2400
0
  if (mdcv) {
2401
0
    if (!mdcvb) {
2402
0
      mdcvb = (GF_MasteringDisplayColourVolumeBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_MDCV);
2403
0
      if (!mdcvb) return GF_OUT_OF_MEM;
2404
0
    }
2405
0
    mdcvb->mdcv = *mdcv;
2406
0
  } else {
2407
0
    if (mdcvb) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *) mdcvb);
2408
0
  }
2409
2410
  /*clli*/
2411
0
  GF_ContentLightLevelBox *cllib = (GF_ContentLightLevelBox *)gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_CLLI);
2412
0
  if (clli) {
2413
0
    if (!cllib) {
2414
0
      cllib = (GF_ContentLightLevelBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_CLLI);
2415
0
      if (!cllib) return GF_OUT_OF_MEM;
2416
0
    }
2417
0
    cllib->clli = *clli;
2418
0
  } else {
2419
0
    if (cllib) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *) cllib);
2420
0
  }
2421
0
  return GF_OK;
2422
0
}
2423
2424
GF_EXPORT
2425
GF_Err gf_isom_set_clean_aperture(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 cleanApertureWidthN, u32 cleanApertureWidthD, u32 cleanApertureHeightN, u32 cleanApertureHeightD, s32 horizOffN, u32 horizOffD, s32 vertOffN, u32 vertOffD)
2426
0
{
2427
0
  GF_Err e;
2428
0
  GF_TrackBox *trak;
2429
0
  GF_SampleEntryBox *entry;
2430
0
  GF_SampleDescriptionBox *stsd;
2431
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
2432
0
  if (e) return e;
2433
2434
0
  trak = gf_isom_get_track_box(movie, trackNumber);
2435
0
  if (!trak) return GF_BAD_PARAM;
2436
2437
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
2438
0
  if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
2439
0
  if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
2440
0
    return movie->LastError = GF_BAD_PARAM;
2441
0
  }
2442
0
  entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
2443
  //no support for generic sample entries (eg, no MPEG4 descriptor)
2444
0
  if (entry == NULL) return GF_BAD_PARAM;
2445
0
  if (!movie->keep_utc)
2446
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
2447
2448
0
  if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
2449
2450
0
  GF_CleanApertureBox *clap = (GF_CleanApertureBox *)gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_CLAP);
2451
0
  if (!cleanApertureHeightD || !cleanApertureWidthD || !horizOffD || !vertOffD) {
2452
0
    if (clap) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)clap);
2453
0
    return GF_OK;
2454
0
  }
2455
0
  if (!clap) {
2456
0
    clap = (GF_CleanApertureBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_CLAP);
2457
0
    if (!clap) return GF_OUT_OF_MEM;
2458
0
  }
2459
2460
0
  clap->cleanApertureWidthN = cleanApertureWidthN;
2461
0
  clap->cleanApertureWidthD = cleanApertureWidthD;
2462
0
  clap->cleanApertureHeightN = cleanApertureHeightN;
2463
0
  clap->cleanApertureHeightD = cleanApertureHeightD;
2464
0
  clap->horizOffN = horizOffN;
2465
0
  clap->horizOffD = horizOffD;
2466
0
  clap->vertOffN = vertOffN;
2467
0
  clap->vertOffD = vertOffD;
2468
0
  return GF_OK;
2469
0
}
2470
2471
#include <gpac/maths.h>
2472
GF_Err gf_isom_update_aperture_info(GF_ISOFile *movie, u32 trackNumber, Bool remove)
2473
0
{
2474
0
  GF_Err e;
2475
0
  GF_Box *box, *enof, *prof, *clef;
2476
0
  GF_TrackBox *trak;
2477
0
  GF_VisualSampleEntryBox *ventry;
2478
0
  GF_PixelAspectRatioBox *pasp;
2479
0
  GF_CleanApertureBox *clap;
2480
0
  u32 j, hspacing, vspacing, clap_width_num, clap_width_den, clap_height_num, clap_height_den, high, low;
2481
0
  Double width, height;
2482
2483
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
2484
0
  if (e) return e;
2485
2486
0
  trak = gf_isom_get_track_box(movie, trackNumber);
2487
0
  if (!trak) return GF_BAD_PARAM;
2488
2489
0
  if (remove) {
2490
0
    if (trak->Aperture) {
2491
0
      gf_isom_box_del(trak->Aperture);
2492
0
      trak->Aperture = NULL;
2493
0
    }
2494
0
    return GF_OK;
2495
0
  }
2496
0
  enof = prof = clef = NULL;
2497
0
  if (!trak->Aperture) {
2498
0
    trak->Aperture = gf_isom_box_new_parent(&trak->child_boxes, GF_QT_BOX_TYPE_TAPT);
2499
0
    if (!trak->Aperture) return GF_OUT_OF_MEM;
2500
0
  }
2501
0
  if (!trak->Aperture->child_boxes) {
2502
0
    trak->Aperture->child_boxes = gf_list_new();
2503
0
    if (!trak->Aperture->child_boxes)
2504
0
      return GF_OUT_OF_MEM;
2505
0
  }
2506
2507
0
  j=0;
2508
0
  while ( (box = gf_list_enum(trak->Aperture->child_boxes, &j))) {
2509
0
    switch (box->type) {
2510
0
    case GF_QT_BOX_TYPE_CLEF: clef = box; break;
2511
0
    case GF_QT_BOX_TYPE_PROF: prof = box; break;
2512
0
    case GF_QT_BOX_TYPE_ENOF: enof = box; break;
2513
0
    }
2514
0
  }
2515
0
  if (!clef) {
2516
0
    clef = gf_isom_box_new(GF_QT_BOX_TYPE_CLEF);
2517
0
    if (!clef) return GF_OUT_OF_MEM;
2518
0
    gf_list_add(trak->Aperture->child_boxes, clef);
2519
0
  }
2520
0
  if (!enof) {
2521
0
    enof = gf_isom_box_new(GF_QT_BOX_TYPE_ENOF);
2522
0
    if (!enof) return GF_OUT_OF_MEM;
2523
0
    gf_list_add(trak->Aperture->child_boxes, enof);
2524
0
  }
2525
0
  if (!prof) {
2526
0
    prof = gf_isom_box_new(GF_QT_BOX_TYPE_PROF);
2527
0
    if (!prof) return GF_OUT_OF_MEM;
2528
0
    gf_list_add(trak->Aperture->child_boxes, prof);
2529
0
  }
2530
2531
0
  ventry = (GF_VisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, 0);
2532
  //no support for generic sample entries (eg, no MPEG4 descriptor)
2533
0
  if (ventry == NULL) return GF_BAD_PARAM;
2534
0
  if (!movie->keep_utc)
2535
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
2536
2537
0
  if (ventry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
2538
2539
0
  pasp = (GF_PixelAspectRatioBox *) gf_isom_box_find_child(ventry->child_boxes, GF_ISOM_BOX_TYPE_PASP);
2540
0
  hspacing = vspacing = 0;
2541
0
  if (pasp) {
2542
0
    hspacing = pasp->hSpacing;
2543
0
    vspacing = pasp->vSpacing;
2544
0
  }
2545
0
  clap_width_num = ventry->Width;
2546
0
  clap_width_den = 1;
2547
0
  clap_height_num = ventry->Height;
2548
0
  clap_height_den = 1;
2549
0
  clap = (GF_CleanApertureBox *) gf_isom_box_find_child(ventry->child_boxes, GF_ISOM_BOX_TYPE_CLAP);
2550
0
  if (clap) {
2551
0
    clap_width_num = clap->cleanApertureWidthN;
2552
0
    clap_width_den = clap->cleanApertureWidthD;
2553
0
    clap_height_num = clap->cleanApertureHeightN;
2554
0
    clap_height_den = clap->cleanApertureHeightD;
2555
0
  }
2556
  //enof: encoded pixels in 16.16
2557
0
  ((GF_ApertureBox *)enof)->width = (ventry->Width)<<16;
2558
0
  ((GF_ApertureBox *)enof)->height = (ventry->Height)<<16;
2559
2560
  //prof: encoded pixels + pasp in 16.16
2561
0
  width = (Float) (ventry->Width * hspacing);
2562
0
  width /= vspacing;
2563
0
  high = (u32) floor((Float)width);
2564
0
  low = (u32) ( 0xFFFF * (width - (Double)high) );
2565
0
  ((GF_ApertureBox *)prof)->width = (high)<<16 | low;
2566
0
  ((GF_ApertureBox *)prof)->height = (ventry->Height)<<16;
2567
2568
  //clef: encoded pixels + pasp + clap in 16.16
2569
0
  width = (Double) (clap_width_num * hspacing);
2570
0
  width /= clap_width_den * vspacing;
2571
0
  height = (Float) clap_height_num;
2572
0
  height /= clap_height_den;
2573
2574
0
  high = (u32) floor((Float)width);
2575
0
  low = (u32) (0xFFFF * (width - (Double)high));
2576
0
  ((GF_ApertureBox *)clef)->width = (high)<<16 | low;
2577
0
  high = (u32) floor((Float)height);
2578
0
  low = (u32) (0xFFFF * (height - (Double)high));
2579
0
  ((GF_ApertureBox *)clef)->height = (high)<<16 | low;
2580
2581
2582
0
  return GF_OK;
2583
0
}
2584
2585
GF_EXPORT
2586
GF_Err gf_isom_set_image_sequence_coding_constraints(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, Bool remove, Bool all_ref_pics_intra, Bool intra_pred_used, u32 max_ref_per_pic)
2587
0
{
2588
0
  GF_Err e;
2589
0
  GF_TrackBox *trak;
2590
0
  GF_SampleEntryBox *entry;
2591
0
  GF_SampleDescriptionBox *stsd;
2592
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
2593
0
  if (e) return e;
2594
2595
0
  trak = gf_isom_get_track_box(movie, trackNumber);
2596
0
  if (!trak) return GF_BAD_PARAM;
2597
2598
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
2599
0
  if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
2600
0
  if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
2601
0
    return movie->LastError = GF_BAD_PARAM;
2602
0
  }
2603
0
  entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
2604
  //no support for generic sample entries (eg, no MPEG4 descriptor)
2605
0
  if (entry == NULL) return GF_BAD_PARAM;
2606
0
  if (!movie->keep_utc)
2607
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
2608
2609
0
  if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
2610
2611
0
  GF_CodingConstraintsBox*ccst = (GF_CodingConstraintsBox*) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_CCST);
2612
0
  if (remove)  {
2613
0
    if (ccst) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)ccst);
2614
0
    return GF_OK;
2615
0
  }
2616
0
  if (!ccst) {
2617
0
    ccst = (GF_CodingConstraintsBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_CCST);
2618
0
    if (!ccst) return GF_OUT_OF_MEM;
2619
0
  }
2620
0
  ccst->all_ref_pics_intra = all_ref_pics_intra;
2621
0
  ccst->intra_pred_used = intra_pred_used;
2622
0
  ccst->max_ref_per_pic = max_ref_per_pic;
2623
0
  return GF_OK;
2624
0
}
2625
2626
GF_EXPORT
2627
GF_Err gf_isom_set_image_sequence_alpha(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, Bool remove)
2628
0
{
2629
0
  GF_Err e;
2630
0
  GF_TrackBox *trak;
2631
0
  GF_SampleEntryBox *entry;
2632
0
  GF_SampleDescriptionBox *stsd;
2633
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
2634
0
  if (e) return e;
2635
2636
0
  trak = gf_isom_get_track_box(movie, trackNumber);
2637
0
  if (!trak) return GF_BAD_PARAM;
2638
2639
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
2640
0
  if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
2641
0
  if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
2642
0
    return movie->LastError = GF_BAD_PARAM;
2643
0
  }
2644
0
  entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
2645
  //no support for generic sample entries (eg, no MPEG4 descriptor)
2646
0
  if (entry == NULL) return GF_BAD_PARAM;
2647
0
  if (!movie->keep_utc)
2648
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
2649
2650
0
  if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
2651
2652
0
  GF_AuxiliaryTypeInfoBox *auxi = (GF_AuxiliaryTypeInfoBox *)gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_AUXI);
2653
0
  if (remove)  {
2654
0
    if (auxi) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)auxi);
2655
0
    return GF_OK;
2656
0
  }
2657
0
  if (!auxi) {
2658
0
    auxi = (GF_AuxiliaryTypeInfoBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_AUXI);
2659
0
    if (!auxi) return GF_OUT_OF_MEM;
2660
0
  }
2661
0
  auxi->aux_track_type = gf_strdup("urn:mpeg:mpegB:cicp:systems:auxiliary:alpha");
2662
0
  return GF_OK;
2663
0
}
2664
2665
GF_EXPORT
2666
GF_Err gf_isom_set_audio_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 sampleRate, u32 nbChannels, u8 bitsPerSample, GF_AudioSampleEntryImportMode asemode)
2667
0
{
2668
0
  GF_Err e;
2669
0
  u32 i, old_qtff_mode=GF_ISOM_AUDIO_QTFF_NONE;
2670
0
  GF_TrackBox *trak;
2671
0
  GF_SampleEntryBox *entry;
2672
0
  GF_AudioSampleEntryBox*aud_entry;
2673
0
  GF_SampleDescriptionBox *stsd;
2674
0
  GF_Box *wave_box = NULL;
2675
0
  GF_Box *gf_isom_audio_sample_get_audio_codec_cfg_box(GF_AudioSampleEntryBox *ptr);
2676
0
  GF_Box *codec_ext = NULL;
2677
#if 0
2678
  GF_ChannelLayoutInfoBox *chan=NULL;
2679
#endif
2680
0
  GF_OriginalFormatBox *frma=NULL;
2681
0
  GF_ChromaInfoBox *enda=NULL;
2682
0
  GF_ESDBox *esds=NULL;
2683
0
  GF_Box *terminator=NULL;
2684
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
2685
0
  if (e) return e;
2686
2687
0
  trak = gf_isom_get_track_box(movie, trackNumber);
2688
0
  if (!trak) return GF_BAD_PARAM;
2689
2690
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
2691
0
  if (!stsd) {
2692
0
    return movie->LastError = GF_ISOM_INVALID_FILE;
2693
0
  }
2694
0
  if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
2695
0
    return movie->LastError = GF_BAD_PARAM;
2696
0
  }
2697
0
  entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
2698
  //no support for generic sample entries (eg, no MPEG4 descriptor)
2699
0
  if (entry == NULL) return GF_BAD_PARAM;
2700
0
  if (!movie->keep_utc)
2701
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
2702
2703
0
  if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_AUDIO) return GF_BAD_PARAM;
2704
0
  aud_entry = (GF_AudioSampleEntryBox*) entry;
2705
2706
0
  if (entry->type==GF_ISOM_BOX_TYPE_MLPA) {
2707
0
    aud_entry->samplerate_hi = sampleRate>>16;
2708
0
    aud_entry->samplerate_lo = sampleRate & 0x0000FFFF;
2709
0
  } else {
2710
0
    aud_entry->samplerate_hi = sampleRate;
2711
0
    aud_entry->samplerate_lo = 0;
2712
0
  }
2713
0
  aud_entry->bitspersample = bitsPerSample;
2714
2715
0
  switch (asemode) {
2716
0
  case GF_IMPORT_AUDIO_SAMPLE_ENTRY_NOT_SET:
2717
0
  case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v0_DEFAULT:
2718
0
    stsd->version = 0;
2719
0
    aud_entry->version = 0;
2720
0
    aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE;
2721
0
    aud_entry->channel_count = nbChannels; // Get from bitstream
2722
2723
    // According to ETSI TS 102 366 V1.4.1 (2017-09), the channel_count of AC3, EC3 (based on AudioSampleEntry)
2724
    // should always be 2, and the sample size should always be 16
2725
0
    if (aud_entry->type == GF_ISOM_BOX_TYPE_AC3 || aud_entry->type == GF_ISOM_BOX_TYPE_EC3 || aud_entry->type == GF_ISOM_BOX_TYPE_AC4) {
2726
0
      aud_entry->channel_count = 2;
2727
0
      aud_entry->bitspersample = 16;
2728
0
    }
2729
0
    break;
2730
0
  case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v0_BS:
2731
0
    stsd->version = 0;
2732
0
    aud_entry->version = 0;
2733
0
    aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE;
2734
0
    aud_entry->channel_count = nbChannels;
2735
0
    break;
2736
0
  case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v0_2:
2737
0
    stsd->version = 0;
2738
0
    aud_entry->version = 0;
2739
0
    aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE;
2740
0
    aud_entry->channel_count = 2;
2741
0
    break;
2742
0
  case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_MPEG:
2743
0
    stsd->version = 1;
2744
0
    aud_entry->version = 1;
2745
0
    aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE;
2746
0
    aud_entry->channel_count = nbChannels;
2747
0
    break;
2748
0
  case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF:
2749
0
    stsd->version = 0;
2750
    //don't change if already v2
2751
0
    if ((aud_entry->version==2) && aud_entry->qtff_mode) {
2752
0
      break;
2753
0
    }
2754
0
    aud_entry->version = 1;
2755
0
    aud_entry->channel_count = nbChannels;
2756
0
    old_qtff_mode = aud_entry->qtff_mode;
2757
0
    if (aud_entry->qtff_mode != GF_ISOM_AUDIO_QTFF_ON_EXT_VALID)
2758
0
      aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_ON_NOEXT;
2759
0
    break;
2760
0
  }
2761
2762
0
  aud_entry->compression_id = 0;
2763
0
  GF_SamplingRateBox *srat = NULL;
2764
  //check for wave+children and chan for QTFF or remove them for isobmff
2765
0
  for (i=0; i<gf_list_count(aud_entry->child_boxes); i++) {
2766
0
    GF_Box *b = gf_list_get(aud_entry->child_boxes, i);
2767
0
    if (b->type == GF_ISOM_BOX_TYPE_SRAT) srat = (GF_SamplingRateBox *)b;
2768
2769
0
    if ((b->type != GF_QT_BOX_TYPE_WAVE) && (b->type != GF_QT_BOX_TYPE_CHAN) ) continue;
2770
0
    if (asemode==GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF) {
2771
0
      if (b->type == GF_QT_BOX_TYPE_WAVE) wave_box = b;
2772
#if 0
2773
      else chan = (GF_ChannelLayoutInfoBox *)b;
2774
#endif
2775
2776
0
    } else {
2777
0
      gf_isom_box_del_parent(&aud_entry->child_boxes, b);
2778
0
      i--;
2779
0
    }
2780
0
  }
2781
2782
0
  if (sampleRate>0xFFFF) {
2783
0
    if (!srat) {
2784
0
      srat = (GF_SamplingRateBox *) gf_isom_box_new_parent(&aud_entry->child_boxes, GF_ISOM_BOX_TYPE_SRAT);
2785
0
    }
2786
0
    if (srat) srat->sampling_rate = sampleRate;
2787
0
  }
2788
2789
  //TODO: insert channelLayout for ISOBMFF
2790
0
  if (asemode!=GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF) return GF_OK;
2791
2792
0
  if (aud_entry->type==GF_ISOM_BOX_TYPE_MP4A)
2793
0
    aud_entry->compression_id = -2;
2794
2795
0
  if (!aud_entry->child_boxes) aud_entry->child_boxes = gf_list_new();
2796
2797
#if 0
2798
  if (!chan) {
2799
    chan = (GF_ChannelLayoutInfoBox *) gf_isom_box_new_parent(&aud_entry->child_boxes, GF_QT_BOX_TYPE_CHAN);
2800
  }
2801
  //TODO, proper channel mapping
2802
  chan->layout_tag = (nbChannels==2) ? 6750210 : 6553601;
2803
#endif
2804
2805
0
  codec_ext = gf_isom_audio_sample_get_audio_codec_cfg_box((GF_AudioSampleEntryBox *)aud_entry);
2806
0
  if (!codec_ext) return GF_OK;
2807
2808
0
  if (!wave_box) {
2809
0
    wave_box = gf_isom_box_new_parent(&aud_entry->child_boxes, GF_QT_BOX_TYPE_WAVE);
2810
0
  }
2811
2812
0
  for (i=0; i<gf_list_count(wave_box->child_boxes); i++) {
2813
0
    GF_Box *b = gf_list_get(wave_box->child_boxes, i);
2814
0
    switch (b->type) {
2815
0
    case GF_QT_BOX_TYPE_ENDA:
2816
0
      enda = (GF_ChromaInfoBox *)b;
2817
0
      break;
2818
0
    case GF_QT_BOX_TYPE_FRMA:
2819
0
      frma = (GF_OriginalFormatBox *)b;
2820
0
      break;
2821
0
    case GF_ISOM_BOX_TYPE_ESDS:
2822
0
      esds = (GF_ESDBox *)b;
2823
0
      break;
2824
0
    case GF_ISOM_BOX_TYPE_UNKNOWN:
2825
0
      if ( ((GF_UnknownBox*)b)->original_4cc == 0)
2826
0
        terminator = b;
2827
0
      break;
2828
0
    case 0:
2829
0
      terminator = b;
2830
0
      break;
2831
0
    }
2832
0
  }
2833
0
  if (!wave_box->child_boxes) wave_box->child_boxes = gf_list_new();
2834
2835
  //do not use new_parent, we do this manually to ensure the order
2836
0
  aud_entry->qtff_mode = old_qtff_mode ? old_qtff_mode : GF_ISOM_AUDIO_QTFF_ON_NOEXT;
2837
0
  if (!frma) {
2838
0
    frma = (GF_OriginalFormatBox *)gf_isom_box_new(GF_QT_BOX_TYPE_FRMA);
2839
0
  } else {
2840
0
    gf_list_del_item(wave_box->child_boxes, frma);
2841
0
  }
2842
0
  gf_list_add(wave_box->child_boxes, frma);
2843
2844
0
  if (esds) gf_list_del_item(wave_box->child_boxes, esds);
2845
0
  if (!esds && (aud_entry->type==GF_ISOM_BOX_TYPE_MP4A) && ((GF_MPEGAudioSampleEntryBox*)aud_entry)->esd) {
2846
0
    gf_list_del_item(entry->child_boxes, (GF_Box *) ((GF_MPEGAudioSampleEntryBox*)aud_entry)->esd);
2847
0
    gf_list_add(wave_box->child_boxes, (GF_Box *) ((GF_MPEGAudioSampleEntryBox*)aud_entry)->esd);
2848
0
  }
2849
2850
0
  if (!enda) {
2851
0
    enda = (GF_ChromaInfoBox *)gf_isom_box_new(GF_QT_BOX_TYPE_ENDA);
2852
0
  } else {
2853
0
    gf_list_del_item(wave_box->child_boxes, enda);
2854
0
  }
2855
0
  enda->chroma=1;
2856
0
  gf_list_add(wave_box->child_boxes, enda);
2857
2858
0
  if (!terminator) {
2859
0
    terminator = gf_isom_box_new(0);
2860
0
  } else {
2861
0
    gf_list_del_item(wave_box->child_boxes, terminator);
2862
0
  }
2863
0
  gf_list_add(wave_box->child_boxes, terminator);
2864
2865
0
  if (aud_entry->type==GF_ISOM_BOX_TYPE_GNRA) {
2866
0
    frma->data_format = ((GF_GenericAudioSampleEntryBox*) aud_entry)->EntryType;
2867
0
  } else {
2868
0
    frma->data_format = aud_entry->type;
2869
0
  }
2870
2871
0
  return GF_OK;
2872
0
}
2873
2874
GF_EXPORT
2875
GF_Err gf_isom_set_audio_layout(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, GF_AudioChannelLayout *layout)
2876
0
{
2877
0
  GF_Err e;
2878
0
  GF_TrackBox *trak;
2879
0
  GF_SampleEntryBox *entry;
2880
0
  GF_AudioSampleEntryBox*aud_entry;
2881
0
  GF_SampleDescriptionBox *stsd;
2882
0
  GF_ChannelLayoutBox *chnl;
2883
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
2884
0
  if (e) return e;
2885
2886
0
  if (!layout) return GF_BAD_PARAM;
2887
0
  if ((layout->stream_structure & 1) && (layout->definedLayout==0) && (layout->channels_count>=64))
2888
0
    return GF_BAD_PARAM;
2889
2890
0
  trak = gf_isom_get_track_box(movie, trackNumber);
2891
0
  if (!trak) return GF_BAD_PARAM;
2892
2893
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
2894
0
  if (!stsd) {
2895
0
    return movie->LastError = GF_ISOM_INVALID_FILE;
2896
0
  }
2897
0
  if (!sampleDescriptionIndex || sampleDescriptionIndex > gf_list_count(stsd->child_boxes)) {
2898
0
    return movie->LastError = GF_BAD_PARAM;
2899
0
  }
2900
0
  entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, sampleDescriptionIndex - 1);
2901
  //no support for generic sample entries (eg, no MPEG4 descriptor)
2902
0
  if (entry == NULL) return GF_BAD_PARAM;
2903
0
  if (!movie->keep_utc)
2904
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
2905
2906
0
  if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_AUDIO) return GF_BAD_PARAM;
2907
0
  aud_entry = (GF_AudioSampleEntryBox*) entry;
2908
0
  if (aud_entry->qtff_mode) {
2909
0
    u32 sr = aud_entry->samplerate_hi;
2910
0
    if (aud_entry->type==GF_ISOM_BOX_TYPE_MLPA) {
2911
0
      sr <<= 16;
2912
0
      sr |= aud_entry->samplerate_lo;
2913
0
    }
2914
0
    e = gf_isom_set_audio_info(movie, trackNumber, sampleDescriptionIndex, sr, aud_entry->channel_count, (u8) aud_entry->bitspersample, GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_MPEG);
2915
0
    if (e) return e;
2916
0
  }
2917
0
  chnl = (GF_ChannelLayoutBox *) gf_isom_box_find_child(aud_entry->child_boxes, GF_ISOM_BOX_TYPE_CHNL);
2918
0
  if (!chnl) {
2919
0
    chnl = (GF_ChannelLayoutBox *)gf_isom_box_new_parent(&aud_entry->child_boxes, GF_ISOM_BOX_TYPE_CHNL);
2920
0
    if (!chnl) return GF_OUT_OF_MEM;
2921
0
  }
2922
0
  aud_entry->channel_count = layout->channels_count;
2923
0
  memcpy(&chnl->layout, layout, sizeof(GF_AudioChannelLayout));
2924
0
  return GF_OK;
2925
0
}
2926
2927
//set the storage mode of a file (FLAT, STREAMABLE, INTERLEAVED)
2928
GF_EXPORT
2929
GF_Err gf_isom_set_storage_mode(GF_ISOFile *movie, GF_ISOStorageMode storageMode)
2930
0
{
2931
0
  GF_Err e;
2932
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
2933
0
  if (e) return e;
2934
2935
0
  switch (storageMode) {
2936
0
  case GF_ISOM_STORE_FLAT:
2937
0
  case GF_ISOM_STORE_STREAMABLE:
2938
0
  case GF_ISOM_STORE_INTERLEAVED:
2939
0
  case GF_ISOM_STORE_DRIFT_INTERLEAVED:
2940
0
  case GF_ISOM_STORE_TIGHT:
2941
0
  case GF_ISOM_STORE_FASTSTART:
2942
0
    movie->storageMode = storageMode;
2943
    //specifying a storage mode disables inplace rewrite
2944
0
    gf_isom_disable_inplace_rewrite(movie);
2945
0
    return GF_OK;
2946
0
  default:
2947
0
    return GF_BAD_PARAM;
2948
0
  }
2949
0
}
2950
2951
2952
GF_EXPORT
2953
GF_Err gf_isom_enable_compression(GF_ISOFile *file, GF_ISOCompressMode compress_mode, u32 compress_flags)
2954
0
{
2955
0
  if (!file) return GF_BAD_PARAM;
2956
0
  file->compress_mode = compress_mode;
2957
0
  file->compress_flags = compress_flags;
2958
0
  return GF_OK;
2959
0
}
2960
2961
GF_EXPORT
2962
GF_Err gf_isom_force_64bit_chunk_offset(GF_ISOFile *file, Bool set_on)
2963
0
{
2964
0
  if (!file) return GF_BAD_PARAM;
2965
0
  file->force_co64 = set_on;
2966
0
  return GF_OK;
2967
0
}
2968
2969
2970
//update or insert a new edit segment in the track time line. Edits are used to modify
2971
//the media normal timing. EditTime and EditDuration are expressed in Movie TimeScale
2972
//If a segment with EditTime already exists, IT IS ERASED
2973
static GF_Err gf_isom_set_edit_internal(GF_ISOFile *movie, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, u32 media_rate, GF_ISOEditType EditMode)
2974
0
{
2975
0
  GF_TrackBox *trak;
2976
0
  GF_EditBox *edts;
2977
0
  GF_EditListBox *elst;
2978
0
  GF_EdtsEntry *ent, *newEnt;
2979
0
  u32 i;
2980
0
  GF_Err e;
2981
0
  u64 startTime;
2982
2983
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
2984
0
  if (e) return e;
2985
2986
0
  trak = gf_isom_get_track_box(movie, trackNumber);
2987
0
  if (!trak) return GF_BAD_PARAM;
2988
2989
0
  edts = trak->editBox;
2990
0
  if (! edts) {
2991
0
    edts = (GF_EditBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_EDTS);
2992
0
    if (!edts) return GF_OUT_OF_MEM;
2993
0
    trak_on_child_box((GF_Box*)trak, (GF_Box *)edts, GF_FALSE);
2994
0
  }
2995
0
  elst = edts->editList;
2996
0
  if (!elst) {
2997
0
    elst = (GF_EditListBox *) gf_isom_box_new_parent(&edts->child_boxes, GF_ISOM_BOX_TYPE_ELST);
2998
0
    if (!elst) return GF_OUT_OF_MEM;
2999
0
    edts_on_child_box((GF_Box*)edts, (GF_Box *)elst, GF_FALSE);
3000
0
  }
3001
3002
0
  if (trak->extl) {
3003
0
    if (!trak->extl->media_timescale) trak->extl->media_timescale = movie->moov->mvhd->timeScale;
3004
0
    trak->extl->flags |= GF_ISOM_EXTK_EDTS_SKIP;
3005
0
  }
3006
0
  startTime = 0;
3007
0
  ent = NULL;
3008
  //get the prev entry to this startTime if any
3009
0
  i=0;
3010
0
  while ((ent = (GF_EdtsEntry *)gf_list_enum(elst->entryList, &i))) {
3011
0
    if ( (startTime <= EditTime) && (startTime + ent->segmentDuration > EditTime) )
3012
0
      goto found;
3013
0
    startTime += ent->segmentDuration;
3014
0
  }
3015
3016
  //not found, add a new entry, insert empty one if gap
3017
0
  if (!ent) {
3018
0
    Bool empty_inserted = GF_FALSE;
3019
0
    if (startTime != EditTime) {
3020
0
      newEnt = CreateEditEntry(EditTime - startTime, 0, 0, GF_ISOM_EDIT_EMPTY);
3021
0
      if (!newEnt) return GF_OUT_OF_MEM;
3022
0
      empty_inserted = GF_TRUE;
3023
0
      gf_list_add(elst->entryList, newEnt);
3024
0
    }
3025
0
    newEnt = CreateEditEntry(EditDuration, MediaTime, media_rate, EditMode);
3026
0
    if (!newEnt) return GF_OUT_OF_MEM;
3027
0
    gf_list_add(elst->entryList, newEnt);
3028
0
    e = SetTrackDuration(trak);
3029
0
    if (e) return e;
3030
0
    return empty_inserted ? GF_EOS : GF_OK;
3031
0
  }
3032
3033
0
  startTime -= ent->segmentDuration;
3034
3035
0
found:
3036
3037
  //if same time, we erase the current one...
3038
0
  if (startTime == EditTime) {
3039
0
    ent->segmentDuration = EditDuration;
3040
0
    switch (EditMode) {
3041
0
    case GF_ISOM_EDIT_EMPTY:
3042
0
      ent->mediaRate = 0x10000;
3043
0
      ent->mediaTime = -1;
3044
0
      break;
3045
0
    case GF_ISOM_EDIT_DWELL:
3046
0
      ent->mediaRate = 0;
3047
0
      ent->mediaTime = MediaTime;
3048
0
      break;
3049
0
    default:
3050
0
      ent->mediaRate = media_rate;
3051
0
      ent->mediaTime = MediaTime;
3052
0
      break;
3053
0
    }
3054
0
    return SetTrackDuration(trak);
3055
0
  }
3056
3057
  //adjust so that the prev ent leads to EntryTime
3058
  //Note: we don't change the next one as it is unknown to us in
3059
  //a lot of case (the author's changes)
3060
0
  ent->segmentDuration = EditTime - startTime;
3061
0
  newEnt = CreateEditEntry(EditDuration, MediaTime, media_rate, EditMode);
3062
0
  if (!newEnt) return GF_OUT_OF_MEM;
3063
  //is it the last entry ???
3064
0
  if (i >= gf_list_count(elst->entryList) - 1) {
3065
    //add the new entry at the end
3066
0
    gf_list_add(elst->entryList, newEnt);
3067
0
    return SetTrackDuration(trak);
3068
0
  } else {
3069
    //insert after the current entry (which is i)
3070
0
    gf_list_insert(elst->entryList, newEnt, i+1);
3071
0
    return SetTrackDuration(trak);
3072
0
  }
3073
0
}
3074
3075
GF_EXPORT
3076
GF_Err gf_isom_set_edit(GF_ISOFile *movie, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, GF_ISOEditType EditMode)
3077
0
{
3078
0
  return gf_isom_set_edit_internal(movie, trackNumber, EditTime, EditDuration, MediaTime, 0x10000, EditMode);
3079
0
}
3080
3081
GF_EXPORT
3082
GF_Err gf_isom_set_edit_with_rate(GF_ISOFile *movie, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, u32 media_rate)
3083
0
{
3084
0
  return gf_isom_set_edit_internal(movie, trackNumber, EditTime, EditDuration, MediaTime, media_rate, GF_ISOM_EDIT_NORMAL);
3085
3086
0
}
3087
3088
//remove the edit segments for the whole track
3089
GF_EXPORT
3090
GF_Err gf_isom_remove_edits(GF_ISOFile *movie, u32 trackNumber)
3091
0
{
3092
0
  GF_Err e;
3093
0
  GF_TrackBox *trak;
3094
0
  trak = gf_isom_get_track_box(movie, trackNumber);
3095
0
  if (!trak) return GF_BAD_PARAM;
3096
3097
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3098
0
  if (e) return e;
3099
3100
0
  if (trak->extl) trak->extl->flags &= ~GF_ISOM_EXTK_EDTS_SKIP;
3101
0
  if (!trak->editBox || !trak->editBox->editList) return GF_OK;
3102
3103
0
  while (gf_list_count(trak->editBox->editList->entryList)) {
3104
0
    GF_EdtsEntry *ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, 0);
3105
0
    gf_free(ent);
3106
0
    e = gf_list_rem(trak->editBox->editList->entryList, 0);
3107
0
    if (e) return e;
3108
0
  }
3109
  //then delete the GF_EditBox...
3110
0
  gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->editBox);
3111
0
  trak->editBox = NULL;
3112
0
  return SetTrackDuration(trak);
3113
0
}
3114
3115
3116
//remove the edit segments for the whole track
3117
GF_EXPORT
3118
GF_Err gf_isom_remove_edit(GF_ISOFile *movie, u32 trackNumber, u32 seg_index)
3119
0
{
3120
0
  GF_Err e;
3121
0
  GF_TrackBox *trak;
3122
0
  GF_EdtsEntry *ent, *next_ent;
3123
0
  trak = gf_isom_get_track_box(movie, trackNumber);
3124
0
  if (!trak || !seg_index) return GF_BAD_PARAM;
3125
3126
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3127
0
  if (e) return e;
3128
3129
0
  if (!trak->editBox || !trak->editBox->editList) return GF_OK;
3130
0
  if (gf_list_count(trak->editBox->editList->entryList)<=1) return gf_isom_remove_edits(movie, trackNumber);
3131
3132
0
  ent = (GF_EdtsEntry*) gf_list_get(trak->editBox->editList->entryList, seg_index-1);
3133
0
  gf_list_rem(trak->editBox->editList->entryList, seg_index-1);
3134
0
  next_ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, seg_index-1);
3135
0
  if (next_ent) next_ent->segmentDuration += ent->segmentDuration;
3136
0
  gf_free(ent);
3137
0
  return SetTrackDuration(trak);
3138
0
}
3139
3140
3141
GF_EXPORT
3142
GF_Err gf_isom_append_edit(GF_ISOFile *movie, u32 trackNumber, u64 EditDuration, u64 MediaTime, GF_ISOEditType EditMode)
3143
0
{
3144
0
  GF_Err e;
3145
0
  GF_TrackBox *trak;
3146
0
  GF_EdtsEntry *ent;
3147
0
  trak = gf_isom_get_track_box(movie, trackNumber);
3148
0
  if (!trak) return GF_BAD_PARAM;
3149
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3150
0
  if (e) return e;
3151
3152
0
  if (!trak->editBox) {
3153
0
    GF_EditBox *edts = (GF_EditBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_EDTS);
3154
0
    if (!edts) return GF_OUT_OF_MEM;
3155
0
    trak_on_child_box((GF_Box*)trak, (GF_Box *)edts, GF_FALSE);
3156
0
    gf_assert(trak->editBox);
3157
0
  }
3158
0
  if (!trak->editBox->editList) {
3159
0
    GF_EditListBox *elst = (GF_EditListBox *) gf_isom_box_new_parent(&trak->editBox->child_boxes, GF_ISOM_BOX_TYPE_ELST);
3160
0
    if (!elst) return GF_OUT_OF_MEM;
3161
0
    edts_on_child_box((GF_Box*)trak->editBox, (GF_Box *)elst, GF_FALSE);
3162
0
    gf_assert(trak->editBox->editList);
3163
0
  }
3164
0
  ent = (GF_EdtsEntry *)gf_malloc(sizeof(GF_EdtsEntry));
3165
0
  if (!ent) return GF_OUT_OF_MEM;
3166
3167
0
  ent->segmentDuration = EditDuration;
3168
0
  switch (EditMode) {
3169
0
  case GF_ISOM_EDIT_EMPTY:
3170
0
    ent->mediaRate = 0x10000;
3171
0
    ent->mediaTime = -1;
3172
0
    break;
3173
0
  case GF_ISOM_EDIT_DWELL:
3174
0
    ent->mediaRate = 0;
3175
0
    ent->mediaTime = MediaTime;
3176
0
    break;
3177
0
  default:
3178
0
    ent->mediaRate = 0x10000;
3179
0
    ent->mediaTime = MediaTime;
3180
0
    break;
3181
0
  }
3182
0
  gf_list_add(trak->editBox->editList->entryList, ent);
3183
0
  return SetTrackDuration(trak);
3184
0
}
3185
3186
GF_EXPORT
3187
GF_Err gf_isom_modify_edit(GF_ISOFile *movie, u32 trackNumber, u32 seg_index, u64 EditDuration, u64 MediaTime, GF_ISOEditType EditMode)
3188
0
{
3189
0
  GF_Err e;
3190
0
  GF_TrackBox *trak;
3191
0
  GF_EdtsEntry *ent;
3192
0
  trak = gf_isom_get_track_box(movie, trackNumber);
3193
0
  if (!trak || !seg_index) return GF_BAD_PARAM;
3194
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3195
0
  if (e) return e;
3196
3197
0
  if (!trak->editBox || !trak->editBox->editList) return GF_OK;
3198
0
  if (gf_list_count(trak->editBox->editList->entryList)<seg_index) return GF_BAD_PARAM;
3199
0
  ent = (GF_EdtsEntry*) gf_list_get(trak->editBox->editList->entryList, seg_index-1);
3200
3201
0
  ent->segmentDuration = EditDuration;
3202
0
  switch (EditMode) {
3203
0
  case GF_ISOM_EDIT_EMPTY:
3204
0
    ent->mediaRate = 0x10000;
3205
0
    ent->mediaTime = -1;
3206
0
    break;
3207
0
  case GF_ISOM_EDIT_DWELL:
3208
0
    ent->mediaRate = 0;
3209
0
    ent->mediaTime = MediaTime;
3210
0
    break;
3211
0
  default:
3212
0
    ent->mediaRate = 0x10000;
3213
0
    ent->mediaTime = MediaTime;
3214
0
    break;
3215
0
  }
3216
0
  return SetTrackDuration(trak);
3217
0
}
3218
3219
static void update_next_track_id(GF_ISOFile *movie)
3220
0
{
3221
0
  GF_TrackBox *trak;
3222
  /*update next track ID*/
3223
0
  movie->moov->mvhd->nextTrackID = 0;
3224
0
  u32 i=0;
3225
0
  while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) {
3226
0
    if (trak->Header->trackID>movie->moov->mvhd->nextTrackID)
3227
0
      movie->moov->mvhd->nextTrackID = trak->Header->trackID;
3228
0
  }
3229
  //shall be larger than the largest track_ID in use
3230
0
  movie->moov->mvhd->nextTrackID++;
3231
0
}
3232
3233
//removes the desired track
3234
GF_EXPORT
3235
GF_Err gf_isom_remove_track(GF_ISOFile *movie, u32 trackNumber)
3236
0
{
3237
0
  GF_Err e;
3238
0
  GF_TrackBox *the_trak, *trak;
3239
0
  GF_TrackReferenceTypeBox *tref;
3240
0
  u32 i, j, k, descIndex;
3241
0
  GF_ISOTrackID *newRefs;
3242
0
  u8 found;
3243
0
  GF_ISOSample *samp;
3244
0
  the_trak = gf_isom_get_track_box(movie, trackNumber);
3245
0
  if (!the_trak) return GF_BAD_PARAM;
3246
3247
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3248
0
  if (e) return e;
3249
3250
0
  if (the_trak && the_trak->Media && the_trak->Media->information) {
3251
0
    if (the_trak->Media->information->dataHandler == movie->movieFileMap || the_trak->Media->information->dataHandler == movie->editFileMap)
3252
0
      the_trak->Media->information->dataHandler = NULL;
3253
3254
0
    if (the_trak->Media->information->scalableDataHandler == movie->movieFileMap || the_trak->Media->information->scalableDataHandler == movie->editFileMap)
3255
0
      the_trak->Media->information->scalableDataHandler = NULL;
3256
0
  }
3257
3258
0
  if (movie->moov->iods && movie->moov->iods->descriptor) {
3259
0
    GF_Descriptor *desc;
3260
0
    GF_ES_ID_Inc *inc;
3261
0
    GF_List *ESDs;
3262
0
    desc = movie->moov->iods->descriptor;
3263
0
    if (desc->tag == GF_ODF_ISOM_IOD_TAG) {
3264
0
      ESDs = ((GF_IsomInitialObjectDescriptor *)desc)->ES_ID_IncDescriptors;
3265
0
    } else if (desc->tag == GF_ODF_ISOM_OD_TAG) {
3266
0
      ESDs = ((GF_IsomObjectDescriptor *)desc)->ES_ID_IncDescriptors;
3267
0
    } else {
3268
0
      return GF_ISOM_INVALID_FILE;
3269
0
    }
3270
3271
    //remove the track ref from the root OD if any
3272
0
    i=0;
3273
0
    while ((inc = (GF_ES_ID_Inc *)gf_list_enum(ESDs, &i))) {
3274
0
      if (inc->trackID == the_trak->Header->trackID) {
3275
0
        gf_odf_desc_del((GF_Descriptor *)inc);
3276
0
        i--;
3277
0
        gf_list_rem(ESDs, i);
3278
0
      }
3279
0
    }
3280
0
  }
3281
3282
  //remove the track from the movie
3283
0
  gf_list_del_item(movie->moov->trackList, the_trak);
3284
3285
  //rewrite any OD tracks
3286
0
  i=0;
3287
0
  while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) {
3288
0
    if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_OD) continue;
3289
3290
    //this is an OD track...
3291
0
    j = gf_isom_get_sample_count(movie, i);
3292
0
    for (k=0; k < j; k++) {
3293
      //getting the sample will remove the references to the deleted track in the output OD frame
3294
0
      samp = gf_isom_get_sample(movie, i, k+1, &descIndex);
3295
0
      if (!samp) break;
3296
      //so let's update with the new OD frame ! If the sample is empty, remove it
3297
0
      if (!samp->dataLength) {
3298
0
        e = gf_isom_remove_sample(movie, i, k+1);
3299
0
        if (e) return e;
3300
0
      } else {
3301
0
        e = gf_isom_update_sample(movie, i, k+1, samp, GF_TRUE);
3302
0
        if (e) return e;
3303
0
      }
3304
      //and don't forget to delete the sample
3305
0
      gf_isom_sample_del(&samp);
3306
0
    }
3307
0
  }
3308
3309
  //remove the track ref from any "tref" box in all tracks, except the one to delete
3310
  //note that we don't touch scal references, as we don't want to rewrite AVC/HEVC samples ...
3311
0
  i=0;
3312
0
  while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) {
3313
0
    if (! trak->References || ! gf_list_count(trak->References->child_boxes)) continue;
3314
3315
0
    j=0;
3316
0
    while ((tref = (GF_TrackReferenceTypeBox *)gf_list_enum(trak->References->child_boxes, &j))) {
3317
0
      if (tref->reference_type==GF_ISOM_REF_SCAL)
3318
0
        continue;
3319
3320
0
      found = 0;
3321
0
      for (k=0; k<tref->trackIDCount; k++) {
3322
0
        if (tref->trackIDs[k] == the_trak->Header->trackID) found++;
3323
0
      }
3324
0
      if (!found) continue;
3325
      //no more refs, remove this ref_type
3326
0
      if (found == tref->trackIDCount) {
3327
0
        gf_isom_box_del_parent(&trak->References->child_boxes, (GF_Box *)tref);
3328
0
        j--;
3329
0
      } else {
3330
0
        newRefs = (GF_ISOTrackID*)gf_malloc(sizeof(GF_ISOTrackID) * (tref->trackIDCount - found));
3331
0
        if (!newRefs) return GF_OUT_OF_MEM;
3332
0
        found = 0;
3333
0
        for (k = 0; k < tref->trackIDCount; k++) {
3334
0
          if (tref->trackIDs[k] != the_trak->Header->trackID) {
3335
0
            newRefs[k-found] = tref->trackIDs[k];
3336
0
          } else {
3337
0
            found++;
3338
0
          }
3339
0
        }
3340
0
        gf_free(tref->trackIDs);
3341
0
        tref->trackIDs = newRefs;
3342
0
        tref->trackIDCount -= found;
3343
0
      }
3344
0
    }
3345
    //a little opt: remove the ref box if empty...
3346
0
    if (! gf_list_count(trak->References->child_boxes)) {
3347
0
      gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->References);
3348
0
      trak->References = NULL;
3349
0
    }
3350
0
  }
3351
3352
0
  gf_isom_disable_inplace_rewrite(movie);
3353
3354
0
  gf_isom_meta_track_remove(movie, the_trak, 0);
3355
3356
  //delete the track
3357
0
  gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box *)the_trak);
3358
3359
  /*update next track ID*/
3360
0
  update_next_track_id(movie);
3361
3362
0
  if (!gf_list_count(movie->moov->trackList)) {
3363
0
    gf_list_del_item(movie->TopBoxes, movie->moov);
3364
0
    gf_isom_box_del((GF_Box *)movie->moov);
3365
0
    movie->moov = NULL;
3366
0
  }
3367
0
  return GF_OK;
3368
0
}
3369
3370
3371
GF_EXPORT
3372
GF_Err gf_isom_set_copyright(GF_ISOFile *movie, const char *threeCharCode, char *notice)
3373
0
{
3374
0
  GF_Err e;
3375
0
  GF_CopyrightBox *ptr;
3376
0
  GF_UserDataMap *map;
3377
0
  u32 count, i;
3378
3379
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3380
0
  if (e) return e;
3381
3382
0
  if (!notice || !threeCharCode) return GF_BAD_PARAM;
3383
3384
0
  e = gf_isom_insert_moov(movie);
3385
0
  if (e) return e;
3386
3387
0
  if (!movie->moov->udta) {
3388
0
    e = moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE);
3389
0
    if (e) return e;
3390
0
  }
3391
0
  map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL);
3392
3393
0
  if (map) {
3394
    //try to find one in our language...
3395
0
    count = gf_list_count(map->boxes);
3396
0
    for (i=0; i<count; i++) {
3397
0
      ptr = (GF_CopyrightBox*)gf_list_get(map->boxes, i);
3398
0
      if (!strcmp(threeCharCode, (const char *) ptr->packedLanguageCode)) {
3399
0
        if (ptr->notice) gf_free(ptr->notice);
3400
0
        ptr->notice = (char*)gf_strdup(notice);
3401
0
        if (!ptr->notice) return GF_OUT_OF_MEM;
3402
0
        return GF_OK;
3403
0
      }
3404
0
    }
3405
0
  }
3406
  //nope, create one
3407
0
  ptr = (GF_CopyrightBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CPRT);
3408
0
  if (!ptr) return GF_OUT_OF_MEM;
3409
3410
0
  memcpy(ptr->packedLanguageCode, threeCharCode, 4);
3411
0
  ptr->notice = gf_strdup(notice);
3412
0
  if (!ptr->notice) return GF_OUT_OF_MEM;
3413
0
  return udta_on_child_box_ex((GF_Box *)movie->moov->udta, (GF_Box *) ptr, GF_FALSE, GF_FALSE);
3414
0
}
3415
3416
GF_EXPORT
3417
GF_Err gf_isom_add_track_kind(GF_ISOFile *movie, u32 trackNumber, const char *schemeURI, const char *value)
3418
0
{
3419
0
  GF_Err e;
3420
0
  GF_KindBox *ptr;
3421
0
  GF_UserDataBox *udta;
3422
0
  GF_UserDataMap *map;
3423
0
  u32 i, count;
3424
3425
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3426
0
  if (e) return e;
3427
3428
0
  e = gf_isom_insert_moov(movie);
3429
0
  if (e) return e;
3430
3431
0
  if (trackNumber) {
3432
0
    GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber);
3433
0
    if (!trak) return GF_BAD_PARAM;
3434
0
    if (!trak->udta) {
3435
0
      e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE);
3436
0
      if (e) return e;
3437
0
    }
3438
0
    udta = trak->udta;
3439
0
  } else {
3440
0
    return GF_BAD_PARAM;
3441
0
  }
3442
3443
  //we may get null on schemeURI if not set in the source
3444
0
  if (!schemeURI) schemeURI = "";
3445
3446
0
  map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_KIND, NULL);
3447
0
  if (map) {
3448
0
    count = gf_list_count(map->boxes);
3449
0
    for (i=0; i<count; i++) {
3450
0
      GF_Box *b = (GF_Box *)gf_list_get(map->boxes, i);
3451
0
      if (b->type == GF_ISOM_BOX_TYPE_KIND) {
3452
0
        GF_KindBox *kb = (GF_KindBox *)b;
3453
0
        if (!strcmp(kb->schemeURI, schemeURI) &&
3454
0
                ((value && kb->value && !strcmp(value, kb->value)) || (!value && !kb->value))) {
3455
          // Already there
3456
0
          return GF_OK;
3457
0
        }
3458
0
        if (!strcmp(kb->schemeURI, schemeURI)) {
3459
0
          if (kb->value) gf_free(kb->value);
3460
0
          kb->value = value ? gf_strdup(value) : NULL;
3461
0
          return GF_OK;
3462
0
        }
3463
0
      }
3464
0
    }
3465
0
  }
3466
3467
0
  ptr = (GF_KindBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_KIND);
3468
0
  if (e) return e;
3469
3470
0
  ptr->schemeURI = gf_strdup(schemeURI);
3471
0
  if (value) ptr->value = gf_strdup(value);
3472
0
  return udta_on_child_box_ex((GF_Box *)udta, (GF_Box *) ptr, GF_FALSE, GF_FALSE);
3473
0
}
3474
3475
GF_EXPORT
3476
GF_Err gf_isom_remove_track_kind(GF_ISOFile *movie, u32 trackNumber, const char *schemeURI, const char *value)
3477
0
{
3478
0
  GF_Err e;
3479
0
  GF_UserDataBox *udta;
3480
0
  GF_UserDataMap *map;
3481
0
  u32 i;
3482
3483
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3484
0
  if (e) return e;
3485
0
  e = gf_isom_insert_moov(movie);
3486
0
  if (e) return e;
3487
3488
0
  if (trackNumber) {
3489
0
    GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber);
3490
0
    if (!trak) return GF_BAD_PARAM;
3491
0
    if (!trak->udta) {
3492
0
      e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE);
3493
0
      if (e) return e;
3494
0
    }
3495
0
    udta = trak->udta;
3496
0
  } else {
3497
0
    return GF_OK;
3498
0
  }
3499
0
  map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_KIND, NULL);
3500
0
  if (map) {
3501
0
    for (i=0; i<gf_list_count(map->boxes); i++) {
3502
0
      GF_Box *b = (GF_Box *)gf_list_get(map->boxes, i);
3503
0
      if (b->type == GF_ISOM_BOX_TYPE_KIND) {
3504
0
        GF_KindBox *kb = (GF_KindBox *)b;
3505
0
        if (!schemeURI ||
3506
0
                (!strcmp(kb->schemeURI, schemeURI) &&
3507
0
                 ((value && kb->value && !strcmp(value, kb->value)) || (!value && !kb->value)))) {
3508
0
          gf_isom_box_del_parent(&map->boxes, b);
3509
0
          i--;
3510
0
        }
3511
0
      }
3512
0
    }
3513
0
  }
3514
0
  return GF_OK;
3515
0
}
3516
3517
GF_EXPORT
3518
GF_Err gf_isom_set_track_loudness_content_type(GF_ISOFile *movie, u32 trackNumber, u8 content_type, u32 flags)
3519
0
{
3520
0
  GF_Err e;
3521
0
  GF_TrackBox *trak;
3522
0
  GF_UserDataMap *map;
3523
0
  GF_ContentTypeForLoudnessControlBox *ptr = NULL;
3524
0
  u32 i;
3525
3526
0
  if (flags & ~(GF_ISOM_CTLC_FLAG_ADVERTISEMENT | GF_ISOM_CTLC_FLAG_IMMERSIVE_AUDIO))
3527
0
    return GF_BAD_PARAM;
3528
3529
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3530
0
  if (e) return e;
3531
0
  e = gf_isom_insert_moov(movie);
3532
0
  if (e) return e;
3533
3534
0
  trak = gf_isom_get_track_box(movie, trackNumber);
3535
0
  if (!trak) return GF_BAD_PARAM;
3536
0
  if (!trak->udta) {
3537
0
    e = trak_on_child_box((GF_Box *)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE);
3538
0
    if (e) return e;
3539
0
  }
3540
3541
0
  map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_CTLC, NULL);
3542
0
  if (map) {
3543
0
    for (i=0; i<gf_list_count(map->boxes); i++) {
3544
0
      GF_Box *box = (GF_Box *)gf_list_get(map->boxes, i);
3545
0
      if (box->type != GF_ISOM_BOX_TYPE_CTLC) continue;
3546
0
      if (!ptr) {
3547
0
        ptr = (GF_ContentTypeForLoudnessControlBox *)box;
3548
0
      } else {
3549
0
        gf_isom_box_del_parent(&map->boxes, box);
3550
0
        i--;
3551
0
      }
3552
0
    }
3553
0
  }
3554
3555
0
  if (!ptr) {
3556
0
    ptr = (GF_ContentTypeForLoudnessControlBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CTLC);
3557
0
    if (!ptr) return GF_OUT_OF_MEM;
3558
0
    e = udta_on_child_box_ex((GF_Box *)trak->udta, (GF_Box *)ptr, GF_FALSE, GF_FALSE);
3559
0
    if (e) {
3560
0
      gf_isom_box_del((GF_Box *)ptr);
3561
0
      return e;
3562
0
    }
3563
0
  }
3564
0
  ptr->version = 0;
3565
0
  ptr->flags = flags;
3566
0
  ptr->content_type = content_type;
3567
0
  return GF_OK;
3568
0
}
3569
3570
GF_EXPORT
3571
GF_Err gf_isom_remove_track_loudness_content_type(GF_ISOFile *movie, u32 trackNumber)
3572
0
{
3573
0
  GF_Err e;
3574
0
  GF_TrackBox *trak;
3575
0
  GF_UserDataMap *map;
3576
0
  u32 i;
3577
3578
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3579
0
  if (e) return e;
3580
0
  e = gf_isom_insert_moov(movie);
3581
0
  if (e) return e;
3582
3583
0
  trak = gf_isom_get_track_box(movie, trackNumber);
3584
0
  if (!trak) return GF_BAD_PARAM;
3585
0
  if (!trak->udta) return GF_OK;
3586
3587
0
  map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_CTLC, NULL);
3588
0
  if (!map) return GF_OK;
3589
0
  for (i=0; i<gf_list_count(map->boxes); i++) {
3590
0
    GF_Box *box = (GF_Box *)gf_list_get(map->boxes, i);
3591
0
    if (box->type != GF_ISOM_BOX_TYPE_CTLC) continue;
3592
0
    gf_isom_box_del_parent(&map->boxes, box);
3593
0
    i--;
3594
0
  }
3595
0
  return GF_OK;
3596
0
}
3597
3598
GF_EXPORT
3599
GF_Err gf_isom_add_chapter(GF_ISOFile *movie, u32 trackNumber, u64 timestamp, char *name)
3600
0
{
3601
0
  GF_Err e;
3602
0
  GF_ChapterListBox *ptr;
3603
0
  u32 i, count;
3604
0
  GF_ChapterEntry *ce;
3605
0
  GF_UserDataBox *udta;
3606
0
  GF_UserDataMap *map;
3607
3608
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3609
0
  if (e) return e;
3610
3611
0
  e = gf_isom_insert_moov(movie);
3612
0
  if (e) return e;
3613
3614
0
  if (trackNumber) {
3615
0
    GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber);
3616
0
    if (!trak) return GF_BAD_PARAM;
3617
0
    if (!trak->udta) {
3618
0
      e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE);
3619
0
      if (e) return e;
3620
0
    }
3621
0
    udta = trak->udta;
3622
0
  } else {
3623
0
    if (!movie->moov->udta) {
3624
0
      e = moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE);
3625
0
      if (e) return e;
3626
0
    }
3627
0
    udta = movie->moov->udta;
3628
0
  }
3629
3630
0
  ptr = NULL;
3631
0
  map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL);
3632
0
  if (!map) {
3633
0
    ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL);
3634
0
    e = udta_on_child_box_ex((GF_Box *)udta, (GF_Box *) ptr, GF_FALSE, GF_FALSE);
3635
0
    if (e) return e;
3636
0
    map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL);
3637
0
  } else {
3638
0
    ptr = (GF_ChapterListBox*)gf_list_get(map->boxes, 0);
3639
0
  }
3640
0
  if (!map) return GF_OUT_OF_MEM;
3641
3642
  /*this may happen if original MP4 is not properly formatted*/
3643
0
  if (!ptr) {
3644
0
    ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL);
3645
0
    if (!ptr) return GF_OUT_OF_MEM;
3646
0
    gf_list_add(map->boxes, ptr);
3647
0
  }
3648
3649
0
  GF_SAFEALLOC(ce, GF_ChapterEntry);
3650
0
  if (!ce) return GF_OUT_OF_MEM;
3651
3652
0
  ce->start_time = timestamp * 10000L;
3653
0
  ce->name = name ? gf_strdup(name) : NULL;
3654
3655
  /*insert in order*/
3656
0
  count = gf_list_count(ptr->list);
3657
0
  for (i=0; i<count; i++) {
3658
0
    GF_ChapterEntry *ace = (GF_ChapterEntry *)gf_list_get(ptr->list, i);
3659
0
    if (ace->start_time == ce->start_time) {
3660
0
      if (ace->name) gf_free(ace->name);
3661
0
      ace->name = ce->name;
3662
0
      gf_free(ce);
3663
0
      return GF_OK;
3664
0
    }
3665
0
    if (ace->start_time >= ce->start_time)
3666
0
      return gf_list_insert(ptr->list, ce, i);
3667
0
  }
3668
0
  return gf_list_add(ptr->list, ce);
3669
0
}
3670
3671
GF_EXPORT
3672
GF_Err gf_isom_remove_chapter(GF_ISOFile *movie, u32 trackNumber, u32 index)
3673
0
{
3674
0
  GF_Err e;
3675
0
  GF_ChapterListBox *ptr;
3676
0
  GF_ChapterEntry *ce;
3677
0
  GF_UserDataBox *udta;
3678
0
  GF_UserDataMap *map;
3679
3680
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3681
0
  if (e) return e;
3682
0
  e = gf_isom_insert_moov(movie);
3683
0
  if (e) return e;
3684
3685
0
  if (trackNumber) {
3686
0
    GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber);
3687
0
    if (!trak) return GF_BAD_PARAM;
3688
0
    if (!trak->udta) return GF_OK;
3689
0
    udta = trak->udta;
3690
0
  } else {
3691
0
    if (!movie->moov->udta) return GF_OK;
3692
0
    udta = movie->moov->udta;
3693
0
  }
3694
3695
0
  map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL);
3696
0
  if (!map) return GF_OK;
3697
0
  ptr = (GF_ChapterListBox*)gf_list_get(map->boxes, 0);
3698
0
  if (!ptr) return GF_OK;
3699
3700
0
  if (index) {
3701
0
    ce = (GF_ChapterEntry *)gf_list_get(ptr->list, index-1);
3702
0
    if (!ce) return GF_BAD_PARAM;
3703
0
    if (ce->name) gf_free(ce->name);
3704
0
    gf_free(ce);
3705
0
    gf_list_rem(ptr->list, index-1);
3706
0
  } else {
3707
0
    while (gf_list_count(ptr->list)) {
3708
0
      ce = (GF_ChapterEntry *)gf_list_get(ptr->list, 0);
3709
0
      if (ce->name) gf_free(ce->name);
3710
0
      gf_free(ce);
3711
0
      gf_list_rem(ptr->list, 0);
3712
0
    }
3713
0
  }
3714
0
  if (!gf_list_count(ptr->list)) {
3715
0
    gf_list_del_item(udta->recordList, map);
3716
0
    gf_isom_box_array_del(map->boxes);
3717
0
    gf_free(map);
3718
0
  }
3719
0
  return GF_OK;
3720
0
}
3721
3722
#if 0 //unused
3723
GF_Err gf_isom_remove_copyright(GF_ISOFile *movie, u32 index)
3724
{
3725
  GF_Err e;
3726
  GF_CopyrightBox *ptr;
3727
  GF_UserDataMap *map;
3728
  u32 count;
3729
3730
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3731
  if (e) return e;
3732
  e = gf_isom_insert_moov(movie);
3733
  if (e) return e;
3734
3735
  if (!index) return GF_BAD_PARAM;
3736
  if (!movie->moov->udta) return GF_OK;
3737
3738
  map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL);
3739
  if (!map) return GF_OK;
3740
3741
  count = gf_list_count(map->boxes);
3742
  if (index>count) return GF_BAD_PARAM;
3743
3744
  ptr = (GF_CopyrightBox*)gf_list_get(map->boxes, index-1);
3745
  if (ptr) {
3746
    gf_list_rem(map->boxes, index-1);
3747
    if (ptr->notice) gf_free(ptr->notice);
3748
    gf_free(ptr);
3749
  }
3750
  /*last copyright, remove*/
3751
  if (!gf_list_count(map->boxes)) {
3752
    gf_list_del_item(movie->moov->udta->recordList, map);
3753
    gf_list_del(map->boxes);
3754
    gf_free(map);
3755
  }
3756
  return GF_OK;
3757
}
3758
3759
GF_Err gf_isom_set_watermark(GF_ISOFile *movie, bin128 UUID, u8* data, u32 length)
3760
{
3761
  GF_Err e;
3762
  GF_UnknownUUIDBox *ptr;
3763
  GF_UserDataMap *map;
3764
3765
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3766
  if (e) return e;
3767
3768
  e = gf_isom_insert_moov(movie);
3769
  if (e) return e;
3770
  if (!movie->moov->udta) {
3771
    e = moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA));
3772
    if (e) return e;
3773
  }
3774
3775
  map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_UUID, (bin128 *) & UUID);
3776
  if (map) {
3777
    ptr = (GF_UnknownUUIDBox *)gf_list_get(map->boxes, 0);
3778
    if (ptr) {
3779
      gf_free(ptr->data);
3780
      ptr->data = (char*)gf_malloc(length);
3781
      if (!ptr->data) return GF_OUT_OF_MEM;
3782
      memcpy(ptr->data, data, length);
3783
      ptr->dataSize = length;
3784
      return GF_OK;
3785
    }
3786
  }
3787
  //nope, create one
3788
  ptr = (GF_UnknownUUIDBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID);
3789
  if (!ptr) return GF_OUT_OF_MEM;
3790
3791
  memcpy(ptr->uuid, UUID, 16);
3792
  ptr->data = (char*)gf_malloc(length);
3793
  if (!ptr->data) return GF_OUT_OF_MEM;
3794
  memcpy(ptr->data, data, length);
3795
  ptr->dataSize = length;
3796
  return udta_on_child_box_ex((GF_Box *)movie->moov->udta, (GF_Box *) ptr, GF_FALSE, GF_FALSE);
3797
}
3798
#endif
3799
3800
//set the interleaving time of media data (INTERLEAVED mode only)
3801
//InterleaveTime is in MovieTimeScale
3802
GF_EXPORT
3803
GF_Err gf_isom_set_interleave_time(GF_ISOFile *movie, u32 InterleaveTime)
3804
0
{
3805
0
  GF_Err e;
3806
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3807
0
  if (e) return e;
3808
3809
0
  if (!InterleaveTime || !movie->moov) return GF_OK;
3810
0
  movie->interleavingTime = InterleaveTime;
3811
0
  return GF_OK;
3812
0
}
3813
3814
3815
3816
//use a compact track version for sample size. This is not usually recommended
3817
//except for speech codecs where the track has a lot of small samples
3818
//compaction is done automatically while writing based on the track's sample sizes
3819
GF_EXPORT
3820
GF_Err gf_isom_use_compact_size(GF_ISOFile *movie, u32 trackNumber, Bool CompactionOn)
3821
0
{
3822
0
  GF_TrackBox *trak;
3823
0
  u32 i, size;
3824
0
  GF_SampleSizeBox *stsz;
3825
0
  GF_Err e;
3826
3827
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3828
0
  if (e) return e;
3829
3830
0
  trak = gf_isom_get_track_box(movie, trackNumber);
3831
0
  if (!trak) return GF_BAD_PARAM;
3832
3833
0
  if (!trak->Media || !trak->Media->information
3834
0
          || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleSize)
3835
0
    return GF_ISOM_INVALID_FILE;
3836
3837
0
  stsz = trak->Media->information->sampleTable->SampleSize;
3838
3839
  //switch to regular table
3840
0
  if (!CompactionOn) {
3841
0
    if (stsz->type == GF_ISOM_BOX_TYPE_STSZ) return GF_OK;
3842
0
    stsz->type = GF_ISOM_BOX_TYPE_STSZ;
3843
    //invalidate the sampleSize and recompute it
3844
0
    stsz->sampleSize = 0;
3845
0
    if (!stsz->sampleCount) return GF_OK;
3846
    //if the table is empty we can only assume the track is empty (no size indication)
3847
0
    if (!stsz->sizes) return GF_OK;
3848
0
    size = stsz->sizes[0];
3849
    //check whether the sizes are all the same or not
3850
0
    for (i=1; i<stsz->sampleCount; i++) {
3851
0
      if (size != stsz->sizes[i]) {
3852
0
        size = 0;
3853
0
        break;
3854
0
      }
3855
0
    }
3856
0
    if (size) {
3857
0
      gf_free(stsz->sizes);
3858
0
      stsz->sizes = NULL;
3859
0
      stsz->sampleSize = size;
3860
0
    }
3861
0
    return GF_OK;
3862
0
  }
3863
3864
  //switch to compact table
3865
0
  if (stsz->type == GF_ISOM_BOX_TYPE_STZ2) return GF_OK;
3866
  //fill the table. Although it seems weird , this is needed in case of edition
3867
  //after the function is called. NOte however than we force regular table
3868
  //at write time if all samples are of same size
3869
0
  if (stsz->sampleSize && stsz->sampleCount) {
3870
    //this is a weird table indeed ;)
3871
0
    if (stsz->sizes) gf_free(stsz->sizes);
3872
0
    stsz->sizes = (u32*) gf_malloc(sizeof(u32)*stsz->sampleCount);
3873
0
    if (!stsz->sizes) return GF_OUT_OF_MEM;
3874
0
    memset(stsz->sizes, stsz->sampleSize, sizeof(u32));
3875
0
  }
3876
  //set the SampleSize to 0 while the file is open
3877
0
  stsz->sampleSize = 0;
3878
0
  stsz->type = GF_ISOM_BOX_TYPE_STZ2;
3879
0
  return GF_OK;
3880
0
}
3881
3882
3883
GF_EXPORT
3884
GF_Err gf_isom_disable_brand_rewrite(GF_ISOFile *movie, Bool do_disable)
3885
0
{
3886
0
  if (!movie) return GF_BAD_PARAM;
3887
0
  movie->disable_brand_rewrite = do_disable ? 1: 0;
3888
0
  return GF_OK;
3889
0
}
3890
3891
GF_EXPORT
3892
GF_Err gf_isom_set_brand_info(GF_ISOFile *movie, u32 MajorBrand, u32 MinorVersion)
3893
0
{
3894
0
  u32 i, *p;
3895
3896
0
  if (!movie) return GF_BAD_PARAM;
3897
0
  if (movie->disable_brand_rewrite) return GF_OK;
3898
0
  if (!MajorBrand) return GF_BAD_PARAM;
3899
3900
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
3901
0
  if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) {
3902
0
    GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3903
0
    if (e) return e;
3904
3905
0
    e = CheckNoData(movie);
3906
0
    if (e) return e;
3907
0
  }
3908
0
#endif
3909
3910
0
  if (!movie->brand) {
3911
0
    movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP);
3912
0
    if (!movie->brand) return GF_OUT_OF_MEM;
3913
0
    gf_list_add(movie->TopBoxes, movie->brand);
3914
0
  }
3915
3916
0
  movie->brand->majorBrand = MajorBrand;
3917
0
  movie->brand->minorVersion = MinorVersion;
3918
3919
0
  if (!movie->brand->altBrand) {
3920
0
    movie->brand->altBrand = (u32*)gf_malloc(sizeof(u32));
3921
0
    if (!movie->brand->altBrand) return GF_OUT_OF_MEM;
3922
0
    movie->brand->altBrand[0] = MajorBrand;
3923
0
    movie->brand->altCount = 1;
3924
0
    return GF_OK;
3925
0
  }
3926
3927
  //if brand already present don't change anything
3928
0
  for (i=0; i<movie->brand->altCount; i++) {
3929
0
    if (movie->brand->altBrand[i] == MajorBrand) return GF_OK;
3930
0
  }
3931
0
  p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount + 1));
3932
0
  if (!p) return GF_OUT_OF_MEM;
3933
0
  memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount);
3934
0
  p[movie->brand->altCount] = MajorBrand;
3935
0
  movie->brand->altCount += 1;
3936
0
  gf_free(movie->brand->altBrand);
3937
0
  movie->brand->altBrand = p;
3938
0
  return GF_OK;
3939
0
}
3940
3941
3942
GF_EXPORT
3943
GF_Err gf_isom_modify_alternate_brand(GF_ISOFile *movie, u32 Brand, Bool AddIt)
3944
0
{
3945
0
  u32 i, k, *p;
3946
3947
0
  if (!movie) return GF_BAD_PARAM;
3948
0
  if (movie->disable_brand_rewrite) return GF_OK;
3949
0
  if (!Brand) return GF_BAD_PARAM;
3950
3951
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
3952
0
  if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) {
3953
0
    GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
3954
0
    if (e) return e;
3955
3956
0
    e = CheckNoData(movie);
3957
0
    if (e) return e;
3958
0
  }
3959
0
#endif
3960
3961
0
  if (!movie->brand && AddIt) {
3962
0
    movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP);
3963
0
    if (!movie->brand) return GF_OUT_OF_MEM;
3964
0
    gf_list_add(movie->TopBoxes, movie->brand);
3965
0
  }
3966
0
  if (!AddIt && !movie->brand) return GF_OK;
3967
3968
  //do not mofify major one
3969
0
  if (!AddIt && movie->brand->majorBrand == Brand) return GF_OK;
3970
3971
0
  if (!AddIt && movie->brand->altCount == 1) {
3972
    //fixes it in case
3973
0
    movie->brand->altBrand[0] = movie->brand->majorBrand;
3974
0
    return GF_OK;
3975
0
  }
3976
  //check for the brand
3977
0
  for (i=0; i<movie->brand->altCount; i++) {
3978
0
    if (movie->brand->altBrand[i] == Brand) goto found;
3979
0
  }
3980
  //Not found
3981
0
  if (!AddIt) return GF_OK;
3982
  //add it
3983
0
  p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount + 1));
3984
0
  if (!p) return GF_OUT_OF_MEM;
3985
0
  if (movie->brand->altBrand) {
3986
0
    memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount);
3987
0
    gf_free(movie->brand->altBrand);
3988
0
  }
3989
0
  p[movie->brand->altCount] = Brand;
3990
0
  movie->brand->altCount += 1;
3991
0
  movie->brand->altBrand = p;
3992
0
  return GF_OK;
3993
3994
0
found:
3995
3996
  //found
3997
0
  if (AddIt) return GF_OK;
3998
0
  gf_assert(movie->brand->altCount>1);
3999
4000
  //remove it
4001
0
  p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount - 1));
4002
0
  if (!p) return GF_OUT_OF_MEM;
4003
0
  k = 0;
4004
0
  for (i=0; i<movie->brand->altCount; i++) {
4005
0
    if (movie->brand->altBrand[i] == Brand) continue;
4006
0
    else {
4007
0
      p[k] = movie->brand->altBrand[i];
4008
0
      k++;
4009
0
    }
4010
0
  }
4011
0
  movie->brand->altCount -= 1;
4012
0
  gf_free(movie->brand->altBrand);
4013
0
  movie->brand->altBrand = p;
4014
0
  return GF_OK;
4015
0
}
4016
4017
GF_EXPORT
4018
GF_Err gf_isom_reset_alt_brands_ex(GF_ISOFile *movie, Bool leave_empty)
4019
0
{
4020
0
  u32 *p;
4021
4022
0
  if (!movie) return GF_BAD_PARAM;
4023
0
  if (movie->disable_brand_rewrite) return GF_OK;
4024
4025
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
4026
0
  if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) {
4027
0
    GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
4028
0
    if (e) return e;
4029
4030
0
    e = CheckNoData(movie);
4031
0
    if (e) return e;
4032
0
  }
4033
0
#endif
4034
4035
0
  if (!movie->brand) {
4036
0
    movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP);
4037
0
    if (!movie->brand) return GF_OUT_OF_MEM;
4038
0
    gf_list_add(movie->TopBoxes, movie->brand);
4039
0
  }
4040
0
  gf_free(movie->brand->altBrand);
4041
0
  if (leave_empty) {
4042
0
    movie->brand->altCount = 0;
4043
0
    movie->brand->altBrand = NULL;
4044
0
  } else {
4045
0
    p = (u32*)gf_malloc(sizeof(u32));
4046
0
    if (!p) return GF_OUT_OF_MEM;
4047
0
    p[0] = movie->brand->majorBrand;
4048
0
    movie->brand->altCount = 1;
4049
0
    movie->brand->altBrand = p;
4050
0
  }
4051
0
  return GF_OK;
4052
0
}
4053
GF_EXPORT
4054
GF_Err gf_isom_reset_alt_brands(GF_ISOFile *movie)
4055
0
{
4056
0
  return gf_isom_reset_alt_brands_ex(movie, GF_FALSE);
4057
0
}
4058
4059
#if 0 //unused
4060
GF_Err gf_isom_set_sample_padding_bits(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u8 NbBits)
4061
{
4062
  GF_TrackBox *trak;
4063
  GF_Err e;
4064
4065
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
4066
  if (e) return e;
4067
4068
  trak = gf_isom_get_track_box(movie, trackNumber);
4069
  if (!trak || NbBits > 7) return GF_BAD_PARAM;
4070
4071
  //set Padding info
4072
  return stbl_SetPaddingBits(trak->Media->information->sampleTable, sampleNumber, NbBits);
4073
}
4074
#endif
4075
4076
GF_EXPORT
4077
GF_Err gf_isom_remove_user_data_item(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, u32 UserDataIndex)
4078
0
{
4079
0
  GF_UserDataMap *map;
4080
0
  GF_Box *a;
4081
0
  u32 i;
4082
0
  bin128 t;
4083
0
  GF_Err e;
4084
0
  GF_TrackBox *trak;
4085
0
  GF_UserDataBox *udta;
4086
4087
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
4088
0
  if (e) return e;
4089
4090
0
  if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0;
4091
0
  memset(t, 1, 16);
4092
4093
0
  if (trackNumber) {
4094
0
    trak = gf_isom_get_track_box(movie, trackNumber);
4095
0
    if (!trak) return GF_BAD_PARAM;
4096
0
    udta = trak->udta;
4097
0
  } else {
4098
0
    if (!movie->moov) return GF_BAD_PARAM;
4099
0
    udta = movie->moov->udta;
4100
0
  }
4101
0
  if (!udta) return GF_BAD_PARAM;
4102
0
  if (!UserDataIndex) return GF_BAD_PARAM;
4103
4104
0
  i=0;
4105
0
  while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) {
4106
0
    if ((map->boxType == GF_ISOM_BOX_TYPE_UUID)  && !memcmp(map->uuid, UUID, 16)) goto found;
4107
0
    else if (map->boxType == UserDataType) goto found;
4108
0
  }
4109
  //not found
4110
0
  return GF_OK;
4111
4112
0
found:
4113
4114
0
  if (UserDataIndex > gf_list_count(map->boxes) ) return GF_BAD_PARAM;
4115
  //delete the box
4116
0
  a = (GF_Box*)gf_list_get(map->boxes, UserDataIndex-1);
4117
0
  gf_isom_box_del_parent(&map->boxes, a);
4118
4119
  //remove the map if empty
4120
0
  if (!gf_list_count(map->boxes)) {
4121
0
    gf_list_rem(udta->recordList, i-1);
4122
0
    gf_isom_box_array_del(map->boxes);
4123
0
    gf_free(map);
4124
0
  }
4125
  //but we keep the UDTA no matter what
4126
0
  return GF_OK;
4127
0
}
4128
4129
GF_EXPORT
4130
GF_Err gf_isom_remove_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID)
4131
0
{
4132
0
  GF_UserDataMap *map;
4133
0
  u32 i;
4134
0
  GF_Err e;
4135
0
  bin128 t;
4136
0
  GF_TrackBox *trak;
4137
0
  GF_UserDataBox *udta;
4138
4139
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
4140
0
  if (e) return e;
4141
4142
0
  if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0;
4143
0
  memset(t, 1, 16);
4144
4145
0
  if (trackNumber) {
4146
0
    trak = gf_isom_get_track_box(movie, trackNumber);
4147
0
    if (!trak) return GF_EOS;
4148
0
    udta = trak->udta;
4149
0
  } else {
4150
0
    if (!movie->moov) return GF_BAD_PARAM;
4151
0
    udta = movie->moov->udta;
4152
0
  }
4153
  //do not return any error if no udta
4154
0
  if (!udta) return GF_EOS;
4155
4156
0
  i=0;
4157
0
  while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) {
4158
0
    if ((map->boxType == GF_ISOM_BOX_TYPE_UUID)  && !memcmp(map->uuid, UUID, 16)) goto found;
4159
0
    else if (map->boxType == UserDataType) goto found;
4160
0
  }
4161
  //not found
4162
0
  return GF_OK;
4163
4164
0
found:
4165
4166
0
  gf_list_rem(udta->recordList, i-1);
4167
0
  gf_isom_box_array_del(map->boxes);
4168
0
  gf_free(map);
4169
4170
  //but we keep the UDTA no matter what
4171
0
  return GF_OK;
4172
0
}
4173
4174
GF_EXPORT
4175
GF_Err gf_isom_add_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, u8 *data, u32 DataLength)
4176
0
{
4177
0
  GF_Err e;
4178
0
  GF_TrackBox *trak;
4179
0
  GF_UserDataBox *udta;
4180
4181
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
4182
0
  if (e) return e;
4183
4184
0
  if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0;
4185
4186
0
  if (trackNumber) {
4187
0
    trak = gf_isom_get_track_box(movie, trackNumber);
4188
0
    if (!trak) return GF_BAD_PARAM;
4189
0
    if (!trak->udta) trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE);
4190
0
    udta = trak->udta;
4191
0
  } else {
4192
0
    if (!movie->moov) return GF_BAD_PARAM;
4193
0
    if (!movie->moov->udta) moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE);
4194
0
    udta = movie->moov->udta;
4195
0
  }
4196
0
  if (!udta) return GF_OUT_OF_MEM;
4197
4198
  //create a default box
4199
0
  if (UserDataType) {
4200
0
    GF_UnknownBox *a = (GF_UnknownBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UNKNOWN);
4201
0
    if (!a) return GF_OUT_OF_MEM;
4202
0
    a->original_4cc = UserDataType;
4203
0
    if (DataLength) {
4204
0
      a->data = (char*)gf_malloc(sizeof(char)*DataLength);
4205
0
      if (!a->data) return GF_OUT_OF_MEM;
4206
0
      memcpy(a->data, data, DataLength);
4207
0
      a->dataSize = DataLength;
4208
0
    }
4209
0
    return udta_on_child_box_ex((GF_Box *)udta, (GF_Box *) a, GF_FALSE, GF_TRUE);
4210
0
  } else if (UUID) {
4211
0
    GF_UnknownUUIDBox *a = (GF_UnknownUUIDBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID);
4212
0
    if (!a) return GF_OUT_OF_MEM;
4213
0
    memcpy(a->uuid, UUID, 16);
4214
0
    if (DataLength) {
4215
0
      a->data = (char*)gf_malloc(sizeof(char)*DataLength);
4216
0
      if (!a->data) return GF_OUT_OF_MEM;
4217
0
      memcpy(a->data, data, DataLength);
4218
0
      a->dataSize = DataLength;
4219
0
    }
4220
0
    return udta_on_child_box_ex((GF_Box *)udta, (GF_Box *) a, GF_FALSE, GF_TRUE);
4221
0
  } else {
4222
0
    return GF_BAD_PARAM;
4223
0
  }
4224
0
  return GF_OK;
4225
0
}
4226
4227
GF_EXPORT
4228
GF_Err gf_isom_add_user_data_boxes(GF_ISOFile *movie, u32 trackNumber, u8 *data, u32 DataLength)
4229
0
{
4230
0
  GF_Err e;
4231
0
  GF_TrackBox *trak;
4232
0
  GF_UserDataBox *udta;
4233
0
  GF_BitStream *bs;
4234
4235
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
4236
0
  if (e) return e;
4237
4238
0
  if (trackNumber) {
4239
0
    trak = gf_isom_get_track_box(movie, trackNumber);
4240
0
    if (!trak) return GF_BAD_PARAM;
4241
0
    if (!trak->udta) trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE);
4242
0
    udta = trak->udta;
4243
0
  } else {
4244
0
    if (!movie->moov) return GF_BAD_PARAM;
4245
0
    if (!movie->moov->udta) moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE);
4246
0
    udta = movie->moov->udta;
4247
0
  }
4248
0
  if (!udta) return GF_OUT_OF_MEM;
4249
4250
0
  bs = gf_bs_new(data, DataLength, GF_BITSTREAM_READ);
4251
0
  while (gf_bs_available(bs)) {
4252
0
    GF_Box *a;
4253
0
    e = gf_isom_box_parse(&a, bs);
4254
0
    if (e) break;
4255
0
    e = udta_on_child_box_ex((GF_Box *)udta, a, GF_FALSE, GF_FALSE);
4256
0
    if (e) break;
4257
0
  }
4258
0
  gf_bs_del(bs);
4259
0
  return e;
4260
0
}
4261
4262
GF_EXPORT
4263
GF_Err gf_isom_clone_pl_indications(GF_ISOFile *orig, GF_ISOFile *dest)
4264
0
{
4265
0
  GF_IsomInitialObjectDescriptor *iod_d;
4266
0
  if (!orig || !dest) return GF_BAD_PARAM;
4267
0
  if (!orig->moov->iods || !orig->moov->iods->descriptor) return GF_OK;
4268
0
  if (orig->moov->iods->descriptor->tag != GF_ODF_ISOM_IOD_TAG) return GF_OK;
4269
4270
0
  AddMovieIOD(dest->moov, 1);
4271
0
  gf_odf_desc_del((GF_Descriptor *)dest->moov->iods->descriptor);
4272
0
  gf_odf_desc_copy((GF_Descriptor *)orig->moov->iods->descriptor, (GF_Descriptor **)&dest->moov->iods->descriptor);
4273
0
  iod_d = (GF_IsomInitialObjectDescriptor *) dest->moov->iods->descriptor;
4274
0
  while (gf_list_count(iod_d->ES_ID_IncDescriptors)) {
4275
0
    GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_IncDescriptors, 0);
4276
0
    gf_list_rem(iod_d->ES_ID_IncDescriptors, 0);
4277
0
    gf_odf_desc_del(d);
4278
0
  }
4279
0
  while (gf_list_count(iod_d->ES_ID_RefDescriptors)) {
4280
0
    GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_RefDescriptors, 0);
4281
0
    gf_list_rem(iod_d->ES_ID_RefDescriptors, 0);
4282
0
    gf_odf_desc_del(d);
4283
0
  }
4284
0
  return GF_OK;
4285
0
}
4286
4287
GF_EXPORT
4288
GF_Err gf_isom_clone_box(GF_Box *src, GF_Box **dst)
4289
0
{
4290
0
  GF_Err e;
4291
0
  u8 *data;
4292
0
  u32 data_size;
4293
0
  GF_BitStream *bs;
4294
4295
0
  if (*dst) {
4296
0
    gf_isom_box_del(*dst);
4297
0
    *dst=NULL;
4298
0
  }
4299
0
  bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
4300
0
  if (!bs) return GF_OUT_OF_MEM;
4301
0
  e = gf_isom_box_size( (GF_Box *) src);
4302
0
  if (!e) e = gf_isom_box_write((GF_Box *) src, bs);
4303
0
  gf_bs_get_content(bs, &data, &data_size);
4304
0
  gf_bs_del(bs);
4305
0
  if (e) {
4306
0
    if (data) gf_free(data);
4307
0
    return e;
4308
0
  }
4309
0
  bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
4310
0
  if (!bs) {
4311
0
    if (data) gf_free(data);
4312
0
    return GF_OUT_OF_MEM;
4313
0
  }
4314
0
  e = gf_isom_box_parse(dst, bs);
4315
0
  gf_bs_del(bs);
4316
0
  gf_free(data);
4317
0
  return e;
4318
0
}
4319
4320
#if 0 //unused
4321
/*clones the entire movie file to destination. Tracks can be cloned if clone_tracks is set, in which case hint tracks can be
4322
kept if keep_hint_tracks is set
4323
if keep_pssh, all pssh boxes will be kept
4324
fragment information (mvex) is not kept*/
4325
GF_Err gf_isom_clone_movie(GF_ISOFile *orig_file, GF_ISOFile *dest_file, Bool clone_tracks, Bool keep_hint_tracks, Bool keep_pssh)
4326
{
4327
  GF_Err e;
4328
  u32 i;
4329
  GF_Box *box;
4330
4331
  e = gf_isom_can_access_movie(dest_file, GF_ISOM_OPEN_WRITE);
4332
  if (e) return e;
4333
4334
  if (orig_file->brand) {
4335
    gf_list_del_item(dest_file->TopBoxes, dest_file->brand);
4336
    gf_isom_box_del((GF_Box *)dest_file->brand);
4337
    dest_file->brand = NULL;
4338
    gf_isom_clone_box((GF_Box *)orig_file->brand, (GF_Box **)&dest_file->brand);
4339
    if (dest_file->brand) gf_list_add(dest_file->TopBoxes, dest_file->brand);
4340
  }
4341
4342
  if (orig_file->meta) {
4343
    gf_list_del_item(dest_file->TopBoxes, dest_file->meta);
4344
    gf_isom_box_del((GF_Box *)dest_file->meta);
4345
    dest_file->meta = NULL;
4346
    /*fixme - check imports*/
4347
    gf_isom_clone_box((GF_Box *)orig_file->meta, (GF_Box **)&dest_file->meta);
4348
    if (dest_file->meta) gf_list_add(dest_file->TopBoxes, dest_file->meta);
4349
  }
4350
  if (orig_file->moov) {
4351
    u32 i, dstTrack;
4352
    GF_Box *iods;
4353
    GF_List *tracks = gf_list_new();
4354
    GF_List *old_tracks = orig_file->moov->trackList;
4355
    orig_file->moov->trackList = tracks;
4356
    iods = (GF_Box*)orig_file->moov->iods;
4357
    orig_file->moov->iods = NULL;
4358
    e = gf_isom_clone_box((GF_Box *)orig_file->moov, (GF_Box **)&dest_file->moov);
4359
    if (e) {
4360
      gf_list_del(tracks);
4361
      orig_file->moov->trackList = old_tracks;
4362
      return e;
4363
    }
4364
    orig_file->moov->trackList = old_tracks;
4365
    gf_list_del(tracks);
4366
    orig_file->moov->iods = (GF_ObjectDescriptorBox*)iods;
4367
    gf_list_add(dest_file->TopBoxes, dest_file->moov);
4368
4369
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
4370
    if (dest_file->moov->mvex) {
4371
      gf_isom_box_del_parent(&dest_file->moov->child_boxes, (GF_Box *)dest_file->moov->mvex);
4372
      dest_file->moov->mvex = NULL;
4373
    }
4374
#endif
4375
4376
    if (clone_tracks) {
4377
      for (i=0; i<gf_list_count(orig_file->moov->trackList); i++) {
4378
        GF_TrackBox *trak = (GF_TrackBox*)gf_list_get( orig_file->moov->trackList, i);
4379
        if (!trak) continue;
4380
        if (keep_hint_tracks || (trak->Media->handler->handlerType != GF_ISOM_MEDIA_HINT)) {
4381
          e = gf_isom_clone_track(orig_file, i+1, dest_file, 0, &dstTrack);
4382
          if (e) return e;
4383
        }
4384
      }
4385
      if (iods)
4386
        gf_isom_clone_box((GF_Box *)orig_file->moov->iods, (GF_Box **)dest_file->moov->iods);
4387
    } else {
4388
      dest_file->moov->mvhd->nextTrackID = 1;
4389
      gf_isom_clone_pl_indications(orig_file, dest_file);
4390
    }
4391
    dest_file->moov->mov = dest_file;
4392
  }
4393
4394
  if (!keep_pssh) {
4395
    i=0;
4396
    while ((box = (GF_Box*)gf_list_get(dest_file->moov->child_boxes, i++))) {
4397
      if (box->type == GF_ISOM_BOX_TYPE_PSSH) {
4398
        i--;
4399
        gf_isom_box_del_parent(&dest_file->moov->child_boxes, box);
4400
      }
4401
    }
4402
  }
4403
4404
  //duplicate other boxes
4405
  i=0;
4406
  while ((box = (GF_Box*)gf_list_get(orig_file->TopBoxes, i++))) {
4407
    switch(box->type) {
4408
    case GF_ISOM_BOX_TYPE_MOOV:
4409
    case GF_ISOM_BOX_TYPE_META:
4410
    case GF_ISOM_BOX_TYPE_MDAT:
4411
    case GF_ISOM_BOX_TYPE_FTYP:
4412
    case GF_ISOM_BOX_TYPE_PDIN:
4413
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
4414
    case GF_ISOM_BOX_TYPE_STYP:
4415
    case GF_ISOM_BOX_TYPE_SIDX:
4416
    case GF_ISOM_BOX_TYPE_SSIX:
4417
    case GF_ISOM_BOX_TYPE_MOOF:
4418
#endif
4419
    case GF_ISOM_BOX_TYPE_JP:
4420
      break;
4421
4422
    case GF_ISOM_BOX_TYPE_PSSH:
4423
      if (!keep_pssh)
4424
        break;
4425
4426
    default:
4427
    {
4428
      GF_Box *box2 = NULL;
4429
      gf_isom_clone_box(box, &box2);
4430
      gf_list_add(dest_file->TopBoxes, box2);
4431
    }
4432
    break;
4433
    }
4434
  }
4435
4436
  return GF_OK;
4437
}
4438
#endif
4439
4440
4441
GF_EXPORT
4442
GF_Err gf_isom_get_raw_user_data(GF_ISOFile *file, u8 **output, u32 *output_size)
4443
138
{
4444
138
  GF_BitStream *bs;
4445
138
  GF_Err e;
4446
138
  GF_Box *b;
4447
138
  u32 i;
4448
4449
138
  *output = NULL;
4450
138
  *output_size = 0;
4451
138
  if (!file || !file->moov || (!file->moov->udta && !file->moov->child_boxes)) return GF_OK;
4452
138
  bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
4453
4454
138
  if (file->moov->udta) {
4455
93
    e = gf_isom_box_size( (GF_Box *) file->moov->udta);
4456
93
    if (e) goto exit;
4457
93
    e = gf_isom_box_write((GF_Box *) file->moov->udta, bs);
4458
93
    if (e) goto exit;
4459
93
  }
4460
138
  e = GF_OK;
4461
138
  i=0;
4462
971
  while ((b = gf_list_enum(file->moov->child_boxes, &i))) {
4463
833
    switch (b->type) {
4464
464
    case GF_ISOM_BOX_TYPE_TRAK:
4465
602
    case GF_ISOM_BOX_TYPE_MVHD:
4466
602
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
4467
646
    case GF_ISOM_BOX_TYPE_MVEX:
4468
646
#endif
4469
651
    case GF_ISOM_BOX_TYPE_IODS:
4470
725
    case GF_ISOM_BOX_TYPE_META:
4471
725
      continue;
4472
833
    }
4473
108
    e = gf_isom_box_size( (GF_Box *) b);
4474
108
    if (e) goto exit;
4475
108
    e = gf_isom_box_write((GF_Box *) b, bs);
4476
108
    if (e) goto exit;
4477
108
  }
4478
4479
138
  gf_bs_get_content(bs, output, output_size);
4480
4481
138
exit:
4482
138
  gf_bs_del(bs);
4483
138
  return e;
4484
138
}
4485
4486
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
4487
static GF_Err gf_isom_get_trex_props(GF_ISOFile *file, GF_TrackBox *trak, GF_TrackExtendsBox **trex, GF_TrackExtensionPropertiesBox **trexprop)
4488
176
{
4489
176
  u32 i;
4490
176
  if (!file->moov->mvex) return GF_NOT_FOUND;
4491
88
  *trex = NULL;
4492
128
  for (i=0; i<gf_list_count(file->moov->mvex->TrackExList); i++) {
4493
128
    *trex = gf_list_get(file->moov->mvex->TrackExList, i);
4494
128
    if ((*trex)->trackID == trak->Header->trackID) break;
4495
40
    *trex = NULL;
4496
40
  }
4497
88
  if (! *trex) return GF_NOT_FOUND;
4498
4499
88
  for (i=0; i<gf_list_count(file->moov->mvex->TrackExPropList); i++) {
4500
16
    *trexprop = gf_list_get(file->moov->mvex->TrackExPropList, i);
4501
16
    if ((*trexprop)->trackID== trak->Header->trackID) break;
4502
0
    *trexprop = NULL;
4503
0
  }
4504
88
  return GF_OK;
4505
88
}
4506
#endif
4507
4508
GF_EXPORT
4509
GF_Err gf_isom_get_track_template(GF_ISOFile *file, u32 track, u8 **output, u32 *output_size)
4510
138
{
4511
138
  GF_TrackBox *trak;
4512
138
  GF_BitStream *bs;
4513
138
  GF_DataReferenceBox *dref;
4514
138
  GF_SampleTableBox *stbl, *stbl_temp;
4515
138
  GF_SampleEncryptionBox *senc;
4516
138
  GF_List *gpac_internals = NULL;
4517
138
  u32 i, count;
4518
4519
138
  *output = NULL;
4520
138
  *output_size = 0;
4521
  /*get orig sample desc and clone it*/
4522
138
  trak = gf_isom_get_track_box(file, track);
4523
138
  if (!trak || !trak->Media) return GF_BAD_PARAM;
4524
4525
  //don't serialize dref
4526
138
  dref = NULL;
4527
138
  if (trak->Media->information->dataInformation) {
4528
138
    dref = trak->Media->information->dataInformation->dref;
4529
138
    trak->Media->information->dataInformation->dref = NULL;
4530
138
    gf_list_del_item(trak->Media->information->dataInformation->child_boxes, dref);
4531
138
  }
4532
4533
  //don't serialize stbl but create a temp one
4534
138
  stbl_temp = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL);
4535
138
  if (!stbl_temp->child_boxes) stbl_temp->child_boxes = gf_list_new();
4536
138
  stbl = trak->Media->information->sampleTable;
4537
138
  gf_list_del_item(trak->Media->information->child_boxes, stbl);
4538
4539
138
  trak->Media->information->sampleTable = stbl_temp;
4540
138
  gf_list_add(trak->Media->information->child_boxes, stbl_temp);
4541
4542
  /*do not clone sampleDescription table but create an empty one*/
4543
138
  stbl_temp->SampleDescription = (GF_SampleDescriptionBox *) gf_isom_box_new_parent(&stbl_temp->child_boxes, GF_ISOM_BOX_TYPE_STSD);
4544
4545
  /*clone sampleGroups description tables if any*/
4546
138
  stbl_temp->sampleGroupsDescription = stbl->sampleGroupsDescription;
4547
138
  count = gf_list_count(stbl->sampleGroupsDescription);
4548
142
  for (i=0;i<count; i++) {
4549
4
    GF_SampleGroupDescriptionBox *b = gf_list_get(stbl->sampleGroupsDescription, i);
4550
    //don't add our internal sample groups
4551
4
    if (b->grouping_type==GF_4CC('E','M','S','G'))
4552
0
      continue;
4553
4
    if (b->grouping_type==GF_4CC('P','S','S','H'))
4554
0
      continue;
4555
4
    gf_list_add(stbl_temp->child_boxes, b);
4556
4
  }
4557
  /*clone CompositionToDecode table, we may remove it later*/
4558
138
  stbl_temp->CompositionToDecode = stbl->CompositionToDecode;
4559
138
  gf_list_add(stbl_temp->child_boxes, stbl->CompositionToDecode);
4560
4561
4562
138
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
4563
  //get CSLG from trex if not in stbl
4564
138
  GF_CompositionToDecodeBox *trex_cslg=NULL;
4565
138
  if (!stbl_temp->CompositionToDecode) {
4566
132
    GF_TrackExtendsBox *trex=NULL;
4567
132
    GF_TrackExtensionPropertiesBox *trexprop=NULL;
4568
132
    gf_isom_get_trex_props(file, trak, &trex, &trexprop);
4569
132
    if (trexprop) {
4570
8
      trex_cslg = (GF_CompositionToDecodeBox *) gf_isom_box_find_child(trexprop->child_boxes, GF_ISOM_BOX_TYPE_CSLG);
4571
8
      if (trex_cslg) {
4572
0
        stbl_temp->CompositionToDecode = trex_cslg;
4573
0
        gf_list_add(stbl_temp->child_boxes, trex_cslg);
4574
0
      }
4575
8
    }
4576
132
  }
4577
138
#endif
4578
4579
138
  count = gf_list_count(trak->child_boxes);
4580
650
  for (i=0;i<count; i++) {
4581
512
    GF_UnknownBox *b = gf_list_get(trak->child_boxes, i);
4582
512
    if (b->type != GF_ISOM_BOX_TYPE_UNKNOWN) continue;
4583
1
    if (b->original_4cc==GF_ISOM_BOX_TYPE_GDAT) {
4584
0
      if (!gpac_internals) gpac_internals = gf_list_new();
4585
0
      gf_list_add(gpac_internals, b);
4586
0
      gf_list_rem(trak->child_boxes, i);
4587
0
      i--;
4588
0
      count--;
4589
0
    }
4590
1
  }
4591
4592
  //don't serialize senc
4593
138
  senc = trak->sample_encryption;
4594
138
  if (senc) {
4595
0
    gf_assert(trak->child_boxes);
4596
0
    gf_list_del_item(trak->child_boxes, senc);
4597
0
    trak->sample_encryption = NULL;
4598
0
  }
4599
4600
138
  bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
4601
4602
138
  gf_isom_box_size( (GF_Box *) trak);
4603
138
  gf_isom_box_write((GF_Box *) trak, bs);
4604
138
  gf_bs_get_content(bs, output, output_size);
4605
138
  gf_bs_del(bs);
4606
4607
  //restore our pointers
4608
138
  if (dref) {
4609
138
    trak->Media->information->dataInformation->dref = dref;
4610
138
    gf_list_add(trak->Media->information->dataInformation->child_boxes, dref);
4611
138
  }
4612
138
  trak->Media->information->sampleTable = stbl;
4613
138
  gf_list_add(trak->Media->information->child_boxes, stbl);
4614
138
  gf_list_del_item(trak->Media->information->child_boxes, stbl_temp);
4615
138
  if (senc) {
4616
0
    trak->sample_encryption = senc;
4617
0
    gf_list_add(trak->child_boxes, senc);
4618
0
  }
4619
138
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
4620
138
  if (trex_cslg) {
4621
0
    stbl_temp->CompositionToDecode = NULL;
4622
0
    gf_list_del_item(stbl_temp->child_boxes, trex_cslg);
4623
0
  }
4624
138
#endif
4625
4626
138
  stbl_temp->sampleGroupsDescription = NULL;
4627
138
  count = gf_list_count(stbl->sampleGroupsDescription);
4628
142
  for (i=0;i<count; i++) {
4629
4
    GF_Box *b = gf_list_get(stbl->sampleGroupsDescription, i);
4630
4
    gf_list_del_item(stbl_temp->child_boxes, b);
4631
4
  }
4632
4633
138
  stbl_temp->CompositionToDecode = NULL;
4634
138
  gf_list_del_item(stbl_temp->child_boxes, stbl->CompositionToDecode);
4635
138
  gf_isom_box_del((GF_Box *)stbl_temp);
4636
4637
138
  if (gpac_internals) {
4638
0
    gf_list_transfer(trak->child_boxes, gpac_internals);
4639
0
    gf_list_del(gpac_internals);
4640
0
  }
4641
138
  return GF_OK;
4642
4643
138
}
4644
4645
GF_EXPORT
4646
GF_Err gf_isom_get_trex_template(GF_ISOFile *file, u32 track, u8 **output, u32 *output_size)
4647
138
{
4648
138
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
4649
138
  GF_TrackBox *trak;
4650
138
  GF_BitStream *bs;
4651
138
  GF_TrackExtendsBox *trex = NULL;
4652
138
  GF_TrackExtensionPropertiesBox *trexprop = NULL;
4653
4654
138
  *output = NULL;
4655
138
  *output_size = 0;
4656
  /*get orig sample desc and clone it*/
4657
138
  trak = gf_isom_get_track_box(file, track);
4658
138
  if (!trak || !trak->Media) return GF_BAD_PARAM;
4659
138
  if (!file->moov->mvex) return GF_NOT_FOUND;
4660
44
  GF_Err e = gf_isom_get_trex_props(file, trak, &trex, &trexprop);
4661
44
  if (e) return e;
4662
4663
44
  bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
4664
44
  gf_isom_box_size( (GF_Box *) trex);
4665
44
  gf_isom_box_write((GF_Box *) trex, bs);
4666
4667
44
  if (trexprop) {
4668
8
    gf_isom_box_size( (GF_Box *) trexprop);
4669
8
    gf_isom_box_write((GF_Box *) trexprop, bs);
4670
8
  }
4671
44
  gf_bs_get_content(bs, output, output_size);
4672
44
  gf_bs_del(bs);
4673
4674
#else
4675
  *output = NULL;
4676
  *output_size = 0;
4677
#endif
4678
44
  return GF_OK;
4679
44
}
4680
4681
GF_EXPORT
4682
GF_Err gf_isom_get_stsd_template(GF_ISOFile *file, u32 track, u32 stsd_idx, u8 **output, u32 *output_size)
4683
151
{
4684
151
  GF_TrackBox *trak;
4685
151
  GF_BitStream *bs;
4686
151
  GF_Box *ent;
4687
4688
151
  *output = NULL;
4689
151
  *output_size = 0;
4690
  /*get orig sample desc and clone it*/
4691
151
  trak = gf_isom_get_track_box(file, track);
4692
151
  if (!trak || !trak->Media || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription) return GF_BAD_PARAM;
4693
4694
151
  if (stsd_idx) {
4695
143
    ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, stsd_idx-1);
4696
143
    if (!ent) return GF_BAD_PARAM;
4697
143
  } else {
4698
8
    ent = (GF_Box*) trak->Media->information->sampleTable->SampleDescription;
4699
8
  }
4700
4701
151
  bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
4702
4703
151
  gf_isom_box_size(ent);
4704
151
  gf_isom_box_write(ent, bs);
4705
151
  gf_bs_get_content(bs, output, output_size);
4706
151
  gf_bs_del(bs);
4707
151
  return GF_OK;
4708
151
}
4709
4710
4711
GF_EXPORT
4712
GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *dest_file, GF_ISOTrackCloneFlags flags, u32 *dest_track)
4713
0
{
4714
0
  GF_TrackBox *trak, *new_tk;
4715
0
  GF_BitStream *bs;
4716
0
  u8 *data;
4717
0
  const u8 *buffer;
4718
0
  u32 data_size;
4719
0
  u32 i, count;
4720
0
  GF_Err e;
4721
0
  GF_SampleTableBox *stbl=NULL, *stbl_temp=NULL;
4722
0
  GF_SampleEncryptionBox *senc=NULL;
4723
4724
0
  e = gf_isom_can_access_movie(dest_file, GF_ISOM_OPEN_WRITE);
4725
0
  if (e) return e;
4726
0
  e = gf_isom_insert_moov(dest_file);
4727
0
  if (e) return e;
4728
4729
  /*get orig sample desc and clone it*/
4730
0
  trak = gf_isom_get_track_box(orig_file, orig_track);
4731
0
  if (!trak) return GF_BAD_PARAM;
4732
0
  if (!trak->Media && !trak->extl) return GF_BAD_PARAM;
4733
4734
  //for non-external tracks only
4735
0
  if (trak->Media) {
4736
0
    stbl = trak->Media->information->sampleTable;
4737
0
    stbl_temp = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL);
4738
0
    if (!stbl_temp->child_boxes) stbl_temp->child_boxes = gf_list_new();
4739
4740
0
    trak->Media->information->sampleTable = stbl_temp;
4741
0
    gf_list_add(trak->Media->information->child_boxes, stbl_temp);
4742
0
    gf_list_del_item(trak->Media->information->child_boxes, stbl);
4743
4744
0
    if (!stbl_temp->child_boxes) stbl_temp->child_boxes = gf_list_new();
4745
4746
    /*clone sampleDescription table*/
4747
0
    stbl_temp->SampleDescription = stbl->SampleDescription;
4748
0
    gf_list_add(stbl_temp->child_boxes, stbl->SampleDescription);
4749
    /*also clone sampleGroups description tables if any*/
4750
0
    stbl_temp->sampleGroupsDescription = stbl->sampleGroupsDescription;
4751
0
    count = gf_list_count(stbl->sampleGroupsDescription);
4752
0
    for (i=0; i<count; i++) {
4753
0
      GF_Box *b = gf_list_get(stbl->sampleGroupsDescription, i);
4754
0
      gf_list_add(stbl_temp->child_boxes, b);
4755
0
    }
4756
    /*clone CompositionToDecode table, we may remove it later*/
4757
0
    stbl_temp->CompositionToDecode = stbl->CompositionToDecode;
4758
0
    gf_list_add(stbl_temp->child_boxes, stbl->CompositionToDecode);
4759
4760
0
    senc = trak->sample_encryption;
4761
0
    if (senc) {
4762
0
      gf_assert(trak->child_boxes);
4763
0
      gf_list_del_item(trak->child_boxes, senc);
4764
0
      trak->sample_encryption = NULL;
4765
0
    }
4766
0
  }
4767
0
  bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
4768
4769
0
  gf_isom_box_size( (GF_Box *) trak);
4770
0
  gf_isom_box_write((GF_Box *) trak, bs);
4771
0
  gf_bs_get_content(bs, &data, &data_size);
4772
0
  gf_bs_del(bs);
4773
0
  bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
4774
0
  if (flags & GF_ISOM_CLONE_TRACK_NO_QT)
4775
0
    gf_bs_set_cookie(bs, GF_ISOM_BS_COOKIE_QT_CONV | GF_ISOM_BS_COOKIE_CLONE_TRACK);
4776
0
  else
4777
0
    gf_bs_set_cookie(bs, GF_ISOM_BS_COOKIE_CLONE_TRACK);
4778
4779
0
  e = gf_isom_box_parse((GF_Box **) &new_tk, bs);
4780
0
  gf_bs_del(bs);
4781
0
  gf_free(data);
4782
4783
0
  if (trak->Media) {
4784
0
    trak->Media->information->sampleTable = stbl;
4785
0
    gf_list_del_item(trak->Media->information->child_boxes, stbl_temp);
4786
0
    gf_list_add(trak->Media->information->child_boxes, stbl);
4787
4788
0
    if (senc) {
4789
0
      trak->sample_encryption = senc;
4790
0
      gf_list_add(trak->child_boxes, senc);
4791
0
    }
4792
0
    gf_list_del_item(stbl_temp->child_boxes, stbl_temp->SampleDescription);
4793
0
    stbl_temp->SampleDescription = NULL;
4794
4795
0
    count = gf_list_count(stbl->sampleGroupsDescription);
4796
0
    for (i=0; i<count; i++) {
4797
0
      GF_Box *b = gf_list_get(stbl->sampleGroupsDescription, i);
4798
0
      gf_list_del_item(stbl_temp->child_boxes, b);
4799
0
    }
4800
0
    stbl_temp->sampleGroupsDescription = NULL;
4801
4802
0
    gf_list_del_item(stbl_temp->child_boxes, stbl_temp->CompositionToDecode);
4803
0
    stbl_temp->CompositionToDecode = NULL;
4804
0
    gf_isom_box_del((GF_Box *)stbl_temp);
4805
0
  }
4806
4807
0
  if (e) {
4808
0
    if (new_tk) gf_isom_box_del((GF_Box *)new_tk);
4809
0
    return e;
4810
0
  }
4811
4812
0
  gf_isom_disable_inplace_rewrite(dest_file);
4813
4814
0
  if (trak->Media) {
4815
    /*create default boxes*/
4816
0
    stbl = new_tk->Media->information->sampleTable;
4817
0
    stbl->ChunkOffset = gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STCO);
4818
0
    if (!stbl->ChunkOffset) return GF_OUT_OF_MEM;
4819
0
    stbl->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSZ);
4820
0
    if (!stbl->SampleSize) return GF_OUT_OF_MEM;
4821
0
    stbl->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSC);
4822
0
    if (!stbl->SampleToChunk) return GF_OUT_OF_MEM;
4823
0
    stbl->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STTS);
4824
0
    if (!stbl->TimeToSample) return GF_OUT_OF_MEM;
4825
0
  }
4826
4827
0
  if (flags & GF_ISOM_CLONE_TRACK_DROP_ID)
4828
0
    new_tk->Header->trackID = 0;
4829
4830
  /*check trackID validity before adding track*/
4831
0
  if (!new_tk->Header->trackID || gf_isom_get_track_by_id(dest_file, new_tk->Header->trackID)) {
4832
0
    u32 ID = 1;
4833
0
    while (1) {
4834
0
      if (RequestTrack(dest_file->moov, ID)) break;
4835
0
      ID += 1;
4836
0
      if (ID == 0xFFFFFFFF) break;
4837
0
    }
4838
0
    new_tk->Header->trackID = ID;
4839
0
  }
4840
0
  if (!dest_file->moov->child_boxes) dest_file->moov->child_boxes = gf_list_new();
4841
0
  gf_list_add(dest_file->moov->child_boxes, new_tk);
4842
0
  moov_on_child_box((GF_Box*)dest_file->moov, (GF_Box *)new_tk, GF_FALSE);
4843
4844
  /*set originalID*/
4845
0
  new_tk->originalID = trak->Header->trackID;
4846
  /*set originalFile*/
4847
0
  buffer = gf_isom_get_filename(orig_file);
4848
0
  new_tk->originalFile = gf_crc_32(buffer, (u32) strlen(buffer));
4849
4850
  /*rewrite edit list segmentDuration to new movie timescale*/
4851
0
  if (dest_file->moov->mvhd->timeScale != orig_file->moov->mvhd->timeScale) {
4852
0
    Double ts_scale = dest_file->moov->mvhd->timeScale;
4853
0
    ts_scale /= orig_file->moov->mvhd->timeScale;
4854
0
    new_tk->Header->duration = (u64) (new_tk->Header->duration * ts_scale);
4855
0
    if (new_tk->editBox && new_tk->editBox->editList) {
4856
0
      count = gf_list_count(new_tk->editBox->editList->entryList);
4857
0
      for (i=0; i<count; i++) {
4858
0
        GF_EdtsEntry *ent = (GF_EdtsEntry *)gf_list_get(new_tk->editBox->editList->entryList, i);
4859
0
        ent->segmentDuration = (u64) (ent->segmentDuration * ts_scale);
4860
0
      }
4861
0
    }
4862
0
  }
4863
4864
0
  if (trak->Media) {
4865
0
    if (flags & GF_ISOM_CLONE_RESET_DURATION)
4866
0
      new_tk->Media->mediaHeader->duration = 0;
4867
4868
0
    if (!new_tk->Media->information->dataInformation->dref) return GF_BAD_PARAM;
4869
4870
    /*reset data ref*/
4871
0
    if (! (flags & GF_ISOM_CLONE_TRACK_KEEP_DREF) ) {
4872
0
      GF_SampleEntryBox *entry;
4873
0
      Bool use_alis = GF_FALSE;
4874
0
      if (! (flags & GF_ISOM_CLONE_TRACK_NO_QT)) {
4875
0
        GF_Box *b = gf_list_get(new_tk->Media->information->dataInformation->dref->child_boxes, 0);
4876
0
        if (b && (b->type==GF_QT_BOX_TYPE_ALIS))
4877
0
          use_alis = GF_TRUE;
4878
0
      }
4879
0
      gf_isom_box_array_del(new_tk->Media->information->dataInformation->dref->child_boxes);
4880
0
      new_tk->Media->information->dataInformation->dref->child_boxes = gf_list_new();
4881
      /*update data ref*/
4882
0
      entry = (GF_SampleEntryBox*)gf_list_get(new_tk->Media->information->sampleTable->SampleDescription->child_boxes, 0);
4883
0
      if (entry) {
4884
0
        u32 dref;
4885
0
        Media_CreateDataRef(dest_file, new_tk->Media->information->dataInformation->dref, use_alis ?  "alis" : NULL, NULL, &dref);
4886
0
        entry->dataReferenceIndex = dref;
4887
0
      }
4888
0
    } else {
4889
0
      for (i=0; i<gf_list_count(new_tk->Media->information->dataInformation->dref->child_boxes); i++) {
4890
0
        GF_DataEntryBox *dref_entry = (GF_DataEntryBox *)gf_list_get(new_tk->Media->information->dataInformation->dref->child_boxes, i);
4891
0
        if (dref_entry->flags & 1) {
4892
0
          dref_entry->flags &= ~1;
4893
0
          e = Media_SetDrefURL((GF_DataEntryURLBox *)dref_entry, orig_file->fileName, dest_file->finalName);
4894
0
          if (e) return e;
4895
0
        }
4896
0
      }
4897
0
    }
4898
0
  }
4899
4900
  //purge all 'gpac' boxes at track level
4901
0
  for (i=0; i<gf_list_count(new_tk->child_boxes); i++) {
4902
0
    GF_UnknownBox *b = (GF_UnknownBox *)gf_list_get(new_tk->child_boxes, i);
4903
0
    if (b->type != GF_ISOM_BOX_TYPE_UNKNOWN) continue;
4904
0
    if (b->original_4cc==GF_ISOM_BOX_TYPE_GDAT) {
4905
0
      gf_list_rem(new_tk->child_boxes, i);
4906
0
      i--;
4907
0
      gf_isom_box_del((GF_Box*)b);
4908
0
    }
4909
0
  }
4910
4911
0
  *dest_track = gf_list_count(dest_file->moov->trackList);
4912
4913
0
  if (dest_file->moov->mvhd->nextTrackID <= new_tk->Header->trackID)
4914
0
    dest_file->moov->mvhd->nextTrackID = new_tk->Header->trackID+1;
4915
4916
  /*if it contains IAMF, add the iamf brand to ftyp*/
4917
0
  if (gf_isom_get_media_subtype(dest_file, new_tk->Header->trackID, 1) == GF_ISOM_SUBTYPE_IAMF) {
4918
0
    gf_isom_modify_alternate_brand(dest_file, GF_ISOM_BRAND_IAMF, GF_TRUE);
4919
0
  }
4920
4921
0
  return GF_OK;
4922
0
}
4923
4924
#if 0
4925
/*clones all sampleDescription entries in new track, after an optional reset of existing entries*/
4926
GF_Err gf_isom_clone_sample_descriptions(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, Bool reset_existing)
4927
{
4928
  u32 i;
4929
  GF_TrackBox *dst_trak, *src_trak;
4930
  GF_Err e = gf_isom_can_access_movie(the_file, GF_ISOM_OPEN_WRITE);
4931
  if (e) return e;
4932
4933
  dst_trak = gf_isom_get_track_box(the_file, trackNumber);
4934
  if (!dst_trak || !dst_trak->Media) return GF_BAD_PARAM;
4935
  src_trak = gf_isom_get_track_box(orig_file, orig_track);
4936
  if (!src_trak || !src_trak->Media) return GF_BAD_PARAM;
4937
4938
  if (reset_existing) {
4939
    gf_isom_box_array_del(dst_trak->Media->information->sampleTable->SampleDescription->child_boxes);
4940
    dst_trak->Media->information->sampleTable->SampleDescription->child_boxes = gf_list_new();
4941
  }
4942
4943
  for (i=0; i<gf_list_count(src_trak->Media->information->sampleTable->SampleDescription->child_boxes); i++) {
4944
    u32 outDesc;
4945
    e = gf_isom_clone_sample_description(the_file, trackNumber, orig_file, orig_track, i+1, NULL, NULL, &outDesc);
4946
    if (e) break;
4947
  }
4948
  return e;
4949
}
4950
#endif
4951
4952
4953
GF_EXPORT
4954
GF_Err gf_isom_clone_sample_description(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, u32 orig_desc_index, const char *URLname, const char *URNname, u32 *outDescriptionIndex)
4955
0
{
4956
0
  GF_TrackBox *trak;
4957
0
  GF_BitStream *bs;
4958
0
  u8 *data;
4959
0
  u32 data_size;
4960
0
  GF_Box *entry;
4961
0
  GF_Err e;
4962
0
  u32 dataRefIndex;
4963
0
    u32 mtype;
4964
0
  u32 internal_type;
4965
4966
0
  e = gf_isom_can_access_movie(the_file, GF_ISOM_OPEN_WRITE);
4967
0
  if (e) return e;
4968
4969
  /*get orig sample desc and clone it*/
4970
0
  trak = gf_isom_get_track_box(orig_file, orig_track);
4971
0
  if (!trak || !trak->Media) return GF_BAD_PARAM;
4972
4973
0
  entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, orig_desc_index-1);
4974
0
  if (!entry) return GF_BAD_PARAM;
4975
0
  internal_type = ((GF_SampleEntryBox *)entry)->internal_type;
4976
4977
4978
  // for generic entries, force the entrytype to unknown to avoid allocating potentially disallowed box types
4979
0
  u32 generic_entry_type = 0;
4980
0
  if (entry->type == GF_ISOM_BOX_TYPE_GNRV) {
4981
0
    generic_entry_type = ((GF_GenericVisualSampleEntryBox *) entry)->EntryType;
4982
0
    ((GF_GenericVisualSampleEntryBox *) entry)->EntryType = GF_ISOM_BOX_TYPE_UNKNOWN;
4983
0
  } else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) {
4984
0
    generic_entry_type = ((GF_GenericAudioSampleEntryBox *) entry)->EntryType;
4985
0
    ((GF_GenericAudioSampleEntryBox *) entry)->EntryType = GF_ISOM_BOX_TYPE_UNKNOWN;
4986
0
  } else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) {
4987
0
    generic_entry_type = ((GF_GenericSampleEntryBox *) entry)->EntryType;
4988
0
    ((GF_GenericSampleEntryBox *) entry)->EntryType = GF_ISOM_BOX_TYPE_UNKNOWN;
4989
0
  }
4990
4991
0
  bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
4992
4993
0
  gf_isom_box_size(entry);
4994
0
  gf_isom_box_write(entry, bs);
4995
0
  gf_bs_get_content(bs, &data, &data_size);
4996
0
  gf_bs_del(bs);
4997
0
  bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
4998
4999
  // restore the original entrytype before losing the entry pointer
5000
0
  if (entry->type == GF_ISOM_BOX_TYPE_GNRV) {
5001
0
    ((GF_GenericVisualSampleEntryBox *) entry)->EntryType = generic_entry_type;
5002
0
  } else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) {
5003
0
    ((GF_GenericAudioSampleEntryBox *) entry)->EntryType = generic_entry_type;
5004
0
  } else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) {
5005
0
    ((GF_GenericSampleEntryBox *) entry)->EntryType = generic_entry_type;
5006
0
  }
5007
5008
5009
0
  e = gf_isom_box_parse(&entry, bs);
5010
0
  gf_bs_del(bs);
5011
0
  gf_free(data);
5012
0
  if (e) return e;
5013
5014
0
  if (entry->type==GF_ISOM_BOX_TYPE_UNKNOWN) {
5015
0
    GF_UnknownBox *ubox = (GF_UnknownBox*)entry;
5016
0
    if (internal_type == GF_ISOM_SAMPLE_ENTRY_VIDEO) {
5017
0
      GF_GenericVisualSampleEntryBox *ve = (GF_GenericVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRV);
5018
0
      ve->EntryType = generic_entry_type ? generic_entry_type : ubox->original_4cc;
5019
0
      ve->data = ubox->data;
5020
0
      ve->data_size = ubox->dataSize;
5021
0
      entry = (GF_Box *) ve;
5022
0
    }
5023
0
    else if (internal_type == GF_ISOM_SAMPLE_ENTRY_AUDIO) {
5024
0
      GF_GenericAudioSampleEntryBox *ae = (GF_GenericAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRA);
5025
0
      ae->EntryType = generic_entry_type ? generic_entry_type : ubox->original_4cc;
5026
0
      ae->data = ubox->data;
5027
0
      ae->data_size = ubox->dataSize;
5028
0
      entry = (GF_Box *) ae;
5029
0
    }
5030
0
    else {
5031
0
      GF_GenericSampleEntryBox *ge = (GF_GenericSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRM);
5032
0
      ge->EntryType = generic_entry_type ? generic_entry_type : ubox->original_4cc;
5033
0
      ge->data = ubox->data;
5034
0
      ge->data_size = ubox->dataSize;
5035
0
      entry = (GF_Box *) ge;
5036
0
    }
5037
0
    ubox->data = NULL;
5038
0
    ubox->dataSize = 0;
5039
0
    gf_isom_box_del((GF_Box *)ubox);
5040
0
  }
5041
5042
  /*get new track and insert clone*/
5043
0
  trak = gf_isom_get_track_box(the_file, trackNumber);
5044
0
  if (!trak || !trak->Media) goto exit;
5045
5046
  /*get or create the data ref*/
5047
0
  e = Media_FindDataRef(trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
5048
0
  if (e) goto exit;
5049
0
  if (!dataRefIndex) {
5050
0
    e = Media_CreateDataRef(the_file, trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
5051
0
    if (e) goto exit;
5052
0
  }
5053
0
  if (!the_file->keep_utc)
5054
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
5055
  /*overwrite dref*/
5056
0
  ((GF_SampleEntryBox *)entry)->dataReferenceIndex = dataRefIndex;
5057
0
  e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, entry);
5058
0
  *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes);
5059
5060
  /*also clone track w/h info*/
5061
0
    mtype = gf_isom_get_media_type(the_file, trackNumber);
5062
0
  if (gf_isom_is_video_handler_type(mtype) ) {
5063
0
    gf_isom_set_visual_info(the_file, trackNumber, (*outDescriptionIndex), ((GF_VisualSampleEntryBox*)entry)->Width, ((GF_VisualSampleEntryBox*)entry)->Height);
5064
0
  }
5065
0
  return e;
5066
5067
0
exit:
5068
0
  gf_isom_box_del(entry);
5069
0
  return e;
5070
0
}
5071
5072
GF_EXPORT
5073
GF_Err gf_isom_new_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, const char *URLname, const char *URNname, GF_GenericSampleDescription *udesc, u32 *outDescriptionIndex)
5074
0
{
5075
0
  GF_TrackBox *trak;
5076
0
  GF_Err e;
5077
0
  u8 **wrap_data;
5078
0
  u32 *wrap_size;
5079
0
  u32 dataRefIndex;
5080
5081
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
5082
0
  if (e) return e;
5083
5084
0
  trak = gf_isom_get_track_box(movie, trackNumber);
5085
0
  if (!trak || !trak->Media || !udesc) return GF_BAD_PARAM;
5086
5087
  //get or create the data ref
5088
0
  e = Media_FindDataRef(trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
5089
0
  if (e) return e;
5090
0
  if (!dataRefIndex) {
5091
0
    e = Media_CreateDataRef(movie, trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex);
5092
0
    if (e) return e;
5093
0
  }
5094
0
  if (!movie->keep_utc)
5095
0
    trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();
5096
5097
0
  if (gf_isom_is_video_handler_type(trak->Media->handler->handlerType)) {
5098
0
    GF_GenericVisualSampleEntryBox *entry;
5099
    //create a new entry
5100
0
    entry = (GF_GenericVisualSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRV);
5101
0
    if (!entry) return GF_OUT_OF_MEM;
5102
5103
0
    if (!udesc->codec_tag) {
5104
0
      entry->EntryType = GF_ISOM_BOX_TYPE_UUID;
5105
0
      memcpy(entry->uuid, udesc->UUID, sizeof(bin128));
5106
0
    } else {
5107
0
      entry->EntryType = udesc->codec_tag;
5108
0
    }
5109
0
    if (entry->EntryType == 0) {
5110
0
      gf_isom_box_del((GF_Box *)entry);
5111
0
      return GF_NOT_SUPPORTED;
5112
0
    }
5113
5114
0
    entry->dataReferenceIndex = dataRefIndex;
5115
0
    entry->vendor = udesc->vendor_code;
5116
0
    entry->version = udesc->version;
5117
0
    entry->revision = udesc->revision;
5118
0
    entry->temporal_quality = udesc->temporal_quality;
5119
0
    entry->spatial_quality = udesc->spatial_quality;
5120
0
    entry->Width = udesc->width;
5121
0
    entry->Height = udesc->height;
5122
0
    gf_strcpy(entry->compressor_name, udesc->compressor_name);
5123
0
    entry->compressor_name[ GF_ARRAY_LENGTH(entry->compressor_name) - 1] = 0;
5124
0
    entry->color_table_index = -1;
5125
0
    entry->frames_per_sample = 1;
5126
0
    entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000;
5127
0
    entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000;
5128
0
    entry->bit_depth = udesc->depth ? udesc->depth : 0x18;
5129
0
    wrap_data = &entry->data;
5130
0
    wrap_size = &entry->data_size;
5131
0
    e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, entry);
5132
0
  }
5133
0
  else if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_AUDIO) {
5134
0
    GF_GenericAudioSampleEntryBox *gena;
5135
    //create a new entry
5136
0
    gena = (GF_GenericAudioSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRA);
5137
0
    if (!gena) return GF_OUT_OF_MEM;
5138
5139
0
    if (!udesc->codec_tag) {
5140
0
      gena->EntryType = GF_ISOM_BOX_TYPE_UUID;
5141
0
      memcpy(gena->uuid, udesc->UUID, sizeof(bin128));
5142
0
    } else {
5143
0
      gena->EntryType = udesc->codec_tag;
5144
0
    }
5145
0
    if (gena->EntryType == 0) {
5146
0
      gf_isom_box_del((GF_Box *)gena);
5147
0
      return GF_NOT_SUPPORTED;
5148
0
    }
5149
5150
0
    gena->dataReferenceIndex = dataRefIndex;
5151
0
    gena->vendor = udesc->vendor_code;
5152
0
    gena->version = udesc->version;
5153
0
    gena->revision = udesc->revision;
5154
0
    gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16;
5155
0
    gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2;
5156
0
    gena->samplerate_hi = udesc->samplerate;
5157
0
    gena->samplerate_lo = 0;
5158
0
    gena->qtff_mode = udesc->is_qtff ? GF_ISOM_AUDIO_QTFF_ON_NOEXT : GF_ISOM_AUDIO_QTFF_NONE;
5159
0
    if (gena->EntryType==GF_QT_SUBTYPE_LPCM) {
5160
0
      gena->version = 2;
5161
0
      gena->qtff_mode = GF_ISOM_AUDIO_QTFF_ON_EXT_VALID;
5162
0
      GF_BitStream *bs = gf_bs_new(gena->extensions, 36, GF_BITSTREAM_WRITE);
5163
0
      gf_bs_write_u32(bs, 72);
5164
0
      gf_bs_write_double(bs, udesc->samplerate);
5165
0
      gf_bs_write_u32(bs, udesc->nb_channels);
5166
0
      gf_bs_write_u32(bs, 0x7F000000);
5167
0
      gf_bs_write_u32(bs, gena->bitspersample);
5168
0
      gf_bs_write_u32(bs, udesc->lpcm_flags);
5169
0
      gf_bs_write_u32(bs, udesc->nb_channels*gena->bitspersample/8); //constBytesPerAudioPacket
5170
0
      gf_bs_write_u32(bs, 1); //constLPCMFramesPerAudioPacket
5171
0
      gf_bs_del(bs);
5172
0
      gena->revision = 0;
5173
0
      gena->vendor = 0;
5174
0
      gena->channel_count = 3;
5175
0
      gena->bitspersample = 16;
5176
0
      gena->compression_id = 0xFFFE;
5177
0
      gena->packet_size = 0;
5178
0
      gena->samplerate_hi = 1;
5179
0
    } else if (udesc->is_qtff) {
5180
0
      GF_Box *b = gf_isom_box_new_parent(&gena->child_boxes, GF_QT_BOX_TYPE_WAVE);
5181
0
      GF_ChromaInfoBox *enda = (GF_ChromaInfoBox*) gf_isom_box_new_parent(&b->child_boxes, GF_QT_BOX_TYPE_ENDA);
5182
0
      ((GF_ChromaInfoBox *)enda)->chroma = (udesc->lpcm_flags & (1<<1)) ? 0 : 1;
5183
5184
0
      GF_UnknownBox *term = (GF_UnknownBox*) gf_isom_box_new_parent(&b->child_boxes, GF_ISOM_BOX_TYPE_UNKNOWN);
5185
0
      if (term) term->original_4cc = 0;
5186
0
    }
5187
5188
0
    wrap_data = &gena->data;
5189
0
    wrap_size = &gena->data_size;
5190
0
    e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, gena);
5191
0
  }
5192
0
  else {
5193
0
    GF_GenericSampleEntryBox *genm;
5194
    //create a new entry
5195
0
    genm = (GF_GenericSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRM);
5196
0
    if (!genm) return GF_OUT_OF_MEM;
5197
5198
0
    if (!udesc->codec_tag) {
5199
0
      genm->EntryType = GF_ISOM_BOX_TYPE_UUID;
5200
0
      memcpy(genm->uuid, udesc->UUID, sizeof(bin128));
5201
0
    } else {
5202
0
      genm->EntryType = udesc->codec_tag;
5203
0
    }
5204
0
    if (genm->EntryType == 0) {
5205
0
      gf_isom_box_del((GF_Box *)genm);
5206
0
      return GF_NOT_SUPPORTED;
5207
0
    }
5208
0
    genm->dataReferenceIndex = dataRefIndex;
5209
0
    wrap_data = &genm->data;
5210
0
    wrap_size = &genm->data_size;
5211
0
    e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, genm);
5212
0
  }
5213
0
  *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes);
5214
5215
0
  if (udesc->extension_buf && udesc->extension_buf_size) {
5216
0
    GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
5217
0
    if (udesc->ext_box_wrap) {
5218
0
      gf_bs_write_u32(bs, 8+udesc->extension_buf_size);
5219
0
      gf_bs_write_u32(bs, udesc->ext_box_wrap);
5220
0
    }
5221
0
    gf_bs_write_data(bs, udesc->extension_buf, udesc->extension_buf_size);
5222
0
    gf_bs_get_content(bs, wrap_data, wrap_size);
5223
0
    gf_bs_del(bs);
5224
0
  }
5225
0
  return e;
5226
0
}
5227
5228
//use carefully. Very useful when you made a lot of changes (IPMP, IPI, OCI, ...)
5229
//THIS WILL REPLACE THE WHOLE DESCRIPTOR ...
5230
#if 0 //unused
5231
/*change the data field of an unknown sample description*/
5232
GF_Err gf_isom_change_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_GenericSampleDescription *udesc)
5233
{
5234
  GF_TrackBox *trak;
5235
  GF_Err e;
5236
  GF_GenericVisualSampleEntryBox *entry;
5237
5238
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
5239
  if (e) return e;
5240
5241
  trak = gf_isom_get_track_box(movie, trackNumber);
5242
  if (!trak || !trak->Media || !StreamDescriptionIndex) return GF_BAD_PARAM;
5243
5244
  entry = (GF_GenericVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex-1);
5245
  if (!entry) return GF_BAD_PARAM;
5246
  if (entry->type == GF_ISOM_BOX_TYPE_GNRV) {
5247
    entry->vendor = udesc->vendor_code;
5248
    entry->version = udesc->version;
5249
    entry->revision = udesc->revision;
5250
    entry->temporal_quality = udesc->temporal_quality;
5251
    entry->spatial_quality = udesc->spatial_quality;
5252
    entry->Width = udesc->width;
5253
    entry->Height = udesc->height;
5254
    memset(entry->compressor_name, 0, 33);
5255
    gf_strcpy(entry->compressor_name, udesc->compressor_name);
5256
    entry->color_table_index = -1;
5257
    entry->frames_per_sample = 1;
5258
    entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000;
5259
    entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000;
5260
    entry->bit_depth = udesc->depth ? udesc->depth : 0x18;
5261
    if (entry->data) gf_free(entry->data);
5262
    entry->data = NULL;
5263
    entry->data_size = 0;
5264
    if (udesc->extension_buf && udesc->extension_buf_size) {
5265
      entry->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
5266
      if (!entry->data) {
5267
        gf_isom_box_del((GF_Box *) entry);
5268
        return GF_OUT_OF_MEM;
5269
      }
5270
      memcpy(entry->data, udesc->extension_buf, udesc->extension_buf_size);
5271
      entry->data_size = udesc->extension_buf_size;
5272
    }
5273
    return GF_OK;
5274
  } else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) {
5275
    GF_GenericAudioSampleEntryBox *gena = (GF_GenericAudioSampleEntryBox *)entry;
5276
    gena->vendor = udesc->vendor_code;
5277
    gena->version = udesc->version;
5278
    gena->revision = udesc->revision;
5279
    gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16;
5280
    gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2;
5281
    gena->samplerate_hi = udesc->samplerate;
5282
    gena->samplerate_lo = 0;
5283
    if (gena->data) gf_free(gena->data);
5284
    gena->data = NULL;
5285
    gena->data_size = 0;
5286
5287
    if (udesc->extension_buf && udesc->extension_buf_size) {
5288
      gena->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
5289
      if (!gena->data) {
5290
        gf_isom_box_del((GF_Box *) gena);
5291
        return GF_OUT_OF_MEM;
5292
      }
5293
      memcpy(gena->data, udesc->extension_buf, udesc->extension_buf_size);
5294
      gena->data_size = udesc->extension_buf_size;
5295
    }
5296
    return GF_OK;
5297
  } else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) {
5298
    GF_GenericSampleEntryBox *genm = (GF_GenericSampleEntryBox *)entry;
5299
    if (genm->data) gf_free(genm->data);
5300
    genm->data = NULL;
5301
    genm->data_size = 0;
5302
5303
    if (udesc->extension_buf && udesc->extension_buf_size) {
5304
      genm->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size);
5305
      if (!genm->data) {
5306
        gf_isom_box_del((GF_Box *) genm);
5307
        return GF_OUT_OF_MEM;
5308
      }
5309
      memcpy(genm->data, udesc->extension_buf, udesc->extension_buf_size);
5310
      genm->data_size = udesc->extension_buf_size;
5311
    }
5312
    return GF_OK;
5313
  }
5314
  return GF_BAD_PARAM;
5315
}
5316
#endif
5317
5318
#if 0
5319
/*removes given stream description*/
5320
GF_Err gf_isom_remove_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 streamDescIndex)
5321
{
5322
  GF_TrackBox *trak;
5323
  GF_Err e;
5324
  GF_Box *entry;
5325
5326
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
5327
  if (e) return e;
5328
  trak = gf_isom_get_track_box(movie, trackNumber);
5329
  if (!trak || !trak->Media || !streamDescIndex) return GF_BAD_PARAM;
5330
  entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, streamDescIndex-1);
5331
  if (!entry) return GF_BAD_PARAM;
5332
  gf_list_rem(trak->Media->information->sampleTable->SampleDescription->child_boxes, streamDescIndex-1);
5333
  gf_isom_box_del(entry);
5334
  return GF_OK;
5335
}
5336
#endif
5337
5338
//sets a track reference
5339
GF_EXPORT
5340
GF_Err gf_isom_set_track_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, GF_ISOTrackID ReferencedTrackID)
5341
0
{
5342
0
  GF_Err e;
5343
0
  GF_TrackBox *trak;
5344
0
  GF_TrackReferenceBox *tref;
5345
0
  GF_TrackReferenceTypeBox *dpnd;
5346
5347
0
  trak = gf_isom_get_track_box(the_file, trackNumber);
5348
0
  if (!trak) return GF_BAD_PARAM;
5349
5350
  //no tref, create one
5351
0
  tref = trak->References;
5352
0
  if (!tref) {
5353
0
    tref = (GF_TrackReferenceBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TREF);
5354
0
    if (!tref) return GF_OUT_OF_MEM;
5355
0
    e = trak_on_child_box((GF_Box*)trak, (GF_Box *) tref, GF_FALSE);
5356
0
    if (e) return e;
5357
0
  }
5358
  //find a ref of the given type
5359
0
  e = Track_FindRef(trak, referenceType, &dpnd);
5360
0
  if (e) return e;
5361
5362
0
  if (!dpnd) {
5363
0
    dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT);
5364
0
    if (!dpnd) return GF_OUT_OF_MEM;
5365
0
    dpnd->reference_type = referenceType;
5366
0
  }
5367
  //add the ref
5368
0
  return reftype_AddRefTrack(dpnd, ReferencedTrackID, NULL);
5369
0
}
5370
5371
GF_EXPORT
5372
GF_Err gf_isom_purge_track_reference(GF_ISOFile *the_file, u32 trackNumber)
5373
0
{
5374
0
  GF_TrackBox *trak;
5375
0
  GF_TrackReferenceTypeBox *ref;
5376
0
  u32 i=0;
5377
0
  trak = gf_isom_get_track_box(the_file, trackNumber);
5378
0
  if (!trak) return GF_BAD_PARAM;
5379
5380
  //no tref, nothing to remove
5381
0
  if (!trak->References) return GF_OK;
5382
5383
0
  while ((ref = gf_list_enum(trak->References->child_boxes, &i))) {
5384
0
    u32 k;
5385
0
    if (!ref->reference_type) continue;
5386
5387
0
    for (k=0; k<ref->trackIDCount; k++) {
5388
0
      u32 tk = gf_isom_get_track_by_id(the_file, ref->trackIDs[k]);
5389
0
      if (!tk) {
5390
0
        memmove(&ref->trackIDs[k], &ref->trackIDs[k+1], ref->trackIDCount-k-1);
5391
0
        k--;
5392
0
        ref->trackIDCount--;
5393
0
      }
5394
0
    }
5395
0
    if (!ref->trackIDCount) {
5396
0
      i--;
5397
0
      gf_isom_box_del_parent(&trak->References->child_boxes, (GF_Box *) ref);
5398
0
    }
5399
0
  }
5400
0
  if (!trak->References->child_boxes || !gf_list_count(trak->References->child_boxes)) {
5401
0
    gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *) trak->References);
5402
0
    trak->References = NULL;
5403
0
  }
5404
0
  return GF_OK;
5405
0
}
5406
5407
//sets a track reference
5408
GF_EXPORT
5409
GF_Err gf_isom_remove_track_references(GF_ISOFile *the_file, u32 trackNumber)
5410
0
{
5411
0
  GF_TrackBox *trak;
5412
5413
0
  trak = gf_isom_get_track_box(the_file, trackNumber);
5414
0
  if (!trak) return GF_BAD_PARAM;
5415
5416
0
  if (trak->References) {
5417
0
    gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->References);
5418
0
    trak->References = NULL;
5419
0
  }
5420
0
  return GF_OK;
5421
0
}
5422
5423
GF_EXPORT
5424
GF_Err gf_isom_remove_track_reference(GF_ISOFile *isom_file, u32 trackNumber, u32 ref_type)
5425
0
{
5426
0
  GF_TrackBox *trak;
5427
0
  u32 i=0;
5428
0
  GF_TrackReferenceTypeBox *ref;
5429
0
  trak = gf_isom_get_track_box(isom_file, trackNumber);
5430
0
  if (!trak) return GF_BAD_PARAM;
5431
5432
0
  if (!trak->References) return GF_OK;
5433
0
  while ((ref = gf_list_enum(trak->References->child_boxes, &i))) {
5434
0
    if (ref->reference_type == ref_type) {
5435
0
      gf_isom_box_del_parent(&trak->References->child_boxes, (GF_Box *)ref);
5436
0
      break;
5437
0
    }
5438
0
  }
5439
0
  if (!gf_list_count(trak->References->child_boxes)) {
5440
0
    gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->References);
5441
0
    trak->References = NULL;
5442
0
  }
5443
0
  return GF_OK;
5444
5445
0
}
5446
5447
//changes track ID
5448
GF_EXPORT
5449
GF_Err gf_isom_set_track_id(GF_ISOFile *movie, u32 trackNumber, GF_ISOTrackID trackID)
5450
0
{
5451
0
  GF_TrackReferenceTypeBox *ref;
5452
0
  GF_TrackBox *trak, *a_trak;
5453
0
  u32 i, j, k;
5454
5455
0
  if (!movie) return GF_BAD_PARAM;
5456
0
  trak = gf_isom_get_track_box(movie, trackNumber);
5457
0
  if (trak && (trak->Header->trackID==trackID)) return GF_OK;
5458
0
  a_trak = gf_isom_get_track_from_id(movie->moov, trackID);
5459
0
  if (!trak || a_trak) return GF_BAD_PARAM;
5460
5461
  /*rewrite all dependencies*/
5462
0
  i=0;
5463
0
  while ((a_trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) {
5464
0
    if (!a_trak->References) continue;
5465
0
    j=0;
5466
0
    while ((ref = (GF_TrackReferenceTypeBox *)gf_list_enum(a_trak->References->child_boxes, &j))) {
5467
0
      for (k=0; k<ref->trackIDCount; k++) {
5468
0
        if (ref->trackIDs[k]==trak->Header->trackID) {
5469
0
          ref->trackIDs[k] = trackID;
5470
0
          break;
5471
0
        }
5472
0
      }
5473
0
    }
5474
0
  }
5475
5476
  /*and update IOD if any*/
5477
0
  if (movie->moov->iods && movie->moov->iods->descriptor) {
5478
0
    GF_ES_ID_Inc *inc;
5479
0
    GF_IsomObjectDescriptor *od = (GF_IsomObjectDescriptor *)movie->moov->iods->descriptor;
5480
5481
0
    i=0;
5482
0
    while ((inc = (GF_ES_ID_Inc*)gf_list_enum(od->ES_ID_IncDescriptors, &i))) {
5483
0
      if (inc->trackID==trak->Header->trackID) inc->trackID = trackID;
5484
0
    }
5485
0
  }
5486
0
  trak->Header->trackID = trackID;
5487
0
  update_next_track_id(movie);
5488
0
  return GF_OK;
5489
0
}
5490
5491
/*force to rewrite all dependencies when the trackID of referenced track changes*/
5492
GF_EXPORT
5493
GF_Err gf_isom_rewrite_track_dependencies(GF_ISOFile *movie, u32 trackNumber)
5494
0
{
5495
0
  GF_TrackReferenceTypeBox *ref;
5496
0
  GF_TrackBox *trak, *a_trak;
5497
0
  u32 i, k;
5498
5499
0
  trak = gf_isom_get_track_box(movie, trackNumber);
5500
0
  if (!trak)
5501
0
    return GF_BAD_PARAM;
5502
0
  if (!trak->References)
5503
0
    return GF_OK;
5504
5505
0
  i=0;
5506
0
  while ((ref = (GF_TrackReferenceTypeBox *)gf_list_enum(trak->References->child_boxes, &i))) {
5507
0
    for (k=0; k < ref->trackIDCount; k++) {
5508
0
      a_trak = gf_isom_get_track_from_original_id(movie->moov, ref->trackIDs[k], trak->originalFile);
5509
0
      if (a_trak) {
5510
0
        ref->trackIDs[k] = a_trak->Header->trackID;
5511
0
      } else {
5512
0
        a_trak = gf_isom_get_track_from_id(movie->moov, ref->trackIDs[k]);
5513
        /*we should have a track with no original ID (not imported) - should we rewrite the dependency ?*/
5514
0
        if (! a_trak || a_trak->originalID) return GF_BAD_PARAM;
5515
0
      }
5516
0
    }
5517
0
  }
5518
5519
0
  return GF_OK;
5520
0
}
5521
5522
#if 0 //unused
5523
5524
/*! changes the sample description index of a sample
5525
\param isom_file the destination ISO file
5526
\param trackNumber the destination track
5527
\param sampleNum the target sample number
5528
\param fnewSampleDescIndex the new sample description index to assign to the sample
5529
\return error if any
5530
*/
5531
GF_EXPORT
5532
GF_Err gf_isom_change_sample_desc_index(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 newSampleDescIndex)
5533
{
5534
  GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber);
5535
  if (!trak || !sample_number || !newSampleDescIndex) return GF_BAD_PARAM;
5536
  if (!trak->is_unpacked) {
5537
    unpack_track(trak);
5538
  }
5539
  if (!trak->Media->information->sampleTable->SampleToChunk) return GF_BAD_PARAM;
5540
  if (trak->Media->information->sampleTable->SampleToChunk->nb_entries < sample_number) return GF_BAD_PARAM;
5541
  trak->Media->information->sampleTable->SampleToChunk->entries[sample_number-1].sampleDescriptionIndex = newSampleDescIndex;
5542
  return GF_OK;
5543
}
5544
5545
/*modify CTS offset of a given sample (used for B-frames) - MUST be called in unpack mode only*/
5546
GF_EXPORT
5547
GF_Err gf_isom_modify_cts_offset(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 offset)
5548
{
5549
  GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber);
5550
  if (!trak) return GF_BAD_PARAM;
5551
  if (!trak->Media->information->sampleTable->CompositionOffset) return GF_BAD_PARAM;
5552
  if (!trak->Media->information->sampleTable->CompositionOffset->unpack_mode) return GF_BAD_PARAM;
5553
  /*we're in unpack mode: one entry per sample*/
5554
  trak->Media->information->sampleTable->CompositionOffset->entries[sample_number - 1].decodingOffset = offset;
5555
  return GF_OK;
5556
}
5557
#endif
5558
5559
GF_EXPORT
5560
GF_Err gf_isom_shift_cts_offset(GF_ISOFile *the_file, u32 trackNumber, s32 offset_shift)
5561
0
{
5562
0
  u32 i;
5563
0
  GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber);
5564
0
  if (!trak) return GF_BAD_PARAM;
5565
0
  if (!trak->Media->information->sampleTable->CompositionOffset) return GF_BAD_PARAM;
5566
0
  if (!trak->Media->information->sampleTable->CompositionOffset->unpack_mode) return GF_BAD_PARAM;
5567
5568
0
  GF_CompositionOffsetBox *ctso = trak->Media->information->sampleTable->CompositionOffset;
5569
0
  for (i=0; i<ctso->nb_entries; i++) {
5570
0
    s64 new_ts = ctso->entries[i].decodingOffset;
5571
0
    new_ts -= offset_shift;
5572
    /*we're in unpack mode: one entry per sample*/
5573
0
    ctso->entries[i].decodingOffset = (s32) new_ts;
5574
0
  }
5575
0
  if (trak->Media->mediaHeader->duration >= -offset_shift) {
5576
0
    s64 new_dur = trak->Media->mediaHeader->duration;
5577
0
    new_dur -= offset_shift;
5578
0
    if (new_dur<0) new_dur = 0;
5579
0
    trak->Media->mediaHeader->duration = (u32) new_dur;
5580
0
  }
5581
0
  return GF_OK;
5582
0
}
5583
5584
#if 0 //unused
5585
GF_Err gf_isom_remove_cts_info(GF_ISOFile *the_file, u32 trackNumber)
5586
{
5587
  GF_SampleTableBox *stbl;
5588
  GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber);
5589
  if (!trak) return GF_BAD_PARAM;
5590
5591
  stbl = trak->Media->information->sampleTable;
5592
  if (!stbl->CompositionOffset) return GF_OK;
5593
5594
  gf_isom_box_del_parent(&stbl->child_boxes, (GF_Box *)stbl->CompositionOffset);
5595
  stbl->CompositionOffset = NULL;
5596
  return GF_OK;
5597
}
5598
#endif
5599
5600
GF_EXPORT
5601
GF_Err gf_isom_set_cts_packing(GF_ISOFile *the_file, u32 trackNumber, Bool unpack)
5602
0
{
5603
0
  GF_Err e;
5604
0
  GF_Err stbl_repackCTS(GF_CompositionOffsetBox *ctts);
5605
0
  GF_Err stbl_unpackCTS(GF_SampleTableBox *stbl);
5606
0
  GF_SampleTableBox *stbl;
5607
5608
0
  GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber);
5609
0
  if (!trak) return GF_BAD_PARAM;
5610
5611
0
  stbl = trak->Media->information->sampleTable;
5612
0
  if (unpack) {
5613
0
    if (!stbl->CompositionOffset) {
5614
0
      stbl->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_CTTS);
5615
0
      if (!stbl->CompositionOffset) return GF_OUT_OF_MEM;
5616
0
    }
5617
0
    e = stbl_unpackCTS(stbl);
5618
0
  } else {
5619
0
    if (!stbl->CompositionOffset) return GF_OK;
5620
0
    e = stbl_repackCTS(stbl->CompositionOffset);
5621
0
  }
5622
0
  if (e) return e;
5623
0
  return SetTrackDuration(trak);
5624
0
}
5625
5626
GF_EXPORT
5627
GF_Err gf_isom_set_track_matrix(GF_ISOFile *the_file, u32 trackNumber, s32 matrix[9])
5628
0
{
5629
0
  GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber);
5630
0
  if (!trak || !trak->Header) return GF_BAD_PARAM;
5631
0
  memcpy(trak->Header->matrix, matrix, sizeof(trak->Header->matrix));
5632
0
  return GF_OK;
5633
0
}
5634
5635
GF_EXPORT
5636
GF_Err gf_isom_set_track_layout_info(GF_ISOFile *the_file, u32 trackNumber, u32 width, u32 height, s32 translation_x, s32 translation_y, s16 layer)
5637
0
{
5638
0
  GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber);
5639
0
  if (!trak || !trak->Header) return GF_BAD_PARAM;
5640
0
  trak->Header->width = width;
5641
0
  trak->Header->height = height;
5642
0
  trak->Header->matrix[6] = translation_x;
5643
0
  trak->Header->matrix[7] = translation_y;
5644
0
  trak->Header->layer = layer;
5645
0
  return GF_OK;
5646
0
}
5647
5648
GF_EXPORT
5649
GF_Err gf_isom_set_media_timescale(GF_ISOFile *the_file, u32 trackNumber, u32 newTS, u32 new_tsinc, u32 force_rescale_type)
5650
0
{
5651
0
  Double scale;
5652
0
  u32 old_ts_inc=0;
5653
0
  u32 old_timescale;
5654
0
  GF_TrackBox *trak;
5655
0
  GF_SampleTableBox *stbl;
5656
5657
0
  trak = gf_isom_get_track_box(the_file, trackNumber);
5658
0
  if (!trak || !trak->Media || !trak->Media->mediaHeader) return GF_BAD_PARAM;
5659
0
  if ((trak->Media->mediaHeader->timeScale==newTS) && !new_tsinc)
5660
0
    return GF_OK; //nothing to do
5661
5662
0
  if (!newTS) newTS = trak->Media->mediaHeader->timeScale;
5663
0
  scale = newTS;
5664
0
  scale /= trak->Media->mediaHeader->timeScale;
5665
0
  old_timescale = trak->Media->mediaHeader->timeScale;
5666
0
  trak->Media->mediaHeader->timeScale = newTS;
5667
5668
0
  stbl = trak->Media->information->sampleTable;
5669
0
  if (new_tsinc) {
5670
0
    u32 i;
5671
0
    if (!stbl->TimeToSample || !stbl->TimeToSample->nb_entries)
5672
0
      return GF_BAD_PARAM;
5673
5674
0
    for (i=0; i<stbl->TimeToSample->nb_entries; i++) {
5675
0
      if (!old_ts_inc)
5676
0
        old_ts_inc = stbl->TimeToSample->entries[i].sampleDelta;
5677
0
      else if (old_ts_inc<stbl->TimeToSample->entries[i].sampleDelta)
5678
0
        old_ts_inc = stbl->TimeToSample->entries[i].sampleDelta;
5679
0
    }
5680
5681
0
    if ((old_timescale==newTS) && (old_ts_inc==new_tsinc) && (force_rescale_type!=2) )
5682
0
      return GF_EOS;
5683
5684
0
    if (!force_rescale_type)
5685
0
      force_rescale_type = 1;
5686
0
    else if (force_rescale_type==2) {
5687
0
      gf_free(stbl->TimeToSample->entries);
5688
0
      stbl->TimeToSample->alloc_size = 1;
5689
0
      stbl->TimeToSample->nb_entries = 1;
5690
0
      stbl->TimeToSample->entries = gf_malloc(sizeof(GF_SttsEntry));
5691
0
      stbl->TimeToSample->entries[0].sampleDelta = new_tsinc;
5692
0
      stbl->TimeToSample->entries[0].sampleCount = stbl->SampleSize->sampleCount;
5693
0
    }
5694
5695
5696
0
    for (i=0; i<stbl->TimeToSample->nb_entries; i++) {
5697
0
      stbl->TimeToSample->entries[i].sampleDelta = new_tsinc;
5698
0
    }
5699
5700
0
    if (stbl->CompositionOffset) {
5701
0
      for (i=0; i<stbl->CompositionOffset->nb_entries; i++) {
5702
0
        u32 old_offset = stbl->CompositionOffset->entries[i].decodingOffset;
5703
0
        if (force_rescale_type==2) {
5704
0
          u32 val = old_offset ;
5705
          //get number of TS delta
5706
0
          old_offset /= old_ts_inc;
5707
0
          if (old_offset * old_ts_inc < val)
5708
0
            old_offset++;
5709
0
          old_offset *= new_tsinc;
5710
0
        } else {
5711
0
          old_offset *= new_tsinc;
5712
0
          old_offset /= old_ts_inc;
5713
0
        }
5714
0
        stbl->CompositionOffset->entries[i].decodingOffset = old_offset;
5715
0
      }
5716
0
    }
5717
5718
0
#define RESCALE_TSVAL(_tsval) {\
5719
0
      s64 val = ((s64) _tsval) * new_tsinc;\
5720
0
      val /= old_ts_inc;\
5721
0
      _tsval = (s32) val;\
5722
0
    }
5723
5724
0
    if (stbl->CompositionToDecode) {
5725
0
      RESCALE_TSVAL(stbl->CompositionToDecode->compositionEndTime)
5726
0
      RESCALE_TSVAL(stbl->CompositionToDecode->compositionStartTime)
5727
0
      RESCALE_TSVAL(stbl->CompositionToDecode->compositionToDTSShift)
5728
0
      RESCALE_TSVAL(stbl->CompositionToDecode->greatestDecodeToDisplayDelta)
5729
0
      RESCALE_TSVAL(stbl->CompositionToDecode->leastDecodeToDisplayDelta)
5730
0
    }
5731
0
    if (trak->editBox) {
5732
0
      GF_EdtsEntry *ent;
5733
0
      i=0;
5734
0
      while ((ent = (GF_EdtsEntry*)gf_list_enum(trak->editBox->editList->entryList, &i))) {
5735
0
        RESCALE_TSVAL(ent->mediaTime)
5736
0
      }
5737
0
    }
5738
0
#undef RESCALE_TSVAL
5739
    //force recompute of duration
5740
0
    trak->Media->mediaHeader->duration=0;
5741
0
    return SetTrackDuration(trak);
5742
0
  }
5743
5744
  //rescale timings
5745
0
  u32 i, k, idx, last_delta;
5746
0
  u64 cur_dts;
5747
0
  u64*DTSs = NULL;
5748
0
  s64*CTSs = NULL;
5749
5750
0
  if (trak->editBox) {
5751
0
    GF_EdtsEntry *ent;
5752
0
    i=0;
5753
0
    while ((ent = (GF_EdtsEntry*)gf_list_enum(trak->editBox->editList->entryList, &i))) {
5754
      //only update if media time is >=0 (neg means empty edit)
5755
0
      if (ent->mediaTime>=0)
5756
0
        ent->mediaTime = (u32) (scale*ent->mediaTime);
5757
0
    }
5758
0
  }
5759
0
  if (! stbl || !stbl->TimeToSample || !stbl->TimeToSample->nb_entries) {
5760
0
    return SetTrackDuration(trak);
5761
0
  }
5762
5763
0
  idx = 0;
5764
0
  cur_dts = 0;
5765
  //unpack the DTSs
5766
0
  DTSs = (u64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount) );
5767
0
  if (!DTSs) return GF_OUT_OF_MEM;
5768
5769
0
  CTSs = NULL;
5770
0
  if (stbl->CompositionOffset) {
5771
0
    CTSs = (s64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount) );
5772
0
    if (!CTSs) return GF_OUT_OF_MEM;
5773
0
  }
5774
5775
0
  for (i=0; i<stbl->TimeToSample->nb_entries; i++) {
5776
0
    for (k=0; k<stbl->TimeToSample->entries[i].sampleCount; k++) {
5777
0
      cur_dts += stbl->TimeToSample->entries[i].sampleDelta;
5778
0
      DTSs[idx] = (u64) (cur_dts * scale);
5779
5780
0
      if (stbl->CompositionOffset) {
5781
0
        s32 cts_o;
5782
0
        stbl_GetSampleCTS(stbl->CompositionOffset, idx+1, &cts_o);
5783
0
        CTSs[idx] = (s64) ( ((s64) cur_dts + cts_o) * scale);
5784
0
      }
5785
0
      idx++;
5786
0
    }
5787
0
  }
5788
0
  last_delta = (u32) (stbl->TimeToSample->entries[stbl->TimeToSample->nb_entries-1].sampleDelta * scale);
5789
5790
  //repack DTS
5791
0
  if (stbl->SampleSize->sampleCount) {
5792
0
    stbl->TimeToSample->entries = gf_realloc(stbl->TimeToSample->entries, sizeof(GF_SttsEntry)*stbl->SampleSize->sampleCount);
5793
0
    memset(stbl->TimeToSample->entries, 0, sizeof(GF_SttsEntry)*stbl->SampleSize->sampleCount);
5794
0
    stbl->TimeToSample->entries[0].sampleDelta = (u32) DTSs[0];
5795
0
    stbl->TimeToSample->entries[0].sampleCount = 1;
5796
0
    idx=0;
5797
0
    for (i=1; i< stbl->SampleSize->sampleCount - 1; i++) {
5798
0
      if (DTSs[i+1] - DTSs[i] == stbl->TimeToSample->entries[idx].sampleDelta) {
5799
0
        stbl->TimeToSample->entries[idx].sampleCount++;
5800
0
      } else {
5801
0
        idx++;
5802
0
        stbl->TimeToSample->entries[idx].sampleDelta = (u32) ( DTSs[i+1] - DTSs[i] );
5803
0
        stbl->TimeToSample->entries[idx].sampleCount=1;
5804
0
      }
5805
0
    }
5806
0
    if (stbl->SampleSize->sampleCount > 1) {
5807
      //add the sample delta for the last sample
5808
0
      if (stbl->TimeToSample->entries[idx].sampleDelta == last_delta) {
5809
0
        stbl->TimeToSample->entries[idx].sampleCount++;
5810
0
      } else {
5811
0
        idx++;
5812
0
        stbl->TimeToSample->entries[idx].sampleDelta = last_delta;
5813
0
        stbl->TimeToSample->entries[idx].sampleCount=1;
5814
0
      }
5815
5816
0
      stbl->TimeToSample->nb_entries = idx+1;
5817
0
      stbl->TimeToSample->entries = gf_realloc(stbl->TimeToSample->entries, sizeof(GF_SttsEntry)*stbl->TimeToSample->nb_entries);
5818
0
    }
5819
0
  }
5820
5821
0
  if (CTSs && stbl->SampleSize->sampleCount>0) {
5822
    //repack CTS
5823
0
    stbl->CompositionOffset->entries = gf_realloc(stbl->CompositionOffset->entries, sizeof(GF_DttsEntry)*stbl->SampleSize->sampleCount);
5824
0
    memset(stbl->CompositionOffset->entries, 0, sizeof(GF_DttsEntry)*stbl->SampleSize->sampleCount);
5825
0
    stbl->CompositionOffset->entries[0].decodingOffset = (s32) (CTSs[0] - DTSs[0]);
5826
0
    stbl->CompositionOffset->entries[0].sampleCount = 1;
5827
0
    idx=0;
5828
0
    for (i=1; i< stbl->SampleSize->sampleCount; i++) {
5829
0
      s32 cts_o = (s32) (CTSs[i] - DTSs[i]);
5830
0
      if (cts_o == stbl->CompositionOffset->entries[idx].decodingOffset) {
5831
0
        stbl->CompositionOffset->entries[idx].sampleCount++;
5832
0
      } else {
5833
0
        idx++;
5834
0
        stbl->CompositionOffset->entries[idx].decodingOffset = cts_o;
5835
0
        stbl->CompositionOffset->entries[idx].sampleCount=1;
5836
0
      }
5837
0
    }
5838
0
    stbl->CompositionOffset->nb_entries = idx+1;
5839
0
    stbl->CompositionOffset->entries = gf_realloc(stbl->CompositionOffset->entries, sizeof(GF_DttsEntry)*stbl->CompositionOffset->nb_entries);
5840
5841
0
    gf_free(CTSs);
5842
0
  }
5843
0
  gf_free(DTSs);
5844
5845
0
  if (stbl->CompositionToDecode) {
5846
0
    stbl->CompositionToDecode->compositionEndTime = (s32) (stbl->CompositionToDecode->compositionEndTime * scale);
5847
0
    stbl->CompositionToDecode->compositionStartTime = (s32)(stbl->CompositionToDecode->compositionStartTime * scale);
5848
0
    stbl->CompositionToDecode->compositionToDTSShift = (s32)(stbl->CompositionToDecode->compositionToDTSShift * scale);
5849
0
    stbl->CompositionToDecode->greatestDecodeToDisplayDelta = (s32)(stbl->CompositionToDecode->greatestDecodeToDisplayDelta * scale);
5850
0
    stbl->CompositionToDecode->leastDecodeToDisplayDelta = (s32)(stbl->CompositionToDecode->leastDecodeToDisplayDelta * scale);
5851
0
  }
5852
5853
0
  return SetTrackDuration(trak);
5854
0
}
5855
5856
GF_EXPORT
5857
Bool gf_isom_box_equal(GF_Box *a, GF_Box *b)
5858
0
{
5859
0
  Bool ret;
5860
0
  u8 *data1, *data2;
5861
0
  u32 data1_size, data2_size;
5862
0
  GF_BitStream *bs;
5863
5864
0
  if (a == b) return GF_TRUE;
5865
0
  if (!a || !b) return GF_FALSE;
5866
  //do NOT check size, they could be set/not set at this point
5867
5868
0
  data1 = data2 = NULL;
5869
5870
0
  bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
5871
0
  gf_isom_box_size(a);
5872
0
  gf_isom_box_write(a, bs);
5873
0
  gf_bs_get_content(bs, &data1, &data1_size);
5874
0
  gf_bs_del(bs);
5875
5876
0
  bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
5877
0
  gf_isom_box_size(b);
5878
0
  gf_isom_box_write(b, bs);
5879
0
  gf_bs_get_content(bs, &data2, &data2_size);
5880
0
  gf_bs_del(bs);
5881
5882
0
  ret = GF_FALSE;
5883
0
  if (data1_size == data2_size) {
5884
0
    ret = (memcmp(data1, data2, sizeof(char)*data1_size) == 0) ? GF_TRUE : GF_FALSE;
5885
0
  }
5886
0
  gf_free(data1);
5887
0
  gf_free(data2);
5888
0
  return ret;
5889
0
}
5890
5891
static u32 base_sample_entry_type(u32 type)
5892
0
{
5893
0
  if (type==GF_ISOM_SUBTYPE_DVH1) return GF_ISOM_SUBTYPE_HVC1;
5894
0
  if (type==GF_ISOM_SUBTYPE_DVHE) return GF_ISOM_SUBTYPE_HEV1;
5895
0
  if (type==GF_ISOM_SUBTYPE_DVA1) return GF_ISOM_SUBTYPE_AVC_H264;
5896
0
  if (type==GF_ISOM_SUBTYPE_DVAV) return GF_ISOM_SUBTYPE_AVC3_H264;
5897
0
  if (type==GF_ISOM_SUBTYPE_DAV1) return GF_ISOM_SUBTYPE_AV01;
5898
0
  return type;
5899
0
}
5900
5901
GF_EXPORT
5902
Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, u32 sdesc_index1, GF_ISOFile *f2, u32 tk2, u32 sdesc_index2)
5903
0
{
5904
0
  u32 i, count;
5905
0
  GF_TrackBox *trak1, *trak2;
5906
0
  GF_ESD *esd1, *esd2;
5907
0
  Bool need_memcmp, ret;
5908
0
  GF_Box *a, *b;
5909
5910
  /*get orig sample desc and clone it*/
5911
0
  trak1 = gf_isom_get_track_box(f1, tk1);
5912
0
  if (!trak1 || !trak1->Media) return GF_FALSE;
5913
0
  trak2 = gf_isom_get_track_box(f2, tk2);
5914
0
  if (!trak2 || !trak2->Media) return GF_FALSE;
5915
5916
0
  if (trak1->Media->handler->handlerType != trak2->Media->handler->handlerType) return GF_FALSE;
5917
0
  count = gf_list_count(trak1->Media->information->sampleTable->SampleDescription->child_boxes);
5918
0
  if (count != gf_list_count(trak2->Media->information->sampleTable->SampleDescription->child_boxes)) {
5919
0
    if (!sdesc_index1 && !sdesc_index2) return GF_FALSE;
5920
0
  }
5921
5922
0
  need_memcmp = GF_TRUE;
5923
0
  for (i=0; i<count; i++) {
5924
0
    u32 type1, type2;
5925
0
    GF_SampleEntryBox *ent1 = (GF_SampleEntryBox *)gf_list_get(trak1->Media->information->sampleTable->SampleDescription->child_boxes, i);
5926
0
    GF_SampleEntryBox *ent2 = (GF_SampleEntryBox *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->child_boxes, i);
5927
5928
0
    if (sdesc_index1) ent1 = (GF_SampleEntryBox *)gf_list_get(trak1->Media->information->sampleTable->SampleDescription->child_boxes, sdesc_index1 - 1);
5929
0
    if (sdesc_index2) ent2 = (GF_SampleEntryBox *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->child_boxes, sdesc_index2 - 1);
5930
5931
0
    if (!ent1 || !ent2) return GF_FALSE;
5932
0
    if (ent1->internal_type != ent2->internal_type) return GF_FALSE;
5933
0
    type1 = base_sample_entry_type(ent1->type);
5934
0
    type2 = base_sample_entry_type(ent2->type);
5935
0
    if (type1 != type2) return GF_FALSE;
5936
5937
0
    switch (ent1->type) {
5938
    /*for MPEG-4 streams, only compare decSpecInfo (bitrate may not be the same but that's not an issue)*/
5939
0
    case GF_ISOM_BOX_TYPE_MP4S:
5940
0
    case GF_ISOM_BOX_TYPE_MP4A:
5941
0
    case GF_ISOM_BOX_TYPE_MP4V:
5942
0
    case GF_ISOM_BOX_TYPE_ENCA:
5943
0
    case GF_ISOM_BOX_TYPE_ENCV:
5944
0
    case GF_ISOM_BOX_TYPE_RESV:
5945
0
    case GF_ISOM_BOX_TYPE_ENCS:
5946
0
      Media_GetESD(trak1->Media, sdesc_index1 ? sdesc_index1 : i+1, &esd1, GF_TRUE);
5947
0
      Media_GetESD(trak2->Media, sdesc_index2 ? sdesc_index2 : i+1, &esd2, GF_TRUE);
5948
0
      if (!esd1 || !esd2 || !esd1->decoderConfig || !esd2->decoderConfig) continue;
5949
0
      need_memcmp = GF_FALSE;
5950
0
      if (esd1->decoderConfig->streamType != esd2->decoderConfig->streamType) return GF_FALSE;
5951
0
      if (esd1->decoderConfig->objectTypeIndication != esd2->decoderConfig->objectTypeIndication) return GF_FALSE;
5952
0
      if (!esd1->decoderConfig->decoderSpecificInfo && esd2->decoderConfig->decoderSpecificInfo) return GF_FALSE;
5953
0
      if (esd1->decoderConfig->decoderSpecificInfo && !esd2->decoderConfig->decoderSpecificInfo) return GF_FALSE;
5954
0
      if (!esd1->decoderConfig->decoderSpecificInfo || !esd2->decoderConfig->decoderSpecificInfo) continue;
5955
0
      if (esd1->decoderConfig->decoderSpecificInfo->dataLength != esd2->decoderConfig->decoderSpecificInfo->dataLength)
5956
0
        return GF_FALSE;
5957
0
      if (memcmp(esd1->decoderConfig->decoderSpecificInfo->data, esd2->decoderConfig->decoderSpecificInfo->data, sizeof(char)*esd1->decoderConfig->decoderSpecificInfo->dataLength)!=0) return GF_FALSE;
5958
0
      break;
5959
0
    case GF_ISOM_BOX_TYPE_HVT1:
5960
0
      return GF_TRUE;
5961
0
    case GF_ISOM_BOX_TYPE_AVC1:
5962
0
    case GF_ISOM_BOX_TYPE_AVC2:
5963
0
    case GF_ISOM_BOX_TYPE_AVC3:
5964
0
    case GF_ISOM_BOX_TYPE_AVC4:
5965
0
    case GF_ISOM_BOX_TYPE_SVC1:
5966
0
    case GF_ISOM_BOX_TYPE_MVC1:
5967
0
    case GF_ISOM_BOX_TYPE_HVC1:
5968
0
    case GF_ISOM_BOX_TYPE_HEV1:
5969
0
    case GF_ISOM_BOX_TYPE_HVC2:
5970
0
    case GF_ISOM_BOX_TYPE_HEV2:
5971
0
    case GF_ISOM_BOX_TYPE_LHE1:
5972
0
    case GF_ISOM_BOX_TYPE_LHV1:
5973
0
    case GF_ISOM_BOX_TYPE_AV01:
5974
0
    case GF_ISOM_BOX_TYPE_VVC1:
5975
0
    case GF_ISOM_BOX_TYPE_VVI1:
5976
0
    case GF_ISOM_BOX_TYPE_DVHE:
5977
0
    case GF_ISOM_BOX_TYPE_DVH1:
5978
0
    case GF_ISOM_BOX_TYPE_DVA1:
5979
0
    case GF_ISOM_BOX_TYPE_DVAV:
5980
0
    case GF_ISOM_BOX_TYPE_DAV1:
5981
0
    {
5982
0
      GF_MPEGVisualSampleEntryBox *avc1 = (GF_MPEGVisualSampleEntryBox *)ent1;
5983
0
      GF_MPEGVisualSampleEntryBox *avc2 = (GF_MPEGVisualSampleEntryBox *)ent2;
5984
5985
0
      if (avc1->hevc_config)
5986
0
        a = (GF_Box *) avc1->hevc_config;
5987
0
      else if (avc1->lhvc_config)
5988
0
        a = (GF_Box *) avc1->lhvc_config;
5989
0
      else if (avc1->svc_config)
5990
0
        a = (GF_Box *) avc1->svc_config;
5991
0
      else if (avc1->mvc_config)
5992
0
        a = (GF_Box *) avc1->mvc_config;
5993
0
      else if (avc1->av1_config)
5994
0
        a = (GF_Box *)avc1->av1_config;
5995
0
      else if (avc1->vvc_config)
5996
0
        a = (GF_Box *)avc1->vvc_config;
5997
0
      else if (avc1->vp_config)
5998
0
        a = (GF_Box *)avc1->vp_config;
5999
0
      else if (avc1->cfg_3gpp)
6000
0
        a = (GF_Box *)avc1->cfg_3gpp;
6001
0
      else
6002
0
        a = (GF_Box *) avc1->avc_config;
6003
6004
0
      if (avc2->hevc_config)
6005
0
        b = (GF_Box *) avc2->hevc_config;
6006
0
      else if (avc2->lhvc_config)
6007
0
        b = (GF_Box *) avc2->lhvc_config;
6008
0
      else if (avc2->svc_config)
6009
0
        b = (GF_Box *) avc2->svc_config;
6010
0
      else if (avc2->mvc_config)
6011
0
        b = (GF_Box *) avc2->mvc_config;
6012
0
      else if (avc2->av1_config)
6013
0
        b = (GF_Box *)avc2->av1_config;
6014
0
      else if (avc2->vvc_config)
6015
0
        b = (GF_Box *)avc2->vvc_config;
6016
0
      else if (avc2->vp_config)
6017
0
        b = (GF_Box *)avc2->vp_config;
6018
0
      else if (avc2->cfg_3gpp)
6019
0
        b = (GF_Box *)avc2->cfg_3gpp;
6020
0
      else
6021
0
        b = (GF_Box *) avc2->avc_config;
6022
6023
0
      Bool res = gf_isom_box_equal(a,b);
6024
0
      if (!res) return GF_FALSE;
6025
6026
      //check dovi config disabled for now
6027
      //res = gf_isom_box_equal((GF_Box*)avc1->dovi_config, (GF_Box*)avc2->dovi_config);
6028
      //if (!res) return GF_FALSE;
6029
0
      return GF_TRUE;
6030
0
    }
6031
0
    break;
6032
0
    case GF_ISOM_BOX_TYPE_LSR1:
6033
0
    {
6034
0
      GF_LASeRSampleEntryBox *lsr1 = (GF_LASeRSampleEntryBox *)ent1;
6035
0
      GF_LASeRSampleEntryBox *lsr2 = (GF_LASeRSampleEntryBox *)ent2;
6036
0
      if (lsr1->lsr_config && lsr2->lsr_config
6037
0
              && lsr1->lsr_config->hdr && lsr2->lsr_config->hdr
6038
0
              && (lsr1->lsr_config->hdr_size==lsr2->lsr_config->hdr_size)
6039
0
              && !memcmp(lsr1->lsr_config->hdr, lsr2->lsr_config->hdr, lsr2->lsr_config->hdr_size)
6040
0
         ) {
6041
0
        return GF_TRUE;
6042
0
      }
6043
0
      return GF_FALSE;
6044
0
    }
6045
0
    break;
6046
0
#ifndef GPAC_DISABLE_VTT
6047
0
    case GF_ISOM_BOX_TYPE_WVTT:
6048
0
    {
6049
0
      GF_WebVTTSampleEntryBox *wvtt1 = (GF_WebVTTSampleEntryBox *)ent1;
6050
0
      GF_WebVTTSampleEntryBox *wvtt2 = (GF_WebVTTSampleEntryBox *)ent2;
6051
0
      if (wvtt1->config && wvtt2->config &&
6052
0
              (wvtt1->config->string && wvtt2->config->string && !strcmp(wvtt1->config->string, wvtt2->config->string))) {
6053
0
        return GF_TRUE;
6054
0
      }
6055
0
      return GF_FALSE;
6056
0
    }
6057
0
    break;
6058
0
#endif
6059
0
    case GF_ISOM_BOX_TYPE_STPP:
6060
0
    {
6061
0
      GF_MetaDataSampleEntryBox *stpp1 = (GF_MetaDataSampleEntryBox *)ent1;
6062
0
      GF_MetaDataSampleEntryBox *stpp2 = (GF_MetaDataSampleEntryBox *)ent2;
6063
0
      if (stpp1->xml_namespace && stpp2->xml_namespace && !strcmp(stpp1->xml_namespace, stpp2->xml_namespace)) {
6064
0
        return GF_TRUE;
6065
0
      }
6066
0
      return GF_FALSE;
6067
0
    }
6068
0
    break;
6069
0
    case GF_ISOM_BOX_TYPE_SBTT:
6070
0
    {
6071
0
      return GF_FALSE;
6072
0
    }
6073
0
    break;
6074
0
    case GF_ISOM_BOX_TYPE_STXT:
6075
0
    {
6076
0
      GF_MetaDataSampleEntryBox *stxt1 = (GF_MetaDataSampleEntryBox *)ent1;
6077
0
      GF_MetaDataSampleEntryBox *stxt2 = (GF_MetaDataSampleEntryBox *)ent2;
6078
0
      if (stxt1->mime_type && stxt2->mime_type &&
6079
0
              ( (!stxt1->config && !stxt2->config) ||
6080
0
                (stxt1->config && stxt2->config && stxt1->config->config && stxt2->config->config &&
6081
0
                 !strcmp(stxt1->config->config, stxt2->config->config)))) {
6082
0
        return GF_TRUE;
6083
0
      }
6084
0
      return GF_FALSE;
6085
0
    }
6086
0
    case GF_ISOM_BOX_TYPE_MP3:
6087
0
    case GF_QT_SUBTYPE_RAW_AUD:
6088
0
    case GF_QT_SUBTYPE_TWOS:
6089
0
    case GF_QT_SUBTYPE_SOWT:
6090
0
    case GF_QT_SUBTYPE_FL32:
6091
0
    case GF_QT_SUBTYPE_FL64:
6092
0
    case GF_QT_SUBTYPE_IN24:
6093
0
    case GF_QT_SUBTYPE_IN32:
6094
0
    case GF_QT_SUBTYPE_ULAW:
6095
0
    case GF_QT_SUBTYPE_ALAW:
6096
0
    case GF_QT_SUBTYPE_ADPCM:
6097
0
    case GF_QT_SUBTYPE_IMA_ADPCM:
6098
0
    case GF_QT_SUBTYPE_DVCA:
6099
0
    case GF_QT_SUBTYPE_QDMC:
6100
0
    case GF_QT_SUBTYPE_QDMC2:
6101
0
    case GF_QT_SUBTYPE_QCELP:
6102
0
    case GF_QT_SUBTYPE_kMP3:
6103
0
    case GF_QT_SUBTYPE_APCH:
6104
0
    case GF_QT_SUBTYPE_APCO:
6105
0
    case GF_QT_SUBTYPE_APCN:
6106
0
    case GF_QT_SUBTYPE_APCS:
6107
0
    case GF_QT_SUBTYPE_AP4X:
6108
0
    case GF_QT_SUBTYPE_AP4H:
6109
0
    case GF_QT_SUBTYPE_RAW_VID:
6110
0
    case GF_QT_SUBTYPE_YUYV:
6111
0
    case GF_QT_SUBTYPE_UYVY:
6112
0
    case GF_QT_SUBTYPE_YUV444:
6113
0
    case GF_QT_SUBTYPE_YUVA444:
6114
0
    case GF_QT_SUBTYPE_YUV422_10:
6115
0
    case GF_QT_SUBTYPE_YUV444_10:
6116
0
    case GF_QT_SUBTYPE_YUV422_16:
6117
0
    case GF_QT_SUBTYPE_YUV420:
6118
0
    case GF_QT_SUBTYPE_I420:
6119
0
    case GF_QT_SUBTYPE_IYUV:
6120
0
    case GF_QT_SUBTYPE_YV12:
6121
0
    case GF_QT_SUBTYPE_YVYU:
6122
0
    case GF_QT_SUBTYPE_RGBA:
6123
0
    case GF_QT_SUBTYPE_ABGR:
6124
0
    default:
6125
0
      if (ent1->internal_type == GF_ISOM_SAMPLE_ENTRY_VIDEO) {
6126
0
        GF_VisualSampleEntryBox *vent1 = (GF_VisualSampleEntryBox *) ent1;
6127
0
        GF_VisualSampleEntryBox *vent2 = (GF_VisualSampleEntryBox *) ent2;
6128
0
        if (vent1->Width != vent2->Width) return GF_FALSE;
6129
0
        if (vent1->Height != vent2->Height) return GF_FALSE;
6130
0
      }
6131
0
      else if (ent1->internal_type == GF_ISOM_SAMPLE_ENTRY_AUDIO) {
6132
0
        GF_AudioSampleEntryBox *aent1 = (GF_AudioSampleEntryBox *) ent1;
6133
0
        GF_AudioSampleEntryBox *aent2 = (GF_AudioSampleEntryBox *) ent2;
6134
0
        if (aent1->samplerate_hi != aent2->samplerate_hi) return GF_FALSE;
6135
0
        if (aent1->samplerate_lo != aent2->samplerate_lo) return GF_FALSE;
6136
0
        if (aent1->channel_count != aent2->channel_count) return GF_FALSE;
6137
0
      }
6138
0
      return GF_TRUE;
6139
0
    }
6140
6141
0
    if (sdesc_index1 && sdesc_index2) break;
6142
0
  }
6143
0
  if (!need_memcmp) return GF_TRUE;
6144
0
  a = (GF_Box *)trak1->Media->information->sampleTable->SampleDescription;
6145
0
  b = (GF_Box *)trak2->Media->information->sampleTable->SampleDescription;
6146
  //we ignore all bitrate boxes when comparing the box, disable their writing
6147
0
  gf_isom_registry_disable(GF_ISOM_BOX_TYPE_BTRT, GF_TRUE);
6148
0
  ret = gf_isom_box_equal(a,b);
6149
  //re-enable btrt writing
6150
0
  gf_isom_registry_disable(GF_ISOM_BOX_TYPE_BTRT, GF_FALSE);
6151
6152
0
  return ret;
6153
0
}
6154
6155
GF_EXPORT
6156
u64 gf_isom_estimate_size(GF_ISOFile *movie)
6157
0
{
6158
0
  GF_Err e;
6159
0
  GF_Box *a;
6160
0
  u32 i, count;
6161
0
  u64 mdat_size;
6162
0
  if (!movie || !movie->moov) return 0;
6163
6164
0
  mdat_size = 0;
6165
0
  count = gf_list_count(movie->moov->trackList);
6166
0
  for (i=0; i<count; i++) {
6167
0
    mdat_size += gf_isom_get_media_data_size(movie, i+1);
6168
0
  }
6169
0
  if (mdat_size) {
6170
0
    mdat_size += 8;
6171
0
    if (mdat_size > 0xFFFFFFFF) mdat_size += 8;
6172
0
  }
6173
6174
0
  i=0;
6175
0
  while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
6176
0
    e = gf_isom_box_size(a);
6177
0
    if (e == GF_OK)
6178
0
      mdat_size += a->size;
6179
0
  }
6180
0
  return mdat_size;
6181
0
}
6182
6183
6184
//set shadowing on/off
6185
#if 0 //unused
6186
GF_Err gf_isom_remove_sync_shadows(GF_ISOFile *movie, u32 trackNumber)
6187
{
6188
  GF_TrackBox *trak;
6189
  GF_SampleTableBox *stbl;
6190
6191
  if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE;
6192
  trak = gf_isom_get_track_box(movie, trackNumber);
6193
  if (!trak) return GF_BAD_PARAM;
6194
6195
  stbl = trak->Media->information->sampleTable;
6196
  if (stbl->ShadowSync) {
6197
    gf_isom_box_del_parent(&stbl->child_boxes, (GF_Box *) stbl->ShadowSync);
6198
    stbl->ShadowSync = NULL;
6199
  }
6200
  return GF_OK;
6201
}
6202
6203
/*Use this function to do the shadowing if you use shadowing.
6204
the sample to be shadowed MUST be a non-sync sample (ignored if not)
6205
the sample shadowing must be a Sync sample (error if not)*/
6206
GF_Err gf_isom_set_sync_shadow(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u32 syncSample)
6207
{
6208
  GF_TrackBox *trak;
6209
  GF_SampleTableBox *stbl;
6210
  GF_ISOSAPType isRAP;
6211
  GF_Err e;
6212
6213
  if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE;
6214
  trak = gf_isom_get_track_box(movie, trackNumber);
6215
  if (!trak || !sampleNumber || !syncSample) return GF_BAD_PARAM;
6216
6217
  stbl = trak->Media->information->sampleTable;
6218
  if (!stbl->ShadowSync) {
6219
    stbl->ShadowSync = (GF_ShadowSyncBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSH);
6220
    if (!stbl->ShadowSync) return GF_OUT_OF_MEM;
6221
  }
6222
6223
  //if no sync, skip
6224
  if (!stbl->SyncSample) return GF_OK;
6225
  //else set the sync shadow.
6226
  //if the sample is sync, ignore
6227
  e = stbl_GetSampleRAP(stbl->SyncSample, sampleNumber, &isRAP, NULL, NULL);
6228
  if (e) return e;
6229
  if (isRAP) return GF_OK;
6230
  //if the shadowing sample is not sync, error
6231
  e = stbl_GetSampleRAP(stbl->SyncSample, syncSample, &isRAP, NULL, NULL);
6232
  if (e) return e;
6233
  if (!isRAP) return GF_BAD_PARAM;
6234
6235
  return stbl_SetSyncShadow(stbl->ShadowSync, sampleNumber, syncSample);
6236
}
6237
#endif
6238
6239
//set the GroupID of a track (only used for interleaving)
6240
GF_EXPORT
6241
GF_Err gf_isom_set_track_interleaving_group(GF_ISOFile *movie, u32 trackNumber, u32 GroupID)
6242
0
{
6243
0
  GF_TrackBox *trak;
6244
6245
0
  if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE;
6246
0
  trak = gf_isom_get_track_box(movie, trackNumber);
6247
0
  if (!trak || !GroupID) return GF_BAD_PARAM;
6248
6249
0
  trak->Media->information->sampleTable->groupID = GroupID;
6250
0
  return GF_OK;
6251
0
}
6252
6253
6254
//set the Priority of a track within a Group (only used for tight interleaving)
6255
//Priority ranges from 1 to 9
6256
GF_EXPORT
6257
GF_Err gf_isom_set_track_priority_in_group(GF_ISOFile *movie, u32 trackNumber, u32 Priority)
6258
0
{
6259
0
  GF_TrackBox *trak;
6260
6261
0
  if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE;
6262
0
  trak = gf_isom_get_track_box(movie, trackNumber);
6263
0
  if (!trak || !Priority) return GF_BAD_PARAM;
6264
6265
0
  trak->Media->information->sampleTable->trackPriority = Priority > 255 ? 255 : Priority;
6266
0
  return GF_OK;
6267
0
}
6268
6269
//set the max SamplesPerChunk (for file optimization)
6270
GF_EXPORT
6271
GF_Err gf_isom_hint_max_chunk_size(GF_ISOFile *movie, u32 trackNumber, u32 maxChunkSize)
6272
0
{
6273
0
  GF_TrackBox *trak;
6274
6275
0
  if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE;
6276
0
  trak = gf_isom_get_track_box(movie, trackNumber);
6277
0
  if (!trak || !maxChunkSize) return GF_BAD_PARAM;
6278
6279
0
  trak->Media->information->sampleTable->MaxChunkSize = maxChunkSize;
6280
0
  return GF_OK;
6281
0
}
6282
6283
6284
//set the max SamplesPerChunk (for file optimization)
6285
GF_EXPORT
6286
GF_Err gf_isom_hint_max_chunk_duration(GF_ISOFile *movie, u32 trackNumber, u32 maxChunkDur)
6287
0
{
6288
0
  GF_TrackBox *trak;
6289
6290
0
  if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE;
6291
0
  trak = gf_isom_get_track_box(movie, trackNumber);
6292
0
  if (!trak) return GF_BAD_PARAM;
6293
6294
0
  trak->Media->information->sampleTable->MaxChunkDur = maxChunkDur;
6295
0
  return GF_OK;
6296
0
}
6297
6298
6299
GF_EXPORT
6300
GF_Err gf_isom_set_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, const GF_SLConfig *slConfig)
6301
0
{
6302
0
  GF_TrackBox *trak;
6303
0
  GF_SampleEntryBox *entry;
6304
0
  GF_Err e;
6305
0
  GF_SLConfig **slc;
6306
0
  GF_ESDBox *esds;
6307
6308
0
  trak = gf_isom_get_track_box(the_file, trackNumber);
6309
0
  if (!trak) return GF_BAD_PARAM;
6310
6311
0
  e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL);
6312
0
  if (e) return e;
6313
6314
  //we must be sure we are not using a remote ESD
6315
0
  switch (entry->type) {
6316
0
  case GF_ISOM_BOX_TYPE_MP4S:
6317
0
    esds = ((GF_MPEGSampleEntryBox *)entry)->esd;
6318
0
    if (!esds || !esds->desc || !esds->desc->slConfig || (esds->desc->slConfig->predefined != SLPredef_MP4))
6319
0
      return GF_ISOM_INVALID_FILE;
6320
0
    slc = & ((GF_MPEGSampleEntryBox *)entry)->slc;
6321
0
    break;
6322
0
  case GF_ISOM_BOX_TYPE_MP4A:
6323
0
    esds = ((GF_MPEGAudioSampleEntryBox *)entry)->esd;
6324
0
    if (!esds || !esds->desc || !esds->desc->slConfig || (esds->desc->slConfig->predefined != SLPredef_MP4))
6325
0
      return GF_ISOM_INVALID_FILE;
6326
0
    slc = & ((GF_MPEGAudioSampleEntryBox *)entry)->slc;
6327
0
    break;
6328
0
  case GF_ISOM_BOX_TYPE_MP4V:
6329
0
    esds = ((GF_MPEGVisualSampleEntryBox *)entry)->esd;
6330
0
    if (!esds || !esds->desc || !esds->desc->slConfig || (esds->desc->slConfig->predefined != SLPredef_MP4))
6331
0
      return GF_ISOM_INVALID_FILE;
6332
0
    slc = & ((GF_MPEGVisualSampleEntryBox *)entry)->slc;
6333
0
    break;
6334
0
  default:
6335
0
    return GF_OK;
6336
0
  }
6337
6338
0
  if (*slc) {
6339
0
    gf_odf_desc_del((GF_Descriptor *)*slc);
6340
0
    *slc = NULL;
6341
0
  }
6342
0
  if (!slConfig) return GF_OK;
6343
  //finally duplicate the SL
6344
0
  return gf_odf_desc_copy((GF_Descriptor *) slConfig, (GF_Descriptor **) slc);
6345
0
}
6346
6347
#if 0 //unused
6348
GF_Err gf_isom_get_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig **slConfig)
6349
{
6350
  GF_TrackBox *trak;
6351
  GF_SampleEntryBox *entry;
6352
  GF_Err e;
6353
  GF_SLConfig *slc;
6354
6355
  trak = gf_isom_get_track_box(the_file, trackNumber);
6356
  if (!trak) return GF_BAD_PARAM;
6357
6358
  e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL);
6359
  if (e) return e;
6360
6361
  //we must be sure we are not using a remote ESD
6362
  slc = NULL;
6363
  *slConfig = NULL;
6364
  switch (entry->type) {
6365
  case GF_ISOM_BOX_TYPE_MP4S:
6366
    if (((GF_MPEGSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
6367
    slc = ((GF_MPEGSampleEntryBox *)entry)->slc;
6368
    break;
6369
  case GF_ISOM_BOX_TYPE_MP4A:
6370
    if (((GF_MPEGAudioSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
6371
    slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc;
6372
    break;
6373
  case GF_ISOM_BOX_TYPE_MP4V:
6374
    if (((GF_MPEGVisualSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM;
6375
    slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc;
6376
    break;
6377
  default:
6378
    return GF_BAD_PARAM;
6379
  }
6380
6381
  if (!slc) return GF_OK;
6382
  //finally duplicate the SL
6383
  return gf_odf_desc_copy((GF_Descriptor *) slc, (GF_Descriptor **) slConfig);
6384
}
6385
6386
u32 gf_isom_get_track_group(GF_ISOFile *the_file, u32 trackNumber)
6387
{
6388
  GF_TrackBox *trak;
6389
  trak = gf_isom_get_track_box(the_file, trackNumber);
6390
  if (!trak) return 0;
6391
  return trak->Media->information->sampleTable->groupID;
6392
}
6393
6394
u32 gf_isom_get_track_priority_in_group(GF_ISOFile *the_file, u32 trackNumber)
6395
{
6396
  GF_TrackBox *trak;
6397
  trak = gf_isom_get_track_box(the_file, trackNumber);
6398
  if (!trak) return 0;
6399
  return trak->Media->information->sampleTable->trackPriority;
6400
}
6401
#endif
6402
6403
6404
GF_EXPORT
6405
GF_Err gf_isom_make_interleave_ex(GF_ISOFile *file, GF_Fraction *fTimeInSec)
6406
0
{
6407
0
  GF_Err e;
6408
0
  u64 itime;
6409
0
  if (!file || !fTimeInSec->den || (fTimeInSec->num<=0)) return GF_BAD_PARAM;
6410
6411
0
  itime = (u64) fTimeInSec->num;
6412
0
  itime *= gf_isom_get_timescale(file);
6413
0
  itime /= fTimeInSec->den;
6414
0
  if (file->storageMode==GF_ISOM_STORE_FASTSTART) {
6415
0
    return gf_isom_set_interleave_time(file, (u32) itime);
6416
0
  }
6417
0
  if (gf_isom_get_mode(file) < GF_ISOM_OPEN_EDIT) return GF_BAD_PARAM;
6418
0
  e = gf_isom_set_storage_mode(file, GF_ISOM_STORE_DRIFT_INTERLEAVED);
6419
0
  if (e) return e;
6420
0
  return gf_isom_set_interleave_time(file, (u32) itime);
6421
0
}
6422
6423
GF_EXPORT
6424
GF_Err gf_isom_make_interleave(GF_ISOFile *file, Double TimeInSec)
6425
0
{
6426
0
  GF_Fraction f;
6427
0
  f.num = (s32) (TimeInSec * 1000);
6428
0
  f.den = 1000;
6429
0
  return gf_isom_make_interleave_ex(file, &f);
6430
6431
0
}
6432
GF_EXPORT
6433
GF_Err gf_isom_set_handler_name(GF_ISOFile *the_file, u32 trackNumber, const char *nameUTF8)
6434
0
{
6435
0
  GF_TrackBox *trak;
6436
0
  trak = gf_isom_get_track_box(the_file, trackNumber);
6437
0
  if (!trak) return GF_BAD_PARAM;
6438
0
  if (trak->Media->handler->nameUTF8) gf_free(trak->Media->handler->nameUTF8);
6439
0
  trak->Media->handler->nameUTF8 = NULL;
6440
6441
0
  if (!nameUTF8) return GF_OK;
6442
6443
0
  if (!strnicmp(nameUTF8, "file://", 7)) {
6444
0
    u8 BOM[4];
6445
0
    FILE *f = gf_fopen(nameUTF8+7, "rb");
6446
0
    u64 size;
6447
0
    if (!f) return GF_URL_ERROR;
6448
0
    size = gf_fsize(f);
6449
0
    if (3!=gf_fread(BOM, 3, f)) {
6450
0
      gf_fclose(f);
6451
0
      return GF_CORRUPTED_DATA;
6452
0
    }
6453
    /*skip BOM if any*/
6454
0
    if ((BOM[0]==0xEF) && (BOM[1]==0xBB) && (BOM[2]==0xBF)) size -= 3;
6455
0
    else if ((BOM[0]==0xEF) || (BOM[0]==0xFF)) {
6456
0
      gf_fclose(f);
6457
0
      return GF_BAD_PARAM;
6458
0
    }
6459
0
    else gf_fseek(f, 0, SEEK_SET);
6460
0
    trak->Media->handler->nameUTF8 = (char*)gf_malloc(sizeof(char)*(size_t)(size+1));
6461
0
    if (!trak->Media->handler->nameUTF8) {
6462
0
      gf_fclose(f);
6463
0
      return GF_OUT_OF_MEM;
6464
0
    }
6465
0
    size = gf_fread(trak->Media->handler->nameUTF8, (size_t)size, f);
6466
0
    trak->Media->handler->nameUTF8[size] = 0;
6467
0
    gf_fclose(f);
6468
0
  } else {
6469
0
    u32 i, j, len;
6470
0
    char szOrig[1024], szLine[1024];
6471
0
    gf_strcpy(szOrig, nameUTF8);
6472
0
    j=0;
6473
0
    len = (u32) strlen(szOrig);
6474
0
    for (i=0; i<len; i++) {
6475
0
      if (szOrig[i] & 0x80) {
6476
        /*non UTF8 (likely some win-CP)*/
6477
0
        if ( (szOrig[i+1] & 0xc0) != 0x80) {
6478
0
          szLine[j] = 0xc0 | ( (szOrig[i] >> 6) & 0x3 );
6479
0
          j++;
6480
0
          szOrig[i] &= 0xbf;
6481
0
        }
6482
        /*UTF8 2 bytes char */
6483
0
        else if ( (szOrig[i] & 0xe0) == 0xc0) {
6484
0
          szLine[j] = szOrig[i];
6485
0
          i++;
6486
0
          j++;
6487
0
        }
6488
        /*UTF8 3 bytes char */
6489
0
        else if ( (szOrig[i] & 0xf0) == 0xe0) {
6490
0
          szLine[j] = szOrig[i];
6491
0
          i++;
6492
0
          j++;
6493
0
          szLine[j] = szOrig[i];
6494
0
          i++;
6495
0
          j++;
6496
0
        }
6497
        /*UTF8 4 bytes char */
6498
0
        else if ( (szOrig[i] & 0xf8) == 0xf0) {
6499
0
          szLine[j] = szOrig[i];
6500
0
          i++;
6501
0
          j++;
6502
0
          szLine[j] = szOrig[i];
6503
0
          i++;
6504
0
          j++;
6505
0
          szLine[j] = szOrig[i];
6506
0
          i++;
6507
0
          j++;
6508
0
        }
6509
0
      }
6510
0
      szLine[j] = szOrig[i];
6511
0
      j++;
6512
0
    }
6513
0
    szLine[j] = 0;
6514
0
    trak->Media->handler->nameUTF8 = gf_strdup(szLine);
6515
0
  }
6516
0
  return GF_OK;
6517
0
}
6518
6519
#if 0 //unused
6520
/*clones root OD from input to output file, without copying root OD track references*/
6521
GF_Err gf_isom_clone_root_od(GF_ISOFile *input, GF_ISOFile *output)
6522
{
6523
  GF_List *esds;
6524
  GF_Err e;
6525
  u32 i;
6526
  GF_Descriptor *desc;
6527
6528
  e = gf_isom_remove_root_od(output);
6529
  if (e) return e;
6530
  if (!input->moov || !input->moov->iods || !input->moov->iods->descriptor) return GF_OK;
6531
  e = gf_isom_insert_moov(output);
6532
  if (e) return e;
6533
  e = AddMovieIOD(output->moov, 0);
6534
  if (e) return e;
6535
  if (output->moov->iods->descriptor) gf_odf_desc_del(output->moov->iods->descriptor);
6536
  output->moov->iods->descriptor = NULL;
6537
  gf_odf_desc_copy(input->moov->iods->descriptor, &output->moov->iods->descriptor);
6538
6539
  switch (output->moov->iods->descriptor->tag) {
6540
  case GF_ODF_ISOM_IOD_TAG:
6541
    esds = ((GF_IsomInitialObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors;
6542
    break;
6543
  case GF_ODF_ISOM_OD_TAG:
6544
    esds = ((GF_IsomObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors;
6545
    break;
6546
  default:
6547
    return GF_ISOM_INVALID_FILE;
6548
  }
6549
6550
  //get the desc
6551
  i=0;
6552
  while ((desc = (GF_Descriptor*)gf_list_enum(esds, &i))) {
6553
    gf_odf_desc_del(desc);
6554
    gf_list_rem(esds, i-1);
6555
  }
6556
  return GF_OK;
6557
}
6558
#endif
6559
6560
GF_EXPORT
6561
GF_Err gf_isom_set_media_type(GF_ISOFile *movie, u32 trackNumber, u32 new_type)
6562
0
{
6563
0
  GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber);
6564
0
  if (!trak || !new_type) return GF_BAD_PARAM;
6565
0
  trak->Media->handler->handlerType = new_type;
6566
0
  return GF_OK;
6567
0
}
6568
6569
GF_EXPORT
6570
GF_Err gf_isom_set_media_subtype(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, u32 new_type)
6571
0
{
6572
0
  GF_SampleEntryBox*entry;
6573
0
  GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber);
6574
0
  if (!trak || !sampleDescriptionIndex || !new_type) return GF_BAD_PARAM;
6575
6576
0
  entry = (GF_SampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex - 1);
6577
0
  if (!entry) return GF_BAD_PARAM;
6578
0
  if (entry->type==GF_ISOM_BOX_TYPE_GNRV) {
6579
0
    ((GF_GenericVisualSampleEntryBox *)entry)->EntryType = new_type;
6580
0
  } else if (entry->type==GF_ISOM_BOX_TYPE_GNRA) {
6581
0
    ((GF_GenericAudioSampleEntryBox *)entry)->EntryType = new_type;
6582
0
  } else if (entry->type==GF_ISOM_BOX_TYPE_GNRM) {
6583
0
    ((GF_GenericSampleEntryBox *)entry)->EntryType = new_type;
6584
0
  } else {
6585
0
    entry->type = new_type;
6586
0
  }
6587
0
  return GF_OK;
6588
0
}
6589
6590
6591
#if 0 //unused
6592
GF_Err gf_isom_set_JPEG2000(GF_ISOFile *mov, Bool set_on)
6593
{
6594
  if (!mov) return GF_BAD_PARAM;
6595
  mov->is_jp2 = set_on;
6596
  return GF_OK;
6597
}
6598
#endif
6599
6600
GF_Err gf_isom_remove_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID)
6601
0
{
6602
0
  u32 i, count;
6603
0
  GF_List *list;
6604
6605
0
  if (trackNumber==(u32) -1) {
6606
0
    if (!movie) return GF_BAD_PARAM;
6607
0
    list = movie->TopBoxes;
6608
0
  } else if (trackNumber) {
6609
0
    GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber);
6610
0
    if (!trak) return GF_BAD_PARAM;
6611
0
    list = trak->child_boxes;
6612
0
  } else {
6613
0
    if (!movie) return GF_BAD_PARAM;
6614
0
    list = movie->moov->child_boxes;
6615
0
  }
6616
6617
0
  count = list ? gf_list_count(list) : 0;
6618
0
  for (i=0; i<count; i++) {
6619
0
    GF_UnknownUUIDBox *uuid = (GF_UnknownUUIDBox *)gf_list_get(list, i);
6620
0
    if (uuid->type != GF_ISOM_BOX_TYPE_UUID) continue;
6621
0
    if (memcmp(UUID, uuid->uuid, sizeof(bin128))) continue;
6622
0
    gf_list_rem(list, i);
6623
0
    i--;
6624
0
    count--;
6625
0
    gf_isom_box_del((GF_Box*)uuid);
6626
0
  }
6627
0
  return GF_OK;
6628
0
}
6629
6630
GF_EXPORT
6631
GF_Err gf_isom_add_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID, const u8 *data, u32 data_size)
6632
0
{
6633
0
  GF_List *list;
6634
0
    u32 btype;
6635
0
  GF_Box *box;
6636
0
  GF_UnknownUUIDBox *uuidb;
6637
6638
0
  if (data_size && !data) return GF_BAD_PARAM;
6639
0
  if (trackNumber==(u32) -1) {
6640
0
    if (!movie) return GF_BAD_PARAM;
6641
0
    list = movie->TopBoxes;
6642
0
  } else if (trackNumber) {
6643
0
    GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber);
6644
0
    if (!trak) return GF_BAD_PARAM;
6645
0
    if (!trak->child_boxes) trak->child_boxes = gf_list_new();
6646
0
    list = trak->child_boxes;
6647
0
  } else {
6648
0
    if (!movie) return GF_BAD_PARAM;
6649
0
    if (!movie->moov) {
6650
0
      GF_Err e = gf_isom_insert_moov(movie);
6651
0
      if (e) return e;
6652
0
    }
6653
0
    if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new();
6654
0
    list = movie->moov->child_boxes;
6655
0
  }
6656
0
    btype = gf_isom_solve_uuid_box((char *) UUID);
6657
0
    if (!btype) btype = GF_ISOM_BOX_TYPE_UUID;
6658
0
    box = gf_isom_box_new(btype);
6659
0
    if (!box) return GF_OUT_OF_MEM;
6660
0
  uuidb = (GF_UnknownUUIDBox*)box;
6661
0
  uuidb->internal_4cc = gf_isom_solve_uuid_box((char *) UUID);
6662
0
  if ((char *) UUID)
6663
0
    memcpy(uuidb->uuid, UUID, sizeof(bin128));
6664
0
  uuidb->dataSize = data_size;
6665
0
  if (data_size) {
6666
0
    uuidb->data = (char*)gf_malloc(sizeof(char)*data_size);
6667
0
    if (!uuidb->data) return GF_OUT_OF_MEM;
6668
0
    memcpy(uuidb->data, data, sizeof(char)*data_size);
6669
0
  }
6670
0
  gf_list_add(list, uuidb);
6671
0
  return GF_OK;
6672
0
}
6673
6674
6675
GF_EXPORT
6676
GF_Err gf_isom_apple_set_tag_ex(GF_ISOFile *mov, GF_ISOiTunesTag tag, const u8 *data, u32 data_len, u64 int_val, u32 int_val2, const char *in_cust_name, const char *in_cust_mean, u32 locale)
6677
0
{
6678
0
  GF_Err e;
6679
0
  GF_ItemListBox *ilst;
6680
0
  GF_MetaBox *meta;
6681
0
  GF_ListItemBox *info;
6682
0
  u32 btype=0, i, itype;
6683
0
  s32 tag_idx;
6684
0
  u32 n=0, d=0;
6685
0
  u8 loc_data[10];
6686
0
  u32 int_flags = 0x15;
6687
0
  GF_DataBox *dbox;
6688
0
  char *cust_mean=NULL, *cust_name=NULL;
6689
6690
0
  if (in_cust_name || in_cust_mean) tag = GF_4CC('c','u','s','t');
6691
6692
0
  e = gf_isom_can_access_movie(mov, GF_ISOM_OPEN_WRITE);
6693
0
  if (e) return e;
6694
6695
0
  tag_idx = gf_itags_find_by_itag(tag);
6696
0
  if (tag_idx<0) {
6697
0
    itype = GF_ITAG_STR;
6698
0
  } else {
6699
0
    itype = gf_itags_get_type(tag_idx);
6700
0
  }
6701
0
  meta = (GF_MetaBox *) gf_isom_create_meta_extensions(mov, GF_FALSE);
6702
0
  if (!meta) return GF_BAD_PARAM;
6703
0
  if (mov->brand->majorBrand == GF_4CC_CSTR("qt  "))
6704
0
    meta->write_qt = 1;
6705
6706
0
  ilst = gf_isom_locate_box(meta->child_boxes, GF_ISOM_BOX_TYPE_ILST, NULL);
6707
0
  if (!ilst) {
6708
0
    ilst = (GF_ItemListBox *) gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_ILST);
6709
0
  }
6710
6711
0
  if (tag==GF_ISOM_ITUNE_RESET) {
6712
0
    gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) ilst);
6713
    //if last, delete udta - we may still have a handler box remaining
6714
0
    if ((gf_list_count(meta->child_boxes) <= 1) && (gf_list_count(mov->moov->udta->recordList)==1)) {
6715
0
      gf_isom_box_del_parent(&mov->moov->child_boxes, (GF_Box *) mov->moov->udta);
6716
0
      mov->moov->udta = NULL;
6717
0
    }
6718
0
    return GF_OK;
6719
0
  }
6720
6721
0
  if (tag==GF_ISOM_ITUNE_GENRE) {
6722
0
    if (!int_val && data) {
6723
0
      int_val = gf_id3_get_genre_tag(data);
6724
0
      if (int_val) {
6725
0
        data = NULL;
6726
0
        data_len = 0;
6727
0
        itype = GF_ITAG_INT16;
6728
0
        int_flags = 0;
6729
0
      }
6730
0
    }
6731
0
    btype = data ? GF_ISOM_ITUNE_GENRE_USER : GF_ISOM_ITUNE_GENRE;
6732
0
  } else if (tag==GF_4CC('c','u','s','t') ) {
6733
0
    if (in_cust_name || in_cust_mean) {
6734
0
      if (in_cust_mean && in_cust_mean[0])
6735
0
        cust_mean = gf_strdup(in_cust_mean);
6736
0
      if (in_cust_name && in_cust_name[0])
6737
0
        cust_name = gf_strdup(in_cust_name);
6738
0
      btype = GF_ISOM_BOX_TYPE_iTunesSpecificInfo;
6739
0
    } else {
6740
0
      char *sep = strchr(data, ',');
6741
0
      if (sep) {
6742
0
        sep[0] = 0;
6743
0
        if (data[0])
6744
0
          cust_mean = gf_strdup(data);
6745
0
        sep[0] = ',';
6746
0
        sep++;
6747
6748
0
        char *sep2 = strchr(sep+1, ',');
6749
0
        if (sep2) {
6750
0
          sep2[0] = 0;
6751
0
          if (sep[0])
6752
0
            cust_name = gf_strdup(sep);
6753
0
          sep2[0] = ',';
6754
0
          data_len -= (u32) (sep2-(char*)data) +1;
6755
0
          data = sep2+1;
6756
0
        } else {
6757
0
          data_len -= (u32) (sep-(char*)data)+1;
6758
0
          data = sep+1;
6759
0
        }
6760
0
        btype = GF_ISOM_BOX_TYPE_iTunesSpecificInfo;
6761
0
      }
6762
0
    }
6763
0
  } else {
6764
0
    btype = tag;
6765
0
  }
6766
  /*remove tag*/
6767
0
  i = 0;
6768
0
  while ((info = (GF_ListItemBox*)gf_list_enum(ilst->child_boxes, &i))) {
6769
0
    if (info->type==GF_ISOM_BOX_TYPE_iTunesSpecificInfo) {
6770
6771
0
      if (info->name && cust_name && !strcmp(info->name->string, cust_name)) {}
6772
0
      else if (!info->name && !cust_name) {}
6773
0
      else continue;
6774
6775
0
      if (info->mean && cust_mean && !strcmp(info->mean->string, cust_mean)) {}
6776
0
      else if (!info->mean && !cust_mean) {}
6777
0
      else continue;
6778
6779
0
      gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *) info);
6780
0
      info = NULL;
6781
0
      break;
6782
0
    }
6783
0
    if (info->type==btype) {
6784
0
      gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *) info);
6785
0
      info = NULL;
6786
0
      break;
6787
0
    }
6788
0
    if (info->type==GF_ISOM_BOX_TYPE_UNKNOWN) {
6789
0
      GF_UnknownBox *u = (GF_UnknownBox *) info;
6790
0
      if (u->original_4cc==btype) {
6791
0
        gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *) info);
6792
0
        info = NULL;
6793
0
        break;
6794
0
      }
6795
0
    }
6796
0
  }
6797
6798
0
  if (!data && data_len) {
6799
0
    if (!gf_list_count(ilst->child_boxes) )
6800
0
      gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) ilst);
6801
0
    if (cust_mean) gf_free(cust_mean);
6802
0
    if (cust_name) gf_free(cust_name);
6803
0
    return GF_OK;
6804
0
  }
6805
6806
  //watch out for cprt, we don't want to create a regular cprt box
6807
0
  if (btype==GF_ISOM_ITUNE_COPYRIGHT) {
6808
0
    info = (GF_ListItemBox *)gf_isom_box_new(GF_ISOM_ITUNE_TOOL);
6809
0
    info->type = GF_ISOM_ITUNE_COPYRIGHT;
6810
0
  } else {
6811
0
    info = (GF_ListItemBox *)gf_isom_box_new(btype);
6812
0
  }
6813
0
  if (info == NULL) {
6814
0
    if (cust_mean) gf_free(cust_mean);
6815
0
    if (cust_name) gf_free(cust_name);
6816
0
    return GF_OUT_OF_MEM;
6817
0
  }
6818
6819
0
  dbox = (GF_DataBox *)gf_isom_box_new_parent(&info->child_boxes, GF_ISOM_BOX_TYPE_DATA);
6820
0
  if (!dbox) {
6821
0
    gf_isom_box_del((GF_Box *)info);
6822
0
    if (cust_mean) gf_free(cust_mean);
6823
0
    if (cust_name) gf_free(cust_name);
6824
0
    return GF_OUT_OF_MEM;
6825
0
  }
6826
0
  dbox->locale = locale;
6827
6828
0
  if (info->type!=GF_ISOM_BOX_TYPE_UNKNOWN) {
6829
0
    info->data = dbox;
6830
6831
0
    if (cust_name) {
6832
0
      info->name = (GF_NameBox *)gf_isom_box_new_parent(&info->child_boxes, GF_QT_BOX_TYPE_NAME);
6833
0
      info->name->string = cust_name;
6834
0
    }
6835
0
    if (cust_mean) {
6836
0
      info->mean = (GF_NameBox *)gf_isom_box_new_parent(&info->child_boxes, GF_QT_BOX_TYPE_MEAN);
6837
0
      info->mean->string = cust_mean;
6838
0
    }
6839
0
  }
6840
6841
0
  switch (itype) {
6842
0
  case GF_ITAG_FRAC6:
6843
0
  case GF_ITAG_FRAC8:
6844
0
    if (data && data_len) {
6845
0
      if (sscanf(data, "%u/%u", &n, &d) != 2) {
6846
0
        n = d = 0;
6847
0
        if (sscanf(data, "%u", &n) != 1)
6848
0
          n = 0;
6849
0
      }
6850
0
    } else {
6851
0
      n = (u32) int_val;
6852
0
      d = int_val2;
6853
0
    }
6854
0
    if (n) {
6855
0
      memset(loc_data, 0, sizeof(char) * 8);
6856
0
      data_len = (itype == GF_ITAG_FRAC6) ? 6 : 8;
6857
0
      loc_data[3] = n;
6858
0
      loc_data[2] = n >> 8;
6859
0
      loc_data[5] = d;
6860
0
      loc_data[4] = d >> 8;
6861
0
      data = loc_data;
6862
0
    } else {
6863
0
      data = NULL;
6864
0
    }
6865
0
    dbox->flags = 0x15;
6866
0
    break;
6867
0
  case GF_ITAG_BOOL:
6868
0
    loc_data[0] = 0;
6869
0
    if (data && data_len) {
6870
0
      if ( !strcmp(data, "yes") || !strcmp(data, "1") || !strcmp(data, "true"))
6871
0
        loc_data[0] = 1;
6872
0
    } else {
6873
0
      loc_data[0] = int_val ? 1 : 0;
6874
0
    }
6875
0
    data = loc_data;
6876
0
    data_len = 1;
6877
0
    dbox->flags = int_flags;
6878
0
    break;
6879
0
  case GF_ITAG_INT8:
6880
0
    loc_data[0] = 0;
6881
0
    if (data && data_len) int_val = atoi(data);
6882
0
    loc_data[0] = (u8) int_val;
6883
0
    data = loc_data;
6884
0
    data_len = 1;
6885
0
    dbox->flags = int_flags;
6886
0
    break;
6887
0
  case GF_ITAG_INT16:
6888
0
    loc_data[0] = 0;
6889
0
    if (data && data_len) int_val = atoi(data);
6890
0
    loc_data[1] = (u8) int_val;
6891
0
    loc_data[0] = (u8) (int_val>>8);
6892
0
    data = loc_data;
6893
0
    data_len = 2;
6894
0
    dbox->flags = int_flags;
6895
0
    break;
6896
0
  case GF_ITAG_INT32:
6897
0
    loc_data[0] = 0;
6898
0
    if (data && data_len) int_val = atoi(data);
6899
0
    loc_data[3] = (u8) int_val;
6900
0
    loc_data[2] = (u8) (int_val>>8);
6901
0
    loc_data[1] = (u8) (int_val>>16);
6902
0
    loc_data[0] = (u8) (int_val>>24);
6903
0
    data = loc_data;
6904
0
    data_len = 4;
6905
0
    dbox->flags = int_flags;
6906
0
    break;
6907
0
  case GF_ITAG_INT64:
6908
0
    loc_data[0] = 0;
6909
0
    if (data && data_len) sscanf(data, LLU, &int_val);
6910
0
    loc_data[7] = (u8) int_val;
6911
0
    loc_data[6] = (u8) (int_val>>8);
6912
0
    loc_data[5] = (u8) (int_val>>16);
6913
0
    loc_data[4] = (u8) (int_val>>24);
6914
0
    loc_data[3] = (u8) (int_val>>32);
6915
0
    loc_data[2] = (u8) (int_val>>40);
6916
0
    loc_data[1] = (u8) (int_val>>48);
6917
0
    loc_data[0] = (u8) (int_val>>56);
6918
0
    data = loc_data;
6919
0
    data_len = 8;
6920
0
    dbox->flags = int_flags;
6921
0
    break;
6922
0
  default:
6923
0
    dbox->flags = 1;
6924
0
    break;
6925
0
  }
6926
6927
0
  if (!data) return GF_BAD_PARAM;
6928
6929
6930
0
  if (tag==GF_ISOM_ITUNE_COVER_ART) {
6931
0
    info->data->flags = 0;
6932
    /*check for PNG sig*/
6933
0
    if ((data_len>4) && (data[0] == 0x89) && (data[1] == 0x50) && (data[2] == 0x4E) && (data[3] == 0x47) ) {
6934
0
      info->data->flags = 14;
6935
0
    }
6936
    //JPG and JFIF - do not check second tag type
6937
0
    else if ((data_len>4) && (data[0] == 0xFF) && (data[1] == 0xD8) && (data[2] == 0xFF) /*&& ((data[3] == 0xE0) || (data[3] == 0xDB))*/ ) {
6938
0
      info->data->flags = 13;
6939
0
    }
6940
    //GIF
6941
0
    else if ((data_len>3) && (data[0] == 'G') && (data[1] == 'I') && (data[2] == 'F') ) {
6942
0
      info->data->flags = 12;
6943
0
    }
6944
0
  }
6945
6946
0
  dbox->dataSize = data_len;
6947
0
  dbox->data = (char*)gf_malloc(sizeof(char)*data_len);
6948
0
  if (!dbox->data) return GF_OUT_OF_MEM;
6949
0
  memcpy(dbox->data, data, sizeof(char)*data_len);
6950
6951
0
  if (!info && !gf_list_count(ilst->child_boxes) ) {
6952
0
    gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) ilst);
6953
0
    return GF_OK;
6954
0
  }
6955
0
  if (!ilst->child_boxes) ilst->child_boxes = gf_list_new();
6956
6957
0
  return gf_list_add(ilst->child_boxes, info);
6958
0
}
6959
6960
GF_EXPORT
6961
GF_Err gf_isom_apple_set_tag(GF_ISOFile *mov, GF_ISOiTunesTag tag, const u8 *data, u32 data_len, u64 int_val, u32 int_val2)
6962
0
{
6963
0
  return gf_isom_apple_set_tag_ex(mov, tag, data, data_len, int_val, int_val2, NULL, NULL, 0);
6964
0
}
6965
6966
#include <gpac/utf.h>
6967
6968
GF_EXPORT
6969
GF_Err gf_isom_wma_set_tag(GF_ISOFile *mov, char *name, char *value)
6970
0
{
6971
0
  GF_Err e;
6972
0
  GF_XtraTag *tag=NULL;
6973
0
  u32 count, i;
6974
0
  GF_XtraBox *xtra;
6975
6976
0
  e = gf_isom_can_access_movie(mov, GF_ISOM_OPEN_WRITE);
6977
0
  if (e) return e;
6978
6979
0
  gf_isom_create_meta_extensions(mov, GF_FALSE);
6980
6981
0
  xtra = (GF_XtraBox *) gf_isom_create_meta_extensions(mov, GF_TRUE);
6982
0
  if (!xtra) return GF_BAD_PARAM;
6983
6984
0
  count = gf_list_count(xtra->tags);
6985
0
  for (i=0; i<count; i++) {
6986
0
    tag = gf_list_get(xtra->tags, i);
6987
0
    if (name && tag->name && !strcmp(tag->name, name)) {
6988
6989
0
    } else {
6990
0
      tag = NULL;
6991
0
      continue;
6992
0
    }
6993
6994
0
    if (!value) {
6995
0
      gf_list_rem(xtra->tags, i);
6996
0
      gf_free(tag->name);
6997
0
      if (tag->prop_value) gf_free(tag->prop_value);
6998
0
      gf_free(tag);
6999
0
      return GF_OK;
7000
0
    }
7001
0
    gf_free(tag->prop_value);
7002
0
    tag->prop_value = NULL;
7003
0
    break;
7004
0
  }
7005
0
  if (!tag) {
7006
0
    if (!name) return GF_OK;
7007
7008
0
    GF_SAFEALLOC(tag, GF_XtraTag);
7009
0
    tag->name = gf_strdup(name);
7010
0
    tag->prop_type = 0;
7011
0
    tag->flags = 1;
7012
0
    gf_list_add(xtra->tags, tag);
7013
0
  }
7014
7015
0
  u32 len = (u32) strlen(value);
7016
0
  tag->prop_value = gf_malloc(sizeof(u16) * ((len/2)*2+2) );
7017
0
  memset(tag->prop_value, 0, sizeof(u16) * ((len/2)*2+2) );
7018
0
  if (len) {
7019
0
    u32 _len = gf_utf8_mbstowcs((u16 *) tag->prop_value, len+1, (const char **) &value);
7020
0
    if (_len == GF_UTF8_FAIL) _len = 0;
7021
0
    tag->prop_value[2 * _len] = 0;
7022
0
    tag->prop_value[2 * _len + 1] = 0;
7023
0
    tag->prop_size = 2 * _len + 2;
7024
0
  } else {
7025
0
    tag->prop_size = 2;
7026
0
  }
7027
0
  return GF_OK;
7028
0
}
7029
7030
GF_EXPORT
7031
GF_Err gf_isom_set_qt_key(GF_ISOFile *movie, GF_QT_UDTAKey *key)
7032
0
{
7033
0
  GF_Err e;
7034
0
  GF_MetaBox *meta;
7035
0
  GF_ItemListBox *ilst;
7036
0
  GF_MetaKeysBox *keys;
7037
0
  u32 i, nb_keys;
7038
7039
0
  if (!movie) return GF_BAD_PARAM;
7040
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
7041
0
  if (e) {
7042
0
    gf_isom_set_last_error(movie, e);
7043
0
    return 0;
7044
0
  }
7045
0
  e = gf_isom_insert_moov(movie);
7046
0
  if (e) return e;
7047
7048
0
  meta = (GF_MetaBox *) gf_isom_create_meta_extensions(movie, 2);
7049
0
  if (!meta) return GF_BAD_PARAM;
7050
7051
0
  keys = (GF_MetaKeysBox *) gf_isom_locate_box(meta->child_boxes, GF_ISOM_BOX_TYPE_KEYS, NULL);
7052
0
  if (!keys) {
7053
0
    keys = (GF_MetaKeysBox *) gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_KEYS);
7054
0
    meta->keys = keys;
7055
0
  }
7056
0
  ilst = (GF_ItemListBox *) gf_isom_locate_box(meta->child_boxes, GF_ISOM_BOX_TYPE_ILST, NULL);
7057
0
  if (!ilst) {
7058
0
    ilst = (GF_ItemListBox *) gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_ILST);
7059
0
  }
7060
0
  if (!keys || !ilst) return GF_OUT_OF_MEM;
7061
7062
0
  nb_keys = gf_list_count(keys->keys);
7063
0
  if (!key) {
7064
0
    gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) keys);
7065
0
    for (i=0; i<gf_list_count(ilst->child_boxes); i++) {
7066
0
      GF_ListItemBox *info = gf_list_get(ilst->child_boxes, i);
7067
0
      if (info->type <= nb_keys) {
7068
0
        gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *) info);
7069
0
        i--;
7070
0
      }
7071
0
    }
7072
0
    if (!gf_list_count(ilst->child_boxes)) {
7073
0
      gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) ilst);
7074
      //if last, delete udta - we may still have a handler box remaining
7075
0
      if ((gf_list_count(meta->child_boxes) <= 1) && (gf_list_count(movie->moov->udta->recordList)==1)) {
7076
0
        gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box *) movie->moov->udta);
7077
0
        movie->moov->udta = NULL;
7078
0
      }
7079
0
    }
7080
0
    return GF_OK;
7081
0
  }
7082
  //locate key
7083
0
  GF_MetaKey *o_key = NULL;
7084
0
  u32 ksize = (u32) strlen(key->name);
7085
0
  for (i=0; i<nb_keys; i++) {
7086
0
    o_key = gf_list_get(keys->keys, i);
7087
0
    if ((o_key->ns == key->ns) && (o_key->size==ksize) && !strcmp(o_key->data, key->name))
7088
0
      break;
7089
0
    o_key = NULL;
7090
0
  }
7091
0
  if (!o_key) {
7092
0
    if (key->type==GF_QT_KEY_REMOVE) return GF_OK;
7093
0
    GF_SAFEALLOC(o_key, GF_MetaKey);
7094
0
    o_key->ns = key->ns;
7095
0
    o_key->data = gf_strdup(key->name);
7096
0
    o_key->size = ksize;
7097
0
    gf_list_add(keys->keys, o_key);
7098
0
  }
7099
0
  u32 key_idx = gf_list_find(keys->keys, o_key)+1;
7100
0
  GF_UnknownBox *info=NULL;
7101
0
  for (i=0; i<gf_list_count(ilst->child_boxes); i++) {
7102
0
    info = gf_list_get(ilst->child_boxes, i);
7103
0
    if (info->original_4cc == key_idx) break;
7104
0
    info = NULL;
7105
0
  }
7106
7107
0
  if (key->type==GF_QT_KEY_REMOVE) {
7108
0
    gf_list_del_item(keys->keys, o_key);
7109
0
    if (o_key->data) gf_free(o_key->data);
7110
0
    gf_free(o_key);
7111
0
    for (i=0; i<gf_list_count(ilst->child_boxes); i++) {
7112
0
      info = gf_list_get(ilst->child_boxes, i);
7113
0
      if (info->type!=GF_ISOM_BOX_TYPE_UNKNOWN) continue;
7114
0
      if (info->original_4cc==key_idx) {
7115
0
        gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *)info);
7116
0
        i--;
7117
0
        continue;
7118
0
      }
7119
0
      if (info->original_4cc>key_idx) {
7120
0
        info->original_4cc--;
7121
0
      }
7122
0
    }
7123
0
    return GF_OK;
7124
0
  }
7125
0
  if (!info) {
7126
0
    info = (GF_UnknownBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_UNKNOWN);
7127
0
    if (!info) return GF_OUT_OF_MEM;
7128
0
    if (!ilst->child_boxes) ilst->child_boxes = gf_list_new();
7129
0
    gf_list_add(ilst->child_boxes, info);
7130
0
  }
7131
7132
0
  GF_DataBox *dbox = (GF_DataBox *) gf_isom_box_find_child(info->child_boxes, GF_ISOM_BOX_TYPE_DATA);
7133
0
  if (!dbox) {
7134
0
    dbox = (GF_DataBox *)gf_isom_box_new_parent(&info->child_boxes, GF_ISOM_BOX_TYPE_DATA);
7135
0
    if (!dbox) {
7136
0
      gf_isom_box_del((GF_Box *)info);
7137
0
      return GF_OUT_OF_MEM;
7138
0
    }
7139
0
  }
7140
0
  u32 nb_bits=0;
7141
0
  info->original_4cc = key_idx;
7142
0
  dbox->version = 0;
7143
0
  dbox->flags = key->type;
7144
  //serialize
7145
0
  GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
7146
7147
0
  switch (key->type) {
7148
0
  case GF_QT_KEY_UTF8:
7149
0
  case GF_QT_KEY_UTF8_SORT:
7150
0
    if (key->value.string)
7151
0
      gf_bs_write_data(bs, key->value.string, (u32) strlen(key->value.string));
7152
0
    break;
7153
7154
0
  case GF_QT_KEY_SIGNED_VSIZE:
7155
0
    if (ABS(key->value.sint)<=0x7F) nb_bits=8;
7156
0
    else if (ABS(key->value.sint)<=0x7FFF) nb_bits=16;
7157
0
    else if (ABS(key->value.sint)<=0x7FFFFF) nb_bits=24;
7158
0
    else nb_bits = 32;
7159
0
    gf_bs_write_int(bs, (s32) key->value.sint, nb_bits);
7160
0
    break;
7161
0
  case GF_QT_KEY_UNSIGNED_VSIZE:
7162
0
    if (key->value.uint<=0xFF) nb_bits=8;
7163
0
    else if (key->value.uint<=0xFFFF) nb_bits=16;
7164
0
    else if (key->value.uint<=0xFFFFFF) nb_bits=24;
7165
0
    else nb_bits = 32;
7166
0
    gf_bs_write_int(bs, (u32) key->value.uint, nb_bits);
7167
0
    break;
7168
0
  case GF_QT_KEY_FLOAT:
7169
0
    gf_bs_write_float(bs, (Float) key->value.number);
7170
0
    break;
7171
0
  case GF_QT_KEY_DOUBLE:
7172
0
    gf_bs_write_double(bs, key->value.number);
7173
0
    break;
7174
0
  case GF_QT_KEY_SIGNED_8:
7175
0
    gf_bs_write_int(bs, (s32) key->value.sint, 8);
7176
0
    break;
7177
0
  case GF_QT_KEY_SIGNED_16:
7178
0
    gf_bs_write_int(bs, (s32) key->value.sint, 16);
7179
0
    break;
7180
0
  case GF_QT_KEY_SIGNED_32:
7181
0
    gf_bs_write_int(bs, (s32) key->value.sint, 32);
7182
0
    break;
7183
0
  case GF_QT_KEY_SIGNED_64:
7184
0
    gf_bs_write_long_int(bs, key->value.sint, 64);
7185
0
    break;
7186
0
  case GF_QT_KEY_POINTF:
7187
0
  case GF_QT_KEY_SIZEF:
7188
0
    gf_bs_write_float(bs, key->value.pos_size.x);
7189
0
    gf_bs_write_float(bs, key->value.pos_size.y);
7190
0
    break;
7191
0
  case GF_QT_KEY_RECTF:
7192
0
    gf_bs_write_float(bs, key->value.rect.x);
7193
0
    gf_bs_write_float(bs, key->value.rect.y);
7194
0
    gf_bs_write_float(bs, key->value.rect.w);
7195
0
    gf_bs_write_float(bs, key->value.rect.h);
7196
0
    break;
7197
7198
0
  case GF_QT_KEY_UNSIGNED_8:
7199
0
    gf_bs_write_int(bs, (s32) key->value.uint, 8);
7200
0
    break;
7201
0
  case GF_QT_KEY_UNSIGNED_16:
7202
0
    gf_bs_write_int(bs, (s32) key->value.uint, 16);
7203
0
    break;
7204
0
  case GF_QT_KEY_UNSIGNED_32:
7205
0
    gf_bs_write_int(bs, (s32) key->value.uint, 32);
7206
0
    break;
7207
0
  case GF_QT_KEY_UNSIGNED_64:
7208
0
    gf_bs_write_long_int(bs, key->value.uint, 64);
7209
0
    break;
7210
0
  case GF_QT_KEY_MATRIXF:
7211
0
    for (i=0; i<9; i++)
7212
0
      gf_bs_write_float(bs, (Float) key->value.matrix[i] );
7213
0
    break;
7214
7215
0
  case GF_QT_KEY_OPAQUE:
7216
0
  case GF_QT_KEY_UTF16_BE:
7217
0
  case GF_QT_KEY_JIS:
7218
0
  case GF_QT_KEY_UTF16_SORT:
7219
0
  case GF_QT_KEY_JPEG:
7220
0
  case GF_QT_KEY_PNG:
7221
0
  case GF_QT_KEY_BMP:
7222
0
  case GF_QT_KEY_METABOX:
7223
0
  default:
7224
0
    gf_bs_write_data(bs, key->value.data.data, key->value.data.data_len);
7225
0
    break;
7226
0
  }
7227
  //write extra 0 at end, not serialized
7228
0
  gf_bs_write_u8(bs, 0);
7229
0
  if (dbox->data) gf_free(dbox->data);
7230
7231
0
  gf_bs_get_content(bs, &dbox->data, &i);
7232
0
  if (i) i--;
7233
0
  dbox->dataSize = i;
7234
0
  gf_bs_del(bs);
7235
0
  return GF_OK;
7236
0
}
7237
7238
7239
GF_EXPORT
7240
GF_Err gf_isom_set_alternate_group_id(GF_ISOFile *movie, u32 trackNumber, u32 groupId)
7241
0
{
7242
0
  GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber);
7243
0
  if (!trak) return GF_BAD_PARAM;
7244
0
  trak->Header->alternate_group = groupId;
7245
0
  return GF_OK;
7246
0
}
7247
7248
7249
GF_EXPORT
7250
GF_Err gf_isom_set_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, u32 trackRefGroup, Bool is_switch_group, u32 *switchGroupID, u32 *criteriaList, u32 criteriaListCount)
7251
0
{
7252
0
  GF_TrackSelectionBox *tsel;
7253
0
  GF_TrackBox *trak;
7254
0
  GF_UserDataMap *map;
7255
0
  GF_Err e;
7256
0
  u32 alternateGroupID = 0;
7257
0
  u32 next_switch_group_id = 0;
7258
7259
0
  trak = gf_isom_get_track_box(movie, trackNumber);
7260
0
  if (!trak || !switchGroupID) return GF_BAD_PARAM;
7261
7262
7263
0
  if (trackRefGroup) {
7264
0
    GF_TrackBox *trak_ref = gf_isom_get_track_box(movie, trackRefGroup);
7265
0
    if (trak_ref != trak) {
7266
0
      if (!trak_ref || !trak_ref->Header->alternate_group) {
7267
0
        GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Track %d has not an alternate group - skipping\n", trak_ref ? trak_ref->Header->trackID : 0));
7268
0
        return GF_BAD_PARAM;
7269
0
      }
7270
0
      alternateGroupID = trak_ref->Header->alternate_group;
7271
0
    } else {
7272
0
      alternateGroupID = trak->Header->alternate_group;
7273
0
    }
7274
0
  }
7275
0
  if (!alternateGroupID) {
7276
    /*there is a function for this ....*/
7277
0
    if (trak->Header->alternate_group && !is_switch_group) {
7278
0
      GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Track %d has already an alternate group - skipping\n", trak->Header->trackID));
7279
0
      return GF_BAD_PARAM;
7280
0
    }
7281
0
    alternateGroupID = gf_isom_get_next_alternate_group_id(movie);
7282
0
  }
7283
7284
0
  if (is_switch_group) {
7285
0
    u32 i=0;
7286
0
    while (i< gf_isom_get_track_count(movie) ) {
7287
      //locate first available ID
7288
0
      GF_TrackBox *a_trak = gf_isom_get_track_box(movie, i+1);
7289
7290
0
      if (a_trak->udta) {
7291
0
        u32 j, count;
7292
0
        map = udta_getEntry(a_trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL);
7293
0
        if (map) {
7294
0
          count = gf_list_count(map->boxes);
7295
0
          for (j=0; j<count; j++) {
7296
0
            tsel = (GF_TrackSelectionBox*)gf_list_get(map->boxes, j);
7297
7298
0
            if (*switchGroupID) {
7299
0
              if (tsel->switchGroup==next_switch_group_id) {
7300
0
                if (a_trak->Header->alternate_group != alternateGroupID) return GF_BAD_PARAM;
7301
0
              }
7302
0
            } else {
7303
0
              if (tsel->switchGroup && (tsel->switchGroup>=next_switch_group_id) )
7304
0
                next_switch_group_id = tsel->switchGroup;
7305
0
            }
7306
0
          }
7307
0
        }
7308
7309
0
      }
7310
0
      i++;
7311
0
    }
7312
0
    if (! *switchGroupID) *switchGroupID = next_switch_group_id+1;
7313
0
  }
7314
7315
0
  if (!trak->Header->alternate_group)
7316
0
    trak->Header->alternate_group = alternateGroupID;
7317
7318
0
  tsel = NULL;
7319
0
  if (*switchGroupID) {
7320
0
    if (!trak->udta) {
7321
0
      e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE);
7322
0
      if (e) return e;
7323
0
    }
7324
7325
0
    map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL);
7326
7327
    /*locate tsel box with no switch group*/
7328
0
    if (map)  {
7329
0
      u32 j, count = gf_list_count(map->boxes);
7330
0
      for (j=0; j<count; j++) {
7331
0
        tsel = (GF_TrackSelectionBox*)gf_list_get(map->boxes, j);
7332
0
        if (tsel->switchGroup == *switchGroupID) break;
7333
0
        tsel = NULL;
7334
0
      }
7335
0
    }
7336
0
    if (!tsel) {
7337
0
      tsel = (GF_TrackSelectionBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TSEL);
7338
0
      if (!tsel) return GF_OUT_OF_MEM;
7339
0
      e = udta_on_child_box_ex((GF_Box *)trak->udta, (GF_Box *) tsel, GF_FALSE, GF_TRUE);
7340
0
      if (e) return e;
7341
0
    }
7342
7343
0
    tsel->switchGroup = *switchGroupID;
7344
0
    tsel->attributeListCount = criteriaListCount;
7345
0
    if (tsel->attributeList) gf_free(tsel->attributeList);
7346
0
    tsel->attributeList = (u32*)gf_malloc(sizeof(u32)*criteriaListCount);
7347
0
    if (!tsel->attributeList) return GF_OUT_OF_MEM;
7348
0
    memcpy(tsel->attributeList, criteriaList, sizeof(u32)*criteriaListCount);
7349
0
  }
7350
0
  return GF_OK;
7351
0
}
7352
7353
void reset_tsel_box(GF_TrackBox *trak)
7354
0
{
7355
0
  GF_UserDataMap *map;
7356
0
  trak->Header->alternate_group = 0;
7357
0
  map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL);
7358
0
  if (map) {
7359
0
    gf_list_del_item(trak->udta->recordList, map);
7360
0
    gf_isom_box_array_del(map->boxes);
7361
0
    gf_free(map);
7362
0
  }
7363
7364
0
}
7365
7366
GF_EXPORT
7367
GF_Err gf_isom_reset_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, Bool reset_all_group)
7368
0
{
7369
0
  GF_TrackBox *trak;
7370
0
  u32 alternateGroupID = 0;
7371
7372
0
  trak = gf_isom_get_track_box(movie, trackNumber);
7373
0
  if (!trak) return GF_BAD_PARAM;
7374
0
  if (!trak->Header->alternate_group) return GF_OK;
7375
7376
0
  alternateGroupID = trak->Header->alternate_group;
7377
0
  if (reset_all_group) {
7378
0
    u32 i=0;
7379
0
    while (i< gf_isom_get_track_count(movie) ) {
7380
      //locate first available ID
7381
0
      GF_TrackBox *a_trak = gf_isom_get_track_box(movie, i+1);
7382
0
      if (a_trak->Header->alternate_group == alternateGroupID) reset_tsel_box(a_trak);
7383
0
      i++;
7384
0
    }
7385
0
  } else {
7386
0
    reset_tsel_box(trak);
7387
0
  }
7388
0
  return GF_OK;
7389
0
}
7390
7391
7392
GF_EXPORT
7393
GF_Err gf_isom_reset_switch_parameters(GF_ISOFile *movie)
7394
0
{
7395
0
  u32 i=0;
7396
0
  while (i< gf_isom_get_track_count(movie) ) {
7397
    //locate first available ID
7398
0
    GF_TrackBox *a_trak = gf_isom_get_track_box(movie, i+1);
7399
0
    reset_tsel_box(a_trak);
7400
0
    i++;
7401
0
  }
7402
0
  return GF_OK;
7403
0
}
7404
7405
7406
GF_Err gf_isom_add_subsample(GF_ISOFile *movie, u32 track, u32 sampleNumber, u32 flags, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable)
7407
0
{
7408
0
  u32 i, count;
7409
0
  GF_SubSampleInformationBox *sub_samples;
7410
0
  GF_TrackBox *trak;
7411
0
  GF_Err e;
7412
7413
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
7414
0
  if (e) return e;
7415
7416
0
  trak = gf_isom_get_track_box(movie, track);
7417
0
  if (!trak || !trak->Media || !trak->Media->information->sampleTable)
7418
0
    return GF_BAD_PARAM;
7419
7420
0
  if (!trak->Media->information->sampleTable->sub_samples) {
7421
0
    trak->Media->information->sampleTable->sub_samples=gf_list_new();
7422
0
  }
7423
7424
0
  sub_samples = NULL;
7425
0
  count = gf_list_count(trak->Media->information->sampleTable->sub_samples);
7426
0
  for (i=0; i<count; i++) {
7427
0
    sub_samples = gf_list_get(trak->Media->information->sampleTable->sub_samples, i);
7428
0
    if (sub_samples->flags==flags) break;
7429
0
    sub_samples = NULL;
7430
0
  }
7431
0
  if (!sub_samples) {
7432
0
    sub_samples = (GF_SubSampleInformationBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_SUBS);
7433
0
    if (!sub_samples) return GF_OUT_OF_MEM;
7434
0
    gf_list_add(trak->Media->information->sampleTable->sub_samples, sub_samples);
7435
0
    sub_samples->version = (subSampleSize>0xFFFF) ? 1 : 0;
7436
0
    sub_samples->flags = flags;
7437
0
  }
7438
0
  return gf_isom_add_subsample_info(sub_samples, sampleNumber, subSampleSize, priority, reserved, discardable);
7439
0
}
7440
7441
7442
GF_EXPORT
7443
GF_Err gf_isom_set_rvc_config(GF_ISOFile *movie, u32 track, u32 sampleDescriptionIndex, u16 rvc_predefined, char *mime, u8 *data, u32 size)
7444
0
{
7445
0
  GF_MPEGVisualSampleEntryBox *entry;
7446
0
  GF_Err e;
7447
0
  GF_TrackBox *trak;
7448
7449
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
7450
0
  if (e) return e;
7451
7452
0
  trak = gf_isom_get_track_box(movie, track);
7453
0
  if (!trak) return GF_BAD_PARAM;
7454
7455
7456
0
  entry = (GF_MPEGVisualSampleEntryBox *) gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex-1);
7457
0
  if (!entry ) return GF_BAD_PARAM;
7458
0
  if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM;
7459
7460
0
  GF_RVCConfigurationBox *rvcc = (GF_RVCConfigurationBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_RVCC);
7461
0
  if (rvcc && rvcc->rvc_meta_idx) {
7462
0
    gf_isom_remove_meta_item(movie, GF_FALSE, track, rvcc->rvc_meta_idx, GF_FALSE, NULL);
7463
0
    rvcc->rvc_meta_idx = 0;
7464
0
  }
7465
7466
0
  if (!rvcc) {
7467
0
    rvcc = (GF_RVCConfigurationBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_RVCC);
7468
0
    if (!rvcc) return GF_OUT_OF_MEM;
7469
0
  }
7470
0
  rvcc->predefined_rvc_config = rvc_predefined;
7471
0
  if (!rvc_predefined) {
7472
0
    u32 it_id=0;
7473
0
    e = gf_isom_set_meta_type(movie, GF_FALSE, track, GF_META_TYPE_RVCI);
7474
0
    if (e) return e;
7475
0
    gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_ISO2, GF_TRUE);
7476
0
    e = gf_isom_add_meta_item_memory(movie, GF_FALSE, track, "rvcconfig.xml", &it_id, GF_META_ITEM_TYPE_MIME, mime, NULL, NULL, data, size, NULL);
7477
0
    if (e) return e;
7478
0
    rvcc->rvc_meta_idx = gf_isom_get_meta_item_count(movie, GF_FALSE, track);
7479
0
  }
7480
0
  return GF_OK;
7481
0
}
7482
7483
/*for now not exported*/
7484
/*expands sampleGroup table for the given grouping type and sample_number. If sample_number is 0, just appends an entry at the end of the table*/
7485
static GF_Err gf_isom_add_sample_group_entry(GF_List *sampleGroups, u32 sample_number, GF_SampleGroupDescriptionBox *sgdesc, u32 grouping_type_parameter, u32 sampleGroupDescriptionIndex, GF_List *parent, GF_SampleTableBox *stbl)
7486
0
{
7487
0
  GF_SampleGroupBox *sgroup = NULL;
7488
0
  u32 i, count, last_sample_in_entry;
7489
0
  Bool all_samples = GF_FALSE;
7490
0
  gf_assert(sampleGroups);
7491
0
  if (!sgdesc) return GF_BAD_PARAM;
7492
0
  count = gf_list_count(sampleGroups);
7493
0
  for (i=0; i<count; i++) {
7494
0
    sgroup = (GF_SampleGroupBox*)gf_list_get(sampleGroups, i);
7495
0
    if (sgroup->grouping_type==sgdesc->grouping_type) break;
7496
0
    sgroup = NULL;
7497
0
  }
7498
0
  if (!sgroup) {
7499
0
    sgroup = (GF_SampleGroupBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SBGP);
7500
0
    if (!sgroup) return GF_OUT_OF_MEM;
7501
0
    sgroup->grouping_type = sgdesc->grouping_type;
7502
0
    sgroup->grouping_type_parameter = grouping_type_parameter;
7503
//    gf_list_add(sampleGroups, sgroup);
7504
    //crude patch to align old arch and filters
7505
0
    gf_list_insert(sampleGroups, sgroup, 0);
7506
0
    gf_assert(parent);
7507
0
    gf_list_add(parent, sgroup);
7508
0
  }
7509
0
  u32 def_insert_value = (sgdesc && (sgdesc->version==2)) ? sgdesc->default_description_index : 0;
7510
7511
  /*used in fragments, means we are adding the last sample*/
7512
0
  if (!sample_number) {
7513
0
    sample_number = 1;
7514
0
    if (sgroup->entry_count) {
7515
0
      for (i=0; i<sgroup->entry_count; i++) {
7516
0
        sample_number += sgroup->sample_entries[i].sample_count;
7517
0
      }
7518
0
    }
7519
0
  } else if (sample_number==(u32) -1) {
7520
0
    all_samples = GF_TRUE;
7521
0
    sample_number = 1;
7522
0
  }
7523
7524
0
  if (!sgroup->entry_count) {
7525
0
    u32 idx = 0;
7526
0
    sgroup->entry_count = (sample_number>1) ? 2 : 1;
7527
0
    sgroup->sample_entries = (GF_SampleGroupEntry*)gf_malloc(sizeof(GF_SampleGroupEntry) * sgroup->entry_count );
7528
0
    if (!sgroup->sample_entries) return GF_OUT_OF_MEM;
7529
0
    if (sample_number>1) {
7530
0
      sgroup->sample_entries[0].sample_count = sample_number-1;
7531
0
      sgroup->sample_entries[0].group_description_index = sampleGroupDescriptionIndex ? def_insert_value : 1;
7532
0
      idx = 1;
7533
0
    }
7534
0
    sgroup->sample_entries[idx].sample_count = 1;
7535
0
    sgroup->sample_entries[idx].group_description_index = sampleGroupDescriptionIndex;
7536
0
    if (all_samples && stbl) {
7537
0
      sgroup->sample_entries[idx].sample_count = stbl->SampleSize->sampleCount;
7538
0
    }
7539
0
    return GF_OK;
7540
0
  }
7541
0
  if (all_samples && stbl) {
7542
0
    sgroup->entry_count = 1;
7543
0
    sgroup->sample_entries[0].group_description_index = sampleGroupDescriptionIndex;
7544
0
    sgroup->sample_entries[0].sample_count = stbl->SampleSize->sampleCount;
7545
0
    return GF_OK;
7546
0
  }
7547
0
  last_sample_in_entry = 0;
7548
0
  for (i=0; i<sgroup->entry_count; i++) {
7549
    /*TODO*/
7550
0
    if (last_sample_in_entry + sgroup->sample_entries[i].sample_count > sample_number)
7551
0
      return GF_NOT_SUPPORTED;
7552
0
    last_sample_in_entry += sgroup->sample_entries[i].sample_count;
7553
0
  }
7554
7555
0
  if (last_sample_in_entry == sample_number) {
7556
0
    if (sgroup->sample_entries[sgroup->entry_count-1].group_description_index==sampleGroupDescriptionIndex)
7557
0
      return GF_OK;
7558
0
    else
7559
0
      return GF_NOT_SUPPORTED;
7560
0
  }
7561
7562
0
  if ((sgroup->sample_entries[sgroup->entry_count-1].group_description_index==sampleGroupDescriptionIndex) && (last_sample_in_entry+1==sample_number)) {
7563
0
    sgroup->sample_entries[sgroup->entry_count-1].sample_count++;
7564
0
    return GF_OK;
7565
0
  }
7566
  /*last entry was an empty desc (no group associated), just add the number of samples missing until new one, then add new one*/
7567
0
  if (! sgroup->sample_entries[sgroup->entry_count-1].group_description_index) {
7568
0
    sgroup->sample_entries[sgroup->entry_count-1].sample_count += sample_number - 1 - last_sample_in_entry;
7569
0
    sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 1) );
7570
0
    sgroup->sample_entries[sgroup->entry_count].sample_count = 1;
7571
0
    sgroup->sample_entries[sgroup->entry_count].group_description_index = sampleGroupDescriptionIndex;
7572
0
    sgroup->entry_count++;
7573
0
    return GF_OK;
7574
0
  }
7575
  /*we are adding a sample with no desc, add entry at the end*/
7576
0
  if (!sampleGroupDescriptionIndex || (sample_number - 1 - last_sample_in_entry==0) ) {
7577
0
    sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 1) );
7578
0
    sgroup->sample_entries[sgroup->entry_count].sample_count = 1;
7579
0
    sgroup->sample_entries[sgroup->entry_count].group_description_index = sampleGroupDescriptionIndex;
7580
0
    sgroup->entry_count++;
7581
0
    return GF_OK;
7582
0
  }
7583
  /*need to insert two entries ...*/
7584
0
  sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 2) );
7585
7586
0
  sgroup->sample_entries[sgroup->entry_count].sample_count = sample_number - 1 - last_sample_in_entry;
7587
0
  sgroup->sample_entries[sgroup->entry_count].group_description_index = def_insert_value;
7588
7589
0
  sgroup->sample_entries[sgroup->entry_count+1].sample_count = 1;
7590
0
  sgroup->sample_entries[sgroup->entry_count+1].group_description_index = sampleGroupDescriptionIndex;
7591
0
  sgroup->entry_count+=2;
7592
0
  return GF_OK;
7593
0
}
7594
7595
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
7596
static GF_SampleGroupDescriptionBox *get_sgdp(GF_SampleTableBox *stbl, GF_TrackFragmentBox *traf, u32 grouping_type, Bool *is_traf_sgdp)
7597
#else
7598
static GF_SampleGroupDescriptionBox *get_sgdp(GF_SampleTableBox *stbl, void *traf, u32 grouping_type, Bool *is_traf_sgdp)
7599
#endif /* GPAC_DISABLE_ISOM_FRAGMENTS */
7600
0
{
7601
0
  GF_List *groupList=NULL;
7602
0
  GF_List **parent=NULL;
7603
0
  GF_SampleGroupDescriptionBox *sgdesc=NULL;
7604
0
  u32 count, i;
7605
7606
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
7607
0
  if (!stbl && traf && traf->trex->track->Media->information->sampleTable)
7608
0
    stbl = traf->trex->track->Media->information->sampleTable;
7609
0
#endif
7610
0
  if (stbl) {
7611
0
    if (!stbl->sampleGroupsDescription && !traf)
7612
0
      stbl->sampleGroupsDescription = gf_list_new();
7613
7614
0
    groupList = stbl->sampleGroupsDescription;
7615
0
    if (is_traf_sgdp) *is_traf_sgdp = GF_FALSE;
7616
0
    parent = &stbl->child_boxes;
7617
7618
0
    count = gf_list_count(groupList);
7619
0
    for (i=0; i<count; i++) {
7620
0
      sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(groupList, i);
7621
0
      if (sgdesc->grouping_type==grouping_type) break;
7622
0
      sgdesc = NULL;
7623
0
    }
7624
0
  }
7625
7626
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
7627
  /*look in stbl or traf for sample sampleGroupsDescription*/
7628
0
  if (!sgdesc && traf) {
7629
0
    if (!traf->sampleGroupsDescription)
7630
0
      traf->sampleGroupsDescription = gf_list_new();
7631
0
    groupList = traf->sampleGroupsDescription;
7632
0
    parent = &traf->child_boxes;
7633
0
    if (is_traf_sgdp) *is_traf_sgdp = GF_TRUE;
7634
7635
0
    count = gf_list_count(groupList);
7636
0
    for (i=0; i<count; i++) {
7637
0
      sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(groupList, i);
7638
0
      if (sgdesc->grouping_type==grouping_type) break;
7639
0
      sgdesc = NULL;
7640
0
    }
7641
0
  }
7642
0
#endif
7643
7644
0
  if (!sgdesc) {
7645
0
    sgdesc = (GF_SampleGroupDescriptionBox *) gf_isom_box_new_parent(parent, GF_ISOM_BOX_TYPE_SGPD);
7646
0
    if (!sgdesc) return NULL;
7647
0
    sgdesc->grouping_type = grouping_type;
7648
0
    gf_assert(groupList);
7649
0
    gf_list_add(groupList, sgdesc);
7650
0
  }
7651
0
  return sgdesc;
7652
0
}
7653
7654
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
7655
static GF_Err gf_isom_set_sample_group_info_ex(GF_SampleTableBox *stbl, GF_TrackFragmentBox *traf, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *udta, void *(*sg_create_entry)(void *udta), Bool (*sg_compare_entry)(void *udta, void *entry))
7656
#else
7657
static GF_Err gf_isom_set_sample_group_info_ex(GF_SampleTableBox *stbl, void *traf, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *udta, void *(*sg_create_entry)(void *udta), Bool (*sg_compare_entry)(void *udta, void *entry))
7658
#endif /* GPAC_DISABLE_ISOM_FRAGMENTS */
7659
0
{
7660
0
  GF_List *groupList, *parent;
7661
0
  void *entry;
7662
0
  Bool is_traf_sgpd;
7663
0
  GF_SampleGroupDescriptionBox *sgdesc = NULL;
7664
0
  u32 i, entry_idx;
7665
7666
0
  if (!stbl && !traf) return GF_BAD_PARAM;
7667
7668
0
  sgdesc = get_sgdp(stbl, traf, grouping_type, &is_traf_sgpd);
7669
0
  if (!sgdesc) return GF_OUT_OF_MEM;
7670
7671
0
  entry = NULL;
7672
0
  if (sg_compare_entry) {
7673
0
    for (i=0; i<gf_list_count(sgdesc->group_descriptions); i++) {
7674
0
      entry = gf_list_get(sgdesc->group_descriptions, i);
7675
0
      if (sg_compare_entry(udta, entry)) break;
7676
0
      entry = NULL;
7677
0
    }
7678
0
  }
7679
0
  if (!entry && sg_create_entry) {
7680
0
    entry = sg_create_entry(udta);
7681
0
    if (!entry) return GF_IO_ERR;
7682
0
    if (traf && !is_traf_sgpd) {
7683
0
      sgdesc = get_sgdp(NULL, traf, grouping_type, &is_traf_sgpd);
7684
0
    }
7685
0
    gf_list_add(sgdesc->group_descriptions, entry);
7686
0
  }
7687
0
  if (!entry)
7688
0
    entry_idx = 0;
7689
0
  else
7690
0
    entry_idx = 1 + gf_list_find(sgdesc->group_descriptions, entry);
7691
7692
  /*look in stbl or traf for sample sampleGroups*/
7693
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
7694
0
  if (traf) {
7695
0
    if (!traf->sampleGroups)
7696
0
      traf->sampleGroups = gf_list_new();
7697
0
    groupList = traf->sampleGroups;
7698
0
    parent = traf->child_boxes;
7699
0
    if (entry_idx && is_traf_sgpd)
7700
0
      entry_idx |= 0x10000;
7701
0
  } else
7702
0
#endif
7703
0
  {
7704
0
    if (!stbl->sampleGroups)
7705
0
      stbl->sampleGroups = gf_list_new();
7706
0
    groupList = stbl->sampleGroups;
7707
0
    parent = stbl->child_boxes;
7708
0
  }
7709
7710
0
  return gf_isom_add_sample_group_entry(groupList, sample_number, sgdesc, grouping_type_parameter, entry_idx, parent, stbl);
7711
0
}
7712
7713
static GF_Err gf_isom_set_sample_group_info_internal(GF_ISOFile *movie, u32 track, u32 trafID, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *udta, void *(*sg_create_entry)(void *udta), Bool (*sg_compare_entry)(void *udta, void *entry))
7714
0
{
7715
0
  GF_Err e;
7716
0
  GF_TrackBox *trak=NULL;
7717
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
7718
0
  GF_TrackFragmentBox *traf=NULL;
7719
0
#endif
7720
0
  if (!trafID && (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) {
7721
0
    trak = gf_isom_get_track_box(movie, track);
7722
0
    if (!trak) return GF_BAD_PARAM;
7723
0
    trafID = trak->Header->trackID;
7724
0
  }
7725
7726
0
  if (trafID) {
7727
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
7728
0
    if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) )
7729
0
      return GF_BAD_PARAM;
7730
7731
0
    traf = gf_isom_get_traf(movie, trafID);
7732
#else
7733
    return GF_NOT_SUPPORTED;
7734
#endif
7735
7736
0
  } else if (track) {
7737
0
    e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
7738
0
    if (e) return e;
7739
7740
0
    trak = gf_isom_get_track_box(movie, track);
7741
0
    if (!trak) return GF_BAD_PARAM;
7742
0
  }
7743
7744
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
7745
0
  return gf_isom_set_sample_group_info_ex(trak ? trak->Media->information->sampleTable : NULL, traf, sample_number, grouping_type, grouping_type_parameter, udta, sg_create_entry, sg_compare_entry);
7746
#else
7747
  return gf_isom_set_sample_group_info_ex(trak->Media->information->sampleTable, NULL, sample_number, grouping_type, grouping_type_parameter, udta, sg_create_entry, sg_compare_entry);
7748
#endif
7749
7750
0
}
7751
7752
void *sgpd_parse_entry(GF_SampleGroupDescriptionBox *p, GF_BitStream *bs, s32 bytes_in_box, u32 entry_size, u32 *total_bytes);
7753
7754
GF_Err gf_isom_add_sample_group_info_internal(GF_ISOFile *movie, u32 track, u32 grouping_type, void *data, u32 data_size, u32 sgpd_flags, u32 *sampleGroupDescriptionIndex, Bool *is_traf_sgpd, Bool check_access, Bool *use_default, GF_SampleGroupDescriptionBox **out_sgdesc)
7755
0
{
7756
0
  GF_Err e;
7757
0
  GF_TrackBox *trak=NULL;
7758
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
7759
0
  GF_TrackFragmentBox *traf=NULL;
7760
#else
7761
  void *traf=NULL;
7762
#endif
7763
0
  u32 trafID=0;
7764
0
  GF_DefaultSampleGroupDescriptionEntry *entry=NULL;
7765
0
  GF_SampleGroupDescriptionBox *sgdesc = NULL;
7766
0
  Bool is_default = sgpd_flags & 0x80000000;
7767
7768
0
  if (sampleGroupDescriptionIndex) *sampleGroupDescriptionIndex = 0;
7769
7770
0
  if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) {
7771
0
    trak = gf_isom_get_track_box(movie, track);
7772
0
    if (!trak) return GF_BAD_PARAM;
7773
0
    trafID = trak->Header->trackID;
7774
0
  }
7775
7776
0
  if (trafID) {
7777
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
7778
0
    if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) )
7779
0
      return GF_BAD_PARAM;
7780
7781
0
    traf = gf_isom_get_traf(movie, trafID);
7782
#else
7783
    return GF_NOT_SUPPORTED;
7784
#endif
7785
0
  } else {
7786
0
    if (check_access) {
7787
0
      e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
7788
0
      if (e) return e;
7789
0
    }
7790
7791
0
    trak = gf_isom_get_track_box(movie, track);
7792
0
  }
7793
0
  if (!trak) return GF_BAD_PARAM;
7794
7795
  //get sample group desc for this grouping type
7796
0
  sgdesc = get_sgdp(trak->Media->information->sampleTable, traf, grouping_type, is_traf_sgpd);
7797
0
  if (!sgdesc) return GF_OUT_OF_MEM;
7798
  //first time we create the sample group description, set flags
7799
0
  if (!gf_list_count(sgdesc->group_descriptions) && !traf) {
7800
0
    if (sgpd_flags&1) sgdesc->flags |= 1;
7801
0
    if (sgpd_flags&2) sgdesc->flags |= 2;
7802
0
    if (sgpd_flags&0x40000000) sgdesc->version=3;
7803
0
  }
7804
7805
7806
0
  GF_BitStream *bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
7807
0
  u32 bytes;
7808
0
  entry = sgpd_parse_entry(sgdesc, bs, data_size, data_size, &bytes);
7809
0
  gf_bs_del(bs);
7810
0
  if (!entry) return GF_NON_COMPLIANT_BITSTREAM;
7811
7812
0
  if (out_sgdesc) *out_sgdesc = sgdesc;
7813
7814
7815
  //find the same entry
7816
0
  u32 k;
7817
0
  for (k=0; k<gf_list_count(sgdesc->group_descriptions); k++) {
7818
0
    void *sgde_dst = gf_list_get(sgdesc->group_descriptions, k);
7819
0
    if (gf_isom_is_identical_sgpd(entry, sgde_dst, sgdesc->grouping_type)) {
7820
0
      if (sampleGroupDescriptionIndex) *sampleGroupDescriptionIndex = k+1;
7821
0
      sgpd_del_entry(sgdesc->grouping_type, entry, sgdesc->is_opaque);
7822
0
      if (use_default) {
7823
0
        u32 idx = k+1;
7824
0
        if (is_traf_sgpd && *is_traf_sgpd) idx |= 0x10000;
7825
0
        *use_default = (sgdesc->default_description_index==idx) ? GF_TRUE : GF_FALSE;
7826
0
      }
7827
0
      return GF_OK;
7828
0
    }
7829
0
  }
7830
7831
0
  if (traf && ! *is_traf_sgpd) {
7832
0
    sgdesc = get_sgdp(NULL, traf, grouping_type, is_traf_sgpd);
7833
0
    if (!sgdesc) return GF_OUT_OF_MEM;
7834
0
  }
7835
0
  if (out_sgdesc) *out_sgdesc = sgdesc;
7836
7837
0
  e = gf_list_add(sgdesc->group_descriptions, entry);
7838
0
  if (e) {
7839
0
    sgpd_del_entry(sgdesc->grouping_type, entry, sgdesc->is_opaque);
7840
0
    return e;
7841
0
  }
7842
7843
#if 0
7844
  if (grouping_type==GF_ISOM_SAMPLE_GROUP_OINF) {
7845
    GF_OperatingPointsInformation *ptr = gf_isom_oinf_new_entry();
7846
    GF_BitStream *bs=gf_bs_new(data, data_size, GF_BITSTREAM_READ);
7847
    e = gf_isom_oinf_read_entry(ptr, bs);
7848
    gf_bs_del(bs);
7849
    if (e) {
7850
      gf_isom_oinf_del_entry(ptr);
7851
      return e;
7852
    }
7853
    //not in track, create new sgdp
7854
    if (traf && ! *is_traf_sgpd) {
7855
      sgdesc  = get_sgdp(NULL, traf, grouping_type, is_traf_sgpd);
7856
      if (!sgdesc) return GF_OUT_OF_MEM;
7857
    }
7858
    e = gf_list_add(sgdesc->group_descriptions, ptr);
7859
    if (e) return e;
7860
    entry = (GF_DefaultSampleGroupDescriptionEntry *) ptr;
7861
  } else if (grouping_type==GF_ISOM_SAMPLE_GROUP_LINF) {
7862
    GF_LHVCLayerInformation *ptr = gf_isom_linf_new_entry();
7863
    GF_BitStream *bs=gf_bs_new(data, data_size, GF_BITSTREAM_READ);
7864
    e = gf_isom_linf_read_entry(ptr, bs);
7865
    gf_bs_del(bs);
7866
    if (e) {
7867
      gf_isom_linf_del_entry(ptr);
7868
      return e;
7869
    }
7870
7871
    if (traf && ! *is_traf_sgpd) {
7872
      sgdesc = get_sgdp(NULL, traf, grouping_type, is_traf_sgpd);
7873
      if (!sgdesc) return GF_OUT_OF_MEM;
7874
    }
7875
7876
    e = gf_list_add(sgdesc->group_descriptions, ptr);
7877
    if (e) return e;
7878
    entry = (GF_DefaultSampleGroupDescriptionEntry *) ptr;
7879
  } else {
7880
    u32 i, count=gf_list_count(sgdesc->group_descriptions);
7881
    for (i=0; i<count; i++) {
7882
      GF_DefaultSampleGroupDescriptionEntry *ent = gf_list_get(sgdesc->group_descriptions, i);
7883
      if ((ent->length == data_size) && !memcmp(ent->data, data, data_size)) {
7884
        entry = ent;
7885
        break;
7886
      }
7887
      entry=NULL;
7888
    }
7889
    if (!entry) {
7890
      GF_SAFEALLOC(entry, GF_DefaultSampleGroupDescriptionEntry);
7891
      if (!entry) return GF_OUT_OF_MEM;
7892
      entry->data = (u8*)gf_malloc(sizeof(char) * data_size);
7893
      if (!entry->data) {
7894
        gf_free(entry);
7895
        return GF_OUT_OF_MEM;
7896
      }
7897
      entry->length = data_size;
7898
      memcpy(entry->data, data, sizeof(char) * data_size);
7899
7900
      if (traf && ! *is_traf_sgpd) {
7901
        sgdesc = get_sgdp(NULL, traf, grouping_type, is_traf_sgpd);
7902
        if (!sgdesc) return GF_OUT_OF_MEM;
7903
      }
7904
7905
      e = gf_list_add(sgdesc->group_descriptions, entry);
7906
      if (e) {
7907
        gf_free(entry->data);
7908
        gf_free(entry);
7909
        return e;
7910
      }
7911
    }
7912
  }
7913
#endif
7914
7915
7916
0
  if (is_default && !sgdesc->default_description_index) {
7917
0
    sgdesc->default_description_index = 1 + gf_list_find(sgdesc->group_descriptions, entry);
7918
0
    if (sgdesc->version < 2) sgdesc->version = 2;
7919
0
    if (is_traf_sgpd && *is_traf_sgpd) {
7920
0
      sgdesc->default_description_index |= 0x10000;
7921
0
    }
7922
0
  }
7923
0
  u32 grp_idx =  1 + gf_list_find(sgdesc->group_descriptions, entry);
7924
0
  if (sampleGroupDescriptionIndex) *sampleGroupDescriptionIndex = grp_idx;
7925
0
  if (use_default) {
7926
0
    if (*is_traf_sgpd)
7927
0
      grp_idx |= 0x10000;
7928
0
    *use_default = (sgdesc->default_description_index==grp_idx) ? GF_TRUE : GF_FALSE;
7929
0
  }
7930
0
  return GF_OK;
7931
0
}
7932
GF_EXPORT
7933
GF_Err gf_isom_add_sample_group_info(GF_ISOFile *movie, u32 track, u32 grouping_type, void *data, u32 data_size, Bool is_default, u32 *sampleGroupDescriptionIndex)
7934
0
{
7935
0
  return gf_isom_add_sample_group_info_internal(movie, track, grouping_type, data, data_size, is_default ? 0x80000000 : 0, sampleGroupDescriptionIndex, NULL, GF_TRUE, NULL, NULL);
7936
0
}
7937
7938
GF_Err gf_isom_set_sample_group_description_internal(GF_ISOFile *movie, u32 track, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *data, u32 data_size, Bool check_access, u32 sgpd_flags)
7939
0
{
7940
0
  u32 sampleGroupDescriptionIndex, trafID=0;
7941
0
  GF_Err e;
7942
0
  GF_SampleGroupDescriptionBox *sgdesc=NULL;
7943
0
  Bool is_traf_sgpd, use_default=GF_FALSE;
7944
0
  GF_List *groupList=NULL, *parent=NULL;
7945
7946
0
  e = gf_isom_add_sample_group_info_internal(movie, track, grouping_type, data, data_size, sgpd_flags, &sampleGroupDescriptionIndex, &is_traf_sgpd, check_access, &use_default, &sgdesc);
7947
0
  if (e) return e;
7948
0
  if (use_default) return GF_OK;
7949
7950
0
  GF_SampleTableBox *stbl=NULL;
7951
0
  GF_TrackBox *trak=NULL;
7952
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
7953
0
  GF_TrackFragmentBox *traf=NULL;
7954
0
#endif
7955
0
  if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) {
7956
0
    trak = gf_isom_get_track_box(movie, track);
7957
0
    if (!trak) return GF_BAD_PARAM;
7958
0
    trafID = trak->Header->trackID;
7959
0
  }
7960
7961
0
  if (trafID) {
7962
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
7963
0
    if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) )
7964
0
      return GF_BAD_PARAM;
7965
7966
0
    traf = gf_isom_get_traf(movie, trafID);
7967
#else
7968
    return GF_NOT_SUPPORTED;
7969
#endif
7970
7971
0
  } else if (track) {
7972
0
    if (check_access) {
7973
0
      e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
7974
0
      if (e) return e;
7975
0
    }
7976
7977
0
    trak = gf_isom_get_track_box(movie, track);
7978
0
    if (!trak) return GF_BAD_PARAM;
7979
0
  }
7980
7981
  /*look in stbl or traf for sample sampleGroups*/
7982
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
7983
0
  if (traf) {
7984
0
    if (!traf->sampleGroups)
7985
0
      traf->sampleGroups = gf_list_new();
7986
0
    groupList = traf->sampleGroups;
7987
0
    parent = traf->child_boxes;
7988
0
    if (sampleGroupDescriptionIndex && is_traf_sgpd)
7989
0
      sampleGroupDescriptionIndex |= 0x10000;
7990
0
  } else
7991
0
#endif
7992
0
  {
7993
0
    stbl = trak->Media->information->sampleTable;
7994
0
    if (!stbl->sampleGroups)
7995
0
      stbl->sampleGroups = gf_list_new();
7996
0
    groupList = stbl->sampleGroups;
7997
0
    parent = stbl->child_boxes;
7998
0
  }
7999
8000
0
  return gf_isom_add_sample_group_entry(groupList, sample_number, sgdesc, grouping_type_parameter, sampleGroupDescriptionIndex, parent, stbl);
8001
8002
0
}
8003
8004
GF_Err gf_isom_set_sample_group_description(GF_ISOFile *movie, u32 track, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *data, u32 data_size, u32 sgpd_flags)
8005
0
{
8006
0
  return gf_isom_set_sample_group_description_internal(movie, track, sample_number, grouping_type, grouping_type_parameter, data, data_size, GF_TRUE, sgpd_flags);
8007
0
}
8008
8009
GF_EXPORT
8010
GF_Err gf_isom_remove_sample_group(GF_ISOFile *movie, u32 track, u32 grouping_type)
8011
0
{
8012
0
  GF_Err e;
8013
0
  GF_TrackBox *trak;
8014
0
  u32 count, i;
8015
8016
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
8017
0
  if (e) return e;
8018
8019
0
  trak = gf_isom_get_track_box(movie, track);
8020
0
  if (!trak) return GF_BAD_PARAM;
8021
8022
0
  if (trak->Media->information->sampleTable->sampleGroupsDescription) {
8023
0
    count = gf_list_count(trak->Media->information->sampleTable->sampleGroupsDescription);
8024
0
    for (i=0; i<count; i++) {
8025
0
      GF_SampleGroupDescriptionBox *sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(trak->Media->information->sampleTable->sampleGroupsDescription, i);
8026
0
      if (sgdesc->grouping_type==grouping_type) {
8027
0
        gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box*)sgdesc);
8028
0
        gf_list_rem(trak->Media->information->sampleTable->sampleGroupsDescription, i);
8029
0
        i--;
8030
0
        count--;
8031
0
      }
8032
0
    }
8033
0
  }
8034
0
  if (trak->Media->information->sampleTable->sampleGroups) {
8035
0
    count = gf_list_count(trak->Media->information->sampleTable->sampleGroups);
8036
0
    for (i=0; i<count; i++) {
8037
0
      GF_SampleGroupBox *sgroup = gf_list_get(trak->Media->information->sampleTable->sampleGroups, i);
8038
0
      if (sgroup->grouping_type==grouping_type) {
8039
0
        gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box*)sgroup);
8040
0
        gf_list_rem(trak->Media->information->sampleTable->sampleGroups, i);
8041
0
        i--;
8042
0
        count--;
8043
0
      }
8044
0
    }
8045
0
  }
8046
0
  return GF_OK;
8047
0
}
8048
8049
GF_EXPORT
8050
GF_Err gf_isom_add_sample_info(GF_ISOFile *movie, u32 track, u32 sample_number, u32 grouping_type, u32 sampleGroupDescriptionIndex, u32 grouping_type_parameter)
8051
0
{
8052
0
  GF_Err e;
8053
0
  GF_TrackBox *trak;
8054
0
  GF_List *groupList;
8055
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
8056
0
  if (e) return e;
8057
8058
0
  trak = gf_isom_get_track_box(movie, track);
8059
0
  if (!trak) return GF_BAD_PARAM;
8060
8061
0
  if (!trak->Media->information->sampleTable->sampleGroups)
8062
0
    trak->Media->information->sampleTable->sampleGroups = gf_list_new();
8063
8064
8065
0
  GF_SampleGroupDescriptionBox *sgdesc = get_sgdp(trak->Media->information->sampleTable, NULL, grouping_type, NULL);
8066
0
  if (!sgdesc) return GF_BAD_PARAM;
8067
8068
0
  groupList = trak->Media->information->sampleTable->sampleGroups;
8069
0
  return gf_isom_add_sample_group_entry(groupList, sample_number, sgdesc, grouping_type_parameter, sampleGroupDescriptionIndex, trak->Media->information->sampleTable->child_boxes, trak->Media->information->sampleTable);
8070
0
}
8071
8072
8073
void *sg_rap_create_entry(void *udta)
8074
0
{
8075
0
  GF_VisualRandomAccessEntry *entry;
8076
0
  u32 *num_leading_samples = (u32 *) udta;
8077
0
  gf_assert(udta);
8078
0
  GF_SAFEALLOC(entry, GF_VisualRandomAccessEntry);
8079
0
  if (!entry) return NULL;
8080
0
  entry->num_leading_samples = *num_leading_samples;
8081
0
  entry->num_leading_samples_known = entry->num_leading_samples ? 1 : 0;
8082
0
  return entry;
8083
0
}
8084
8085
Bool sg_rap_compare_entry(void *udta, void *entry)
8086
0
{
8087
0
  u32 *num_leading_samples = (u32 *) udta;
8088
0
  if (*num_leading_samples == ((GF_VisualRandomAccessEntry *)entry)->num_leading_samples) return GF_TRUE;
8089
0
  return GF_FALSE;
8090
0
}
8091
8092
GF_EXPORT
8093
GF_Err gf_isom_set_sample_rap_group(GF_ISOFile *movie, u32 track, u32 sample_number, Bool is_rap, u32 num_leading_samples)
8094
0
{
8095
0
  return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_RAP, 0, &num_leading_samples, is_rap ? sg_rap_create_entry : NULL, is_rap ? sg_rap_compare_entry : NULL);
8096
0
}
8097
8098
GF_Err gf_isom_fragment_set_sample_rap_group(GF_ISOFile *movie, GF_ISOTrackID trackID, u32 sample_number_in_frag, Bool is_rap, u32 num_leading_samples)
8099
0
{
8100
0
  return gf_isom_set_sample_group_info_internal(movie, 0, trackID, sample_number_in_frag, GF_ISOM_SAMPLE_GROUP_RAP, 0, &num_leading_samples, is_rap ? sg_rap_create_entry : NULL, is_rap ? sg_rap_compare_entry : NULL);
8101
0
}
8102
8103
8104
void *sg_av1s_create_entry(void *udta)
8105
0
{
8106
0
  GF_AV1SwitchingEntry *entry;
8107
0
  GF_SAFEALLOC(entry, GF_AV1SwitchingEntry);
8108
0
  if (!entry) return NULL;
8109
0
  return entry;
8110
0
}
8111
8112
Bool sg_av1s_compare_entry(void *udta, void *entry)
8113
0
{
8114
0
  return GF_TRUE;
8115
0
}
8116
8117
GF_EXPORT
8118
GF_Err gf_isom_set_sample_av1_switch_frame_group(GF_ISOFile *movie, u32 track, u32 sample_number, Bool is_switch_Frame)
8119
0
{
8120
0
  return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_AV1S, 0, NULL, is_switch_Frame ? sg_av1s_create_entry : NULL, is_switch_Frame ? sg_av1s_compare_entry : NULL);
8121
0
}
8122
8123
GF_Err gf_isom_fragment_set_sample_av1_switch_frame_group(GF_ISOFile *movie, GF_ISOTrackID trackID, u32 sample_number_in_frag, Bool is_switch_Frame)
8124
0
{
8125
0
  return gf_isom_set_sample_group_info_internal(movie, 0, trackID, sample_number_in_frag, GF_ISOM_SAMPLE_GROUP_AV1S, 0, NULL, is_switch_Frame ? sg_av1s_create_entry : NULL, is_switch_Frame ? sg_av1s_compare_entry : NULL);
8126
0
}
8127
8128
8129
8130
void *sg_roll_create_entry(void *udta)
8131
0
{
8132
0
  GF_RollRecoveryEntry *entry;
8133
0
  s16 *roll_distance = (s16 *) udta;
8134
0
  GF_SAFEALLOC(entry, GF_RollRecoveryEntry);
8135
0
  if (!entry) return NULL;
8136
0
  entry->roll_distance = *roll_distance;
8137
0
  return entry;
8138
0
}
8139
8140
Bool sg_roll_compare_entry(void *udta, void *entry)
8141
0
{
8142
0
  s16 *roll_distance = (s16 *) udta;
8143
0
  if (*roll_distance == ((GF_RollRecoveryEntry *)entry)->roll_distance) return GF_TRUE;
8144
0
  return GF_FALSE;
8145
0
}
8146
8147
GF_EXPORT
8148
GF_Err gf_isom_set_sample_roll_group(GF_ISOFile *movie, u32 track, u32 sample_number, GF_ISOSampleRollType roll_type, s16 roll_distance)
8149
0
{
8150
0
  u32 grp_type = (roll_type>=GF_ISOM_SAMPLE_PREROLL) ? GF_ISOM_SAMPLE_GROUP_PROL : GF_ISOM_SAMPLE_GROUP_ROLL;
8151
0
  if (roll_type==GF_ISOM_SAMPLE_PREROLL_NONE)
8152
0
    roll_type = 0;
8153
8154
0
  return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, grp_type, 0, &roll_distance, roll_type ? sg_roll_create_entry : NULL, roll_type ? sg_roll_compare_entry : NULL);
8155
0
}
8156
8157
GF_EXPORT
8158
GF_Err gf_isom_fragment_set_sample_roll_group(GF_ISOFile *movie, GF_ISOTrackID trackID, u32 sample_number_in_frag, GF_ISOSampleRollType roll_type, s16 roll_distance)
8159
0
{
8160
0
  u32 grp_type = (roll_type>=GF_ISOM_SAMPLE_PREROLL) ? GF_ISOM_SAMPLE_GROUP_PROL : GF_ISOM_SAMPLE_GROUP_ROLL;
8161
0
  if (roll_type==GF_ISOM_SAMPLE_PREROLL_NONE)
8162
0
    roll_type = 0;
8163
8164
0
  return gf_isom_set_sample_group_info_internal(movie, 0, trackID, sample_number_in_frag, grp_type, 0, &roll_distance, roll_type ? sg_roll_create_entry : NULL, roll_type ? sg_roll_compare_entry : NULL);
8165
0
}
8166
8167
8168
void *sg_encryption_create_entry(void *udta)
8169
0
{
8170
0
  GF_CENCSampleEncryptionGroupEntry *entry, *from_entry;
8171
0
  GF_SAFEALLOC(entry, GF_CENCSampleEncryptionGroupEntry);
8172
0
  if (!entry) return NULL;
8173
0
  from_entry = (GF_CENCSampleEncryptionGroupEntry *)udta;
8174
0
  memcpy(entry, from_entry, sizeof(GF_CENCSampleEncryptionGroupEntry) );
8175
0
  entry->key_info = gf_malloc(sizeof(u8) * entry->key_info_size);
8176
0
  if (!entry->key_info) {
8177
0
    gf_free(entry);
8178
0
    return NULL;
8179
0
  }
8180
0
  memcpy(entry->key_info, from_entry->key_info, entry->key_info_size);
8181
0
  return entry;
8182
0
}
8183
8184
Bool sg_encryption_compare_entry(void *udta, void *_entry)
8185
0
{
8186
0
  GF_CENCSampleEncryptionGroupEntry *entry = (GF_CENCSampleEncryptionGroupEntry *)_entry;
8187
0
  GF_CENCSampleEncryptionGroupEntry *with_entry = (GF_CENCSampleEncryptionGroupEntry *)udta;
8188
8189
0
  if (entry->IsProtected != with_entry->IsProtected) return GF_FALSE;
8190
0
  if (entry->skip_byte_block != with_entry->skip_byte_block) return GF_FALSE;
8191
0
  if (entry->crypt_byte_block != with_entry->crypt_byte_block) return GF_FALSE;
8192
0
  if (entry->key_info_size != with_entry->key_info_size) return GF_FALSE;
8193
8194
0
  if (!memcmp(entry->key_info, with_entry->key_info, with_entry->key_info_size))
8195
0
    return GF_TRUE;
8196
0
  return GF_FALSE;
8197
0
}
8198
8199
8200
/*sample encryption information group can be in stbl or traf*/
8201
GF_EXPORT
8202
GF_Err gf_isom_set_sample_cenc_group(GF_ISOFile *movie, u32 track, u32 sample_number, u8 isEncrypted, u32 crypt_byte_block, u32 skip_byte_block, u8 *key_info, u32 key_info_size)
8203
0
{
8204
0
  GF_CENCSampleEncryptionGroupEntry entry;
8205
0
  if (!key_info || (key_info_size<19))
8206
0
    return GF_BAD_PARAM;
8207
8208
0
  memset(&entry, 0, sizeof(GF_CENCSampleEncryptionGroupEntry));
8209
0
  entry.crypt_byte_block = crypt_byte_block;
8210
0
  entry.skip_byte_block = skip_byte_block;
8211
0
  entry.IsProtected = isEncrypted;
8212
0
  entry.key_info = key_info;
8213
0
  entry.key_info_size = key_info_size;
8214
8215
0
  return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_SEIG, 0, &entry, sg_encryption_create_entry, sg_encryption_compare_entry);
8216
0
}
8217
8218
8219
8220
GF_EXPORT
8221
GF_Err gf_isom_set_sample_cenc_default_group(GF_ISOFile *movie, u32 track, u32 sample_number)
8222
0
{
8223
0
  return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_SEIG, 0, NULL, NULL, NULL);
8224
0
}
8225
8226
GF_Err gf_isom_force_ctts(GF_ISOFile *file, u32 track)
8227
0
{
8228
0
  GF_TrackBox *trak;
8229
0
  GF_Err e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
8230
0
  if (e) return e;
8231
0
  trak = gf_isom_get_track_box(file, track);
8232
0
  if (!trak) return GF_BAD_PARAM;
8233
0
  if (trak->Media->information->sampleTable->CompositionOffset) return GF_OK;
8234
8235
0
  trak->Media->information->sampleTable->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_CTTS);
8236
0
  if (!trak->Media->information->sampleTable->CompositionOffset) return GF_OUT_OF_MEM;
8237
0
  trak->Media->information->sampleTable->CompositionOffset->nb_entries = 1;
8238
0
  trak->Media->information->sampleTable->CompositionOffset->entries = gf_malloc(sizeof(GF_DttsEntry));
8239
0
  trak->Media->information->sampleTable->CompositionOffset->entries[0].decodingOffset = 0;
8240
0
  trak->Media->information->sampleTable->CompositionOffset->entries[0].sampleCount =  trak->Media->information->sampleTable->SampleSize->sampleCount;
8241
0
  return GF_OK;
8242
0
}
8243
8244
GF_EXPORT
8245
GF_Err gf_isom_set_ctts_v1(GF_ISOFile *file, u32 track, u32 ctts_shift)
8246
0
{
8247
0
  u32 i, shift;
8248
0
  u64 duration;
8249
0
  GF_CompositionOffsetBox *ctts;
8250
0
  GF_CompositionToDecodeBox *cslg;
8251
0
  s32 leastCTTS, greatestCTTS;
8252
0
  GF_TrackBox *trak;
8253
0
  GF_Err e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
8254
0
  if (e) return e;
8255
8256
0
  trak = gf_isom_get_track_box(file, track);
8257
0
  if (!trak) return GF_BAD_PARAM;
8258
8259
0
  ctts = trak->Media->information->sampleTable->CompositionOffset;
8260
0
  if (ctts->version) {
8261
0
    shift = ctts_shift;
8262
0
  } else {
8263
0
    shift = ctts->nb_entries ? ctts->entries[0].decodingOffset : 0;
8264
0
  }
8265
0
  leastCTTS = GF_INT_MAX;
8266
0
  greatestCTTS = 0;
8267
0
  for (i=0; i<ctts->nb_entries; i++) {
8268
0
    if (!ctts->version)
8269
0
      ctts->entries[i].decodingOffset -= shift;
8270
8271
0
    if ((s32)ctts->entries[i].decodingOffset < leastCTTS)
8272
0
      leastCTTS = ctts->entries[i].decodingOffset;
8273
0
    if ((s32)ctts->entries[i].decodingOffset > greatestCTTS)
8274
0
      greatestCTTS = ctts->entries[i].decodingOffset;
8275
0
  }
8276
0
  if (!ctts->version) {
8277
0
    ctts->version = 1;
8278
    //if we had edit lists, shift all media times by the given amount
8279
0
    if (trak->editBox && trak->editBox->editList) {
8280
0
      for (i=0; i<gf_list_count(trak->editBox->editList->entryList); i++) {
8281
0
        GF_EdtsEntry *ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, i);
8282
        //empty edit
8283
0
        if (ent->mediaTime<0) continue;
8284
0
        if (ent->mediaTime>=shift) ent->mediaTime -= shift;
8285
0
        else ent->mediaTime = 0;
8286
        //no offset and last entry, trash edit
8287
0
        if (!ent->mediaTime && (gf_list_count(trak->editBox->editList->entryList)==1)) {
8288
0
          gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->editBox);
8289
0
          trak->editBox = NULL;
8290
0
          break;
8291
0
        }
8292
0
      }
8293
0
      SetTrackDuration(trak);
8294
0
    }
8295
0
  }
8296
8297
0
  if (!trak->Media->information->sampleTable->CompositionToDecode) {
8298
0
    trak->Media->information->sampleTable->CompositionToDecode = (GF_CompositionToDecodeBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_CSLG);
8299
0
    if (!trak->Media->information->sampleTable->CompositionToDecode)
8300
0
      return GF_OUT_OF_MEM;
8301
0
  }
8302
8303
0
  cslg = trak->Media->information->sampleTable->CompositionToDecode;
8304
0
  if (cslg) {
8305
0
    cslg->compositionToDTSShift = shift;
8306
0
    cslg->leastDecodeToDisplayDelta = leastCTTS;
8307
0
    cslg->greatestDecodeToDisplayDelta = greatestCTTS;
8308
0
    cslg->compositionStartTime = 0;
8309
    /*for our use case (first CTS set to 0), the composition end time is the media duration if it fits on 32 bits*/
8310
0
    duration = gf_isom_get_media_duration(file, track);
8311
0
    cslg->compositionEndTime = (duration<0x7FFFFFFF) ? (s32) duration : 0;
8312
0
  }
8313
8314
0
  gf_isom_modify_alternate_brand(file, GF_ISOM_BRAND_ISO4, GF_TRUE);
8315
0
  return GF_OK;
8316
0
}
8317
8318
static GF_Err gf_isom_set_ctts_v0(GF_ISOFile *file, GF_TrackBox *trak)
8319
0
{
8320
0
  u32 i;
8321
0
  s32 shift;
8322
0
  GF_CompositionOffsetBox *ctts;
8323
0
  GF_CompositionToDecodeBox *cslg;
8324
8325
0
  ctts = trak->Media->information->sampleTable->CompositionOffset;
8326
8327
0
  if (!trak->Media->information->sampleTable->CompositionToDecode)
8328
0
  {
8329
0
    shift = 0;
8330
0
    for (i=0; i<ctts->nb_entries; i++) {
8331
0
      if (-ctts->entries[i].decodingOffset > shift)
8332
0
        shift = -ctts->entries[i].decodingOffset;
8333
0
    }
8334
0
    if (shift > 0)
8335
0
    {
8336
0
      for (i=0; i<ctts->nb_entries; i++) {
8337
0
        s64 new_ts = ctts->entries[i].decodingOffset;
8338
0
        new_ts += shift;
8339
0
        ctts->entries[i].decodingOffset = (s32) new_ts;
8340
0
      }
8341
0
    }
8342
0
  }
8343
0
  else
8344
0
  {
8345
0
    cslg = trak->Media->information->sampleTable->CompositionToDecode;
8346
0
    shift = (s32) cslg->compositionToDTSShift;
8347
0
    for (i=0; i<ctts->nb_entries; i++) {
8348
0
      s64 new_ts = ctts->entries[i].decodingOffset;
8349
0
      new_ts += shift;
8350
0
      ctts->entries[i].decodingOffset = (s32) new_ts;
8351
0
    }
8352
0
    gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box *)cslg);
8353
0
    trak->Media->information->sampleTable->CompositionToDecode = NULL;
8354
0
  }
8355
0
  if (shift>0) {
8356
    //no edits, insert one
8357
0
    if (! trak->editBox) {
8358
0
      u64 dur = trak->Media->mediaHeader->duration;
8359
0
      dur *= file->moov->mvhd->timeScale;
8360
0
      dur /= trak->Media->mediaHeader->timeScale;
8361
0
      gf_isom_set_edit(file, gf_list_find(file->moov->trackList, trak)+1, 0, dur, shift, GF_ISOM_EDIT_NORMAL);
8362
0
    } else {
8363
      //otherwise shift media times in all entries
8364
0
      for (i=0; i<gf_list_count(trak->editBox->editList->entryList); i++) {
8365
0
        GF_EdtsEntry *ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, i);
8366
        //empty edit
8367
0
        if (ent->mediaTime<0) continue;
8368
0
        ent->mediaTime += shift;
8369
0
      }
8370
0
      SetTrackDuration(trak);
8371
0
    }
8372
0
  }
8373
0
  ctts->version = 0;
8374
0
  gf_isom_modify_alternate_brand(file, GF_ISOM_BRAND_ISO4, GF_FALSE);
8375
0
  return GF_OK;
8376
0
}
8377
8378
GF_EXPORT
8379
GF_Err gf_isom_set_composition_offset_mode(GF_ISOFile *file, u32 track, Bool use_negative_offsets)
8380
0
{
8381
0
  GF_Err e;
8382
0
  GF_TrackBox *trak;
8383
0
  GF_CompositionOffsetBox *ctts;
8384
8385
0
  e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
8386
0
  if (e) return e;
8387
8388
0
  trak = gf_isom_get_track_box(file, track);
8389
0
  if (!trak) return GF_BAD_PARAM;
8390
8391
0
  ctts = trak->Media->information->sampleTable->CompositionOffset;
8392
0
  if (!ctts) {
8393
0
    if (!use_negative_offsets && trak->Media->information->sampleTable->CompositionToDecode) {
8394
0
      gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box *)trak->Media->information->sampleTable->CompositionToDecode);
8395
0
      trak->Media->information->sampleTable->CompositionToDecode = NULL;
8396
0
    }
8397
0
    return GF_OK;
8398
0
  }
8399
8400
0
  if (use_negative_offsets) {
8401
0
    return gf_isom_set_ctts_v1(file, track, 0);
8402
0
  } else {
8403
0
    if (ctts->version==0) return GF_OK;
8404
0
    return gf_isom_set_ctts_v0(file, trak);
8405
0
  }
8406
0
}
8407
8408
#if 0 //unused
8409
GF_Err gf_isom_set_sync_table(GF_ISOFile *file, u32 track)
8410
{
8411
  GF_Err e;
8412
  GF_TrackBox *trak;
8413
8414
  e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
8415
  if (e) return e;
8416
8417
  trak = gf_isom_get_track_box(file, track);
8418
  if (!trak) return GF_BAD_PARAM;
8419
8420
  if (!trak->Media->information->sampleTable->SyncSample) {
8421
    trak->Media->information->sampleTable->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STSS);
8422
8423
    if (!trak->Media->information->sampleTable->SyncSample)
8424
      return GF_OUT_OF_MEM;
8425
  }
8426
  return GF_OK;
8427
}
8428
#endif
8429
8430
GF_Err gf_isom_set_sample_flags(GF_ISOFile *file, u32 track, u32 sampleNumber, u32 isLeading, u32 dependsOn, u32 dependedOn, u32 redundant)
8431
0
{
8432
0
  GF_Err e;
8433
0
  GF_TrackBox *trak;
8434
8435
0
  e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
8436
0
  if (e) return e;
8437
8438
0
  trak = gf_isom_get_track_box(file, track);
8439
0
  if (!trak) return GF_BAD_PARAM;
8440
0
  return stbl_SetDependencyType(trak->Media->information->sampleTable, sampleNumber, isLeading, dependsOn, dependedOn, redundant);
8441
0
}
8442
8443
#if 0 //unused
8444
GF_Err gf_isom_sample_set_dep_info(GF_ISOFile *file, u32 track, u32 sampleNumber, u32 isLeading, u32 dependsOn, u32 dependedOn, u32 redundant)
8445
{
8446
  GF_TrackBox *trak;
8447
8448
  trak = gf_isom_get_track_box(file, track);
8449
  if (!trak) return GF_BAD_PARAM;
8450
8451
  return stbl_AddDependencyType(trak->Media->information->sampleTable, sampleNumber, isLeading, dependsOn, dependedOn, redundant);
8452
}
8453
#endif
8454
8455
GF_EXPORT
8456
GF_Err gf_isom_copy_sample_info(GF_ISOFile *dst, u32 dst_track, GF_ISOFile *src, u32 src_track, u32 sampleNumber)
8457
0
{
8458
0
  u32 i, count, idx, dst_sample_num, subs_flags;
8459
0
  GF_SubSampleInfoEntry *sub_sample;
8460
0
  GF_Err e;
8461
0
  GF_TrackBox *src_trak, *dst_trak;
8462
8463
0
  src_trak = gf_isom_get_track_box(src, src_track);
8464
0
  if (!src_trak) return GF_BAD_PARAM;
8465
8466
0
  dst_trak = gf_isom_get_track_box(dst, dst_track);
8467
0
  if (!dst_trak) return GF_BAD_PARAM;
8468
8469
0
  dst_sample_num = dst_trak->Media->information->sampleTable->SampleSize->sampleCount;
8470
8471
  /*modify depends flags*/
8472
0
  if (src_trak->Media->information->sampleTable->SampleDep) {
8473
0
    u32 isLeading, dependsOn, dependedOn, redundant;
8474
8475
0
    isLeading = dependsOn = dependedOn = redundant = 0;
8476
8477
0
    e = stbl_GetSampleDepType(src_trak->Media->information->sampleTable->SampleDep, sampleNumber, &isLeading, &dependsOn, &dependedOn, &redundant);
8478
0
    if (e) return e;
8479
8480
0
    e = stbl_AppendDependencyType(dst_trak->Media->information->sampleTable, isLeading, dependsOn, dependedOn, redundant);
8481
0
    if (e) return e;
8482
0
  }
8483
8484
  /*copy subsample info if any*/
8485
0
  idx=1;
8486
0
  while (gf_isom_get_subsample_types(src, src_track, idx, &subs_flags)) {
8487
0
    GF_SubSampleInformationBox *dst_subs=NULL;
8488
0
    idx++;
8489
8490
0
    if ( ! gf_isom_sample_get_subsample_entry(src, src_track, sampleNumber, subs_flags, &sub_sample))
8491
0
      continue;
8492
8493
    /*create subsample if needed*/
8494
0
    if (!dst_trak->Media->information->sampleTable->sub_samples) {
8495
0
      dst_trak->Media->information->sampleTable->sub_samples = gf_list_new();
8496
0
    }
8497
0
    count = gf_list_count(dst_trak->Media->information->sampleTable->sub_samples);
8498
0
    for (i=0; i<count; i++) {
8499
0
      dst_subs = gf_list_get(dst_trak->Media->information->sampleTable->sub_samples, i);
8500
0
      if (dst_subs->flags==subs_flags) break;
8501
0
      dst_subs=NULL;
8502
0
    }
8503
0
    if (!dst_subs) {
8504
0
      dst_subs = (GF_SubSampleInformationBox *) gf_isom_box_new_parent(&dst_trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_SUBS);
8505
0
      if (!dst_subs) return GF_OUT_OF_MEM;
8506
0
      dst_subs->version=0;
8507
0
      dst_subs->flags = subs_flags;
8508
0
      gf_list_add(dst_trak->Media->information->sampleTable->sub_samples, dst_subs);
8509
0
    }
8510
8511
0
    count = gf_list_count(sub_sample->SubSamples);
8512
0
    for (i=0; i<count; i++) {
8513
0
      GF_SubSampleEntry *entry = (GF_SubSampleEntry*)gf_list_get(sub_sample->SubSamples, i);
8514
0
      e = gf_isom_add_subsample_info(dst_subs, dst_sample_num, entry->subsample_size, entry->subsample_priority, entry->reserved, entry->discardable);
8515
0
      if (e) return e;
8516
0
    }
8517
0
  }
8518
8519
  /*copy sampleToGroup info if any*/
8520
0
  count = 0;
8521
0
  if (src_trak->Media->information->sampleTable->sampleGroups)
8522
0
    count = gf_list_count(src_trak->Media->information->sampleTable->sampleGroups);
8523
8524
0
  for (i=0; i<count; i++) {
8525
0
    GF_SampleGroupBox *sg;
8526
0
    u32 j, k, default_index;
8527
0
    u32 first_sample_in_entry, last_sample_in_entry, group_desc_index_src, group_desc_index_dst;
8528
0
    first_sample_in_entry = 1;
8529
8530
0
    sg = (GF_SampleGroupBox*)gf_list_get(src_trak->Media->information->sampleTable->sampleGroups, i);
8531
0
    for (j=0; j<sg->entry_count; j++) {
8532
0
      GF_SampleGroupDescriptionBox *sgd_dst = NULL;
8533
0
      last_sample_in_entry = first_sample_in_entry + sg->sample_entries[j].sample_count - 1;
8534
0
      if ((sampleNumber<first_sample_in_entry) || (sampleNumber>last_sample_in_entry)) {
8535
0
        first_sample_in_entry = last_sample_in_entry+1;
8536
0
        continue;
8537
0
      }
8538
8539
0
      if (!dst_trak->Media->information->sampleTable->sampleGroups)
8540
0
        dst_trak->Media->information->sampleTable->sampleGroups = gf_list_new();
8541
8542
0
      group_desc_index_src = group_desc_index_dst = sg->sample_entries[j].group_description_index;
8543
8544
0
      if (group_desc_index_src) {
8545
0
        GF_SampleGroupDescriptionBox *sgd_src;
8546
0
        void *sgde_src, *sgde_dst;
8547
8548
0
        group_desc_index_dst = 0;
8549
        //check that the sample group description exists !!
8550
0
        sgde_src = gf_isom_get_sample_group_info_entry(src, src_trak, sg->grouping_type, sg->sample_entries[j].group_description_index, &default_index, &sgd_src);
8551
8552
0
        if (!sgde_src) break;
8553
8554
0
        if (!dst_trak->Media->information->sampleTable->sampleGroupsDescription)
8555
0
          dst_trak->Media->information->sampleTable->sampleGroupsDescription = gf_list_new();
8556
8557
0
        sgd_dst = NULL;
8558
0
        for (k=0; k< gf_list_count(dst_trak->Media->information->sampleTable->sampleGroupsDescription); k++) {
8559
0
          sgd_dst = gf_list_get(dst_trak->Media->information->sampleTable->sampleGroupsDescription, k);
8560
0
          if (sgd_dst->grouping_type==sgd_src->grouping_type) break;
8561
0
          sgd_dst = NULL;
8562
0
        }
8563
0
        if (!sgd_dst) {
8564
0
          gf_isom_clone_box( (GF_Box *) sgd_src, (GF_Box **) &sgd_dst);
8565
0
          if (!sgd_dst) return GF_OUT_OF_MEM;
8566
0
          gf_list_add(dst_trak->Media->information->sampleTable->sampleGroupsDescription, sgd_dst);
8567
0
          gf_list_add(dst_trak->Media->information->sampleTable->child_boxes, sgd_dst);
8568
0
        }
8569
8570
        //find the same entry
8571
0
        for (k=0; k<gf_list_count(sgd_dst->group_descriptions); k++) {
8572
0
          sgde_dst = gf_list_get(sgd_dst->group_descriptions, k);
8573
0
          if (gf_isom_is_identical_sgpd(sgde_src, sgde_dst, sgd_src->grouping_type)) {
8574
0
            group_desc_index_dst = k+1;
8575
0
            break;
8576
0
          }
8577
0
        }
8578
0
        if (!group_desc_index_dst) {
8579
0
          GF_SampleGroupDescriptionBox *cloned=NULL;
8580
0
          gf_isom_clone_box( (GF_Box *) sgd_src, (GF_Box **)  &cloned);
8581
0
          if (!cloned) return GF_OUT_OF_MEM;
8582
0
          sgde_dst = gf_list_get(cloned->group_descriptions, group_desc_index_dst);
8583
0
          gf_list_rem(cloned->group_descriptions, group_desc_index_dst);
8584
0
          gf_isom_box_del( (GF_Box *) cloned);
8585
0
          gf_list_add(sgd_dst->group_descriptions, sgde_dst);
8586
0
          group_desc_index_dst = gf_list_count(sgd_dst->group_descriptions);
8587
0
        }
8588
0
      } else {
8589
0
        gf_isom_get_sample_group_info_entry(dst, dst_trak, sg->grouping_type, 1, NULL, &sgd_dst);
8590
0
        if (!sgd_dst) continue;
8591
0
      }
8592
8593
      /*found our sample, add it to trak->sampleGroups*/
8594
0
      e = gf_isom_add_sample_group_entry(dst_trak->Media->information->sampleTable->sampleGroups, dst_sample_num, sgd_dst, sg->grouping_type_parameter, group_desc_index_dst, dst_trak->Media->information->sampleTable->child_boxes, NULL);
8595
0
      if (e) return e;
8596
0
      break;
8597
0
    }
8598
0
  }
8599
8600
  //copy auxiliary info
8601
0
  count = gf_list_count(src_trak->Media->information->sampleTable->sai_sizes);
8602
0
  for (i=0; i<count; i++) {
8603
0
    u32 j;
8604
0
    GF_SampleAuxiliaryInfoOffsetBox *saio = NULL;
8605
0
    GF_SampleAuxiliaryInfoSizeBox *saiz = gf_list_get(src_trak->Media->information->sampleTable->sai_sizes, i);
8606
8607
0
    switch (saiz->aux_info_type) {
8608
0
    case GF_ISOM_CENC_SCHEME:
8609
0
    case GF_ISOM_CBC_SCHEME:
8610
0
    case GF_ISOM_CENS_SCHEME:
8611
0
    case GF_ISOM_CBCS_SCHEME:
8612
0
    case GF_ISOM_PIFF_SCHEME:
8613
0
    case 0:
8614
0
      continue;
8615
0
    default:
8616
0
      break;
8617
0
    }
8618
    //no aux sample associated
8619
0
    if (saiz->sample_count<sampleNumber) continue;
8620
    //no size associated
8621
0
    if (!saiz->default_sample_info_size && !saiz->sample_info_size[sampleNumber-1]) continue;
8622
8623
0
    for (j=0; j<gf_list_count(src_trak->Media->information->sampleTable->sai_offsets); j++) {
8624
0
      saio = gf_list_get(src_trak->Media->information->sampleTable->sai_offsets, j);
8625
0
      if ((saio->aux_info_type==saiz->aux_info_type) && (saio->aux_info_type_parameter==saiz->aux_info_type_parameter)) break;
8626
0
      saio=NULL;
8627
0
    }
8628
0
    if (!saio) continue;
8629
0
    if (!saio->offsets && !saio->sai_data) continue;
8630
8631
0
    u64 offset = saio->offsets ? saio->offsets[0] : 0;
8632
0
    u32 nb_saio = saio->entry_count;
8633
0
    if ((nb_saio>1) && (saio->entry_count != saiz->sample_count)) continue;
8634
8635
0
    u32 size;
8636
8637
0
    if (nb_saio == 1) {
8638
0
      for (j=0; j < sampleNumber-1; j++) {
8639
0
        size = saiz->default_sample_info_size ? saiz->default_sample_info_size : saiz->sample_info_size[j];
8640
0
        offset += size;
8641
0
      }
8642
0
    } else {
8643
0
      offset = saio->offsets[sampleNumber-1];
8644
0
    }
8645
8646
0
    size = saiz->default_sample_info_size ? saiz->default_sample_info_size : saiz->sample_info_size[j];
8647
8648
0
    if (saio->sai_data) {
8649
0
      e = gf_isom_add_sample_aux_info_internal(dst_trak, NULL, j+1, saiz->aux_info_type, saiz->aux_info_type_parameter, saio->sai_data->data + offset, size);
8650
0
    } else {
8651
0
      u8 *sai = gf_malloc(size);
8652
0
      if (!sai) return GF_OUT_OF_MEM;
8653
8654
0
      u64 cur_position = gf_bs_get_position(src_trak->moov->mov->movieFileMap->bs);
8655
0
      gf_bs_seek(src_trak->moov->mov->movieFileMap->bs, offset);
8656
8657
0
      gf_bs_read_data(src_trak->moov->mov->movieFileMap->bs, sai, size);
8658
0
      gf_bs_seek(src_trak->moov->mov->movieFileMap->bs, cur_position);
8659
0
      e = gf_isom_add_sample_aux_info_internal(dst_trak, NULL, j+1, saiz->aux_info_type, saiz->aux_info_type_parameter, sai, size);
8660
0
      gf_free(sai);
8661
0
    }
8662
8663
0
    if (e) {
8664
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] Failed to clone sai data: %s\n", gf_error_to_string(e) ));
8665
0
    }
8666
0
  }
8667
0
  return GF_OK;
8668
0
}
8669
8670
GF_EXPORT
8671
GF_Err gf_isom_text_set_display_flags(GF_ISOFile *file, u32 track, u32 desc_index, u32 flags, GF_TextFlagsMode op_type)
8672
0
{
8673
0
  u32 i;
8674
0
  GF_Err e;
8675
0
  GF_TrackBox *trak;
8676
8677
0
  e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
8678
0
  if (e) return e;
8679
8680
0
  trak = gf_isom_get_track_box(file, track);
8681
0
  if (!trak) return GF_BAD_PARAM;
8682
8683
0
  for (i=0; i < gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes); i++) {
8684
0
    GF_Tx3gSampleEntryBox *txt;
8685
0
    if (desc_index && (i+1 != desc_index)) continue;
8686
8687
0
    txt = (GF_Tx3gSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, i);
8688
0
    if (txt->type != GF_ISOM_BOX_TYPE_TX3G) continue;
8689
8690
0
    switch (op_type) {
8691
0
    case GF_ISOM_TEXT_FLAGS_TOGGLE:
8692
0
      txt->displayFlags |= flags;
8693
0
      break;
8694
0
    case GF_ISOM_TEXT_FLAGS_UNTOGGLE:
8695
0
      txt->displayFlags &= ~flags;
8696
0
      break;
8697
0
    default:
8698
0
      txt->displayFlags = flags;
8699
0
      break;
8700
0
    }
8701
0
  }
8702
0
  return GF_OK;
8703
8704
0
}
8705
8706
8707
GF_EXPORT
8708
GF_Err gf_isom_update_duration(GF_ISOFile *movie)
8709
147
{
8710
147
  u32 i;
8711
147
  u64 maxDur;
8712
147
  GF_TrackBox *trak;
8713
8714
147
  if (!movie || !movie->moov) return GF_BAD_PARAM;
8715
8716
  //if file was open in Write or Edit mode, recompute the duration
8717
  //the duration of a movie is the MaxDuration of all the tracks...
8718
8719
147
  maxDur = 0;
8720
147
  i=0;
8721
597
  while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) {
8722
458
    if( (movie->LastError = SetTrackDuration(trak))  ) return movie->LastError;
8723
450
    if (trak->Header && (trak->Header->duration > maxDur))
8724
153
      maxDur = trak->Header->duration;
8725
450
  }
8726
139
  movie->moov->mvhd->duration = maxDur;
8727
8728
139
  return GF_OK;
8729
147
}
8730
8731
GF_EXPORT
8732
GF_Err gf_isom_update_edit_list_duration(GF_ISOFile *file, u32 track)
8733
0
{
8734
0
  u32 i;
8735
0
  u64 trackDuration;
8736
0
  GF_EdtsEntry *ent;
8737
0
  GF_Err e;
8738
0
  GF_TrackBox *trak;
8739
8740
0
  e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
8741
0
  if (e) return e;
8742
8743
0
  trak = gf_isom_get_track_box(file, track);
8744
0
  if (!trak) return GF_BAD_PARAM;
8745
8746
8747
  //the total duration is the media duration: adjust it in case...
8748
0
  e = Media_SetDuration(trak);
8749
0
  if (e) return e;
8750
8751
  //assert the timeScales are non-NULL
8752
0
  if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) return GF_ISOM_INVALID_FILE;
8753
0
  trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
8754
8755
  //if we have an edit list, the duration is the sum of all the editList
8756
  //entries' duration (always expressed in MovieTimeScale)
8757
0
  if (trak->editBox && trak->editBox->editList) {
8758
0
    u64 editListDuration = 0;
8759
0
    GF_EditListBox *elst = trak->editBox->editList;
8760
0
    i=0;
8761
0
    while ((ent = (GF_EdtsEntry*)gf_list_enum(elst->entryList, &i))) {
8762
0
      if ((ent->mediaTime>=0) && (ent->mediaRate==0x10000) && (ent->segmentDuration > trackDuration))
8763
0
        ent->segmentDuration = trackDuration;
8764
8765
0
      if (!ent->segmentDuration) {
8766
0
        u64 diff;
8767
0
        ent->segmentDuration = trackDuration;
8768
0
        if (ent->mediaTime>0) {
8769
0
          diff = ent->mediaTime;
8770
0
          diff *= trak->moov->mvhd->timeScale;
8771
0
          diff /= trak->Media->mediaHeader->timeScale;
8772
0
          if (diff < ent->segmentDuration)
8773
0
            ent->segmentDuration -= diff;
8774
          /*
8775
          else
8776
            diff = 0;
8777
          */
8778
0
        }
8779
0
      }
8780
0
      if ((ent->mediaTime>=0) && ((u64) ent->mediaTime>=trak->Media->mediaHeader->duration)) {
8781
0
        ent->mediaTime = trak->Media->mediaHeader->duration;
8782
0
      }
8783
0
      editListDuration += ent->segmentDuration;
8784
0
    }
8785
0
    trackDuration = editListDuration;
8786
0
  }
8787
0
  if (!trackDuration) {
8788
0
    trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
8789
0
  }
8790
0
  trak->Header->duration = trackDuration;
8791
8792
0
  return GF_OK;
8793
8794
0
}
8795
8796
8797
GF_EXPORT
8798
0
GF_Err gf_isom_clone_pssh(GF_ISOFile *output, GF_ISOFile *input, Bool in_moof) {
8799
0
  GF_Box *a;
8800
0
  u32 i;
8801
0
  i = 0;
8802
8803
0
  while ((a = (GF_Box *)gf_list_enum(input->moov->child_boxes, &i))) {
8804
0
    if (a->type == GF_ISOM_BOX_TYPE_PSSH) {
8805
0
      GF_List **child_boxes = &output->moov->child_boxes;
8806
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
8807
0
      if (in_moof)
8808
0
        child_boxes = &output->moof->child_boxes;
8809
0
#endif
8810
8811
0
      GF_ProtectionSystemHeaderBox *pssh = (GF_ProtectionSystemHeaderBox *)gf_isom_box_new_parent(child_boxes, GF_ISOM_BOX_TYPE_PSSH);
8812
0
      if (!pssh) return GF_OUT_OF_MEM;
8813
0
      memmove(pssh->SystemID, ((GF_ProtectionSystemHeaderBox *)a)->SystemID, 16);
8814
0
      if (((GF_ProtectionSystemHeaderBox *)a)->KIDs && ((GF_ProtectionSystemHeaderBox *)a)->KID_count > 0) {
8815
0
        pssh->KID_count = ((GF_ProtectionSystemHeaderBox *)a)->KID_count;
8816
0
        pssh->KIDs = (bin128 *)gf_malloc(pssh->KID_count*sizeof(bin128));
8817
0
        if (!pssh->KIDs) return GF_OUT_OF_MEM;
8818
0
        memmove(pssh->KIDs, ((GF_ProtectionSystemHeaderBox *)a)->KIDs, pssh->KID_count*sizeof(bin128));
8819
0
      }
8820
0
      pssh->private_data_size = ((GF_ProtectionSystemHeaderBox *)a)->private_data_size;
8821
0
      if (!pssh->private_data_size) {
8822
0
        pssh->private_data = NULL;
8823
0
      } else {
8824
0
        pssh->private_data = (u8 *)gf_malloc(pssh->private_data_size*sizeof(char));
8825
0
        if (!pssh->private_data) return GF_OUT_OF_MEM;
8826
0
        memmove(pssh->private_data, ((GF_ProtectionSystemHeaderBox *)a)->private_data, pssh->private_data_size);
8827
0
      }
8828
0
    }
8829
0
  }
8830
0
  return GF_OK;
8831
0
}
8832
8833
GF_EXPORT
8834
GF_Err gf_isom_set_track_group(GF_ISOFile *file, u32 track_number, u32 track_group_id, u32 group_type, Bool do_add)
8835
0
{
8836
0
  u32 i, j;
8837
0
  GF_TrackGroupTypeBox *trgt;
8838
0
  GF_Err e;
8839
0
  GF_TrackBox *trak;
8840
8841
0
  e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
8842
0
  if (e) return e;
8843
8844
0
  trak = gf_isom_get_track_box(file, track_number);
8845
0
  if (!trak) return GF_BAD_PARAM;
8846
0
  if (!trak->groups) trak->groups = (GF_TrackGroupBox*) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TRGR);
8847
0
  if (!trak->groups) return GF_OUT_OF_MEM;
8848
8849
0
  for (j=0; j<gf_list_count(file->moov->trackList); j++) {
8850
0
    GF_TrackBox *a_trak = gf_list_get(file->moov->trackList, j);
8851
0
    if (!a_trak->groups) continue;
8852
8853
0
    for (i=0; i<gf_list_count(a_trak->groups->groups); i++) {
8854
0
      trgt = gf_list_get(a_trak->groups->groups, i);
8855
8856
0
      if (trgt->track_group_id==track_group_id) {
8857
0
        if (trgt->group_type != group_type) {
8858
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("A track with same group ID is already defined for different group type %s\n", gf_4cc_to_str(trgt->group_type) ));
8859
0
          return GF_BAD_PARAM;
8860
0
        }
8861
0
        if (a_trak==trak) {
8862
0
          if (!do_add) {
8863
0
            gf_list_rem(trak->groups->groups, i);
8864
0
            gf_isom_box_del_parent(&trak->groups->child_boxes, (GF_Box *)trgt);
8865
0
          }
8866
0
          return GF_OK;
8867
0
        }
8868
0
      }
8869
0
    }
8870
0
  }
8871
  //not found, add new group
8872
0
  trgt = (GF_TrackGroupTypeBox*) gf_isom_box_new_parent(&trak->groups->child_boxes, GF_ISOM_BOX_TYPE_TRGT);
8873
0
  if (!trgt) return GF_OUT_OF_MEM;
8874
0
  trgt->track_group_id = track_group_id;
8875
0
  trgt->group_type = group_type;
8876
0
  return gf_list_add(trak->groups->groups, trgt);
8877
0
}
8878
8879
GF_EXPORT
8880
GF_Err gf_isom_set_nalu_length_field(GF_ISOFile *file, u32 track, u32 StreamDescriptionIndex, u32 nalu_size_length)
8881
0
{
8882
0
  GF_Err e;
8883
0
  GF_TrackBox *trak;
8884
0
  GF_SampleEntryBox *entry;
8885
0
  GF_MPEGVisualSampleEntryBox *ve;
8886
0
  GF_SampleDescriptionBox *stsd;
8887
8888
0
  e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
8889
0
  if (e) return e;
8890
8891
0
  trak = gf_isom_get_track_box(file, track);
8892
0
  if (!trak) return GF_BAD_PARAM;
8893
8894
0
  stsd = trak->Media->information->sampleTable->SampleDescription;
8895
0
  if (!stsd || !StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
8896
0
    return GF_BAD_PARAM;
8897
0
  }
8898
8899
0
  entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
8900
  //no support for generic sample entries (eg, no MPEG4 descriptor)
8901
0
  if (!entry || ! gf_isom_is_nalu_based_entry(trak->Media, entry)) {
8902
0
    return GF_BAD_PARAM;
8903
0
  }
8904
8905
0
  ve = (GF_MPEGVisualSampleEntryBox*)entry;
8906
0
  if (ve->avc_config) ve->avc_config->config->nal_unit_size = nalu_size_length;
8907
0
  if (ve->svc_config) ve->svc_config->config->nal_unit_size = nalu_size_length;
8908
0
  if (ve->hevc_config) ve->hevc_config->config->nal_unit_size = nalu_size_length;
8909
0
  if (ve->lhvc_config) ve->lhvc_config->config->nal_unit_size = nalu_size_length;
8910
0
  if (ve->vvc_config) ve->vvc_config->config->nal_unit_size = nalu_size_length;
8911
0
  return GF_OK;
8912
0
}
8913
8914
GF_EXPORT
8915
GF_Err gf_isom_set_sample_group_in_traf(GF_ISOFile *file)
8916
0
{
8917
0
  GF_Err e;
8918
0
  e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
8919
0
  if (e) return e;
8920
8921
0
  file->sample_groups_in_traf = GF_TRUE;
8922
0
  return GF_OK;
8923
0
}
8924
8925
GF_EXPORT
8926
void gf_isom_set_progress_callback(GF_ISOFile *file, void (*progress_cbk)(void *udta, u64 nb_done, u64 nb_total), void *progress_cbk_udta)
8927
0
{
8928
0
  if (file) {
8929
0
    file->progress_cbk = progress_cbk;
8930
0
    file->progress_cbk_udta = progress_cbk_udta;
8931
8932
0
  }
8933
0
}
8934
8935
GF_Err gf_isom_update_video_sample_entry_fields(GF_ISOFile *file, u32 track, u32 stsd_idx, u16 revision, u32 vendor, u32 temporalQ, u32 spatialQ, u32 horiz_res, u32 vert_res, u16 frames_per_sample, const char *compressor_name, s16 color_table_index)
8936
0
{
8937
8938
0
  GF_TrackBox *trak;
8939
0
  GF_MPEGVisualSampleEntryBox *vid_ent;
8940
8941
  /*get orig sample desc and clone it*/
8942
0
  trak = gf_isom_get_track_box(file, track);
8943
0
  if (!trak || !stsd_idx) return GF_BAD_PARAM;
8944
8945
0
  if (!trak->Media || !trak->Media->handler || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription)
8946
0
    return GF_ISOM_INVALID_FILE;
8947
8948
0
  switch (trak->Media->handler->handlerType) {
8949
0
  case GF_ISOM_MEDIA_VISUAL:
8950
0
    case GF_ISOM_MEDIA_AUXV:
8951
0
    case GF_ISOM_MEDIA_PICT:
8952
0
      break;
8953
0
  default:
8954
0
    return GF_BAD_PARAM;
8955
0
  }
8956
0
  vid_ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, stsd_idx-1);
8957
0
  if (!vid_ent)
8958
0
    return GF_BAD_PARAM;
8959
0
  if (vid_ent->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO)
8960
0
    return GF_ISOM_INVALID_FILE;
8961
8962
0
  vid_ent->revision = revision;
8963
0
  vid_ent->vendor = vendor;
8964
0
  vid_ent->temporal_quality = temporalQ;
8965
0
  vid_ent->spatial_quality = spatialQ;
8966
0
  vid_ent->horiz_res = horiz_res;
8967
0
  vid_ent->vert_res = vert_res;
8968
0
  vid_ent->frames_per_sample = frames_per_sample;
8969
0
  if (compressor_name) {
8970
0
    memset(vid_ent->compressor_name, 0, 33);
8971
0
    gf_strcpy(vid_ent->compressor_name, compressor_name);
8972
0
  }
8973
8974
0
  vid_ent->color_table_index = color_table_index;
8975
0
  return GF_OK;
8976
0
}
8977
8978
GF_EXPORT
8979
GF_Err gf_isom_update_sample_description_from_template(GF_ISOFile *file, u32 track, u32 sampleDescriptionIndex, u8 *data, u32 size)
8980
0
{
8981
0
  GF_BitStream *bs;
8982
0
  GF_TrackBox *trak;
8983
0
  GF_Box *ent, *tpl_ent;
8984
0
  GF_Err e;
8985
  /*get orig sample desc and clone it*/
8986
0
  trak = gf_isom_get_track_box(file, track);
8987
0
  if (!trak || !sampleDescriptionIndex) return GF_BAD_PARAM;
8988
8989
0
  if (!trak->Media || !trak->Media->handler || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription)
8990
0
    return GF_ISOM_INVALID_FILE;
8991
8992
0
  ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex-1);
8993
0
  if (!ent) return GF_BAD_PARAM;
8994
8995
0
  bs = gf_bs_new(data, size, GF_BITSTREAM_READ);
8996
//  e = gf_isom_box_parse(&tpl_ent, bs);
8997
0
  e = gf_isom_box_parse_ex(&tpl_ent, bs, GF_ISOM_BOX_TYPE_STSD, GF_FALSE, 0);
8998
0
  gf_bs_del(bs);
8999
0
  if (e) return e;
9000
9001
0
  while (gf_list_count(tpl_ent->child_boxes)) {
9002
0
    u32 j=0;
9003
0
    Bool found = GF_FALSE;
9004
0
    GF_Box *abox = gf_list_pop_front(tpl_ent->child_boxes);
9005
9006
0
    switch (abox->type) {
9007
0
    case GF_ISOM_BOX_TYPE_SINF:
9008
0
    case GF_ISOM_BOX_TYPE_RINF:
9009
0
    case GF_ISOM_BOX_TYPE_BTRT:
9010
0
      found = GF_TRUE;
9011
0
      break;
9012
0
    }
9013
9014
0
    if (found) {
9015
0
      gf_isom_box_del(abox);
9016
0
      continue;
9017
0
    }
9018
9019
0
    if (!ent->child_boxes) ent->child_boxes = gf_list_new();
9020
0
    for (j=0; j<gf_list_count(ent->child_boxes); j++) {
9021
0
      GF_Box *b = gf_list_get(ent->child_boxes, j);
9022
0
      if (b->type == abox->type) {
9023
0
        found = GF_TRUE;
9024
0
        break;
9025
0
      }
9026
0
    }
9027
0
    if (!found) {
9028
0
      gf_list_add(ent->child_boxes, abox);
9029
0
    } else {
9030
0
      gf_isom_box_del(abox);
9031
0
    }
9032
0
  }
9033
0
  gf_isom_box_del(tpl_ent);
9034
9035
  //patch for old export
9036
0
  GF_Box *abox = gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_SINF);
9037
0
  if (abox) {
9038
0
    gf_list_del_item(ent->child_boxes, abox);
9039
0
    gf_list_add(ent->child_boxes, abox);
9040
0
  }
9041
0
  return GF_OK;
9042
0
}
9043
9044
9045
#include <gpac/xml.h>
9046
GF_EXPORT
9047
GF_Err gf_isom_apply_box_patch(GF_ISOFile *file, GF_ISOTrackID globalTrackID, const char *box_patch_filename, Bool for_fragments)
9048
0
{
9049
0
  GF_Err e;
9050
0
  GF_DOMParser *dom;
9051
0
  u32 i;
9052
0
  GF_XMLNode *root;
9053
0
  u8 *box_data=NULL;
9054
0
  u32 box_data_size;
9055
0
  if (!file || !box_patch_filename) return GF_BAD_PARAM;
9056
#ifdef GPAC_DISABLE_ISOM_FRAGMENTS
9057
  if (for_fragments) return GF_NOT_SUPPORTED;
9058
#endif
9059
0
  dom = gf_xml_dom_new();
9060
0
  if (strstr(box_patch_filename, "<?xml")) {
9061
0
    e = gf_xml_dom_parse_string(dom, (char *) box_patch_filename);
9062
0
  } else {
9063
0
    e = gf_xml_dom_parse(dom, box_patch_filename, NULL, NULL);
9064
0
  }
9065
0
  if (e) goto err_exit;
9066
9067
0
  root = gf_xml_dom_get_root(dom);
9068
0
  if (!root || strcmp(root->name, "GPACBOXES")) {
9069
0
    e = GF_NON_COMPLIANT_BITSTREAM;
9070
0
    goto err_exit;
9071
0
  }
9072
9073
  //compute size of each child boxes to freeze the order
9074
0
  if (for_fragments) {
9075
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
9076
0
    u32 count = file->moof ? gf_list_count(file->moof->child_boxes) : 0;
9077
0
    for (i=0; i<count; i++) {
9078
0
      GF_Box *box = gf_list_get(file->moof->child_boxes, i);
9079
0
      if (!(box->internal_flags & GF_ISOM_ORDER_FREEZE)) {
9080
0
        gf_isom_box_size(box);
9081
0
        gf_isom_box_freeze_order(box);
9082
0
      }
9083
0
    }
9084
0
#endif
9085
0
  } else {
9086
0
    for (i=0; i<gf_list_count(file->TopBoxes); i++) {
9087
0
      GF_Box *box = gf_list_get(file->TopBoxes, i);
9088
0
      if (!(box->internal_flags & GF_ISOM_ORDER_FREEZE)) {
9089
0
        gf_isom_box_size(box);
9090
0
        gf_isom_box_freeze_order(box);
9091
0
      }
9092
0
    }
9093
0
  }
9094
9095
0
  for (i=0; i<gf_list_count(root->content); i++) {
9096
0
    u32 j;
9097
0
    u32 path_len;
9098
0
    Bool essential_prop=GF_FALSE;
9099
0
    u32 trackID=globalTrackID;
9100
0
    u32 item_id=trackID;
9101
0
    Bool is_frag_box;
9102
0
    char *box_path=NULL;
9103
0
    GF_Box *parent_box = NULL;
9104
0
    GF_XMLNode *box_edit = gf_list_get(root->content, i);
9105
0
    if (!box_edit->name || strcmp(box_edit->name, "Box")) continue;
9106
9107
0
    for (j=0; j<gf_list_count(box_edit->attributes);j++) {
9108
0
      GF_XMLAttribute *att = gf_list_get(box_edit->attributes, j);
9109
0
      if (!strcmp(att->name, "path")) box_path = att->value;
9110
0
      else if (!strcmp(att->name, "essential")) {
9111
0
        if (!strcmp(att->value, "yes") || !strcmp(att->value, "true") || !strcmp(att->value, "1")) {
9112
0
          essential_prop=GF_TRUE;
9113
0
        }
9114
0
      }
9115
0
      else if (!strcmp(att->name, "itemID"))
9116
0
        item_id = atoi(att->value);
9117
0
      else if (!globalTrackID && !strcmp(att->name, "trackID"))
9118
0
        trackID = atoi(att->value);
9119
0
    }
9120
9121
0
    if (!box_path) continue;
9122
0
    path_len = (u32) strlen(box_path);
9123
9124
0
    is_frag_box = !strncmp(box_path, "traf", 4) ? GF_TRUE : GF_FALSE;
9125
9126
0
    if (for_fragments && !is_frag_box) continue;
9127
0
    else if (!for_fragments && is_frag_box) continue;
9128
9129
0
    gf_xml_parse_bit_sequence(box_edit, box_patch_filename, &box_data, &box_data_size);
9130
0
    if (box_data_size && (box_data_size<4) ) {
9131
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Wrong BS specification for box, shall either be empty or at least 4 bytes for box type\n"));
9132
0
      e = GF_NON_COMPLIANT_BITSTREAM;
9133
0
      goto err_exit;
9134
0
    }
9135
9136
0
    while (box_path && (path_len>=4)) {
9137
0
      u32 parent_list_box_type;
9138
0
      GF_List **parent_list;
9139
0
      u32 box_type = GF_4CC(box_path[0],box_path[1],box_path[2],box_path[3]);
9140
0
      GF_Box *box=NULL;
9141
0
      GF_BitStream *bs;
9142
0
      s32 insert_pos = -1;
9143
0
      box_path+=4;
9144
0
      path_len-=4;
9145
9146
0
      if (!parent_box) {
9147
0
        box=gf_isom_box_find_child(file->TopBoxes, box_type);
9148
0
        if (!box) {
9149
0
          if (box_type==GF_ISOM_BOX_TYPE_TRAK) {
9150
0
            if (trackID) {
9151
0
              box = (GF_Box *) gf_isom_get_track_box(file, gf_isom_get_track_by_id(file, trackID) );
9152
0
            }
9153
0
            if (!box && gf_list_count(file->moov->trackList)==1) {
9154
0
              box = gf_list_get(file->moov->trackList, 0);
9155
0
            }
9156
0
          }
9157
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
9158
0
          else if (box_type==GF_ISOM_BOX_TYPE_TRAF) {
9159
0
            if (trackID) {
9160
0
              box = (GF_Box *) gf_isom_get_traf(file, trackID);
9161
0
            }
9162
0
            if (!box && file->moof && gf_list_count(file->moof->TrackList)==1) {
9163
0
              box = gf_list_get(file->moof->TrackList, 0);
9164
0
            }
9165
0
          }
9166
0
#endif
9167
0
        }
9168
0
        if (!box) {
9169
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Cannot locate box type %s at root or as track\n", gf_4cc_to_str(box_type) ));
9170
0
          e = GF_BAD_PARAM;
9171
0
          goto err_exit;
9172
0
        }
9173
0
      } else {
9174
0
        box = gf_isom_box_find_child(parent_box->child_boxes, box_type);
9175
0
        if (!box) {
9176
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Cannot locate box type %s at child of %s\n", gf_4cc_to_str(box_type), gf_4cc_to_str(parent_box->type)));
9177
0
          e = GF_BAD_PARAM;
9178
0
          goto err_exit;
9179
0
        }
9180
0
      }
9181
      // '.' is child access
9182
0
      if (path_len && (box_path[0]=='.')) {
9183
0
        box_path += 1;
9184
0
        path_len-=1;
9185
0
        parent_box = box;
9186
0
        continue;
9187
0
      }
9188
9189
0
      if (parent_box && !parent_box->child_boxes) parent_box->child_boxes = gf_list_new();
9190
0
      parent_list = parent_box ? &parent_box->child_boxes : &file->TopBoxes;
9191
0
      parent_list_box_type = parent_box ? parent_box->type : 0;
9192
9193
      // '+' is append after, '-' is insert before
9194
0
      if (path_len && ((box_path[0]=='-') || (box_path[0]=='+')) ) {
9195
0
        s32 idx = gf_list_find(*parent_list, box);
9196
0
        if (idx<0) {
9197
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Invalid index for path %s\n", box_path));
9198
0
          e = GF_NON_COMPLIANT_BITSTREAM;
9199
0
          goto err_exit;
9200
0
        }
9201
0
        if (box_path[0]=='+') insert_pos = idx+1;
9202
0
        else insert_pos = idx;
9203
0
      }
9204
0
      else if (path_len) {
9205
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Invalid path %s, expecting either '-', '+' or '.' as separators\n", box_path));
9206
0
        e = GF_NON_COMPLIANT_BITSTREAM;
9207
0
        goto err_exit;
9208
0
      }
9209
9210
0
      if (!box_data) {
9211
0
        if (insert_pos>=0) {
9212
0
          GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[ISOBMFF] Invalid path %s for box removal, ignoring position\n", box_path));
9213
0
        }
9214
0
        switch (box->type) {
9215
0
        case GF_ISOM_BOX_TYPE_MOOV:
9216
0
          file->moov = NULL;
9217
0
          break;
9218
0
        case GF_ISOM_BOX_TYPE_MDAT:
9219
0
          file->mdat = NULL;
9220
0
          break;
9221
0
        case GF_ISOM_BOX_TYPE_PDIN:
9222
0
          file->pdin = NULL;
9223
0
          break;
9224
0
        case GF_ISOM_BOX_TYPE_FTYP:
9225
0
          file->brand = NULL;
9226
0
          break;
9227
0
        case GF_ISOM_BOX_TYPE_META:
9228
0
          if ((GF_Box *) file->meta == box) file->meta = NULL;
9229
0
          break;
9230
0
        }
9231
0
        if (parent_box) {
9232
0
          gf_isom_box_remove_from_parent(parent_box, box);
9233
0
        }
9234
0
        gf_isom_box_del_parent(parent_list, box);
9235
0
      } else {
9236
0
        u32 size;
9237
9238
        //target box may not have any child yet
9239
0
        if ((insert_pos<0) && !box->child_boxes) box->child_boxes = gf_list_new();
9240
9241
0
        bs = gf_bs_new(box_data, box_data_size, GF_BITSTREAM_READ);
9242
0
        size = gf_bs_read_u32(bs);
9243
0
        if (size != box_data_size) {
9244
0
          GF_UnknownBox *new_box = (GF_UnknownBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UNKNOWN);
9245
0
          new_box->original_4cc = size;
9246
0
          new_box->dataSize = (u32) gf_bs_available(bs);
9247
0
          new_box->data = gf_malloc(sizeof(u8)*new_box->dataSize);
9248
0
          gf_bs_read_data(bs, new_box->data, new_box->dataSize);
9249
0
          if (insert_pos<0) {
9250
0
            gf_list_add(box->child_boxes, new_box);
9251
0
            insert_pos = gf_list_find(box->child_boxes, new_box);
9252
0
          } else {
9253
0
            gf_list_insert(*parent_list, new_box, insert_pos);
9254
0
          }
9255
9256
0
          if (parent_box && (parent_box->type==GF_ISOM_BOX_TYPE_IPRP)) {
9257
0
            GF_ItemPropertyAssociationBox *ipma = (GF_ItemPropertyAssociationBox *) gf_isom_box_find_child(parent_box->child_boxes, GF_ISOM_BOX_TYPE_IPMA);
9258
0
            if (!item_id) {
9259
0
              GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[ISOBMFF] Inserting box in ipco without itemID, no association added\n"));
9260
0
            } else if (ipma) {
9261
0
              u32 nb_asso, k, insert_pos;
9262
0
              GF_ItemPropertyAssociationEntry *entry = NULL;
9263
0
              nb_asso = gf_list_count(ipma->entries);
9264
0
              insert_pos = 0;
9265
0
              for (k=0; k<nb_asso;k++) {
9266
0
                entry = gf_list_get(ipma->entries, k);
9267
0
                if (entry->item_id==item_id) break;
9268
                // item ids must appear in increasing order
9269
0
                if (item_id>entry->item_id) ++insert_pos;
9270
0
                entry = NULL;
9271
0
              }
9272
0
              if (!entry) {
9273
0
                GF_SAFEALLOC(entry, GF_ItemPropertyAssociationEntry);
9274
0
                if (!entry) return GF_OUT_OF_MEM;
9275
0
                gf_list_insert(ipma->entries, entry, insert_pos);
9276
0
                entry->item_id = item_id;
9277
0
              }
9278
0
              entry->associations = gf_realloc(entry->associations, sizeof(GF_ItemPropertyAssociationSlot) * (entry->nb_associations+1));
9279
0
              entry->associations[entry->nb_associations].essential = essential_prop;
9280
0
              entry->associations[entry->nb_associations].index = 1+insert_pos;
9281
0
              entry->nb_associations++;
9282
0
            }
9283
0
          }
9284
0
        } else {
9285
0
          u32 box_idx = 0;
9286
9287
0
          gf_bs_seek(bs, 0);
9288
0
          while (gf_bs_available(bs)) {
9289
0
            GF_Box *new_box;
9290
0
            e = gf_isom_box_parse_ex(&new_box, bs, (insert_pos<0) ? box->type : parent_list_box_type, parent_box ? GF_FALSE : GF_TRUE, 0);
9291
0
            if (e) {
9292
0
              GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] failed to parse box\n", box_path));
9293
0
              gf_bs_del(bs);
9294
0
              goto err_exit;
9295
0
            }
9296
0
            if (insert_pos<0) {
9297
0
              gf_list_add(box->child_boxes, new_box);
9298
0
            } else {
9299
0
              gf_list_insert(*parent_list, new_box, insert_pos+box_idx);
9300
0
              box_idx++;
9301
0
            }
9302
0
          }
9303
0
        }
9304
0
        gf_bs_del(bs);
9305
9306
0
      }
9307
0
      gf_free(box_data);
9308
0
      box_data = NULL;
9309
0
      box_path = NULL;
9310
0
    }
9311
0
  }
9312
9313
0
err_exit:
9314
9315
0
  gf_xml_dom_del(dom);
9316
0
  if (box_data) gf_free(box_data);
9317
0
  return e;
9318
0
}
9319
9320
GF_EXPORT
9321
GF_Err gf_isom_set_track_magic(GF_ISOFile *movie, u32 trackNumber, u64 magic)
9322
0
{
9323
0
  GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber);
9324
0
  if (!trak) return GF_BAD_PARAM;
9325
0
  trak->magic = magic;
9326
0
  return GF_OK;
9327
0
}
9328
9329
GF_EXPORT
9330
GF_Err gf_isom_set_track_index(GF_ISOFile *movie, u32 trackNumber, u32 index)
9331
0
{
9332
0
  GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber);
9333
0
  if (!trak) return GF_BAD_PARAM;
9334
0
  trak->mux_index = index;
9335
0
  if (index)
9336
0
    movie->tracks_use_mux_index ++;
9337
0
  else if (movie->tracks_use_mux_index)
9338
0
    movie->tracks_use_mux_index --;
9339
0
  return GF_OK;
9340
0
}
9341
9342
GF_EXPORT
9343
GF_Err gf_isom_set_ipod_compatible(GF_ISOFile *the_file, u32 trackNumber)
9344
0
{
9345
0
  GF_TrackBox *trak;
9346
0
  GF_Err e;
9347
0
  GF_MPEGVisualSampleEntryBox *entry;
9348
9349
0
  e = gf_isom_can_access_movie(the_file, GF_ISOM_OPEN_WRITE);
9350
0
  if (e) return e;
9351
0
  trak = gf_isom_get_track_box(the_file, trackNumber);
9352
0
  if (!trak || !trak->Media) return GF_BAD_PARAM;
9353
0
  entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, 0);
9354
0
  if (!entry) return GF_OK;
9355
0
  switch (entry->type) {
9356
0
  case GF_ISOM_BOX_TYPE_AVC1:
9357
0
  case GF_ISOM_BOX_TYPE_AVC2:
9358
0
  case GF_ISOM_BOX_TYPE_AVC3:
9359
0
  case GF_ISOM_BOX_TYPE_AVC4:
9360
0
  case GF_ISOM_BOX_TYPE_SVC1:
9361
0
  case GF_ISOM_BOX_TYPE_MVC1:
9362
0
  case GF_ISOM_BOX_TYPE_HVC1:
9363
0
  case GF_ISOM_BOX_TYPE_HEV1:
9364
0
  case GF_ISOM_BOX_TYPE_HVT1:
9365
0
    break;
9366
0
  default:
9367
0
    return GF_OK;
9368
0
  }
9369
9370
0
  if (!entry->ipod_ext) {
9371
0
    entry->ipod_ext = (GF_UnknownUUIDBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_UUID);
9372
0
    if (!entry->ipod_ext) return GF_OUT_OF_MEM;
9373
0
  }
9374
0
  memcpy(entry->ipod_ext->uuid, GF_ISOM_IPOD_EXT, sizeof(u8)*16);
9375
0
  entry->ipod_ext->dataSize = 4;
9376
0
  entry->ipod_ext->data = gf_malloc(sizeof(u8)*4);
9377
0
  if (!entry->ipod_ext->data) return GF_OUT_OF_MEM;
9378
0
  memset(entry->ipod_ext->data, 0, sizeof(u8)*4);
9379
0
  return GF_OK;
9380
0
}
9381
9382
GF_EXPORT
9383
Bool gf_isom_is_inplace_rewrite(GF_ISOFile *movie)
9384
0
{
9385
0
  if (!movie) return GF_FALSE;
9386
0
  if (!movie->no_inplace_rewrite) {
9387
    //things where added to the file, no inplace rewrite
9388
0
    if (movie->editFileMap && gf_bs_get_size(movie->editFileMap->bs))
9389
0
      movie->no_inplace_rewrite = GF_TRUE;
9390
    //block redirect (used by mp4mx), no inplace rewrite
9391
0
    else if (movie->on_block_out || (movie->finalName && !strcmp(movie->finalName, "_gpac_isobmff_redirect")))
9392
0
      movie->no_inplace_rewrite = GF_TRUE;
9393
    //stdout redirect, no inplace rewrite
9394
0
    else if (movie->finalName && !strcmp(movie->finalName, "std"))
9395
0
      movie->no_inplace_rewrite = GF_TRUE;
9396
    //new file, no inplace rewrite
9397
0
    else if (!movie->fileName)
9398
0
      movie->no_inplace_rewrite = GF_TRUE;
9399
0
  }
9400
0
  if (movie->no_inplace_rewrite) return GF_FALSE;
9401
9402
0
  return GF_TRUE;
9403
0
}
9404
9405
GF_EXPORT
9406
void gf_isom_disable_inplace_rewrite(GF_ISOFile *movie)
9407
2.49k
{
9408
2.49k
  if (movie)
9409
2.49k
    movie->no_inplace_rewrite = GF_TRUE;
9410
2.49k
}
9411
9412
GF_EXPORT
9413
GF_Err gf_isom_set_y3d_info(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, GF_ISOM_Y3D_Info *info)
9414
0
{
9415
0
  GF_Err e;
9416
0
  u32 proj_type;
9417
0
  GF_SampleEntryBox *ent;
9418
0
  GF_TrackBox *trak;
9419
9420
0
  e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE);
9421
0
  if (e) return e;
9422
9423
0
  trak = gf_isom_get_track_box(movie, trackNumber);
9424
0
  if (!trak || !trak->Media || !info) return GF_BAD_PARAM;
9425
9426
0
  ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex-1);
9427
0
  if (!ent) return GF_BAD_PARAM;
9428
9429
0
  if (info->projection_type > GF_PROJ360_EQR) return GF_NOT_SUPPORTED;
9430
9431
0
  GF_Stereo3DBox *st3d = (GF_Stereo3DBox *) gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_ST3D);
9432
0
  if (st3d) {
9433
0
    if (info->stereo_type) {
9434
0
      st3d->stereo_type = info->stereo_type;
9435
0
    } else {
9436
0
      gf_isom_box_del_parent(&ent->child_boxes, (GF_Box *) st3d);
9437
0
    }
9438
0
  } else if (info->stereo_type) {
9439
0
    st3d = (GF_Stereo3DBox *) gf_isom_box_new_parent(&ent->child_boxes, GF_ISOM_BOX_TYPE_ST3D);
9440
0
    if (!st3d) return GF_OUT_OF_MEM;
9441
0
    st3d->stereo_type = info->stereo_type;
9442
0
  }
9443
9444
9445
0
  GF_Box *sv3d = gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_SV3D);
9446
0
  if (sv3d && !info->projection_type) {
9447
0
    gf_isom_box_del_parent(&ent->child_boxes, sv3d);
9448
0
    return GF_OK;
9449
0
  }
9450
9451
0
  if (!sv3d && !info->projection_type) {
9452
0
    return GF_OK;
9453
0
  }
9454
0
  if (!sv3d) {
9455
0
    sv3d = gf_isom_box_new_parent(&ent->child_boxes, GF_ISOM_BOX_TYPE_SV3D);
9456
0
    if (!sv3d) return GF_OUT_OF_MEM;
9457
0
  }
9458
9459
  //svhd mandatory
9460
0
  GF_SphericalVideoInfoBox *svhd = (GF_SphericalVideoInfoBox *) gf_isom_box_find_child(sv3d->child_boxes, GF_ISOM_BOX_TYPE_SVHD);
9461
0
  if (svhd) {
9462
0
    if (svhd->string) gf_free(svhd->string);
9463
0
  } else {
9464
0
    svhd = (GF_SphericalVideoInfoBox *) gf_isom_box_new_parent(&sv3d->child_boxes, GF_ISOM_BOX_TYPE_SVHD);
9465
0
    if (!svhd) return GF_OUT_OF_MEM;
9466
0
  }
9467
0
  svhd->string = gf_strdup(info->meta_data ? info->meta_data : "");
9468
9469
  //proj mandatory
9470
0
  GF_Box *proj = gf_isom_box_find_child(sv3d->child_boxes, GF_ISOM_BOX_TYPE_PROJ);
9471
0
  if (!proj) {
9472
0
    proj = gf_isom_box_new_parent(&sv3d->child_boxes, GF_ISOM_BOX_TYPE_PROJ);
9473
0
    if (!proj) return GF_OUT_OF_MEM;
9474
0
  }
9475
9476
0
  GF_ProjectionHeaderBox *projh = (GF_ProjectionHeaderBox *) gf_isom_box_find_child(proj->child_boxes, GF_ISOM_BOX_TYPE_PRHD);
9477
  //prj header mandatory
9478
0
  if (!projh) {
9479
0
    projh = (GF_ProjectionHeaderBox *) gf_isom_box_new_parent(&proj->child_boxes, GF_ISOM_BOX_TYPE_PRHD);
9480
0
    if (!projh) return GF_OUT_OF_MEM;
9481
0
  }
9482
0
  projh->yaw = info->yaw;
9483
0
  projh->pitch = info->pitch;
9484
0
  projh->roll = info->roll;
9485
9486
0
  proj_type = (info->projection_type==GF_PROJ360_CUBE_MAP) ? GF_ISOM_BOX_TYPE_CBMP : GF_ISOM_BOX_TYPE_EQUI;
9487
9488
0
  GF_ProjectionTypeBox *projt = (GF_ProjectionTypeBox *) gf_isom_box_find_child(proj->child_boxes, proj_type);
9489
0
  if (!projt) {
9490
0
    projt = (GF_ProjectionTypeBox *) gf_isom_box_new_parent(&proj->child_boxes, proj_type);
9491
0
    if (!projt) return GF_OUT_OF_MEM;
9492
0
  }
9493
0
  if (info->projection_type==GF_PROJ360_CUBE_MAP) {
9494
0
    projt->layout = info->layout;
9495
0
    projt->padding = info->padding;
9496
0
  } else {
9497
0
    projt->bounds_top = info->top;
9498
0
    projt->bounds_bottom = info->bottom;
9499
0
    projt->bounds_left = info->left;
9500
0
    projt->bounds_right = info->right;
9501
0
  }
9502
9503
  //remove other ones
9504
0
  GF_Box *b = gf_isom_box_new_parent(&proj->child_boxes, GF_ISOM_BOX_TYPE_MSHP);
9505
0
  if (b) gf_isom_box_del_parent(&proj->child_boxes, b);
9506
0
  if (info->projection_type==GF_PROJ360_CUBE_MAP) {
9507
0
    b = gf_isom_box_new_parent(&proj->child_boxes, GF_ISOM_BOX_TYPE_EQUI);
9508
0
    if (b) gf_isom_box_del_parent(&proj->child_boxes, b);
9509
0
  } else {
9510
0
    b = gf_isom_box_new_parent(&proj->child_boxes, GF_ISOM_BOX_TYPE_EQUI);
9511
0
    if (b) gf_isom_box_del_parent(&proj->child_boxes, b);
9512
9513
0
  }
9514
0
  return GF_OK;
9515
0
}
9516
9517
#endif //!defined(GPAC_DISABLE_ISOM_WRITE)
9518
9519
#ifndef GPAC_DISABLE_ISOM
9520
9521
GF_Err gf_isom_add_sample_aux_info_internal(GF_TrackBox *trak, void *_traf, u32 sampleNumber, u32 aux_type, u32 aux_info, u8 *data, u32 size)
9522
0
{
9523
0
  u32 i, count;
9524
0
  GF_List **child_box_cont, **child_box_sai, **child_box_saiz, **child_box_saio;
9525
0
  GF_UnknownBox *sai_cont = NULL;
9526
9527
0
  if (!trak && !_traf) return GF_BAD_PARAM;
9528
9529
0
  if (trak) {
9530
0
    child_box_cont = &trak->child_boxes;
9531
0
    child_box_sai = &trak->Media->information->sampleTable->child_boxes;
9532
0
    child_box_saiz = &trak->Media->information->sampleTable->sai_sizes;
9533
0
    child_box_saio = &trak->Media->information->sampleTable->sai_offsets;
9534
0
  } else {
9535
0
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
9536
0
    GF_TrackFragmentBox *traf = (GF_TrackFragmentBox *)_traf;
9537
9538
0
    child_box_cont = &traf->child_boxes;
9539
0
    child_box_sai = &traf->child_boxes;
9540
0
    child_box_saiz = &traf->sai_sizes;
9541
0
    child_box_saio = &traf->sai_offsets;
9542
0
#endif
9543
0
  }
9544
9545
0
  count = gf_list_count(*child_box_cont);
9546
0
  for (i=0; i<count; i++) {
9547
0
    GF_UnknownBox *unkn = gf_list_get(*child_box_cont, i);
9548
0
    if (unkn->type != GF_ISOM_BOX_TYPE_UNKNOWN) continue;
9549
0
    if (unkn->original_4cc != GF_ISOM_BOX_TYPE_GDAT) continue;
9550
0
    if (unkn->sai_type != aux_type) continue;
9551
0
    if (unkn->sai_aux_info != aux_info) continue;
9552
0
    sai_cont = unkn;
9553
0
    break;
9554
0
  }
9555
0
  if (!sai_cont) {
9556
0
    sai_cont = (GF_UnknownBox *) gf_isom_box_new_parent(child_box_cont, GF_ISOM_BOX_TYPE_UNKNOWN);
9557
0
    if (!sai_cont) return GF_OUT_OF_MEM;
9558
0
    sai_cont->original_4cc = GF_ISOM_BOX_TYPE_GDAT;
9559
0
    sai_cont->sai_type = aux_type;
9560
0
    sai_cont->sai_aux_info = aux_info;
9561
0
  }
9562
0
  sai_cont->data = gf_realloc(sai_cont->data, (size+sai_cont->dataSize));
9563
0
  if (!sai_cont->data) return GF_OUT_OF_MEM;
9564
0
  memcpy(sai_cont->data+sai_cont->dataSize, data, size);
9565
0
  sai_cont->dataSize += size;
9566
9567
0
  GF_SampleAuxiliaryInfoSizeBox *saiz=NULL;
9568
0
  GF_SampleAuxiliaryInfoOffsetBox *saio=NULL;
9569
0
  count = gf_list_count(*child_box_saiz);
9570
0
  for (i=0; i<count; i++) {
9571
0
    saiz = gf_list_get(*child_box_saiz, i);
9572
0
    if ((saiz->aux_info_type==aux_type) && (saiz->aux_info_type_parameter==aux_info)) break;
9573
0
    saiz = NULL;
9574
0
  }
9575
0
  if (!saiz) {
9576
0
    saiz = (GF_SampleAuxiliaryInfoSizeBox *) gf_isom_box_new_parent(child_box_sai, GF_ISOM_BOX_TYPE_SAIZ);
9577
0
    if (!saiz) return GF_OUT_OF_MEM;
9578
0
    if (! *child_box_saiz) *child_box_saiz = gf_list_new();
9579
0
    gf_list_add(*child_box_saiz, saiz);
9580
9581
0
    saiz->aux_info_type = aux_type;
9582
0
    saiz->aux_info_type_parameter = aux_info;
9583
0
  }
9584
9585
0
  if (saiz->sample_count >= sampleNumber)
9586
0
    return GF_BAD_PARAM;
9587
9588
0
  if ( (!saiz->sample_count && (sampleNumber==1))
9589
0
    || ((saiz->default_sample_info_size==size) && size)
9590
0
  ) {
9591
0
    saiz->sample_count ++;
9592
0
    saiz->default_sample_info_size = size;
9593
0
  } else {
9594
0
    if (sampleNumber > saiz->sample_alloc) {
9595
0
      saiz->sample_alloc = sampleNumber+10;
9596
0
      saiz->sample_info_size = (u8*)gf_realloc(saiz->sample_info_size, sizeof(u8)*(saiz->sample_alloc));
9597
0
    }
9598
9599
0
    if (saiz->default_sample_info_size) {
9600
0
      for (i=0; i<saiz->sample_count; i++)
9601
0
        saiz->sample_info_size[i] = saiz->default_sample_info_size;
9602
0
      saiz->default_sample_info_size = 0;
9603
0
    }
9604
0
    for (i=saiz->sample_count; i<sampleNumber-1; i++)
9605
0
      saiz->sample_info_size[i] = 0;
9606
9607
0
    saiz->sample_info_size[sampleNumber-1] = size;
9608
0
    saiz->sample_count = sampleNumber;
9609
0
  }
9610
9611
9612
0
  count = gf_list_count(*child_box_saio);
9613
0
  for (i=0; i<count; i++) {
9614
0
    saio = gf_list_get(*child_box_saio, i);
9615
0
    if ((saio->aux_info_type==aux_type) && (saio->aux_info_type_parameter==aux_info)) break;
9616
0
    saio = NULL;
9617
0
  }
9618
0
  if (!saio) {
9619
0
    saio = (GF_SampleAuxiliaryInfoOffsetBox *) gf_isom_box_new_parent(child_box_sai, GF_ISOM_BOX_TYPE_SAIO);
9620
0
    if (!saio) return GF_OUT_OF_MEM;
9621
0
    if (! *child_box_saio) *child_box_saio = gf_list_new();
9622
0
    gf_list_add(*child_box_saio, saio);
9623
0
    saio->aux_info_type = aux_type;
9624
0
    saio->aux_info_type_parameter = aux_info;
9625
0
  }
9626
0
  if (!saio->sai_data) saio->sai_data = sai_cont;
9627
0
  saio->version = 1;
9628
0
  saio->entry_count = 1;
9629
9630
0
  return GF_OK;
9631
0
}
9632
#endif // GPAC_DISABLE_ISOM
9633
9634
9635
#if !defined(GPAC_DISABLE_ISOM_WRITE)
9636
9637
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
9638
GF_Err gf_isom_fragment_set_sample_aux_info(GF_ISOFile *movie, u32 trackID, u32 sample_number_in_frag, u32 aux_type, u32 aux_info, u8 *data, u32 size)
9639
0
{
9640
0
  GF_TrackFragmentBox *traf;
9641
0
  if (!movie || !movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM;
9642
9643
0
  traf = gf_isom_get_traf(movie, trackID);
9644
0
  if (!traf) return GF_BAD_PARAM;
9645
0
  return gf_isom_add_sample_aux_info_internal(NULL, traf, sample_number_in_frag, aux_type, aux_info, data, size);
9646
0
}
9647
#endif
9648
9649
GF_Err gf_isom_add_sample_aux_info(GF_ISOFile *file, u32 track, u32 sampleNumber, u32 aux_type, u32 aux_info, u8 *data, u32 size)
9650
0
{
9651
0
  GF_Err e;
9652
0
  GF_TrackBox *trak;
9653
9654
0
  e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
9655
0
  if (e) return e;
9656
9657
0
  trak = gf_isom_get_track_box(file, track);
9658
0
  if (!trak) return GF_BAD_PARAM;
9659
9660
0
  return gf_isom_add_sample_aux_info_internal(trak, NULL, sampleNumber, aux_type, aux_info, data, size);
9661
0
}
9662
9663
9664
GF_Err gf_isom_set_meta_qt(GF_ISOFile *file)
9665
0
{
9666
0
  u32 i, count;
9667
0
  if (!file) return GF_BAD_PARAM;
9668
0
  GF_Err e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
9669
0
  if (e) return e;
9670
0
  if (file->moov->meta)
9671
0
    file->moov->meta->write_qt = 1;
9672
9673
0
  count = gf_list_count(file->moov->trackList);
9674
0
  for (i=0; i<count; i++) {
9675
0
    GF_TrackBox *trak = gf_list_get(file->moov->trackList, i);
9676
0
    if (trak->meta)
9677
0
      trak->meta->write_qt = 1;
9678
0
  }
9679
0
  return GF_OK;
9680
0
}
9681
9682
9683
GF_EXPORT
9684
GF_Err gf_isom_set_mpegh_compatible_profiles(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescIndex, const u32 *profiles, u32 nb_compat_profiles)
9685
0
{
9686
0
  u32 i, type;
9687
0
  GF_SampleEntryBox *ent;
9688
0
  GF_MHACompatibleProfilesBox *mhap;
9689
0
  GF_TrackBox *trak;
9690
9691
0
  trak = gf_isom_get_track_box(movie, trackNumber);
9692
0
  if (!trak || !trak->Media) return GF_BAD_PARAM;
9693
0
  ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescIndex-1);
9694
0
  if (!ent) return GF_BAD_PARAM;
9695
0
  type = ent->type;
9696
0
  if (type==GF_ISOM_BOX_TYPE_GNRA)
9697
0
    type = ((GF_GenericAudioSampleEntryBox *)ent)->EntryType;
9698
9699
0
  switch (type) {
9700
0
  case GF_ISOM_BOX_TYPE_MHA1:
9701
0
  case GF_ISOM_BOX_TYPE_MHA2:
9702
0
  case GF_ISOM_BOX_TYPE_MHM1:
9703
0
  case GF_ISOM_BOX_TYPE_MHM2:
9704
0
    break;
9705
0
  default:
9706
0
    return GF_BAD_PARAM;
9707
0
  }
9708
0
  mhap = (GF_MHACompatibleProfilesBox *) gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_MHAP);
9709
0
  if (!mhap) {
9710
0
    if (! profiles || !nb_compat_profiles) return GF_OK;
9711
0
    mhap = (GF_MHACompatibleProfilesBox *) gf_isom_box_new_parent(&ent->child_boxes, GF_ISOM_BOX_TYPE_MHAP);
9712
0
  } else if (! profiles || !nb_compat_profiles) {
9713
0
    gf_isom_box_del_parent(&ent->child_boxes, (GF_Box*)mhap);
9714
0
    return GF_OK;
9715
0
  }
9716
0
  if (mhap->compat_profiles) gf_free(mhap->compat_profiles);
9717
0
  mhap->compat_profiles = gf_malloc(sizeof(u8) * nb_compat_profiles);
9718
0
  if (!mhap->compat_profiles) return GF_OUT_OF_MEM;
9719
0
  for (i=0; i<nb_compat_profiles; i++) {
9720
0
    mhap->compat_profiles[i] = (u8) profiles[i];
9721
0
  }
9722
0
  mhap->num_profiles = nb_compat_profiles;
9723
0
  return GF_OK;
9724
0
}
9725
9726
GF_Err gf_isom_set_sample_description_restricted(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescIndex, u32 scheme_type)
9727
0
{
9728
0
  u32 type;
9729
0
  GF_SampleEntryBox *ent;
9730
0
  GF_TrackBox *trak;
9731
9732
0
  trak = gf_isom_get_track_box(movie, trackNumber);
9733
0
  if (!trak || !trak->Media) return GF_BAD_PARAM;
9734
0
  ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescIndex-1);
9735
0
  if (!ent) return GF_BAD_PARAM;
9736
0
  type = ent->type;
9737
9738
0
  u32 original_format = type;
9739
0
  u32 gnr_type=0;
9740
0
  if (original_format==GF_ISOM_BOX_TYPE_GNRA) {
9741
0
    gnr_type = original_format;
9742
0
    type = ((GF_GenericAudioSampleEntryBox*)ent)->EntryType;
9743
0
  } else if (original_format==GF_ISOM_BOX_TYPE_GNRV) {
9744
0
    gnr_type = original_format;
9745
0
    type = ((GF_GenericVisualSampleEntryBox*)ent)->EntryType;
9746
0
  } else if (original_format==GF_ISOM_BOX_TYPE_GNRM) {
9747
0
    gnr_type = original_format;
9748
0
    type = ((GF_GenericSampleEntryBox*)ent)->EntryType;
9749
0
  }
9750
9751
0
  switch (type) {
9752
0
  case GF_ISOM_BOX_TYPE_RESV:
9753
0
  case GF_ISOM_BOX_TYPE_RESA:
9754
0
  case GF_ISOM_BOX_TYPE_RESM:
9755
0
  case GF_ISOM_BOX_TYPE_REST:
9756
0
  case GF_ISOM_BOX_TYPE_RESU:
9757
0
  case GF_ISOM_BOX_TYPE_RESS:
9758
0
  case GF_ISOM_BOX_TYPE_RESF:
9759
0
  case GF_ISOM_BOX_TYPE_RESP:
9760
0
  case GF_ISOM_BOX_TYPE_RES3:
9761
0
    return GF_OK;
9762
0
  case GF_ISOM_BOX_TYPE_STXT:
9763
0
    type = GF_ISOM_BOX_TYPE_REST;
9764
0
    break;
9765
0
  case GF_ISOM_BOX_TYPE_STPP:
9766
0
    type = GF_ISOM_BOX_TYPE_RESU;
9767
0
    break;
9768
0
  case GF_ISOM_BOX_TYPE_METX:
9769
0
  case GF_ISOM_BOX_TYPE_METT:
9770
0
  case GF_ISOM_BOX_TYPE_URIM:
9771
0
  case GF_ISOM_BOX_TYPE_MEBX:
9772
0
    type = GF_ISOM_BOX_TYPE_RESM;
9773
0
    break;
9774
0
  default:
9775
0
    type=0;
9776
0
    switch (trak->Media->handler->handlerType) {
9777
0
    case GF_ISOM_MEDIA_VISUAL:
9778
0
    case GF_ISOM_MEDIA_AUXV:
9779
0
    case GF_ISOM_MEDIA_PICT:
9780
0
      type = GF_ISOM_BOX_TYPE_RESV;
9781
0
      break;
9782
0
    case GF_ISOM_MEDIA_AUDIO:
9783
0
      type = GF_ISOM_BOX_TYPE_RESA;
9784
0
      break;
9785
0
    case GF_ISOM_MEDIA_OD:
9786
0
    case GF_ISOM_MEDIA_OCR:
9787
0
    case GF_ISOM_MEDIA_SCENE:
9788
0
    case GF_ISOM_MEDIA_MPEG7:
9789
0
    case GF_ISOM_MEDIA_OCI:
9790
0
    case GF_ISOM_MEDIA_IPMP:
9791
0
    case GF_ISOM_MEDIA_MPEGJ:
9792
0
      type = GF_ISOM_BOX_TYPE_RESS;
9793
0
      break;
9794
0
    }
9795
0
    break;
9796
0
  }
9797
0
  if (!type) return GF_NOT_SUPPORTED;
9798
9799
0
  GF_ProtectionSchemeInfoBox *rinf;
9800
0
  rinf = (GF_ProtectionSchemeInfoBox *) gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_RINF);
9801
0
  if (rinf) gf_isom_box_del_parent(&ent->child_boxes, (GF_Box *)rinf);
9802
9803
0
  rinf = (GF_ProtectionSchemeInfoBox *)gf_isom_box_new_parent(&ent->child_boxes, GF_ISOM_BOX_TYPE_RINF);
9804
0
  if (!rinf) return GF_OUT_OF_MEM;
9805
9806
9807
0
  rinf->original_format = (GF_OriginalFormatBox *)gf_isom_box_new_parent(&rinf->child_boxes, GF_ISOM_BOX_TYPE_FRMA);
9808
0
  if (!rinf->original_format) return GF_OUT_OF_MEM;
9809
0
  if (gnr_type) {
9810
0
    rinf->original_format->data_format = gnr_type;
9811
0
    rinf->original_format->gnr_type = original_format;
9812
0
  } else {
9813
0
    rinf->original_format->data_format = original_format;
9814
0
  }
9815
  //common to isma, cenc and oma
9816
0
  rinf->scheme_type = (GF_SchemeTypeBox *)gf_isom_box_new_parent(&rinf->child_boxes, GF_ISOM_BOX_TYPE_SCHM);
9817
0
  if (!rinf->scheme_type) return GF_OUT_OF_MEM;
9818
0
  rinf->scheme_type->scheme_type  = scheme_type;
9819
9820
0
  ent->type = type;
9821
0
  return GF_OK;
9822
0
}
9823
9824
GF_Err isom_sample_refs_push(GF_SampleReferences *sref, s32 refID, u32 nb_refs, s32 *refs)
9825
0
{
9826
0
  GF_SampleRefEntry *ent;
9827
0
  GF_SAFEALLOC(ent, GF_SampleRefEntry);
9828
0
  if (!ent) return GF_OUT_OF_MEM;
9829
0
  refID += sref->id_shift;
9830
0
  if (refID<0) {
9831
0
    u32 new_shift = -refID;
9832
0
    sref->id_shift += -refID;
9833
0
    refID = 0;
9834
0
    u32 i, j, count = gf_list_count(sref->entries);
9835
0
    for (i=0; i<count; i++) {
9836
0
      GF_SampleRefEntry *a = gf_list_get(sref->entries, i);
9837
0
      a->sampleID += new_shift;
9838
0
      for (j=0; j<a->nb_refs; j++)
9839
0
        a->sample_refs[j] += new_shift;
9840
0
    }
9841
0
  }
9842
9843
0
  ent->sampleID = refID;
9844
0
  if (nb_refs) {
9845
0
    u32 j;
9846
0
    ent->nb_refs = nb_refs;
9847
0
    ent->sample_refs = gf_malloc(sizeof(u32)*nb_refs);
9848
0
    memcpy(ent->sample_refs, refs, sizeof(s32)*nb_refs);
9849
0
    if (sref->id_shift) {
9850
0
      for (j=0; j<ent->nb_refs; j++)
9851
0
        ent->sample_refs[j] += sref->id_shift;
9852
0
    }
9853
0
  }
9854
0
  sref->cdrf_cache_size = 0;
9855
0
  return gf_list_add(sref->entries, ent);
9856
0
}
9857
9858
GF_Err gf_isom_set_sample_references(GF_ISOFile *file, u32 track, u32 sampleNumber, s32 refID, u32 nb_refs, s32 *refs)
9859
0
{
9860
0
  GF_Err e;
9861
0
  GF_TrackBox *trak;
9862
0
  GF_SampleTableBox *stbl;
9863
9864
0
  e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE);
9865
0
  if (e) return e;
9866
9867
0
  trak = gf_isom_get_track_box(file, track);
9868
0
  if (!trak) return GF_BAD_PARAM;
9869
0
  stbl = trak->Media->information->sampleTable;
9870
0
  if (sampleNumber != stbl->SampleSize->sampleCount)
9871
0
    return GF_BAD_PARAM;
9872
9873
0
  if (!stbl->SampleRefs) {
9874
0
    stbl->SampleRefs =  (GF_SampleReferences *)gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_CDRF);
9875
0
    if (!stbl->SampleRefs) return GF_OUT_OF_MEM;
9876
0
  }
9877
0
  return isom_sample_refs_push(stbl->SampleRefs, refID, nb_refs, refs);
9878
0
}
9879
9880
GF_EXPORT
9881
GF_Err gf_isom_enable_auto_track_reorder(GF_ISOFile *file, Bool enabled)
9882
0
{
9883
0
  if (!file) return GF_BAD_PARAM;
9884
0
  file->auto_reorder_tracks = enabled;
9885
0
  return GF_OK;
9886
0
}
9887
9888
#endif  /*!defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)*/