Coverage Report

Created: 2026-05-30 07:13

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