Coverage Report

Created: 2025-11-24 06:43

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