/src/gpac/src/isomedia/isom_write.c
Line | Count | Source |
1 | | /* |
2 | | * GPAC - Multimedia Framework C SDK |
3 | | * |
4 | | * Authors: Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2000-2026 |
6 | | * All rights reserved |
7 | | * |
8 | | * This file is part of GPAC / ISO Media File Format sub-project |
9 | | * |
10 | | * GPAC is free software; you can redistribute it and/or modify |
11 | | * it under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2, or (at your option) |
13 | | * any later version. |
14 | | * |
15 | | * GPAC is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public |
21 | | * License along with this library; see the file COPYING. If not, write to |
22 | | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
23 | | * |
24 | | */ |
25 | | |
26 | | #include <gpac/internal/isomedia_dev.h> |
27 | | #include <gpac/constants.h> |
28 | | #include <gpac/iso639.h> |
29 | | |
30 | | |
31 | | #if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE) |
32 | | |
33 | | GF_EXPORT |
34 | | GF_Err gf_isom_can_access_movie(GF_ISOFile *movie, GF_ISOOpenMode Mode) |
35 | 0 | { |
36 | 0 | if (!movie) return GF_BAD_PARAM; |
37 | 0 | if (movie->openMode < Mode) return GF_ISOM_INVALID_MODE; |
38 | | |
39 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
40 | 0 | if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_ISOM_INVALID_MODE; |
41 | 0 | #endif |
42 | 0 | return GF_OK; |
43 | 0 | } |
44 | | |
45 | | static GF_Err unpack_track(GF_TrackBox *trak) |
46 | 0 | { |
47 | 0 | GF_Err e = GF_OK; |
48 | 0 | if (!trak->is_unpacked) { |
49 | 0 | e = stbl_UnpackOffsets(trak->Media->information->sampleTable); |
50 | 0 | if (e) return e; |
51 | 0 | e = stbl_unpackCTS(trak->Media->information->sampleTable); |
52 | 0 | trak->is_unpacked = GF_TRUE; |
53 | 0 | } |
54 | 0 | return e; |
55 | 0 | } |
56 | | |
57 | | |
58 | | GF_Err FlushCaptureMode(GF_ISOFile *movie) |
59 | 0 | { |
60 | 0 | GF_Err e; |
61 | 0 | if (movie->openMode != GF_ISOM_OPEN_WRITE) { |
62 | 0 | if (!movie->editFileMap) return GF_ISOM_INVALID_MODE; |
63 | 0 | return GF_OK; |
64 | 0 | } |
65 | | /*make sure nothing was added*/ |
66 | 0 | if (gf_bs_get_position(movie->editFileMap->bs)) return GF_OK; |
67 | | |
68 | 0 | if (movie->fileName && !strcmp(movie->fileName, "_gpac_isobmff_redirect")) { |
69 | 0 | if (!movie->on_block_out) { |
70 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block callback, cannot write\n")); |
71 | 0 | return GF_BAD_PARAM; |
72 | 0 | } |
73 | | |
74 | 0 | gf_bs_del(movie->editFileMap->bs); |
75 | 0 | movie->editFileMap->bs = gf_bs_new_cbk(isom_on_block_out, movie, movie->on_block_out_block_size); |
76 | 0 | } |
77 | | |
78 | | /*add all first boxes*/ |
79 | 0 | if (movie->brand) { |
80 | 0 | e = gf_isom_box_size((GF_Box *)movie->brand); |
81 | 0 | if (e) return e; |
82 | 0 | e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs); |
83 | 0 | if (e) return e; |
84 | 0 | } |
85 | 0 | if (movie->pdin) { |
86 | 0 | e = gf_isom_box_size((GF_Box *)movie->pdin); |
87 | 0 | if (e) return e; |
88 | 0 | e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs); |
89 | 0 | if (e) return e; |
90 | 0 | } |
91 | 0 | movie->mdat->bsOffset = gf_bs_get_position(movie->editFileMap->bs); |
92 | | |
93 | | /*we have a trick here: the data will be stored on the fly, so the first |
94 | | thing in the file is the MDAT. As we don't know if we have a large file (>4 GB) or not |
95 | | do as if we had one and write 16 bytes: 4 (type) + 4 (size) + 8 (largeSize)...*/ |
96 | 0 | gf_bs_write_long_int(movie->editFileMap->bs, 0, 64); |
97 | 0 | gf_bs_write_long_int(movie->editFileMap->bs, 0, 64); |
98 | 0 | return GF_OK; |
99 | 0 | } |
100 | | |
101 | | static GF_Err CheckNoData(GF_ISOFile *movie) |
102 | 0 | { |
103 | 0 | if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_OK; |
104 | 0 | if (gf_bs_get_position(movie->editFileMap->bs)) return GF_BAD_PARAM; |
105 | 0 | return GF_OK; |
106 | 0 | } |
107 | | |
108 | | /************************************************************** |
109 | | File Writing / Editing |
110 | | **************************************************************/ |
111 | | //quick function to add an IOD/OD to the file if not present (iods is optional) |
112 | | GF_Err AddMovieIOD(GF_MovieBox *moov, u8 isIOD) |
113 | 0 | { |
114 | 0 | GF_Descriptor *od; |
115 | 0 | GF_ObjectDescriptorBox *iods; |
116 | | |
117 | | //do we have an IOD ?? If not, create one. |
118 | 0 | if (moov->iods) return GF_OK; |
119 | | |
120 | 0 | if (isIOD) { |
121 | 0 | od = gf_odf_desc_new(GF_ODF_ISOM_IOD_TAG); |
122 | 0 | } else { |
123 | 0 | od = gf_odf_desc_new(GF_ODF_ISOM_OD_TAG); |
124 | 0 | } |
125 | 0 | if (!od) return GF_OUT_OF_MEM; |
126 | 0 | ((GF_IsomObjectDescriptor *)od)->objectDescriptorID = 1; |
127 | |
|
128 | 0 | iods = (GF_ObjectDescriptorBox *) gf_isom_box_new_parent(&moov->child_boxes, GF_ISOM_BOX_TYPE_IODS); |
129 | 0 | if (!iods) return GF_OUT_OF_MEM; |
130 | 0 | iods->descriptor = od; |
131 | 0 | return moov_on_child_box((GF_Box*)moov, (GF_Box *)iods, GF_FALSE); |
132 | 0 | } |
133 | | |
134 | | //add a track to the root OD |
135 | | GF_EXPORT |
136 | | GF_Err gf_isom_add_track_to_root_od(GF_ISOFile *movie, u32 trackNumber) |
137 | 0 | { |
138 | 0 | GF_Err e; |
139 | 0 | GF_ES_ID_Inc *inc; |
140 | |
|
141 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
142 | 0 | if (e) return e; |
143 | 0 | e = gf_isom_insert_moov(movie); |
144 | 0 | if (e) return e; |
145 | | |
146 | 0 | if (!movie->moov->iods) AddMovieIOD(movie->moov, 0); |
147 | |
|
148 | 0 | if (gf_isom_is_track_in_root_od(movie, trackNumber) == 1) return GF_OK; |
149 | | |
150 | 0 | inc = (GF_ES_ID_Inc *) gf_odf_desc_new(GF_ODF_ESD_INC_TAG); |
151 | 0 | inc->trackID = gf_isom_get_track_id(movie, trackNumber); |
152 | 0 | if (!inc->trackID) { |
153 | 0 | gf_odf_desc_del((GF_Descriptor *)inc); |
154 | 0 | return movie->LastError; |
155 | 0 | } |
156 | 0 | if ( (movie->LastError = gf_isom_add_desc_to_root_od(movie, (GF_Descriptor *)inc) ) ) { |
157 | 0 | return movie->LastError; |
158 | 0 | } |
159 | 0 | gf_odf_desc_del((GF_Descriptor *)inc); |
160 | 0 | return GF_OK; |
161 | 0 | } |
162 | | |
163 | | //remove the root OD |
164 | | GF_EXPORT |
165 | | GF_Err gf_isom_remove_root_od(GF_ISOFile *movie) |
166 | 0 | { |
167 | 0 | GF_Err e; |
168 | |
|
169 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
170 | 0 | if (e) return e; |
171 | 0 | if (!movie->moov || !movie->moov->iods) return GF_OK; |
172 | 0 | gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box *)movie->moov->iods); |
173 | 0 | movie->moov->iods = NULL; |
174 | 0 | return GF_OK; |
175 | 0 | } |
176 | | |
177 | | //remove a track to the root OD |
178 | | GF_EXPORT |
179 | | GF_Err gf_isom_remove_track_from_root_od(GF_ISOFile *movie, u32 trackNumber) |
180 | 0 | { |
181 | 0 | GF_List *esds; |
182 | 0 | GF_ES_ID_Inc *inc; |
183 | 0 | u32 i; |
184 | 0 | GF_Err e; |
185 | |
|
186 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
187 | 0 | if (e) return e; |
188 | 0 | if (!movie->moov) return GF_OK; |
189 | | |
190 | 0 | if (!gf_isom_is_track_in_root_od(movie, trackNumber)) return GF_OK; |
191 | | |
192 | 0 | if (!movie->moov->iods) { |
193 | 0 | e = AddMovieIOD(movie->moov, 0); |
194 | 0 | if (e) return e; |
195 | 0 | } |
196 | 0 | switch (movie->moov->iods->descriptor->tag) { |
197 | 0 | case GF_ODF_ISOM_IOD_TAG: |
198 | 0 | esds = ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors; |
199 | 0 | break; |
200 | 0 | case GF_ODF_ISOM_OD_TAG: |
201 | 0 | esds = ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors; |
202 | 0 | break; |
203 | 0 | default: |
204 | 0 | return GF_ISOM_INVALID_FILE; |
205 | 0 | } |
206 | | |
207 | | //get the desc |
208 | 0 | i=0; |
209 | 0 | while ((inc = (GF_ES_ID_Inc*)gf_list_enum(esds, &i))) { |
210 | 0 | if (inc->trackID == (u32) gf_isom_get_track_id(movie, trackNumber)) { |
211 | 0 | gf_odf_desc_del((GF_Descriptor *)inc); |
212 | 0 | gf_list_rem(esds, i-1); |
213 | 0 | break; |
214 | 0 | } |
215 | 0 | } |
216 | | //we don't remove the iod for P&Ls and other potential info |
217 | 0 | return GF_OK; |
218 | 0 | } |
219 | | |
220 | | GF_EXPORT |
221 | | GF_Err gf_isom_set_creation_time(GF_ISOFile *movie, u64 ctime, u64 mtime) |
222 | 0 | { |
223 | 0 | if (!movie || !movie->moov) return GF_BAD_PARAM; |
224 | 0 | movie->moov->mvhd->creationTime = ctime; |
225 | 0 | movie->moov->mvhd->modificationTime = mtime; |
226 | 0 | return GF_OK; |
227 | 0 | } |
228 | | |
229 | | GF_EXPORT |
230 | | GF_Err gf_isom_set_track_creation_time(GF_ISOFile *movie,u32 trackNumber, u64 ctime, u64 mtime) |
231 | 0 | { |
232 | 0 | GF_TrackBox *trak; |
233 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
234 | 0 | if (!trak) return GF_BAD_PARAM; |
235 | | |
236 | 0 | trak->Header->creationTime = ctime; |
237 | 0 | trak->Header->modificationTime = mtime; |
238 | 0 | return GF_OK; |
239 | 0 | } |
240 | | |
241 | | GF_EXPORT |
242 | | GF_Err gf_isom_set_media_creation_time(GF_ISOFile *movie,u32 trackNumber, u64 ctime, u64 mtime) |
243 | 0 | { |
244 | 0 | GF_TrackBox *trak; |
245 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
246 | 0 | if (!trak) return GF_BAD_PARAM; |
247 | 0 | if (!trak->Media || !trak->Media->mediaHeader) return GF_ISOM_INVALID_FILE; |
248 | | |
249 | 0 | trak->Media->mediaHeader->creationTime = ctime; |
250 | 0 | trak->Media->mediaHeader->modificationTime = mtime; |
251 | 0 | return GF_OK; |
252 | 0 | } |
253 | | |
254 | | //sets the enable flag of a track |
255 | | GF_EXPORT |
256 | | GF_Err gf_isom_set_track_enabled(GF_ISOFile *movie, u32 trackNumber, Bool enableTrack) |
257 | 0 | { |
258 | 0 | GF_Err e; |
259 | 0 | GF_TrackBox *trak; |
260 | |
|
261 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
262 | 0 | if (e) return e; |
263 | | |
264 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
265 | 0 | if (!trak) return GF_BAD_PARAM; |
266 | | |
267 | 0 | if (enableTrack) { |
268 | 0 | trak->Header->flags |= 1; |
269 | 0 | } else { |
270 | 0 | trak->Header->flags &= ~1; |
271 | 0 | } |
272 | 0 | return GF_OK; |
273 | 0 | } |
274 | | |
275 | | //sets the enable flag of a track |
276 | | GF_EXPORT |
277 | | GF_Err gf_isom_set_track_flags(GF_ISOFile *movie, u32 trackNumber, u32 flags, GF_ISOMTrackFlagOp op) |
278 | 0 | { |
279 | 0 | GF_Err e; |
280 | 0 | GF_TrackBox *trak; |
281 | |
|
282 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
283 | 0 | if (e) return e; |
284 | | |
285 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
286 | 0 | if (!trak) return GF_BAD_PARAM; |
287 | 0 | if (op==GF_ISOM_TKFLAGS_ADD) |
288 | 0 | trak->Header->flags |= flags; |
289 | 0 | else if (op==GF_ISOM_TKFLAGS_REM) |
290 | 0 | trak->Header->flags &= ~flags; |
291 | 0 | else |
292 | 0 | trak->Header->flags = flags; |
293 | 0 | return GF_OK; |
294 | 0 | } |
295 | | |
296 | | GF_EXPORT |
297 | | GF_Err gf_isom_set_media_language(GF_ISOFile *movie, u32 trackNumber, char *code) |
298 | 0 | { |
299 | 0 | GF_Err e; |
300 | 0 | GF_TrackBox *trak; |
301 | |
|
302 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
303 | 0 | if (!trak || !code) return GF_BAD_PARAM; |
304 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
305 | 0 | if (e) return e; |
306 | | |
307 | 0 | if (trak->extl) { |
308 | 0 | GF_ExtendedLanguageBox *elng = (GF_ExtendedLanguageBox *) gf_isom_box_find_child(trak->child_boxes, GF_ISOM_BOX_TYPE_ELNG); |
309 | 0 | if (!elng) { |
310 | 0 | elng = (GF_ExtendedLanguageBox *)gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_ELNG); |
311 | 0 | if (!elng) return GF_OUT_OF_MEM; |
312 | 0 | } |
313 | 0 | if (elng->extended_language) gf_free(elng->extended_language); |
314 | 0 | elng->extended_language = gf_strdup(code); |
315 | |
|
316 | 0 | if (!movie->keep_utc) |
317 | 0 | trak->Header->modificationTime = gf_isom_get_mp4time(); |
318 | 0 | return GF_OK; |
319 | 0 | } |
320 | | |
321 | | // Old language-storage processing |
322 | | // if the new code is on 3 chars, we use it |
323 | | // otherwise, we find the associated 3 chars code and use it |
324 | 0 | if (strlen(code) == 3) { |
325 | 0 | memcpy(trak->Media->mediaHeader->packedLanguage, code, sizeof(char)*3); |
326 | 0 | } else { |
327 | 0 | s32 lang_idx; |
328 | 0 | const char *code_3cc; |
329 | 0 | lang_idx = gf_lang_find(code); |
330 | 0 | if (lang_idx == -1) { |
331 | 0 | if (code[0]!=0) { |
332 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("The given code is not a valid one: %s, using 'und' as 3-letter code\n", code)); |
333 | 0 | } |
334 | 0 | code_3cc = "und"; |
335 | 0 | } else { |
336 | 0 | code_3cc = gf_lang_get_3cc(lang_idx); |
337 | 0 | } |
338 | 0 | memcpy(trak->Media->mediaHeader->packedLanguage, code_3cc, sizeof(char)*3); |
339 | 0 | } |
340 | | |
341 | | // New language-storage processing |
342 | | // change the code in the extended language box (if any) |
343 | | // otherwise add an extended language box only if the given code is not 3 chars |
344 | 0 | { |
345 | 0 | u32 i, count; |
346 | 0 | GF_ExtendedLanguageBox *elng; |
347 | 0 | elng = NULL; |
348 | 0 | count = gf_list_count(trak->Media->child_boxes); |
349 | 0 | for (i = 0; i < count; i++) { |
350 | 0 | GF_Box *box = (GF_Box *)gf_list_get(trak->Media->child_boxes, i); |
351 | 0 | if (box->type == GF_ISOM_BOX_TYPE_ELNG) { |
352 | 0 | elng = (GF_ExtendedLanguageBox *)box; |
353 | 0 | break; |
354 | 0 | } |
355 | 0 | } |
356 | 0 | if (!elng && (strlen(code) > 3)) { |
357 | 0 | elng = (GF_ExtendedLanguageBox *)gf_isom_box_new_parent(&trak->Media->child_boxes, GF_ISOM_BOX_TYPE_ELNG); |
358 | 0 | if (!elng) return GF_OUT_OF_MEM; |
359 | 0 | } |
360 | 0 | if (elng) { |
361 | 0 | if (elng->extended_language) { |
362 | 0 | gf_free(elng->extended_language); |
363 | 0 | } |
364 | 0 | elng->extended_language = gf_strdup(code); |
365 | 0 | } |
366 | 0 | } |
367 | 0 | if (!movie->keep_utc) |
368 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
369 | 0 | return GF_OK; |
370 | 0 | } |
371 | | |
372 | | static GF_Err gf_isom_set_root_iod(GF_ISOFile *movie) |
373 | 0 | { |
374 | 0 | GF_IsomInitialObjectDescriptor *iod; |
375 | 0 | GF_IsomObjectDescriptor *od; |
376 | 0 | GF_Err e; |
377 | |
|
378 | 0 | e = gf_isom_insert_moov(movie); |
379 | 0 | if (e) return e; |
380 | 0 | if (!movie->moov->iods) { |
381 | 0 | AddMovieIOD(movie->moov, 1); |
382 | 0 | return GF_OK; |
383 | 0 | } |
384 | | //if OD, switch to IOD |
385 | 0 | if (movie->moov->iods->descriptor->tag == GF_ODF_ISOM_IOD_TAG) return GF_OK; |
386 | 0 | od = (GF_IsomObjectDescriptor *) movie->moov->iods->descriptor; |
387 | 0 | iod = (GF_IsomInitialObjectDescriptor*)gf_malloc(sizeof(GF_IsomInitialObjectDescriptor)); |
388 | 0 | if (!iod) return GF_OUT_OF_MEM; |
389 | | |
390 | 0 | memset(iod, 0, sizeof(GF_IsomInitialObjectDescriptor)); |
391 | |
|
392 | 0 | iod->ES_ID_IncDescriptors = od->ES_ID_IncDescriptors; |
393 | 0 | od->ES_ID_IncDescriptors = NULL; |
394 | | //not used in root OD |
395 | 0 | iod->ES_ID_RefDescriptors = NULL; |
396 | 0 | iod->extensionDescriptors = od->extensionDescriptors; |
397 | 0 | od->extensionDescriptors = NULL; |
398 | 0 | iod->IPMP_Descriptors = od->IPMP_Descriptors; |
399 | 0 | od->IPMP_Descriptors = NULL; |
400 | 0 | iod->objectDescriptorID = od->objectDescriptorID; |
401 | 0 | iod->OCIDescriptors = od->OCIDescriptors; |
402 | 0 | od->OCIDescriptors = NULL; |
403 | 0 | iod->tag = GF_ODF_ISOM_IOD_TAG; |
404 | 0 | iod->URLString = od->URLString; |
405 | 0 | od->URLString = NULL; |
406 | |
|
407 | 0 | gf_odf_desc_del((GF_Descriptor *) od); |
408 | 0 | movie->moov->iods->descriptor = (GF_Descriptor *)iod; |
409 | 0 | return GF_OK; |
410 | 0 | } |
411 | | |
412 | | GF_EXPORT |
413 | | GF_Err gf_isom_add_desc_to_root_od(GF_ISOFile *movie, const GF_Descriptor *theDesc) |
414 | 0 | { |
415 | 0 | GF_Err e; |
416 | 0 | GF_Descriptor *desc, *dupDesc; |
417 | |
|
418 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
419 | 0 | if (e) return e; |
420 | 0 | e = gf_isom_insert_moov(movie); |
421 | 0 | if (e) return e; |
422 | | |
423 | 0 | if (!movie->moov->iods) { |
424 | 0 | e = AddMovieIOD(movie->moov, 0); |
425 | 0 | if (e) return e; |
426 | 0 | } |
427 | 0 | if (theDesc->tag==GF_ODF_IPMP_TL_TAG) gf_isom_set_root_iod(movie); |
428 | |
|
429 | 0 | desc = movie->moov->iods->descriptor; |
430 | | //the type of desc is handled at the OD/IOD level, we'll be notified |
431 | | //if the desc is not allowed |
432 | 0 | switch (desc->tag) { |
433 | 0 | case GF_ODF_ISOM_IOD_TAG: |
434 | 0 | case GF_ODF_ISOM_OD_TAG: |
435 | | //duplicate the desc |
436 | 0 | e = gf_odf_desc_copy((GF_Descriptor *)theDesc, &dupDesc); |
437 | 0 | if (e) return e; |
438 | | //add it (MUST BE (I)OD level desc) |
439 | 0 | movie->LastError = gf_odf_desc_add_desc(desc, dupDesc); |
440 | 0 | if (movie->LastError) gf_odf_desc_del((GF_Descriptor *)dupDesc); |
441 | 0 | break; |
442 | 0 | default: |
443 | 0 | movie->LastError = GF_ISOM_INVALID_FILE; |
444 | 0 | break; |
445 | 0 | } |
446 | 0 | return movie->LastError; |
447 | 0 | } |
448 | | |
449 | | |
450 | | GF_EXPORT |
451 | | GF_Err gf_isom_set_timescale(GF_ISOFile *movie, u32 timeScale) |
452 | 0 | { |
453 | 0 | GF_TrackBox *trak; |
454 | 0 | u32 i; |
455 | 0 | GF_Err e; |
456 | 0 | if (!timeScale) return GF_BAD_PARAM; |
457 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
458 | 0 | if (e) return e; |
459 | 0 | e = gf_isom_insert_moov(movie); |
460 | 0 | if (e) return e; |
461 | | |
462 | 0 | if (movie->moov->mvhd->timeScale == timeScale) return GF_OK; |
463 | | |
464 | | /*rewrite all durations and edit lists*/ |
465 | 0 | movie->moov->mvhd->duration *= timeScale; |
466 | 0 | movie->moov->mvhd->duration /= movie->moov->mvhd->timeScale; |
467 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
468 | 0 | if (movie->moov->mvex && movie->moov->mvex->mehd) { |
469 | 0 | movie->moov->mvex->mehd->fragment_duration *= timeScale; |
470 | 0 | movie->moov->mvex->mehd->fragment_duration /= movie->moov->mvhd->timeScale; |
471 | 0 | } |
472 | 0 | #endif |
473 | |
|
474 | 0 | i=0; |
475 | 0 | while ((trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) { |
476 | 0 | trak->Header->duration *= timeScale; |
477 | 0 | trak->Header->duration /= movie->moov->mvhd->timeScale; |
478 | |
|
479 | 0 | if (trak->editBox && trak->editBox->editList) { |
480 | 0 | u32 j, count = gf_list_count(trak->editBox->editList->entryList); |
481 | 0 | for (j=0; j<count; j++) { |
482 | 0 | GF_EdtsEntry *ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, j); |
483 | 0 | ent->segmentDuration *= timeScale; |
484 | 0 | ent->segmentDuration /= movie->moov->mvhd->timeScale; |
485 | 0 | } |
486 | 0 | } |
487 | 0 | } |
488 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
489 | 0 | if (movie->moov->mvex && movie->moov->mvex->mehd) { |
490 | 0 | movie->moov->mvex->mehd->fragment_duration *= timeScale; |
491 | 0 | movie->moov->mvex->mehd->fragment_duration /= movie->moov->mvhd->timeScale; |
492 | 0 | } |
493 | 0 | #endif |
494 | 0 | movie->moov->mvhd->timeScale = timeScale; |
495 | 0 | movie->interleavingTime = timeScale; |
496 | 0 | return GF_OK; |
497 | 0 | } |
498 | | |
499 | | GF_EXPORT |
500 | | GF_Err gf_isom_set_vexu(GF_ISOFile *movie, u32 hero_eye) |
501 | 0 | { |
502 | 0 | GF_TrackBox *trak; |
503 | 0 | GF_SampleDescriptionBox* stsd; |
504 | 0 | GF_Box *box, *hvc1, *dvh1, *vexu; |
505 | 0 | GF_StereoViewBox *eyes; |
506 | 0 | GF_HeroStereoEyeDescriptionBox *hero; |
507 | 0 | u32 i = 0; |
508 | 0 | GF_Err e; |
509 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
510 | 0 | if (e) return e; |
511 | 0 | e = gf_isom_insert_moov(movie); |
512 | 0 | if (e) return e; |
513 | | |
514 | 0 | if (hero_eye >= 3) { |
515 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Failed to insert VEXU box: hero_eye_indicator >= 3 is reserved.\n")); |
516 | 0 | return GF_BAD_PARAM; |
517 | 0 | } |
518 | | |
519 | 0 | while ((trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) { |
520 | 0 | if (!gf_isom_is_video_handler_type(trak->Media->handler->handlerType)) { |
521 | 0 | continue; |
522 | 0 | } |
523 | | |
524 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
525 | 0 | hvc1 = gf_isom_box_find_child(stsd->child_boxes, GF_ISOM_BOX_TYPE_HVC1); |
526 | 0 | dvh1 = gf_isom_box_find_child(stsd->child_boxes, GF_ISOM_BOX_TYPE_DVH1); |
527 | 0 | box = hvc1 ? hvc1 : dvh1; |
528 | |
|
529 | 0 | if(!box) { |
530 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("No additional VEXU box is needed for Track %u: lack of dvh1/hvc1 boxes.\n", i)); |
531 | 0 | continue; |
532 | 0 | } |
533 | 0 | if (!gf_isom_box_find_child(box->child_boxes, GF_ISOM_BOX_TYPE_LHVC)) { |
534 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("No additional VEXU box is needed for Track %u: the video is monoscopic.\n", i)); |
535 | 0 | continue; |
536 | 0 | } |
537 | 0 | if (gf_isom_box_find_child(box->child_boxes, GF_ISOM_BOX_TYPE_VEXU)) { |
538 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Track %u already has VEXU box.\n", i)); |
539 | 0 | continue; |
540 | 0 | } |
541 | | |
542 | | // ISO Base Media File Format and Apple HEVC Stereo Video Version 0.9 |
543 | 0 | vexu = gf_isom_box_new_parent(&box->child_boxes, GF_ISOM_BOX_TYPE_VEXU); |
544 | 0 | if (!vexu) return GF_OUT_OF_MEM; |
545 | | |
546 | 0 | eyes = (GF_StereoViewBox*)gf_isom_box_new_parent(&vexu->child_boxes, GF_ISOM_BOX_TYPE_EYES); |
547 | 0 | if (!eyes) return GF_OUT_OF_MEM; |
548 | 0 | eyes->stri.has_left_eye_view = 1; |
549 | 0 | eyes->stri.has_right_eye_view = 1; |
550 | |
|
551 | 0 | hero = (GF_HeroStereoEyeDescriptionBox*)gf_isom_box_new_parent(&eyes->child_boxes, GF_ISOM_BOX_TYPE_HERO); |
552 | 0 | if (!hero) return GF_OUT_OF_MEM; |
553 | 0 | hero->hero_eye_indicator = hero_eye; |
554 | |
|
555 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("VEXU box is added to Track %u\n", i)); |
556 | 0 | } |
557 | 0 | return GF_OK; |
558 | 0 | } |
559 | | |
560 | | |
561 | | GF_EXPORT |
562 | | GF_Err gf_isom_set_pl_indication(GF_ISOFile *movie, GF_ISOProfileLevelType PL_Code, u8 ProfileLevel) |
563 | 0 | { |
564 | 0 | GF_IsomInitialObjectDescriptor *iod; |
565 | 0 | GF_Err e; |
566 | |
|
567 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
568 | 0 | if (e) return e; |
569 | | |
570 | 0 | e = gf_isom_set_root_iod(movie); |
571 | 0 | if (e) return e; |
572 | | |
573 | 0 | iod = (GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor; |
574 | |
|
575 | 0 | switch (PL_Code) { |
576 | 0 | case GF_ISOM_PL_AUDIO: |
577 | 0 | iod->audio_profileAndLevel = ProfileLevel; |
578 | 0 | break; |
579 | 0 | case GF_ISOM_PL_GRAPHICS: |
580 | 0 | iod->graphics_profileAndLevel = ProfileLevel; |
581 | 0 | break; |
582 | 0 | case GF_ISOM_PL_OD: |
583 | 0 | iod->OD_profileAndLevel = ProfileLevel; |
584 | 0 | break; |
585 | 0 | case GF_ISOM_PL_SCENE: |
586 | 0 | iod->scene_profileAndLevel = ProfileLevel; |
587 | 0 | break; |
588 | 0 | case GF_ISOM_PL_VISUAL: |
589 | 0 | iod->visual_profileAndLevel = ProfileLevel; |
590 | 0 | break; |
591 | 0 | case GF_ISOM_PL_INLINE: |
592 | 0 | iod->inlineProfileFlag = ProfileLevel ? 1 : 0; |
593 | 0 | break; |
594 | 0 | default: |
595 | 0 | break; |
596 | 0 | } |
597 | 0 | return GF_OK; |
598 | 0 | } |
599 | | |
600 | | GF_EXPORT |
601 | | GF_Err gf_isom_set_root_od_id(GF_ISOFile *movie, u32 OD_ID) |
602 | 0 | { |
603 | 0 | GF_Err e; |
604 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
605 | 0 | if (e) return e; |
606 | | |
607 | 0 | e = gf_isom_insert_moov(movie); |
608 | 0 | if (e) return e; |
609 | 0 | if (!movie->moov->iods) { |
610 | 0 | e = AddMovieIOD(movie->moov, 0); |
611 | 0 | if (e) return e; |
612 | 0 | } |
613 | | |
614 | 0 | switch (movie->moov->iods->descriptor->tag) { |
615 | 0 | case GF_ODF_ISOM_OD_TAG: |
616 | 0 | ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID; |
617 | 0 | break; |
618 | 0 | case GF_ODF_ISOM_IOD_TAG: |
619 | 0 | ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID; |
620 | 0 | break; |
621 | 0 | default: |
622 | 0 | return GF_ISOM_INVALID_FILE; |
623 | 0 | } |
624 | 0 | return GF_OK; |
625 | 0 | } |
626 | | |
627 | | GF_EXPORT |
628 | | GF_Err gf_isom_set_root_od_url(GF_ISOFile *movie, const char *url_string) |
629 | 0 | { |
630 | 0 | GF_Err e; |
631 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
632 | 0 | if (e) return e; |
633 | 0 | e = gf_isom_insert_moov(movie); |
634 | 0 | if (e) return e; |
635 | | |
636 | 0 | if (!movie->moov->iods) { |
637 | 0 | e = AddMovieIOD(movie->moov, 0); |
638 | 0 | if (e) return e; |
639 | 0 | } |
640 | | |
641 | 0 | switch (movie->moov->iods->descriptor->tag) { |
642 | 0 | case GF_ODF_ISOM_OD_TAG: |
643 | 0 | if (((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString) gf_free(((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString); |
644 | 0 | ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? gf_strdup(url_string) : NULL; |
645 | 0 | break; |
646 | 0 | case GF_ODF_ISOM_IOD_TAG: |
647 | 0 | if (((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString) gf_free(((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString); |
648 | 0 | ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? gf_strdup(url_string) : NULL; |
649 | 0 | break; |
650 | 0 | default: |
651 | 0 | return GF_ISOM_INVALID_FILE; |
652 | 0 | } |
653 | 0 | return GF_OK; |
654 | 0 | } |
655 | | |
656 | | GF_EXPORT |
657 | | GF_ISOTrackID gf_isom_get_last_created_track_id(GF_ISOFile *movie) |
658 | 0 | { |
659 | 0 | return movie ? movie->last_created_track_id : 0; |
660 | 0 | } |
661 | | |
662 | | |
663 | | GF_EXPORT |
664 | | GF_Err gf_isom_load_extra_boxes(GF_ISOFile *movie, u8 *moov_boxes, u32 moov_boxes_size, Bool udta_only) |
665 | 0 | { |
666 | 0 | GF_BitStream *bs; |
667 | |
|
668 | 0 | GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
669 | 0 | if (e) return e; |
670 | 0 | e = gf_isom_insert_moov(movie); |
671 | 0 | if (e) return e; |
672 | | |
673 | 0 | bs = gf_bs_new(moov_boxes, moov_boxes_size, GF_BITSTREAM_READ); |
674 | | |
675 | | //we may have terminators in some QT files (4 bytes set to 0 ...) |
676 | 0 | while (gf_bs_available(bs) >= 8) { |
677 | 0 | GF_Box *a_box; |
678 | 0 | e = gf_isom_box_parse_ex((GF_Box**)&a_box, bs, GF_ISOM_BOX_TYPE_MOOV, GF_FALSE, 0); |
679 | 0 | if (e || !a_box) goto exit; |
680 | | |
681 | 0 | if (a_box->type == GF_ISOM_BOX_TYPE_UDTA) { |
682 | 0 | if (movie->moov->udta) gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box*)movie->moov->udta); |
683 | 0 | movie->moov->udta = (GF_UserDataBox*) a_box; |
684 | |
|
685 | 0 | if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new(); |
686 | 0 | gf_list_add(movie->moov->child_boxes, a_box); |
687 | |
|
688 | 0 | } else if (!udta_only && (a_box->type!=GF_ISOM_BOX_TYPE_PSSH) ) { |
689 | 0 | if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new(); |
690 | 0 | gf_list_add(movie->moov->child_boxes, a_box); |
691 | 0 | } else { |
692 | 0 | gf_isom_box_del(a_box); |
693 | 0 | } |
694 | 0 | } |
695 | 0 | exit: |
696 | 0 | gf_bs_del(bs); |
697 | 0 | return e; |
698 | 0 | } |
699 | | |
700 | | GF_EXPORT |
701 | | u32 gf_isom_new_track_from_template(GF_ISOFile *movie, GF_ISOTrackID trakID, u32 MediaType, u32 TimeScale, u8 *tk_box, u32 tk_box_size, Bool udta_only) |
702 | 0 | { |
703 | 0 | GF_Err e; |
704 | 0 | u64 now; |
705 | 0 | u8 isHint; |
706 | 0 | GF_TrackBox *trak; |
707 | 0 | GF_TrackHeaderBox *tkhd; |
708 | 0 | GF_MediaBox *mdia; |
709 | 0 | GF_UserDataBox *udta = NULL; |
710 | |
|
711 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
712 | 0 | if (e) { |
713 | 0 | gf_isom_set_last_error(movie, e); |
714 | 0 | return 0; |
715 | 0 | } |
716 | 0 | e = gf_isom_insert_moov(movie); |
717 | 0 | if (e) return e; |
718 | | |
719 | | |
720 | 0 | isHint = 0; |
721 | | //we're creating a hint track... it's the same, but mode HAS TO BE EDIT |
722 | 0 | if (MediaType == GF_ISOM_MEDIA_HINT) { |
723 | | // if (movie->openMode != GF_ISOM_OPEN_EDIT) return 0; |
724 | 0 | isHint = 1; |
725 | 0 | } |
726 | |
|
727 | 0 | mdia = NULL; |
728 | 0 | tkhd = NULL; |
729 | 0 | trak = NULL; |
730 | 0 | if (trakID) { |
731 | | //check if we are in ES_ID boundaries |
732 | 0 | if (!isHint && (trakID > 0xFFFF)) { |
733 | 0 | gf_isom_set_last_error(movie, GF_BAD_PARAM); |
734 | 0 | return 0; |
735 | 0 | } |
736 | | //here we should look for available IDs ... |
737 | 0 | if (!RequestTrack(movie->moov, trakID)) return 0; |
738 | 0 | } else { |
739 | 0 | trakID = movie->moov->mvhd->nextTrackID; |
740 | 0 | if (!trakID) trakID = 1; |
741 | | /*ESIDs are on 16 bits*/ |
742 | 0 | if (! isHint && (trakID > 0xFFFF)) trakID = 1; |
743 | |
|
744 | 0 | while (1) { |
745 | 0 | if (RequestTrack(movie->moov, trakID)) break; |
746 | 0 | trakID += 1; |
747 | 0 | if (trakID == 0xFFFFFFFF) break; |
748 | 0 | } |
749 | 0 | if (trakID == 0xFFFFFFFF) { |
750 | 0 | gf_isom_set_last_error(movie, GF_BAD_PARAM); |
751 | 0 | return 0; |
752 | 0 | } |
753 | 0 | if (! isHint && (trakID > 0xFFFF)) { |
754 | 0 | gf_isom_set_last_error(movie, GF_BAD_PARAM); |
755 | 0 | return 0; |
756 | 0 | } |
757 | 0 | } |
758 | | |
759 | 0 | if (tk_box) { |
760 | 0 | GF_BitStream *bs = gf_bs_new(tk_box, tk_box_size, GF_BITSTREAM_READ); |
761 | 0 | gf_bs_set_cookie(bs, GF_ISOM_BS_COOKIE_NO_LOGS|GF_ISOM_BS_COOKIE_CLONE_TRACK); |
762 | |
|
763 | 0 | e = gf_isom_box_parse_ex((GF_Box**)&trak, bs, GF_ISOM_BOX_TYPE_MOOV, GF_FALSE, 0); |
764 | 0 | gf_bs_del(bs); |
765 | 0 | if (e) trak = NULL; |
766 | 0 | else if (udta_only) { |
767 | 0 | udta = trak->udta; |
768 | 0 | trak->udta = NULL; |
769 | 0 | gf_list_del_item(trak->child_boxes, udta); |
770 | 0 | gf_isom_box_del((GF_Box*)trak); |
771 | 0 | trak = NULL; |
772 | 0 | } else { |
773 | 0 | Bool tpl_ok = GF_TRUE; |
774 | 0 | if (!trak->Header || !trak->Media || !trak->Media->handler || !trak->Media->mediaHeader || !trak->Media->information) tpl_ok = GF_FALSE; |
775 | | |
776 | 0 | else { |
777 | 0 | if (!MediaType) MediaType = trak->Media->handler->handlerType; |
778 | 0 | e = NewMedia(&trak->Media, MediaType, TimeScale); |
779 | 0 | if (e) tpl_ok = GF_FALSE; |
780 | 0 | } |
781 | 0 | if (!tpl_ok) { |
782 | 0 | udta = trak->udta; |
783 | 0 | trak->udta = NULL; |
784 | 0 | gf_isom_box_del((GF_Box*)trak); |
785 | 0 | } |
786 | 0 | } |
787 | 0 | } |
788 | 0 | now = gf_isom_get_mp4time(); |
789 | 0 | if (!trak) { |
790 | | //OK, now create a track... |
791 | 0 | trak = (GF_TrackBox *) gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_TRAK); |
792 | 0 | if (!trak) { |
793 | 0 | gf_isom_set_last_error(movie, GF_OUT_OF_MEM); |
794 | 0 | return 0; |
795 | 0 | } |
796 | 0 | tkhd = (GF_TrackHeaderBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TKHD); |
797 | 0 | if (!tkhd) { |
798 | 0 | gf_isom_set_last_error(movie, GF_OUT_OF_MEM); |
799 | 0 | return 0; |
800 | 0 | } |
801 | | |
802 | | //OK, set up the media trak |
803 | 0 | e = NewMedia(&mdia, MediaType, TimeScale); |
804 | 0 | if (e) { |
805 | 0 | gf_isom_box_del((GF_Box *)mdia); |
806 | 0 | return 0; |
807 | 0 | } |
808 | 0 | gf_assert(trak->child_boxes); |
809 | 0 | gf_list_add(trak->child_boxes, mdia); |
810 | | |
811 | | //OK, add this media to our track |
812 | 0 | mdia->mediaTrack = trak; |
813 | |
|
814 | 0 | e = trak_on_child_box((GF_Box*)trak, (GF_Box *) tkhd, GF_FALSE); |
815 | 0 | if (e) goto err_exit; |
816 | 0 | e = trak_on_child_box((GF_Box*)trak, (GF_Box *) mdia, GF_FALSE); |
817 | 0 | if (e) goto err_exit; |
818 | 0 | tkhd->trackID = trakID; |
819 | |
|
820 | 0 | if (gf_sys_is_test_mode() ) { |
821 | 0 | tkhd->creationTime = 0; |
822 | 0 | mdia->mediaHeader->creationTime = 0; |
823 | 0 | } else { |
824 | 0 | tkhd->creationTime = now; |
825 | 0 | mdia->mediaHeader->creationTime = now; |
826 | 0 | } |
827 | |
|
828 | 0 | } else { |
829 | 0 | tkhd = trak->Header; |
830 | 0 | tkhd->trackID = trakID; |
831 | 0 | mdia = trak->Media; |
832 | 0 | mdia->mediaTrack = trak; |
833 | 0 | mdia->mediaHeader->timeScale = TimeScale; |
834 | 0 | if (mdia->handler->handlerType != MediaType) { |
835 | 0 | mdia->handler->handlerType = MediaType; |
836 | 0 | tkhd->width = 0; |
837 | 0 | tkhd->height = 0; |
838 | 0 | tkhd->volume = 0; |
839 | 0 | } else { |
840 | 0 | MediaType = 0; |
841 | 0 | } |
842 | 0 | trak->Header->duration = 0; |
843 | 0 | mdia->mediaHeader->duration = 0; |
844 | |
|
845 | 0 | if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new(); |
846 | 0 | gf_list_add(movie->moov->child_boxes, trak); |
847 | 0 | } |
848 | 0 | if (MediaType) { |
849 | | //some default properties for Audio, Visual or private tracks |
850 | 0 | switch (MediaType) { |
851 | 0 | case GF_ISOM_MEDIA_VISUAL: |
852 | 0 | case GF_ISOM_MEDIA_AUXV: |
853 | 0 | case GF_ISOM_MEDIA_PICT: |
854 | 0 | case GF_ISOM_MEDIA_SCENE: |
855 | 0 | case GF_ISOM_MEDIA_TEXT: |
856 | 0 | case GF_ISOM_MEDIA_SUBT: |
857 | | /*320-240 pix in 16.16*/ |
858 | 0 | tkhd->width = 0x01400000; |
859 | 0 | tkhd->height = 0x00F00000; |
860 | 0 | break; |
861 | 0 | case GF_ISOM_MEDIA_AUDIO: |
862 | 0 | tkhd->volume = 0x0100; |
863 | 0 | break; |
864 | 0 | } |
865 | 0 | } |
866 | 0 | movie->last_created_track_id = tkhd->trackID; |
867 | |
|
868 | 0 | if (!movie->keep_utc && !gf_sys_is_test_mode() ) { |
869 | 0 | tkhd->modificationTime = now; |
870 | 0 | mdia->mediaHeader->modificationTime = now; |
871 | 0 | } |
872 | | |
873 | | //OK, add our trak |
874 | 0 | e = moov_on_child_box((GF_Box*)movie->moov, (GF_Box *)trak, GF_FALSE); |
875 | 0 | if (e) goto err_exit; |
876 | | //set the next track ID available |
877 | 0 | if (trakID >= movie->moov->mvhd->nextTrackID) |
878 | 0 | movie->moov->mvhd->nextTrackID = trakID+1; |
879 | |
|
880 | 0 | if (udta) { |
881 | 0 | trak->udta = udta; |
882 | 0 | gf_list_add(trak->child_boxes, udta); |
883 | 0 | } |
884 | | |
885 | | //and return our track number |
886 | 0 | return gf_isom_get_track_by_id(movie, trakID); |
887 | | |
888 | 0 | err_exit: |
889 | | //tkhd is registered with track and will be destroyed there |
890 | 0 | if (trak) gf_isom_box_del((GF_Box *)trak); |
891 | 0 | if (mdia) gf_isom_box_del((GF_Box *)mdia); |
892 | 0 | return 0; |
893 | 0 | } |
894 | | |
895 | | GF_EXPORT |
896 | | GF_Err gf_isom_set_track_stsd_templates(GF_ISOFile *movie, u32 trackNumber, u8 *stsd_data, u32 stsd_data_size) |
897 | 0 | { |
898 | 0 | GF_TrackBox *trak; |
899 | 0 | GF_Err e; |
900 | 0 | GF_SampleDescriptionBox *stsd=NULL; |
901 | 0 | GF_List *tmp; |
902 | |
|
903 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
904 | 0 | if (e) return e; |
905 | | |
906 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
907 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
908 | 0 | if (gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes)) |
909 | 0 | return GF_BAD_PARAM; |
910 | | |
911 | 0 | GF_BitStream *bs = gf_bs_new(stsd_data, stsd_data_size, GF_BITSTREAM_READ); |
912 | 0 | e = gf_isom_box_parse_ex((GF_Box **) &stsd, bs, GF_ISOM_BOX_TYPE_STBL, GF_FALSE, 0); |
913 | 0 | gf_bs_del(bs); |
914 | 0 | if (!e && (stsd->type==GF_ISOM_BOX_TYPE_STSD)) { |
915 | 0 | tmp = trak->Media->information->sampleTable->SampleDescription->child_boxes; |
916 | 0 | trak->Media->information->sampleTable->SampleDescription->child_boxes = stsd->child_boxes; |
917 | 0 | stsd->child_boxes = tmp; |
918 | 0 | } |
919 | 0 | if (stsd) gf_isom_box_del((GF_Box*)stsd); |
920 | 0 | return e; |
921 | 0 | } |
922 | | |
923 | | GF_EXPORT |
924 | | u32 gf_isom_new_track(GF_ISOFile *movie, GF_ISOTrackID trakID, u32 MediaType, u32 TimeScale) |
925 | 0 | { |
926 | 0 | return gf_isom_new_track_from_template(movie, trakID, MediaType, TimeScale, NULL, 0, GF_FALSE); |
927 | 0 | } |
928 | | |
929 | | GF_EXPORT |
930 | | u32 gf_isom_new_external_track(GF_ISOFile *movie, GF_ISOTrackID trakID, GF_ISOTrackID refTrakID, u32 MediaType, u32 TimeScale, const char *uri) |
931 | 0 | { |
932 | 0 | GF_TrackBox *trak; |
933 | 0 | if (!uri) { |
934 | 0 | gf_isom_set_last_error(movie, GF_BAD_PARAM); |
935 | 0 | return 0; |
936 | 0 | } |
937 | 0 | u32 track_num = gf_isom_new_track_from_template(movie, trakID, MediaType, TimeScale, NULL, 0, GF_FALSE); |
938 | 0 | if (!track_num) return GF_FALSE; |
939 | 0 | trak = gf_isom_get_track_box(movie, track_num); |
940 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
941 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box*)trak->Media); |
942 | 0 | trak->Media = NULL; |
943 | |
|
944 | 0 | trak->type = GF_ISOM_BOX_TYPE_EXTK; |
945 | 0 | trak->extl = (GF_ExternalTrackLocationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_EXTL); |
946 | 0 | gf_list_add(trak->child_boxes, trak->extl); |
947 | 0 | trak->extl->referenced_track_ID = refTrakID; |
948 | 0 | trak->extl->referenced_handler_type = MediaType; |
949 | 0 | trak->extl->media_timescale = TimeScale; |
950 | 0 | trak->extl->location = gf_strdup(uri); |
951 | 0 | trak->Header->flags = GF_ISOM_TK_IN_MOVIE | GF_ISOM_TK_ENABLED; |
952 | 0 | trak->Header->duration = 0xFFFFFFFF; |
953 | 0 | return track_num; |
954 | 0 | } |
955 | | |
956 | | GF_EXPORT |
957 | | GF_Err gf_isom_force_track_duration(GF_ISOFile *movie, u32 trackNumber, u64 dur) |
958 | 0 | { |
959 | 0 | GF_TrackBox *trak; |
960 | 0 | GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
961 | 0 | if (e) return e; |
962 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
963 | 0 | if (!trak || !trak->Header) return GF_BAD_PARAM; |
964 | 0 | trak->Header->duration = dur; |
965 | 0 | return GF_OK; |
966 | 0 | } |
967 | | |
968 | | |
969 | | GF_EXPORT |
970 | | GF_Err gf_isom_remove_stream_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex) |
971 | 0 | { |
972 | 0 | GF_TrackBox *trak; |
973 | 0 | GF_Err e; |
974 | 0 | GF_SampleEntryBox *entry; |
975 | |
|
976 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
977 | 0 | if (e) return e; |
978 | | |
979 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
980 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
981 | | |
982 | 0 | if (!movie->keep_utc) |
983 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
984 | |
|
985 | 0 | entry = (GF_SampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1); |
986 | 0 | if (!entry) return GF_BAD_PARAM; |
987 | 0 | gf_list_rem(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1); |
988 | 0 | gf_isom_box_del((GF_Box *)entry); |
989 | 0 | return GF_OK; |
990 | 0 | } |
991 | | |
992 | | //Create a new StreamDescription in the file. The URL and URN are used to describe external media |
993 | | GF_EXPORT |
994 | | GF_Err gf_isom_new_mpeg4_description(GF_ISOFile *movie, |
995 | | u32 trackNumber, |
996 | | const GF_ESD *esd, |
997 | | const char *URLname, |
998 | | const char *URNname, |
999 | | u32 *outDescriptionIndex) |
1000 | 0 | { |
1001 | 0 | GF_TrackBox *trak; |
1002 | 0 | GF_Err e; |
1003 | 0 | u32 dataRefIndex; |
1004 | 0 | GF_ESD *new_esd; |
1005 | |
|
1006 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1007 | 0 | if (e) return e; |
1008 | | |
1009 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1010 | 0 | if (!trak || !trak->Media || |
1011 | 0 | !esd || !esd->decoderConfig || |
1012 | 0 | !esd->slConfig) return GF_BAD_PARAM; |
1013 | | |
1014 | | //get or create the data ref |
1015 | 0 | e = Media_FindDataRef(trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex); |
1016 | 0 | if (e) return e; |
1017 | 0 | if (!dataRefIndex) { |
1018 | 0 | e = Media_CreateDataRef(movie, trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex); |
1019 | 0 | if (e) return e; |
1020 | 0 | } |
1021 | | //duplicate our desc |
1022 | 0 | e = gf_odf_desc_copy((GF_Descriptor *)esd, (GF_Descriptor **)&new_esd); |
1023 | 0 | if (e) return e; |
1024 | 0 | if (!movie->keep_utc) |
1025 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1026 | 0 | e = Track_SetStreamDescriptor(trak, 0, dataRefIndex, new_esd, outDescriptionIndex); |
1027 | 0 | if (e) { |
1028 | 0 | gf_odf_desc_del((GF_Descriptor *)new_esd); |
1029 | 0 | return e; |
1030 | 0 | } |
1031 | 0 | return e; |
1032 | 0 | } |
1033 | | |
1034 | | GF_Err gf_isom_flush_chunk(GF_TrackBox *trak, Bool is_final) |
1035 | 0 | { |
1036 | 0 | GF_Err e; |
1037 | 0 | u64 data_offset; |
1038 | 0 | u32 sample_number; |
1039 | 0 | u8 *chunk_data; |
1040 | 0 | u32 chunk_size, chunk_alloc; |
1041 | 0 | if (!trak->chunk_cache) return GF_OK; |
1042 | | |
1043 | 0 | gf_bs_get_content_no_truncate(trak->chunk_cache, &chunk_data, &chunk_size, &chunk_alloc); |
1044 | |
|
1045 | 0 | data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler); |
1046 | |
|
1047 | 0 | e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, chunk_data, chunk_size); |
1048 | 0 | if (e) return e; |
1049 | | |
1050 | 0 | sample_number = 1 + trak->Media->information->sampleTable->SampleSize->sampleCount; |
1051 | 0 | sample_number -= trak->nb_samples_in_cache; |
1052 | |
|
1053 | 0 | e = stbl_AddChunkOffset(trak->Media, sample_number, trak->chunk_stsd_idx, data_offset, trak->nb_samples_in_cache); |
1054 | |
|
1055 | 0 | if (is_final) { |
1056 | 0 | gf_free(chunk_data); |
1057 | 0 | gf_bs_del(trak->chunk_cache); |
1058 | 0 | trak->chunk_cache = NULL; |
1059 | 0 | } else { |
1060 | 0 | gf_bs_reassign_buffer(trak->chunk_cache, chunk_data, chunk_alloc); |
1061 | 0 | } |
1062 | 0 | return e; |
1063 | 0 | } |
1064 | | |
1065 | | static GF_Err trak_add_sample(GF_ISOFile *movie, GF_TrackBox *trak, const GF_ISOSample *sample, u32 descIndex, u64 data_offset, u32 syncShadowSampleNum) |
1066 | 0 | { |
1067 | 0 | Bool skip_data = GF_FALSE; |
1068 | 0 | GF_Err e; |
1069 | | |
1070 | | //faststart mode with interleaving time, cache data until we have a full chunk |
1071 | 0 | if ((movie->storageMode==GF_ISOM_STORE_FASTSTART) && movie->interleavingTime) { |
1072 | 0 | Bool flush_chunk = GF_FALSE; |
1073 | 0 | u64 stime = sample->DTS; |
1074 | 0 | stime *= movie->moov->mvhd->timeScale; |
1075 | 0 | stime /= trak->Media->mediaHeader->timeScale; |
1076 | |
|
1077 | 0 | if (stime - trak->first_dts_chunk > movie->interleavingTime) |
1078 | 0 | flush_chunk = GF_TRUE; |
1079 | |
|
1080 | 0 | if (movie->next_flush_chunk_time < stime) |
1081 | 0 | flush_chunk = GF_TRUE; |
1082 | |
|
1083 | 0 | if (trak->chunk_stsd_idx != descIndex) |
1084 | 0 | flush_chunk = GF_TRUE; |
1085 | |
|
1086 | 0 | if (trak->Media->information->sampleTable->MaxChunkSize && trak->Media->information->sampleTable->MaxChunkSize < trak->chunk_cache_size + sample->dataLength) |
1087 | 0 | flush_chunk = GF_TRUE; |
1088 | |
|
1089 | 0 | if (flush_chunk) { |
1090 | 0 | movie->next_flush_chunk_time = stime + movie->interleavingTime; |
1091 | 0 | if (trak->chunk_cache) { |
1092 | 0 | e = gf_isom_flush_chunk(trak, GF_FALSE); |
1093 | 0 | if (e) return e; |
1094 | 0 | } |
1095 | 0 | trak->nb_samples_in_cache = 0; |
1096 | 0 | trak->chunk_cache_size = 0; |
1097 | 0 | trak->first_dts_chunk = stime; |
1098 | 0 | } |
1099 | 0 | if (!trak->chunk_cache) |
1100 | 0 | trak->chunk_cache = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
1101 | 0 | gf_bs_write_data(trak->chunk_cache, sample->data, sample->dataLength); |
1102 | 0 | trak->nb_samples_in_cache += sample->nb_pack ? sample->nb_pack : 1; |
1103 | 0 | trak->chunk_cache_size += sample->dataLength; |
1104 | 0 | trak->chunk_stsd_idx = descIndex; |
1105 | |
|
1106 | 0 | skip_data = GF_TRUE; |
1107 | 0 | } |
1108 | | |
1109 | 0 | e = Media_AddSample(trak->Media, data_offset, sample, descIndex, syncShadowSampleNum); |
1110 | 0 | if (e) return e; |
1111 | | |
1112 | 0 | if (!skip_data && sample->dataLength) { |
1113 | 0 | e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, sample->data, sample->dataLength); |
1114 | 0 | if (e) return e; |
1115 | 0 | } |
1116 | | |
1117 | 0 | return GF_OK; |
1118 | 0 | } |
1119 | | |
1120 | | //Add samples to a track. Use streamDescriptionIndex to specify the desired stream (if several) |
1121 | | GF_EXPORT |
1122 | | GF_Err gf_isom_add_sample(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, const GF_ISOSample *sample) |
1123 | 0 | { |
1124 | 0 | GF_Err e; |
1125 | 0 | GF_TrackBox *trak; |
1126 | 0 | GF_SampleEntryBox *entry; |
1127 | 0 | u32 dataRefIndex; |
1128 | 0 | u64 data_offset; |
1129 | 0 | u32 descIndex; |
1130 | 0 | GF_DataEntryURLBox *Dentry; |
1131 | |
|
1132 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1133 | 0 | if (e) return e; |
1134 | | |
1135 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1136 | 0 | if (!trak) return GF_BAD_PARAM; |
1137 | | |
1138 | 0 | e = FlushCaptureMode(movie); |
1139 | 0 | if (e) return e; |
1140 | | |
1141 | 0 | e = unpack_track(trak); |
1142 | 0 | if (e) return e; |
1143 | | |
1144 | | //OK, add the sample |
1145 | | //1- Get the streamDescriptionIndex and dataRefIndex |
1146 | | //not specified, get the latest used... |
1147 | 0 | descIndex = StreamDescriptionIndex; |
1148 | 0 | if (!StreamDescriptionIndex) { |
1149 | 0 | descIndex = trak->Media->information->sampleTable->currentEntryIndex; |
1150 | 0 | } |
1151 | 0 | e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); |
1152 | 0 | if (e) return e; |
1153 | 0 | if (!entry || !dataRefIndex) return GF_BAD_PARAM; |
1154 | | //set the current to this one |
1155 | 0 | trak->Media->information->sampleTable->currentEntryIndex = descIndex; |
1156 | | |
1157 | | |
1158 | | //get this dataRef and return false if not self contained |
1159 | 0 | Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1); |
1160 | 0 | if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM; |
1161 | | |
1162 | | //Open our data map. We are adding stuff, so use EDIT |
1163 | 0 | e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1); |
1164 | 0 | if (e) return e; |
1165 | | |
1166 | | //Get the offset... |
1167 | 0 | data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler); |
1168 | | |
1169 | | /*rewrite OD frame*/ |
1170 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { |
1171 | 0 | GF_ISOSample *od_sample = NULL; |
1172 | |
|
1173 | 0 | e = Media_ParseODFrame(trak->Media, sample, &od_sample); |
1174 | 0 | if (e) return e; |
1175 | | |
1176 | 0 | e = trak_add_sample(movie, trak, od_sample, descIndex, data_offset, 0); |
1177 | |
|
1178 | 0 | if (od_sample) |
1179 | 0 | gf_isom_sample_del(&od_sample); |
1180 | 0 | } else { |
1181 | 0 | e = trak_add_sample(movie, trak, sample, descIndex, data_offset, 0); |
1182 | 0 | } |
1183 | 0 | if (e) return e; |
1184 | | |
1185 | 0 | if (!movie->keep_utc) |
1186 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1187 | | |
1188 | | //update media duration |
1189 | 0 | if ((s64)sample->DTS + sample->CTS_Offset>=0) { |
1190 | 0 | GF_TimeToSampleBox *stts = trak->Media->information->sampleTable->TimeToSample; |
1191 | 0 | u64 dur = sample->DTS + sample->CTS_Offset; |
1192 | 0 | dur += stts->entries[stts->nb_entries-1].sampleDelta; |
1193 | |
|
1194 | 0 | if (dur > trak->Media->mediaHeader->duration) { |
1195 | 0 | trak->Media->mediaHeader->duration = dur; |
1196 | 0 | } |
1197 | 0 | } |
1198 | | //do not update track duration yet, this is done on close |
1199 | 0 | return GF_OK; |
1200 | 0 | } |
1201 | | |
1202 | | GF_EXPORT |
1203 | | GF_Err gf_isom_add_sample_shadow(GF_ISOFile *movie, u32 trackNumber, GF_ISOSample *sample) |
1204 | 0 | { |
1205 | 0 | GF_Err e; |
1206 | 0 | GF_TrackBox *trak; |
1207 | 0 | GF_ISOSample *prev; |
1208 | 0 | GF_SampleEntryBox *entry; |
1209 | 0 | u32 dataRefIndex; |
1210 | 0 | u64 data_offset; |
1211 | 0 | u32 descIndex; |
1212 | 0 | u32 sampleNum, prevSampleNum; |
1213 | 0 | GF_DataEntryURLBox *Dentry; |
1214 | 0 | Bool offset_times = GF_FALSE; |
1215 | |
|
1216 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1217 | 0 | if (e) return e; |
1218 | | |
1219 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1220 | 0 | if (!trak || !sample) return GF_BAD_PARAM; |
1221 | | |
1222 | 0 | e = FlushCaptureMode(movie); |
1223 | 0 | if (e) return e; |
1224 | | |
1225 | 0 | e = unpack_track(trak); |
1226 | 0 | if (e) return e; |
1227 | | |
1228 | 0 | e = stbl_findEntryForTime(trak->Media->information->sampleTable, sample->DTS, 0, &sampleNum, &prevSampleNum); |
1229 | 0 | if (e) return e; |
1230 | | /*we need the EXACT match*/ |
1231 | 0 | if (!sampleNum) return GF_BAD_PARAM; |
1232 | | |
1233 | 0 | prev = gf_isom_get_sample_info(movie, trackNumber, sampleNum, &descIndex, NULL); |
1234 | 0 | if (!prev) return gf_isom_last_error(movie); |
1235 | | /*for conformance*/ |
1236 | 0 | if (sample->DTS==prev->DTS) offset_times = GF_TRUE; |
1237 | 0 | gf_isom_sample_del(&prev); |
1238 | |
|
1239 | 0 | e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); |
1240 | 0 | if (e) return e; |
1241 | 0 | if (!entry || !dataRefIndex) return GF_BAD_PARAM; |
1242 | 0 | trak->Media->information->sampleTable->currentEntryIndex = descIndex; |
1243 | | |
1244 | | //get this dataRef and return false if not self contained |
1245 | 0 | Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1); |
1246 | 0 | if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM; |
1247 | | |
1248 | | //Open our data map. We are adding stuff, so use EDIT |
1249 | 0 | e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1); |
1250 | 0 | if (e) return e; |
1251 | | |
1252 | 0 | data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler); |
1253 | 0 | if (offset_times) sample->DTS += 1; |
1254 | | |
1255 | | /*REWRITE ANY OD STUFF*/ |
1256 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { |
1257 | 0 | GF_ISOSample *od_sample = NULL; |
1258 | 0 | e = Media_ParseODFrame(trak->Media, sample, &od_sample); |
1259 | 0 | if (e) return e; |
1260 | | |
1261 | 0 | e = trak_add_sample(movie, trak, od_sample, descIndex, data_offset, sampleNum); |
1262 | 0 | if (od_sample) |
1263 | 0 | gf_isom_sample_del(&od_sample); |
1264 | 0 | } else { |
1265 | 0 | e = trak_add_sample(movie, trak, sample, descIndex, data_offset, sampleNum); |
1266 | 0 | } |
1267 | 0 | if (e) return e; |
1268 | 0 | if (offset_times) sample->DTS -= 1; |
1269 | | |
1270 | | //OK, update duration |
1271 | 0 | e = Media_SetDuration(trak); |
1272 | 0 | if (e) return e; |
1273 | 0 | if (!movie->keep_utc) |
1274 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1275 | 0 | return SetTrackDuration(trak); |
1276 | 0 | } |
1277 | | |
1278 | | GF_EXPORT |
1279 | | GF_Err gf_isom_append_sample_data(GF_ISOFile *movie, u32 trackNumber, u8 *data, u32 data_size) |
1280 | 0 | { |
1281 | 0 | GF_Err e; |
1282 | 0 | GF_TrackBox *trak; |
1283 | 0 | GF_SampleEntryBox *entry; |
1284 | 0 | u32 dataRefIndex; |
1285 | 0 | u32 descIndex; |
1286 | 0 | GF_DataEntryURLBox *Dentry; |
1287 | |
|
1288 | 0 | if (!data_size) return GF_OK; |
1289 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1290 | 0 | if (e) return e; |
1291 | | |
1292 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1293 | 0 | if (!trak) return GF_BAD_PARAM; |
1294 | | |
1295 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) return GF_BAD_PARAM; |
1296 | | |
1297 | | //OK, add the sample |
1298 | 0 | descIndex = trak->Media->information->sampleTable->currentEntryIndex; |
1299 | |
|
1300 | 0 | e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); |
1301 | 0 | if (e) return e; |
1302 | 0 | if (!entry || !dataRefIndex) return GF_BAD_PARAM; |
1303 | | |
1304 | | //get this dataRef and return false if not self contained |
1305 | 0 | Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1); |
1306 | 0 | if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM; |
1307 | | |
1308 | | //Open our data map. We are adding stuff, so use EDIT |
1309 | 0 | e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1); |
1310 | 0 | if (e) return e; |
1311 | | |
1312 | | //add the media data |
1313 | 0 | if (trak->chunk_cache) { |
1314 | 0 | gf_bs_write_data(trak->chunk_cache, data, data_size); |
1315 | 0 | trak->chunk_cache_size += data_size; |
1316 | 0 | } else { |
1317 | 0 | e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, data, data_size); |
1318 | 0 | if (e) return e; |
1319 | 0 | } |
1320 | | //update data size |
1321 | 0 | return stbl_SampleSizeAppend(trak->Media->information->sampleTable->SampleSize, data_size); |
1322 | 0 | } |
1323 | | |
1324 | | |
1325 | | //Add sample reference to a track. The SampleOffset is the offset of the data in the referenced file |
1326 | | //you must have created a StreamDescription with URL or URN specifying your referenced file |
1327 | | //the data offset specifies the beginning of the chunk |
1328 | | //Use streamDescriptionIndex to specify the desired stream (if several) |
1329 | | GF_EXPORT |
1330 | | GF_Err gf_isom_add_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_ISOSample *sample, u64 dataOffset) |
1331 | 0 | { |
1332 | 0 | GF_TrackBox *trak; |
1333 | 0 | GF_SampleEntryBox *entry; |
1334 | 0 | u32 dataRefIndex; |
1335 | 0 | u32 descIndex; |
1336 | 0 | GF_DataEntryURLBox *Dentry; |
1337 | 0 | GF_Err e; |
1338 | |
|
1339 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1340 | 0 | if (e) return e; |
1341 | | |
1342 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1343 | 0 | if (!trak) return GF_BAD_PARAM; |
1344 | | |
1345 | 0 | e = unpack_track(trak); |
1346 | 0 | if (e) return e; |
1347 | | |
1348 | | //OD is not allowed as a data ref |
1349 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { |
1350 | 0 | return GF_BAD_PARAM; |
1351 | 0 | } |
1352 | | //OK, add the sample |
1353 | | //1- Get the streamDescriptionIndex and dataRefIndex |
1354 | | //not specified, get the latest used... |
1355 | 0 | descIndex = StreamDescriptionIndex; |
1356 | 0 | if (!StreamDescriptionIndex) { |
1357 | 0 | descIndex = trak->Media->information->sampleTable->currentEntryIndex; |
1358 | 0 | } |
1359 | 0 | e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); |
1360 | 0 | if (e) return e; |
1361 | 0 | if (!entry || !dataRefIndex) return GF_BAD_PARAM; |
1362 | | //set the current to this one |
1363 | 0 | trak->Media->information->sampleTable->currentEntryIndex = descIndex; |
1364 | | |
1365 | | |
1366 | | //get this dataRef and return false if self contained |
1367 | 0 | Dentry =(GF_DataEntryURLBox*) gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1); |
1368 | 0 | if (Dentry->flags == 1) return GF_BAD_PARAM; |
1369 | | |
1370 | | //add the meta data |
1371 | 0 | e = Media_AddSample(trak->Media, dataOffset, sample, descIndex, 0); |
1372 | 0 | if (e) return e; |
1373 | | |
1374 | 0 | if (!movie->keep_utc) |
1375 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1376 | | //OK, update duration |
1377 | 0 | e = Media_SetDuration(trak); |
1378 | 0 | if (e) return e; |
1379 | 0 | return SetTrackDuration(trak); |
1380 | |
|
1381 | 0 | } |
1382 | | |
1383 | | //set the duration of the last media sample. If not set, the duration of the last sample is the |
1384 | | //duration of the previous one if any, or 1000 (default value). |
1385 | | static GF_Err gf_isom_set_last_sample_duration_internal(GF_ISOFile *movie, u32 trackNumber, u64 dur_num, u32 dur_den, u32 mode) |
1386 | 0 | { |
1387 | 0 | GF_TrackBox *trak; |
1388 | 0 | GF_SttsEntry *ent; |
1389 | 0 | GF_TimeToSampleBox *stts; |
1390 | 0 | u64 mdur; |
1391 | 0 | u32 duration; |
1392 | 0 | GF_Err e; |
1393 | 0 | Bool is_patch = GF_FALSE; |
1394 | |
|
1395 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1396 | 0 | if (e) return e; |
1397 | | |
1398 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1399 | 0 | if (!trak) return GF_BAD_PARAM; |
1400 | | |
1401 | 0 | if (mode==0) { |
1402 | 0 | duration = (u32) dur_num; |
1403 | 0 | } else if (mode==1) { |
1404 | 0 | duration = (u32) dur_num; |
1405 | 0 | if (dur_den) { |
1406 | 0 | duration *= trak->Media->mediaHeader->timeScale; |
1407 | 0 | duration /= dur_den; |
1408 | 0 | } |
1409 | 0 | } else { |
1410 | 0 | is_patch = GF_TRUE; |
1411 | 0 | } |
1412 | 0 | mdur = trak->Media->mediaHeader->duration; |
1413 | 0 | stts = trak->Media->information->sampleTable->TimeToSample; |
1414 | 0 | if (!stts->nb_entries) return GF_BAD_PARAM; |
1415 | | |
1416 | 0 | if (is_patch) { |
1417 | 0 | u32 i, avg_dur, nb_samp=0; |
1418 | 0 | u64 cum_dur=0; |
1419 | 0 | for (i=0; i<stts->nb_entries; i++) { |
1420 | 0 | ent = (GF_SttsEntry*) &stts->entries[i]; |
1421 | 0 | cum_dur += ent->sampleCount*ent->sampleDelta; |
1422 | 0 | nb_samp += ent->sampleCount; |
1423 | 0 | } |
1424 | 0 | if (cum_dur <= dur_num || !nb_samp) return GF_OK; |
1425 | 0 | avg_dur = (u32) (dur_num / nb_samp); |
1426 | |
|
1427 | 0 | stts->entries[0].sampleDelta = avg_dur; |
1428 | 0 | stts->entries[0].sampleCount = nb_samp; |
1429 | 0 | stts->nb_entries = 1; |
1430 | 0 | stts->w_LastDTS = dur_num - avg_dur; |
1431 | 0 | return GF_OK; |
1432 | 0 | } |
1433 | | //get the last entry |
1434 | 0 | ent = (GF_SttsEntry*) &stts->entries[stts->nb_entries-1]; |
1435 | 0 | if ((mode==1) && !duration && !dur_den) { |
1436 | | //same as previous, nothing to adjust |
1437 | 0 | if (ent->sampleCount>1) return GF_OK; |
1438 | 0 | if (stts->nb_entries==1) return GF_OK; |
1439 | 0 | duration = stts->entries[stts->nb_entries-2].sampleDelta; |
1440 | 0 | } |
1441 | | |
1442 | 0 | mdur -= ent->sampleDelta; |
1443 | 0 | mdur += duration; |
1444 | | |
1445 | | //we only have one sample |
1446 | 0 | if (ent->sampleCount == 1) { |
1447 | 0 | ent->sampleDelta = (u32) duration; |
1448 | 0 | if (mode && (stts->nb_entries>1) && (stts->entries[stts->nb_entries-2].sampleDelta==duration)) { |
1449 | 0 | stts->entries[stts->nb_entries-2].sampleCount++; |
1450 | 0 | stts->nb_entries--; |
1451 | | //and update the write cache |
1452 | 0 | stts->w_currentSampleNum = trak->Media->information->sampleTable->SampleSize->sampleCount; |
1453 | 0 | } |
1454 | 0 | } else { |
1455 | 0 | if (ent->sampleDelta == duration) return GF_OK; |
1456 | 0 | ent->sampleCount -= 1; |
1457 | |
|
1458 | 0 | if (stts->nb_entries==stts->alloc_size) { |
1459 | 0 | stts->alloc_size++; |
1460 | 0 | stts->entries = (GF_SttsEntry*)gf_realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size); |
1461 | 0 | if (!stts->entries) return GF_OUT_OF_MEM; |
1462 | 0 | } |
1463 | 0 | stts->entries[stts->nb_entries].sampleCount = 1; |
1464 | 0 | stts->entries[stts->nb_entries].sampleDelta = (u32) duration; |
1465 | 0 | stts->nb_entries++; |
1466 | | //and update the write cache |
1467 | 0 | stts->w_currentSampleNum = trak->Media->information->sampleTable->SampleSize->sampleCount; |
1468 | 0 | } |
1469 | 0 | if (!movie->keep_utc) |
1470 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1471 | | |
1472 | | //update media duration if duration was set |
1473 | 0 | if (trak->Media->mediaHeader->duration) |
1474 | 0 | trak->Media->mediaHeader->duration = mdur; |
1475 | | //do not update track duration yet, this is done on close |
1476 | 0 | return GF_OK; |
1477 | 0 | } |
1478 | | |
1479 | | GF_EXPORT |
1480 | | GF_Err gf_isom_set_last_sample_duration(GF_ISOFile *movie, u32 trackNumber, u32 duration) |
1481 | 0 | { |
1482 | 0 | return gf_isom_set_last_sample_duration_internal(movie, trackNumber, duration, 0, 0); |
1483 | 0 | } |
1484 | | |
1485 | | GF_EXPORT |
1486 | | GF_Err gf_isom_patch_last_sample_duration(GF_ISOFile *movie, u32 trackNumber, u64 next_dts) |
1487 | 0 | { |
1488 | 0 | return gf_isom_set_last_sample_duration_internal(movie, trackNumber, next_dts, 0, 2); |
1489 | 0 | } |
1490 | | |
1491 | | GF_EXPORT |
1492 | | GF_Err gf_isom_set_last_sample_duration_ex(GF_ISOFile *movie, u32 trackNumber, u32 dur_num, u32 dur_den) |
1493 | 0 | { |
1494 | 0 | return gf_isom_set_last_sample_duration_internal(movie, trackNumber, dur_num, dur_den, 1); |
1495 | 0 | } |
1496 | | |
1497 | | //update a sample data in the media. Note that the sample MUST exists |
1498 | | GF_EXPORT |
1499 | | GF_Err gf_isom_update_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, Bool data_only) |
1500 | 0 | { |
1501 | 0 | GF_Err e; |
1502 | 0 | GF_TrackBox *trak; |
1503 | |
|
1504 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_EDIT); |
1505 | 0 | if (e) return e; |
1506 | | |
1507 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1508 | 0 | if (!trak) return GF_BAD_PARAM; |
1509 | | |
1510 | 0 | e = unpack_track(trak); |
1511 | 0 | if (e) return e; |
1512 | | |
1513 | | //block for hint tracks |
1514 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM; |
1515 | | |
1516 | | //REWRITE ANY OD STUFF |
1517 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { |
1518 | 0 | GF_ISOSample *od_sample = NULL; |
1519 | 0 | e = Media_ParseODFrame(trak->Media, sample, &od_sample); |
1520 | 0 | if (!e) e = Media_UpdateSample(trak->Media, sampleNumber, od_sample, data_only); |
1521 | 0 | if (od_sample) gf_isom_sample_del(&od_sample); |
1522 | 0 | gf_isom_disable_inplace_rewrite(movie); |
1523 | 0 | } else { |
1524 | 0 | e = Media_UpdateSample(trak->Media, sampleNumber, sample, data_only); |
1525 | 0 | if (data_only || sample->data) |
1526 | 0 | gf_isom_disable_inplace_rewrite(movie); |
1527 | 0 | } |
1528 | 0 | if (e) return e; |
1529 | 0 | if (!movie->keep_utc) |
1530 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1531 | |
|
1532 | 0 | return GF_OK; |
1533 | 0 | } |
1534 | | |
1535 | | //update a sample data in the media. Note that the sample MUST exists, |
1536 | | //that sample->data MUST be NULL and sample->dataLength must be NON NULL; |
1537 | | GF_EXPORT |
1538 | | GF_Err gf_isom_update_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset) |
1539 | 0 | { |
1540 | 0 | GF_Err e; |
1541 | 0 | GF_TrackBox *trak; |
1542 | |
|
1543 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_EDIT); |
1544 | 0 | if (e) return e; |
1545 | | |
1546 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1547 | 0 | if (!trak) return GF_BAD_PARAM; |
1548 | | |
1549 | | //block for hint tracks |
1550 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM; |
1551 | | |
1552 | 0 | if (!sampleNumber || !sample) return GF_BAD_PARAM; |
1553 | | |
1554 | 0 | e = unpack_track(trak); |
1555 | 0 | if (e) return e; |
1556 | | |
1557 | | //OD is not allowed as a data ref |
1558 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { |
1559 | 0 | return GF_BAD_PARAM; |
1560 | 0 | } |
1561 | | //OK, update it |
1562 | 0 | e = Media_UpdateSampleReference(trak->Media, sampleNumber, sample, data_offset); |
1563 | 0 | if (e) return e; |
1564 | | |
1565 | 0 | if (!movie->keep_utc) |
1566 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1567 | 0 | return GF_OK; |
1568 | 0 | } |
1569 | | |
1570 | | //for gf_isom_remove_sample and gf_isom_remove_track: check all items sharing their data with a sample being removed |
1571 | | //and remove sharing flag |
1572 | | // sample_number can be 0 for complete track removal |
1573 | | static void gf_isom_meta_track_remove(GF_ISOFile *movie, GF_TrackBox *trak, u32 sample_number) |
1574 | 0 | { |
1575 | 0 | u32 i, count; |
1576 | 0 | if (!movie || !movie->meta || !movie->meta->use_item_sample_sharing) |
1577 | 0 | return; |
1578 | | |
1579 | 0 | count = gf_list_count(movie->meta->item_locations->location_entries); |
1580 | 0 | for (i=0; i<count; i++) { |
1581 | 0 | u32 j; |
1582 | 0 | GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(movie->meta->item_locations->location_entries, i); |
1583 | | /*get item info*/ |
1584 | 0 | GF_ItemInfoEntryBox *iinf = NULL; |
1585 | 0 | j=0; |
1586 | 0 | while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(movie->meta->item_infos->item_infos, &j))) { |
1587 | 0 | if (iinf->item_ID==iloc->item_ID) break; |
1588 | 0 | } |
1589 | 0 | if (!iinf || !iinf->tk_id) continue; |
1590 | 0 | if (iinf->tk_id != trak->Header->trackID) continue; |
1591 | | |
1592 | 0 | if (sample_number && (iinf->sample_num != sample_number)) continue; |
1593 | 0 | iinf->tk_id = 0; |
1594 | 0 | iinf->sample_num = 0; |
1595 | 0 | } |
1596 | 0 | } |
1597 | | |
1598 | | |
1599 | | |
1600 | | //Remove a given sample |
1601 | | GF_EXPORT |
1602 | | GF_Err gf_isom_remove_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber) |
1603 | 0 | { |
1604 | 0 | GF_Err e; |
1605 | 0 | GF_TrackBox *trak; |
1606 | |
|
1607 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_EDIT); |
1608 | 0 | if (e) return e; |
1609 | | |
1610 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1611 | 0 | if (!trak || !sampleNumber || (sampleNumber > trak->Media->information->sampleTable->SampleSize->sampleCount) ) |
1612 | 0 | return GF_BAD_PARAM; |
1613 | | |
1614 | | //block for hint tracks |
1615 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM; |
1616 | | |
1617 | 0 | e = unpack_track(trak); |
1618 | 0 | if (e) return e; |
1619 | | //do NOT change the order DTS, CTS, size chunk |
1620 | | |
1621 | | //remove DTS |
1622 | 0 | e = stbl_RemoveDTS(trak->Media->information->sampleTable, sampleNumber, 1, trak->Media->mediaHeader->timeScale); |
1623 | 0 | if (e) return e; |
1624 | | //remove CTS if any |
1625 | 0 | if (trak->Media->information->sampleTable->CompositionOffset) { |
1626 | 0 | e = stbl_RemoveCTS(trak->Media->information->sampleTable, sampleNumber, 1); |
1627 | 0 | if (e) return e; |
1628 | 0 | } |
1629 | | //remove size |
1630 | 0 | e = stbl_RemoveSize(trak->Media->information->sampleTable, sampleNumber, 1); |
1631 | 0 | if (e) return e; |
1632 | | //remove sampleToChunk and chunk |
1633 | 0 | e = stbl_RemoveChunk(trak->Media->information->sampleTable, sampleNumber, 1); |
1634 | 0 | if (e) return e; |
1635 | | //remove sync |
1636 | 0 | if (trak->Media->information->sampleTable->SyncSample) { |
1637 | 0 | e = stbl_RemoveRAP(trak->Media->information->sampleTable, sampleNumber); |
1638 | 0 | if (e) return e; |
1639 | 0 | } |
1640 | | //remove sample dep |
1641 | 0 | if (trak->Media->information->sampleTable->SampleDep) { |
1642 | 0 | e = stbl_RemoveRedundant(trak->Media->information->sampleTable, sampleNumber, 1); |
1643 | 0 | if (e) return e; |
1644 | 0 | } |
1645 | | //remove shadow |
1646 | 0 | e = stbl_RemoveShadow(trak->Media->information->sampleTable, sampleNumber); |
1647 | 0 | if (e) return e; |
1648 | | |
1649 | | //remove padding |
1650 | 0 | e = stbl_RemovePaddingBits(trak->Media->information->sampleTable, sampleNumber); |
1651 | 0 | if (e) return e; |
1652 | | |
1653 | 0 | e = stbl_RemoveSubSample(trak->Media->information->sampleTable, sampleNumber); |
1654 | 0 | if (e) return e; |
1655 | | |
1656 | 0 | e = stbl_RemoveSampleGroup(trak->Media->information->sampleTable, sampleNumber); |
1657 | 0 | if (e) return e; |
1658 | | |
1659 | 0 | gf_isom_disable_inplace_rewrite(movie); |
1660 | |
|
1661 | 0 | gf_isom_meta_track_remove(movie, trak, sampleNumber); |
1662 | |
|
1663 | 0 | return SetTrackDuration(trak); |
1664 | 0 | } |
1665 | | |
1666 | | |
1667 | | GF_EXPORT |
1668 | | GF_Err gf_isom_set_final_name(GF_ISOFile *movie, char *filename) |
1669 | 0 | { |
1670 | 0 | GF_Err e; |
1671 | 0 | if (!movie ) return GF_BAD_PARAM; |
1672 | | |
1673 | | //if mode is not OPEN_EDIT file was created under the right name |
1674 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_EDIT); |
1675 | 0 | if (e) return e; |
1676 | | |
1677 | 0 | if (filename) { |
1678 | | //we don't allow file overwriting |
1679 | 0 | if ( (movie->openMode == GF_ISOM_OPEN_EDIT) |
1680 | 0 | && movie->fileName && !strcmp(filename, movie->fileName)) |
1681 | 0 | return GF_BAD_PARAM; |
1682 | 0 | if (movie->finalName) gf_free(movie->finalName); |
1683 | 0 | movie->finalName = gf_strdup(filename); |
1684 | 0 | if (!movie->finalName) return GF_OUT_OF_MEM; |
1685 | 0 | gf_isom_disable_inplace_rewrite(movie); |
1686 | 0 | } |
1687 | 0 | return GF_OK; |
1688 | 0 | } |
1689 | | |
1690 | | //Add a system descriptor to the ESD of a stream(EDIT or WRITE mode only) |
1691 | | GF_EXPORT |
1692 | | GF_Err gf_isom_add_desc_to_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, const GF_Descriptor *theDesc) |
1693 | 0 | { |
1694 | 0 | GF_IPIPtr *ipiD; |
1695 | 0 | GF_Err e; |
1696 | 0 | u16 tmpRef; |
1697 | 0 | GF_TrackBox *trak; |
1698 | 0 | GF_Descriptor *desc; |
1699 | 0 | GF_ESD *esd; |
1700 | 0 | GF_TrackReferenceBox *tref; |
1701 | 0 | GF_TrackReferenceTypeBox *dpnd; |
1702 | 0 | GF_MPEGVisualSampleEntryBox *entry; |
1703 | 0 | u32 msubtype; |
1704 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1705 | 0 | if (e) return e; |
1706 | | |
1707 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1708 | 0 | if (!trak) return GF_BAD_PARAM; |
1709 | | |
1710 | | /*GETS NATIVE DESCRIPTOR ONLY*/ |
1711 | 0 | e = Media_GetESD(trak->Media, StreamDescriptionIndex, &esd, GF_TRUE); |
1712 | 0 | if (e) return e; |
1713 | | |
1714 | 0 | entry = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex-1); |
1715 | 0 | if (!entry) return GF_BAD_PARAM; |
1716 | 0 | msubtype = entry->type; |
1717 | 0 | if ((msubtype==GF_ISOM_BOX_TYPE_ENCV) || (msubtype==GF_ISOM_BOX_TYPE_ENCA)) |
1718 | 0 | gf_isom_get_original_format_type(movie, trackNumber, StreamDescriptionIndex, &msubtype); |
1719 | | |
1720 | | //duplicate the desc |
1721 | 0 | e = gf_odf_desc_copy((GF_Descriptor *)theDesc, &desc); |
1722 | 0 | if (e) return e; |
1723 | | |
1724 | | //and add it to the ESD EXCEPT IPI PTR (we need to translate from ES_ID to TrackID!!! |
1725 | 0 | if (!movie->keep_utc) |
1726 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1727 | |
|
1728 | 0 | switch (desc->tag) { |
1729 | 0 | case GF_ODF_IPI_PTR_TAG: |
1730 | 0 | goto insertIPI; |
1731 | 0 | default: |
1732 | 0 | break; |
1733 | 0 | } |
1734 | | |
1735 | 0 | if ((msubtype==GF_ISOM_BOX_TYPE_MP4S) || (msubtype==GF_ISOM_BOX_TYPE_MP4V) || (msubtype==GF_ISOM_BOX_TYPE_MP4A)) { |
1736 | 0 | return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc); |
1737 | 0 | } |
1738 | | |
1739 | 0 | if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_VISUAL) { |
1740 | 0 | gf_odf_desc_del(desc); |
1741 | 0 | return GF_NOT_SUPPORTED; |
1742 | 0 | } |
1743 | 0 | GF_MPEG4ExtensionDescriptorsBox *mdesc = (GF_MPEG4ExtensionDescriptorsBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_M4DS); |
1744 | 0 | if (!mdesc) { |
1745 | 0 | mdesc = (GF_MPEG4ExtensionDescriptorsBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_M4DS); |
1746 | 0 | } |
1747 | 0 | return gf_list_add(mdesc->descriptors, desc); |
1748 | | |
1749 | 0 | insertIPI: |
1750 | 0 | if (esd->ipiPtr) { |
1751 | 0 | gf_odf_desc_del((GF_Descriptor *) esd->ipiPtr); |
1752 | 0 | esd->ipiPtr = NULL; |
1753 | 0 | } |
1754 | |
|
1755 | 0 | ipiD = (GF_IPIPtr *) desc; |
1756 | | //find a tref |
1757 | 0 | if (!trak->References) { |
1758 | 0 | tref = (GF_TrackReferenceBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TREF); |
1759 | 0 | if (!tref) return GF_OUT_OF_MEM; |
1760 | 0 | e = trak_on_child_box((GF_Box*)trak, (GF_Box *)tref, GF_FALSE); |
1761 | 0 | if (e) return e; |
1762 | 0 | } |
1763 | 0 | tref = trak->References; |
1764 | |
|
1765 | 0 | e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd); |
1766 | 0 | if (e) return e; |
1767 | 0 | if (!dpnd) { |
1768 | 0 | tmpRef = 0; |
1769 | 0 | dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT); |
1770 | 0 | if (!dpnd) return GF_OUT_OF_MEM; |
1771 | 0 | dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR; |
1772 | 0 | e = reftype_AddRefTrack(dpnd, ipiD->IPI_ES_Id, &tmpRef); |
1773 | 0 | if (e) return e; |
1774 | | //and replace the tag and value... |
1775 | 0 | ipiD->IPI_ES_Id = tmpRef; |
1776 | 0 | ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG; |
1777 | 0 | } else { |
1778 | | //Watch out! ONLY ONE IPI dependency is allowed per stream |
1779 | 0 | dpnd->trackIDCount = 1; |
1780 | 0 | dpnd->trackIDs[0] = ipiD->IPI_ES_Id; |
1781 | | //and replace the tag and value... |
1782 | 0 | ipiD->IPI_ES_Id = 1; |
1783 | 0 | ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG; |
1784 | 0 | } |
1785 | | //and add the desc to the esd... |
1786 | 0 | return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc); |
1787 | 0 | } |
1788 | | |
1789 | | |
1790 | | //use carefully. Very useful when you made a lot of changes (IPMP, IPI, OCI, ...) |
1791 | | //THIS WILL REPLACE THE WHOLE DESCRIPTOR ... |
1792 | | GF_EXPORT |
1793 | | GF_Err gf_isom_change_mpeg4_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, const GF_ESD *newESD) |
1794 | 0 | { |
1795 | 0 | GF_Err e; |
1796 | 0 | GF_ESD *esd; |
1797 | 0 | GF_TrackBox *trak; |
1798 | 0 | GF_SampleEntryBox *entry; |
1799 | 0 | GF_SampleDescriptionBox *stsd; |
1800 | |
|
1801 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1802 | 0 | if (e) return e; |
1803 | | |
1804 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1805 | 0 | if (!trak) return GF_BAD_PARAM; |
1806 | | |
1807 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
1808 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
1809 | | |
1810 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
1811 | 0 | return movie->LastError = GF_BAD_PARAM; |
1812 | 0 | } |
1813 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
1814 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
1815 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
1816 | | |
1817 | 0 | if (!movie->keep_utc) |
1818 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1819 | | //duplicate our desc |
1820 | 0 | e = gf_odf_desc_copy((GF_Descriptor *)newESD, (GF_Descriptor **)&esd); |
1821 | 0 | if (e) return e; |
1822 | 0 | e = Track_SetStreamDescriptor(trak, StreamDescriptionIndex, entry->dataReferenceIndex, esd, NULL); |
1823 | 0 | if (e != GF_OK) { |
1824 | 0 | gf_odf_desc_del((GF_Descriptor *) esd); |
1825 | 0 | } |
1826 | 0 | return e; |
1827 | 0 | } |
1828 | | |
1829 | | GF_EXPORT |
1830 | | GF_Err gf_isom_set_visual_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 Width, u32 Height) |
1831 | 0 | { |
1832 | 0 | GF_Err e; |
1833 | 0 | GF_TrackBox *trak; |
1834 | 0 | GF_SampleEntryBox *entry; |
1835 | 0 | GF_SampleDescriptionBox *stsd; |
1836 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1837 | 0 | if (e) return e; |
1838 | | |
1839 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1840 | 0 | if (!trak) return GF_BAD_PARAM; |
1841 | | |
1842 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
1843 | 0 | if (!stsd) { |
1844 | 0 | return movie->LastError = GF_ISOM_INVALID_FILE; |
1845 | 0 | } |
1846 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
1847 | 0 | return movie->LastError = GF_BAD_PARAM; |
1848 | 0 | } |
1849 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
1850 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
1851 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
1852 | 0 | if (!movie->keep_utc) |
1853 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1854 | | |
1855 | | //valid for MPEG visual, JPG and 3GPP H263 |
1856 | 0 | if (entry->internal_type == GF_ISOM_SAMPLE_ENTRY_VIDEO) { |
1857 | 0 | ((GF_VisualSampleEntryBox*)entry)->Width = Width; |
1858 | 0 | ((GF_VisualSampleEntryBox*)entry)->Height = Height; |
1859 | 0 | trak->Header->width = Width<<16; |
1860 | 0 | trak->Header->height = Height<<16; |
1861 | 0 | return GF_OK; |
1862 | 0 | } else if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_SCENE) { |
1863 | 0 | trak->Header->width = Width<<16; |
1864 | 0 | trak->Header->height = Height<<16; |
1865 | 0 | return GF_OK; |
1866 | 0 | } else { |
1867 | 0 | return GF_BAD_PARAM; |
1868 | 0 | } |
1869 | 0 | } |
1870 | | |
1871 | | GF_EXPORT |
1872 | | GF_Err gf_isom_set_visual_bit_depth(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u16 bitDepth) |
1873 | 0 | { |
1874 | 0 | GF_Err e; |
1875 | 0 | GF_TrackBox *trak; |
1876 | 0 | GF_MPEGVisualSampleEntryBox *entry; |
1877 | 0 | GF_SampleDescriptionBox *stsd; |
1878 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1879 | 0 | if (e) return e; |
1880 | | |
1881 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1882 | 0 | if (!trak) return GF_BAD_PARAM; |
1883 | | |
1884 | 0 | switch (trak->Media->handler->handlerType) { |
1885 | 0 | case GF_ISOM_MEDIA_VISUAL: |
1886 | 0 | case GF_ISOM_MEDIA_PICT: |
1887 | 0 | case GF_ISOM_MEDIA_AUXV: |
1888 | 0 | break; |
1889 | 0 | default: |
1890 | 0 | return GF_OK; |
1891 | 0 | } |
1892 | | |
1893 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
1894 | 0 | if (!stsd) { |
1895 | 0 | return movie->LastError = GF_ISOM_INVALID_FILE; |
1896 | 0 | } |
1897 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
1898 | 0 | return movie->LastError = GF_BAD_PARAM; |
1899 | 0 | } |
1900 | 0 | entry = (GF_MPEGVisualSampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
1901 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
1902 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
1903 | 0 | entry->bit_depth = bitDepth; |
1904 | 0 | return GF_OK; |
1905 | 0 | } |
1906 | | |
1907 | | GF_EXPORT |
1908 | | GF_Err gf_isom_set_pixel_aspect_ratio(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, s32 hSpacing, s32 vSpacing, Bool force_par) |
1909 | 0 | { |
1910 | 0 | GF_Err e; |
1911 | 0 | GF_TrackBox *trak; |
1912 | 0 | GF_SampleEntryBox *entry; |
1913 | 0 | GF_SampleDescriptionBox *stsd; |
1914 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1915 | 0 | if (e) return e; |
1916 | | |
1917 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1918 | 0 | if (!trak) return GF_BAD_PARAM; |
1919 | | |
1920 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
1921 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
1922 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
1923 | 0 | return movie->LastError = GF_BAD_PARAM; |
1924 | 0 | } |
1925 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
1926 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
1927 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
1928 | 0 | if (!movie->keep_utc) |
1929 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1930 | |
|
1931 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
1932 | | |
1933 | 0 | if (hSpacing<0) hSpacing = 1; |
1934 | 0 | if (vSpacing<0) vSpacing = 1; |
1935 | |
|
1936 | 0 | GF_PixelAspectRatioBox *pasp = (GF_PixelAspectRatioBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_PASP); |
1937 | 0 | if (!hSpacing || !vSpacing || ((hSpacing == vSpacing) && !force_par)) { |
1938 | 0 | if (pasp) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *)pasp); |
1939 | 0 | return GF_OK; |
1940 | 0 | } |
1941 | 0 | if (!pasp) { |
1942 | 0 | pasp = (GF_PixelAspectRatioBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_PASP); |
1943 | 0 | if (!pasp) return GF_OUT_OF_MEM; |
1944 | 0 | } |
1945 | 0 | pasp->hSpacing = (u32) hSpacing; |
1946 | 0 | pasp->vSpacing = (u32) vSpacing; |
1947 | 0 | return GF_OK; |
1948 | 0 | } |
1949 | | |
1950 | | GF_EXPORT |
1951 | | GF_Err gf_isom_set_visual_color_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 colour_type, u16 colour_primaries, u16 transfer_characteristics, u16 matrix_coefficients, Bool full_range_flag, u8 *icc_data, u32 icc_size) |
1952 | 0 | { |
1953 | 0 | GF_Err e; |
1954 | 0 | GF_TrackBox *trak; |
1955 | 0 | GF_SampleEntryBox *entry; |
1956 | 0 | GF_SampleDescriptionBox *stsd; |
1957 | 0 | GF_ColourInformationBox *clr=NULL; |
1958 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1959 | 0 | if (e) return e; |
1960 | | |
1961 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1962 | 0 | if (!trak) return GF_BAD_PARAM; |
1963 | | |
1964 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
1965 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
1966 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
1967 | 0 | return movie->LastError = GF_BAD_PARAM; |
1968 | 0 | } |
1969 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
1970 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
1971 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
1972 | 0 | if (!movie->keep_utc) |
1973 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1974 | |
|
1975 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_OK; |
1976 | | |
1977 | 0 | clr = (GF_ColourInformationBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_COLR); |
1978 | 0 | if (!colour_type) { |
1979 | 0 | if (clr) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *)clr); |
1980 | 0 | return GF_OK; |
1981 | 0 | } |
1982 | 0 | if (clr) { |
1983 | | //create another color box |
1984 | 0 | if (clr->opaque && !icc_data) clr = NULL; |
1985 | 0 | else if (!clr->opaque && icc_data) clr = NULL; |
1986 | 0 | } |
1987 | |
|
1988 | 0 | if (!clr) { |
1989 | 0 | clr = (GF_ColourInformationBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_COLR); |
1990 | 0 | if (!clr) return GF_OUT_OF_MEM; |
1991 | 0 | } |
1992 | 0 | clr->colour_type = colour_type; |
1993 | 0 | clr->colour_primaries = colour_primaries; |
1994 | 0 | clr->transfer_characteristics = transfer_characteristics; |
1995 | 0 | clr->matrix_coefficients = matrix_coefficients; |
1996 | 0 | clr->full_range_flag = full_range_flag; |
1997 | 0 | if (clr->opaque) gf_free(clr->opaque); |
1998 | 0 | clr->opaque = NULL; |
1999 | 0 | clr->opaque_size = 0; |
2000 | 0 | if ((colour_type==GF_ISOM_SUBTYPE_RICC) || (colour_type==GF_ISOM_SUBTYPE_PROF)) { |
2001 | 0 | clr->opaque_size = icc_data ? icc_size : 0; |
2002 | 0 | if (clr->opaque_size) { |
2003 | 0 | clr->opaque = gf_malloc(sizeof(char)*clr->opaque_size); |
2004 | 0 | if (!clr->opaque) return GF_OUT_OF_MEM; |
2005 | 0 | memcpy(clr->opaque, icc_data, sizeof(char)*clr->opaque_size); |
2006 | 0 | } |
2007 | 0 | } |
2008 | 0 | return GF_OK; |
2009 | 0 | } |
2010 | | |
2011 | | GF_EXPORT |
2012 | | GF_Err gf_isom_set_ambient_viewing_environment(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 ambient_illuminance, u16 ambient_light_x, u16 ambient_light_y) |
2013 | 0 | { |
2014 | 0 | GF_Err e; |
2015 | 0 | GF_TrackBox *trak; |
2016 | 0 | GF_SampleEntryBox *entry; |
2017 | 0 | GF_SampleDescriptionBox *stsd; |
2018 | 0 | GF_AmbientViewingEnvBox *amve=NULL; |
2019 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2020 | 0 | if (e) return e; |
2021 | | |
2022 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2023 | 0 | if (!trak) return GF_BAD_PARAM; |
2024 | | |
2025 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2026 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
2027 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2028 | 0 | return movie->LastError = GF_BAD_PARAM; |
2029 | 0 | } |
2030 | 0 | entry = (GF_SampleEntryBox*)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
2031 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2032 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2033 | 0 | if (!movie->keep_utc) |
2034 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2035 | |
|
2036 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_OK; |
2037 | | |
2038 | 0 | amve = (GF_AmbientViewingEnvBox*) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_AMVE); |
2039 | | |
2040 | | // ambient_illuminance shall not be equal to 0 |
2041 | | // The values of ambient_light_x and ambient_light_y shall be in the range of 0 to 50000, inclusive |
2042 | 0 | if (ambient_illuminance == 0 || ambient_light_x > 50000 || ambient_light_y > 50000) { |
2043 | 0 | if (amve) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *)amve); |
2044 | 0 | return GF_OK; |
2045 | 0 | } |
2046 | | |
2047 | 0 | if (!amve) { |
2048 | 0 | amve = (GF_AmbientViewingEnvBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_AMVE); |
2049 | 0 | if (!amve) return GF_OUT_OF_MEM; |
2050 | 0 | } |
2051 | 0 | amve->ambient_illuminance = ambient_illuminance; |
2052 | 0 | amve->ambient_light_x = ambient_light_x; |
2053 | 0 | amve->ambient_light_y = ambient_light_y; |
2054 | |
|
2055 | 0 | return GF_OK; |
2056 | 0 | } |
2057 | | |
2058 | | GF_EXPORT |
2059 | | GF_Err gf_isom_set_dolby_vision_profile(GF_ISOFile* movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_DOVIDecoderConfigurationRecord *dvcc) |
2060 | 0 | { |
2061 | 0 | GF_Err e; |
2062 | 0 | GF_TrackBox* trak; |
2063 | 0 | GF_Box *dv_cfge = NULL; |
2064 | 0 | GF_MPEGVisualSampleEntryBox* entry; |
2065 | 0 | Bool switch_type = GF_FALSE; |
2066 | 0 | Bool is_avc = GF_FALSE; |
2067 | 0 | GF_SampleDescriptionBox* stsd; |
2068 | 0 | GF_DOVIConfigurationBox* dovi = NULL; |
2069 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2070 | 0 | if (e) return e; |
2071 | | |
2072 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2073 | 0 | if (!trak) return GF_BAD_PARAM; |
2074 | | |
2075 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2076 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
2077 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2078 | 0 | return movie->LastError = GF_BAD_PARAM; |
2079 | 0 | } |
2080 | 0 | entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
2081 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2082 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2083 | 0 | if (!movie->keep_utc) |
2084 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2085 | |
|
2086 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_OK; |
2087 | | |
2088 | 0 | if (dvcc) { |
2089 | 0 | switch_type = dvcc->force_dv; |
2090 | 0 | switch (dvcc->dv_profile) { |
2091 | 0 | case 1: |
2092 | 0 | case 3: |
2093 | 0 | case 5: |
2094 | 0 | switch_type = 1; |
2095 | 0 | break; |
2096 | 0 | } |
2097 | 0 | } |
2098 | | |
2099 | 0 | switch (entry->type) { |
2100 | 0 | case GF_ISOM_BOX_TYPE_HEV1: |
2101 | 0 | case GF_ISOM_BOX_TYPE_HEV2: |
2102 | 0 | case GF_ISOM_BOX_TYPE_DVHE: |
2103 | 0 | entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVHE : GF_ISOM_BOX_TYPE_HEV1; |
2104 | 0 | break; |
2105 | 0 | case GF_ISOM_BOX_TYPE_HVC1: |
2106 | 0 | case GF_ISOM_BOX_TYPE_HVC2: |
2107 | 0 | case GF_ISOM_BOX_TYPE_DVH1: |
2108 | 0 | entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVH1 : GF_ISOM_BOX_TYPE_HVC1; |
2109 | 0 | break; |
2110 | 0 | case GF_ISOM_BOX_TYPE_AVC1: |
2111 | 0 | case GF_ISOM_BOX_TYPE_DVA1: |
2112 | 0 | is_avc = GF_TRUE; |
2113 | 0 | entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVA1 : GF_ISOM_BOX_TYPE_AVC1; |
2114 | 0 | break; |
2115 | 0 | case GF_ISOM_BOX_TYPE_AVC3: |
2116 | 0 | case GF_ISOM_BOX_TYPE_DVAV: |
2117 | 0 | is_avc = GF_TRUE; |
2118 | 0 | entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVAV : GF_ISOM_BOX_TYPE_AVC3; |
2119 | 0 | break; |
2120 | 0 | case GF_ISOM_BOX_TYPE_AV01: |
2121 | 0 | case GF_ISOM_BOX_TYPE_DAV1: |
2122 | 0 | entry->type = switch_type ? GF_ISOM_BOX_TYPE_DAV1 : GF_ISOM_BOX_TYPE_AV01; |
2123 | 0 | break; |
2124 | 0 | default: |
2125 | 0 | return GF_NOT_SUPPORTED; |
2126 | 0 | } |
2127 | | |
2128 | 0 | u32 dve_type = is_avc ? GF_ISOM_BOX_TYPE_AVCE : GF_ISOM_BOX_TYPE_HVCE; |
2129 | 0 | dv_cfge = gf_isom_box_find_child(entry->child_boxes, dve_type); |
2130 | |
|
2131 | 0 | dovi = entry->dovi_config; |
2132 | 0 | if (!dvcc) { |
2133 | 0 | if (dovi) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)dovi); |
2134 | 0 | entry->dovi_config = NULL; |
2135 | |
|
2136 | 0 | if (dv_cfge) gf_isom_box_del_parent(&entry->child_boxes, dv_cfge); |
2137 | | //reverse entry type |
2138 | 0 | switch (entry->type) { |
2139 | 0 | case GF_ISOM_BOX_TYPE_DVHE: |
2140 | 0 | entry->type = GF_ISOM_BOX_TYPE_HEV1; |
2141 | 0 | break; |
2142 | 0 | case GF_ISOM_BOX_TYPE_DVH1: |
2143 | 0 | entry->type = GF_ISOM_BOX_TYPE_HVC1; |
2144 | 0 | break; |
2145 | 0 | case GF_ISOM_BOX_TYPE_DVA1: |
2146 | 0 | entry->type = GF_ISOM_BOX_TYPE_AVC1; |
2147 | 0 | break; |
2148 | 0 | case GF_ISOM_BOX_TYPE_DVAV: |
2149 | 0 | entry->type = GF_ISOM_BOX_TYPE_AVC3; |
2150 | 0 | break; |
2151 | 0 | case GF_ISOM_BOX_TYPE_DAV1: |
2152 | 0 | entry->type = GF_ISOM_BOX_TYPE_AV01; |
2153 | 0 | break; |
2154 | 0 | default: |
2155 | 0 | break; |
2156 | 0 | } |
2157 | 0 | return GF_OK; |
2158 | 0 | } |
2159 | 0 | if (!dovi) { |
2160 | 0 | dovi = (GF_DOVIConfigurationBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_DVCC); |
2161 | 0 | if (!dovi) return GF_OUT_OF_MEM; |
2162 | 0 | entry->dovi_config = dovi; |
2163 | 0 | } |
2164 | 0 | if (dvcc->dv_profile < 8) { |
2165 | 0 | dovi->type = GF_ISOM_BOX_TYPE_DVCC; |
2166 | 0 | } else { |
2167 | 0 | dovi->type = GF_ISOM_BOX_TYPE_DVVC; |
2168 | 0 | } |
2169 | 0 | dovi->DOVIConfig = *dvcc; |
2170 | |
|
2171 | 0 | if (dvcc->dv_profile == 20) { |
2172 | 0 | if (dvcc->dv_bl_signal_compatibility_id != 4) { |
2173 | 0 | entry->type = GF_ISOM_BOX_TYPE_DVH1; |
2174 | 0 | } |
2175 | 0 | dovi->type = GF_ISOM_BOX_TYPE_DVCC; |
2176 | 0 | } |
2177 | 0 | if (dvcc->dv_profile == 10 && dvcc->dv_bl_signal_compatibility_id == 0) { |
2178 | 0 | entry->type = GF_ISOM_BOX_TYPE_DAV1; |
2179 | 0 | dovi->type = GF_ISOM_BOX_TYPE_DVVC; |
2180 | 0 | } |
2181 | |
|
2182 | 0 | if (dv_cfge) gf_isom_box_del_parent(&entry->child_boxes, dv_cfge); |
2183 | 0 | dv_cfge = NULL; |
2184 | | |
2185 | | //inject avcE / hvcE if enhancement layer and RPU present in single-track case |
2186 | | //not clear from the spec what is supposed to be in these, we just clone avcC/hvcC |
2187 | 0 | if (dvcc->bl_present_flag && dvcc->el_present_flag && dvcc->rpu_present_flag) { |
2188 | 0 | GF_Box *src = is_avc ? (GF_Box *)entry->avc_config : (GF_Box *)entry->hevc_config; |
2189 | 0 | if (!src) return GF_BAD_PARAM; |
2190 | 0 | e = gf_isom_clone_box(src, &dv_cfge); |
2191 | 0 | if (e) return e; |
2192 | 0 | dv_cfge->type = dve_type; |
2193 | 0 | gf_list_add(entry->child_boxes, dv_cfge); |
2194 | 0 | } |
2195 | 0 | return GF_OK; |
2196 | 0 | } |
2197 | | |
2198 | | GF_EXPORT |
2199 | | GF_Err gf_isom_set_high_dynamic_range_info(GF_ISOFile* movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_MasteringDisplayColourVolumeInfo* mdcv, GF_ContentLightLevelInfo* clli) |
2200 | 0 | { |
2201 | 0 | GF_Err e; |
2202 | 0 | GF_TrackBox* trak; |
2203 | 0 | GF_SampleEntryBox* entry; |
2204 | 0 | GF_SampleDescriptionBox* stsd; |
2205 | |
|
2206 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2207 | 0 | if (e) return e; |
2208 | | |
2209 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2210 | 0 | if (!trak) return GF_BAD_PARAM; |
2211 | | |
2212 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2213 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
2214 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2215 | 0 | return movie->LastError = GF_BAD_PARAM; |
2216 | 0 | } |
2217 | 0 | entry = (GF_SampleEntryBox*)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
2218 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2219 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2220 | 0 | if (!movie->keep_utc) |
2221 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2222 | |
|
2223 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
2224 | | |
2225 | 0 | GF_MasteringDisplayColourVolumeBox *mdcvb = (GF_MasteringDisplayColourVolumeBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_MDCV); |
2226 | 0 | if (mdcv) { |
2227 | 0 | if (!mdcvb) { |
2228 | 0 | mdcvb = (GF_MasteringDisplayColourVolumeBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_MDCV); |
2229 | 0 | if (!mdcvb) return GF_OUT_OF_MEM; |
2230 | 0 | } |
2231 | 0 | mdcvb->mdcv = *mdcv; |
2232 | 0 | } else { |
2233 | 0 | if (mdcvb) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *) mdcvb); |
2234 | 0 | } |
2235 | | |
2236 | | /*clli*/ |
2237 | 0 | GF_ContentLightLevelBox *cllib = (GF_ContentLightLevelBox *)gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_CLLI); |
2238 | 0 | if (clli) { |
2239 | 0 | if (!cllib) { |
2240 | 0 | cllib = (GF_ContentLightLevelBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_CLLI); |
2241 | 0 | if (!cllib) return GF_OUT_OF_MEM; |
2242 | 0 | } |
2243 | 0 | cllib->clli = *clli; |
2244 | 0 | } else { |
2245 | 0 | if (cllib) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *) cllib); |
2246 | 0 | } |
2247 | 0 | return GF_OK; |
2248 | 0 | } |
2249 | | |
2250 | | GF_EXPORT |
2251 | | GF_Err gf_isom_set_clean_aperture(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 cleanApertureWidthN, u32 cleanApertureWidthD, u32 cleanApertureHeightN, u32 cleanApertureHeightD, s32 horizOffN, u32 horizOffD, s32 vertOffN, u32 vertOffD) |
2252 | 0 | { |
2253 | 0 | GF_Err e; |
2254 | 0 | GF_TrackBox *trak; |
2255 | 0 | GF_SampleEntryBox *entry; |
2256 | 0 | GF_SampleDescriptionBox *stsd; |
2257 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2258 | 0 | if (e) return e; |
2259 | | |
2260 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2261 | 0 | if (!trak) return GF_BAD_PARAM; |
2262 | | |
2263 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2264 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
2265 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2266 | 0 | return movie->LastError = GF_BAD_PARAM; |
2267 | 0 | } |
2268 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
2269 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2270 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2271 | 0 | if (!movie->keep_utc) |
2272 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2273 | |
|
2274 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
2275 | | |
2276 | 0 | GF_CleanApertureBox *clap = (GF_CleanApertureBox *)gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_CLAP); |
2277 | 0 | if (!cleanApertureHeightD || !cleanApertureWidthD || !horizOffD || !vertOffD) { |
2278 | 0 | if (clap) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)clap); |
2279 | 0 | return GF_OK; |
2280 | 0 | } |
2281 | 0 | if (!clap) { |
2282 | 0 | clap = (GF_CleanApertureBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_CLAP); |
2283 | 0 | if (!clap) return GF_OUT_OF_MEM; |
2284 | 0 | } |
2285 | | |
2286 | 0 | clap->cleanApertureWidthN = cleanApertureWidthN; |
2287 | 0 | clap->cleanApertureWidthD = cleanApertureWidthD; |
2288 | 0 | clap->cleanApertureHeightN = cleanApertureHeightN; |
2289 | 0 | clap->cleanApertureHeightD = cleanApertureHeightD; |
2290 | 0 | clap->horizOffN = horizOffN; |
2291 | 0 | clap->horizOffD = horizOffD; |
2292 | 0 | clap->vertOffN = vertOffN; |
2293 | 0 | clap->vertOffD = vertOffD; |
2294 | 0 | return GF_OK; |
2295 | 0 | } |
2296 | | |
2297 | | #include <gpac/maths.h> |
2298 | | GF_Err gf_isom_update_aperture_info(GF_ISOFile *movie, u32 trackNumber, Bool remove) |
2299 | 0 | { |
2300 | 0 | GF_Err e; |
2301 | 0 | GF_Box *box, *enof, *prof, *clef; |
2302 | 0 | GF_TrackBox *trak; |
2303 | 0 | GF_VisualSampleEntryBox *ventry; |
2304 | 0 | GF_PixelAspectRatioBox *pasp; |
2305 | 0 | GF_CleanApertureBox *clap; |
2306 | 0 | u32 j, hspacing, vspacing, clap_width_num, clap_width_den, clap_height_num, clap_height_den, high, low; |
2307 | 0 | Double width, height; |
2308 | |
|
2309 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2310 | 0 | if (e) return e; |
2311 | | |
2312 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2313 | 0 | if (!trak) return GF_BAD_PARAM; |
2314 | | |
2315 | 0 | if (remove) { |
2316 | 0 | if (trak->Aperture) { |
2317 | 0 | gf_isom_box_del(trak->Aperture); |
2318 | 0 | trak->Aperture = NULL; |
2319 | 0 | } |
2320 | 0 | return GF_OK; |
2321 | 0 | } |
2322 | 0 | enof = prof = clef = NULL; |
2323 | 0 | if (!trak->Aperture) { |
2324 | 0 | trak->Aperture = gf_isom_box_new_parent(&trak->child_boxes, GF_QT_BOX_TYPE_TAPT); |
2325 | 0 | if (!trak->Aperture) return GF_OUT_OF_MEM; |
2326 | 0 | } |
2327 | 0 | if (!trak->Aperture->child_boxes) { |
2328 | 0 | trak->Aperture->child_boxes = gf_list_new(); |
2329 | 0 | if (!trak->Aperture->child_boxes) |
2330 | 0 | return GF_OUT_OF_MEM; |
2331 | 0 | } |
2332 | | |
2333 | 0 | j=0; |
2334 | 0 | while ( (box = gf_list_enum(trak->Aperture->child_boxes, &j))) { |
2335 | 0 | switch (box->type) { |
2336 | 0 | case GF_QT_BOX_TYPE_CLEF: clef = box; break; |
2337 | 0 | case GF_QT_BOX_TYPE_PROF: prof = box; break; |
2338 | 0 | case GF_QT_BOX_TYPE_ENOF: enof = box; break; |
2339 | 0 | } |
2340 | 0 | } |
2341 | 0 | if (!clef) { |
2342 | 0 | clef = gf_isom_box_new(GF_QT_BOX_TYPE_CLEF); |
2343 | 0 | if (!clef) return GF_OUT_OF_MEM; |
2344 | 0 | gf_list_add(trak->Aperture->child_boxes, clef); |
2345 | 0 | } |
2346 | 0 | if (!enof) { |
2347 | 0 | enof = gf_isom_box_new(GF_QT_BOX_TYPE_ENOF); |
2348 | 0 | if (!enof) return GF_OUT_OF_MEM; |
2349 | 0 | gf_list_add(trak->Aperture->child_boxes, enof); |
2350 | 0 | } |
2351 | 0 | if (!prof) { |
2352 | 0 | prof = gf_isom_box_new(GF_QT_BOX_TYPE_PROF); |
2353 | 0 | if (!prof) return GF_OUT_OF_MEM; |
2354 | 0 | gf_list_add(trak->Aperture->child_boxes, prof); |
2355 | 0 | } |
2356 | | |
2357 | 0 | ventry = (GF_VisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, 0); |
2358 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2359 | 0 | if (ventry == NULL) return GF_BAD_PARAM; |
2360 | 0 | if (!movie->keep_utc) |
2361 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2362 | |
|
2363 | 0 | if (ventry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
2364 | | |
2365 | 0 | pasp = (GF_PixelAspectRatioBox *) gf_isom_box_find_child(ventry->child_boxes, GF_ISOM_BOX_TYPE_PASP); |
2366 | 0 | hspacing = vspacing = 0; |
2367 | 0 | if (pasp) { |
2368 | 0 | hspacing = pasp->hSpacing; |
2369 | 0 | vspacing = pasp->vSpacing; |
2370 | 0 | } |
2371 | 0 | clap_width_num = ventry->Width; |
2372 | 0 | clap_width_den = 1; |
2373 | 0 | clap_height_num = ventry->Height; |
2374 | 0 | clap_height_den = 1; |
2375 | 0 | clap = (GF_CleanApertureBox *) gf_isom_box_find_child(ventry->child_boxes, GF_ISOM_BOX_TYPE_CLAP); |
2376 | 0 | if (clap) { |
2377 | 0 | clap_width_num = clap->cleanApertureWidthN; |
2378 | 0 | clap_width_den = clap->cleanApertureWidthD; |
2379 | 0 | clap_height_num = clap->cleanApertureHeightN; |
2380 | 0 | clap_height_den = clap->cleanApertureHeightD; |
2381 | 0 | } |
2382 | | //enof: encoded pixels in 16.16 |
2383 | 0 | ((GF_ApertureBox *)enof)->width = (ventry->Width)<<16; |
2384 | 0 | ((GF_ApertureBox *)enof)->height = (ventry->Height)<<16; |
2385 | | |
2386 | | //prof: encoded pixels + pasp in 16.16 |
2387 | 0 | width = (Float) (ventry->Width * hspacing); |
2388 | 0 | width /= vspacing; |
2389 | 0 | high = (u32) floor((Float)width); |
2390 | 0 | low = (u32) ( 0xFFFF * (width - (Double)high) ); |
2391 | 0 | ((GF_ApertureBox *)prof)->width = (high)<<16 | low; |
2392 | 0 | ((GF_ApertureBox *)prof)->height = (ventry->Height)<<16; |
2393 | | |
2394 | | //clef: encoded pixels + pasp + clap in 16.16 |
2395 | 0 | width = (Double) (clap_width_num * hspacing); |
2396 | 0 | width /= clap_width_den * vspacing; |
2397 | 0 | height = (Float) clap_height_num; |
2398 | 0 | height /= clap_height_den; |
2399 | |
|
2400 | 0 | high = (u32) floor((Float)width); |
2401 | 0 | low = (u32) (0xFFFF * (width - (Double)high)); |
2402 | 0 | ((GF_ApertureBox *)clef)->width = (high)<<16 | low; |
2403 | 0 | high = (u32) floor((Float)height); |
2404 | 0 | low = (u32) (0xFFFF * (height - (Double)high)); |
2405 | 0 | ((GF_ApertureBox *)clef)->height = (high)<<16 | low; |
2406 | | |
2407 | |
|
2408 | 0 | return GF_OK; |
2409 | 0 | } |
2410 | | |
2411 | | GF_EXPORT |
2412 | | GF_Err gf_isom_set_image_sequence_coding_constraints(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, Bool remove, Bool all_ref_pics_intra, Bool intra_pred_used, u32 max_ref_per_pic) |
2413 | 0 | { |
2414 | 0 | GF_Err e; |
2415 | 0 | GF_TrackBox *trak; |
2416 | 0 | GF_SampleEntryBox *entry; |
2417 | 0 | GF_SampleDescriptionBox *stsd; |
2418 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2419 | 0 | if (e) return e; |
2420 | | |
2421 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2422 | 0 | if (!trak) return GF_BAD_PARAM; |
2423 | | |
2424 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2425 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
2426 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2427 | 0 | return movie->LastError = GF_BAD_PARAM; |
2428 | 0 | } |
2429 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
2430 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2431 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2432 | 0 | if (!movie->keep_utc) |
2433 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2434 | |
|
2435 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
2436 | | |
2437 | 0 | GF_CodingConstraintsBox*ccst = (GF_CodingConstraintsBox*) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_CCST); |
2438 | 0 | if (remove) { |
2439 | 0 | if (ccst) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)ccst); |
2440 | 0 | return GF_OK; |
2441 | 0 | } |
2442 | 0 | if (!ccst) { |
2443 | 0 | ccst = (GF_CodingConstraintsBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_CCST); |
2444 | 0 | if (!ccst) return GF_OUT_OF_MEM; |
2445 | 0 | } |
2446 | 0 | ccst->all_ref_pics_intra = all_ref_pics_intra; |
2447 | 0 | ccst->intra_pred_used = intra_pred_used; |
2448 | 0 | ccst->max_ref_per_pic = max_ref_per_pic; |
2449 | 0 | return GF_OK; |
2450 | 0 | } |
2451 | | |
2452 | | GF_EXPORT |
2453 | | GF_Err gf_isom_set_image_sequence_alpha(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, Bool remove) |
2454 | 0 | { |
2455 | 0 | GF_Err e; |
2456 | 0 | GF_TrackBox *trak; |
2457 | 0 | GF_SampleEntryBox *entry; |
2458 | 0 | GF_SampleDescriptionBox *stsd; |
2459 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2460 | 0 | if (e) return e; |
2461 | | |
2462 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2463 | 0 | if (!trak) return GF_BAD_PARAM; |
2464 | | |
2465 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2466 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
2467 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2468 | 0 | return movie->LastError = GF_BAD_PARAM; |
2469 | 0 | } |
2470 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
2471 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2472 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2473 | 0 | if (!movie->keep_utc) |
2474 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2475 | |
|
2476 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
2477 | | |
2478 | 0 | GF_AuxiliaryTypeInfoBox *auxi = (GF_AuxiliaryTypeInfoBox *)gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_AUXI); |
2479 | 0 | if (remove) { |
2480 | 0 | if (auxi) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)auxi); |
2481 | 0 | return GF_OK; |
2482 | 0 | } |
2483 | 0 | if (!auxi) { |
2484 | 0 | auxi = (GF_AuxiliaryTypeInfoBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_AUXI); |
2485 | 0 | if (!auxi) return GF_OUT_OF_MEM; |
2486 | 0 | } |
2487 | 0 | auxi->aux_track_type = gf_strdup("urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"); |
2488 | 0 | return GF_OK; |
2489 | 0 | } |
2490 | | |
2491 | | GF_EXPORT |
2492 | | GF_Err gf_isom_set_audio_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 sampleRate, u32 nbChannels, u8 bitsPerSample, GF_AudioSampleEntryImportMode asemode) |
2493 | 0 | { |
2494 | 0 | GF_Err e; |
2495 | 0 | u32 i, old_qtff_mode=GF_ISOM_AUDIO_QTFF_NONE; |
2496 | 0 | GF_TrackBox *trak; |
2497 | 0 | GF_SampleEntryBox *entry; |
2498 | 0 | GF_AudioSampleEntryBox*aud_entry; |
2499 | 0 | GF_SampleDescriptionBox *stsd; |
2500 | 0 | GF_Box *wave_box = NULL; |
2501 | 0 | GF_Box *gf_isom_audio_sample_get_audio_codec_cfg_box(GF_AudioSampleEntryBox *ptr); |
2502 | 0 | GF_Box *codec_ext = NULL; |
2503 | | #if 0 |
2504 | | GF_ChannelLayoutInfoBox *chan=NULL; |
2505 | | #endif |
2506 | 0 | GF_OriginalFormatBox *frma=NULL; |
2507 | 0 | GF_ChromaInfoBox *enda=NULL; |
2508 | 0 | GF_ESDBox *esds=NULL; |
2509 | 0 | GF_Box *terminator=NULL; |
2510 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2511 | 0 | if (e) return e; |
2512 | | |
2513 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2514 | 0 | if (!trak) return GF_BAD_PARAM; |
2515 | | |
2516 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2517 | 0 | if (!stsd) { |
2518 | 0 | return movie->LastError = GF_ISOM_INVALID_FILE; |
2519 | 0 | } |
2520 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2521 | 0 | return movie->LastError = GF_BAD_PARAM; |
2522 | 0 | } |
2523 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
2524 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2525 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2526 | 0 | if (!movie->keep_utc) |
2527 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2528 | |
|
2529 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_AUDIO) return GF_BAD_PARAM; |
2530 | 0 | aud_entry = (GF_AudioSampleEntryBox*) entry; |
2531 | |
|
2532 | 0 | if (entry->type==GF_ISOM_BOX_TYPE_MLPA) { |
2533 | 0 | aud_entry->samplerate_hi = sampleRate>>16; |
2534 | 0 | aud_entry->samplerate_lo = sampleRate & 0x0000FFFF; |
2535 | 0 | } else { |
2536 | 0 | aud_entry->samplerate_hi = sampleRate; |
2537 | 0 | aud_entry->samplerate_lo = 0; |
2538 | 0 | } |
2539 | 0 | aud_entry->bitspersample = bitsPerSample; |
2540 | |
|
2541 | 0 | switch (asemode) { |
2542 | 0 | case GF_IMPORT_AUDIO_SAMPLE_ENTRY_NOT_SET: |
2543 | 0 | case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v0_DEFAULT: |
2544 | 0 | stsd->version = 0; |
2545 | 0 | aud_entry->version = 0; |
2546 | 0 | aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE; |
2547 | 0 | aud_entry->channel_count = nbChannels; // Get from bitstream |
2548 | | |
2549 | | // According to ETSI TS 102 366 V1.4.1 (2017-09), the channel_count of AC3, EC3 (based on AudioSampleEntry) |
2550 | | // should always be 2, and the sample size should always be 16 |
2551 | 0 | if (aud_entry->type == GF_ISOM_BOX_TYPE_AC3 || aud_entry->type == GF_ISOM_BOX_TYPE_EC3 || aud_entry->type == GF_ISOM_BOX_TYPE_AC4) { |
2552 | 0 | aud_entry->channel_count = 2; |
2553 | 0 | aud_entry->bitspersample = 16; |
2554 | 0 | } |
2555 | 0 | break; |
2556 | 0 | case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v0_BS: |
2557 | 0 | stsd->version = 0; |
2558 | 0 | aud_entry->version = 0; |
2559 | 0 | aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE; |
2560 | 0 | aud_entry->channel_count = nbChannels; |
2561 | 0 | break; |
2562 | 0 | case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v0_2: |
2563 | 0 | stsd->version = 0; |
2564 | 0 | aud_entry->version = 0; |
2565 | 0 | aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE; |
2566 | 0 | aud_entry->channel_count = 2; |
2567 | 0 | break; |
2568 | 0 | case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_MPEG: |
2569 | 0 | stsd->version = 1; |
2570 | 0 | aud_entry->version = 1; |
2571 | 0 | aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE; |
2572 | 0 | aud_entry->channel_count = nbChannels; |
2573 | 0 | break; |
2574 | 0 | case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF: |
2575 | 0 | stsd->version = 0; |
2576 | | //don't change if already v2 |
2577 | 0 | if ((aud_entry->version==2) && aud_entry->qtff_mode) { |
2578 | 0 | break; |
2579 | 0 | } |
2580 | 0 | aud_entry->version = 1; |
2581 | 0 | aud_entry->channel_count = nbChannels; |
2582 | 0 | old_qtff_mode = aud_entry->qtff_mode; |
2583 | 0 | if (aud_entry->qtff_mode != GF_ISOM_AUDIO_QTFF_ON_EXT_VALID) |
2584 | 0 | aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_ON_NOEXT; |
2585 | 0 | break; |
2586 | 0 | } |
2587 | | |
2588 | 0 | aud_entry->compression_id = 0; |
2589 | 0 | GF_SamplingRateBox *srat = NULL; |
2590 | | //check for wave+children and chan for QTFF or remove them for isobmff |
2591 | 0 | for (i=0; i<gf_list_count(aud_entry->child_boxes); i++) { |
2592 | 0 | GF_Box *b = gf_list_get(aud_entry->child_boxes, i); |
2593 | 0 | if (b->type == GF_ISOM_BOX_TYPE_SRAT) srat = (GF_SamplingRateBox *)b; |
2594 | |
|
2595 | 0 | if ((b->type != GF_QT_BOX_TYPE_WAVE) && (b->type != GF_QT_BOX_TYPE_CHAN) ) continue; |
2596 | 0 | if (asemode==GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF) { |
2597 | 0 | if (b->type == GF_QT_BOX_TYPE_WAVE) wave_box = b; |
2598 | | #if 0 |
2599 | | else chan = (GF_ChannelLayoutInfoBox *)b; |
2600 | | #endif |
2601 | |
|
2602 | 0 | } else { |
2603 | 0 | gf_isom_box_del_parent(&aud_entry->child_boxes, b); |
2604 | 0 | i--; |
2605 | 0 | } |
2606 | 0 | } |
2607 | |
|
2608 | 0 | if (sampleRate>0xFFFF) { |
2609 | 0 | if (!srat) { |
2610 | 0 | srat = (GF_SamplingRateBox *) gf_isom_box_new_parent(&aud_entry->child_boxes, GF_ISOM_BOX_TYPE_SRAT); |
2611 | 0 | } |
2612 | 0 | if (srat) srat->sampling_rate = sampleRate; |
2613 | 0 | } |
2614 | | |
2615 | | //TODO: insert channelLayout for ISOBMFF |
2616 | 0 | if (asemode!=GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF) return GF_OK; |
2617 | | |
2618 | 0 | if (aud_entry->type==GF_ISOM_BOX_TYPE_MP4A) |
2619 | 0 | aud_entry->compression_id = -2; |
2620 | |
|
2621 | 0 | if (!aud_entry->child_boxes) aud_entry->child_boxes = gf_list_new(); |
2622 | |
|
2623 | | #if 0 |
2624 | | if (!chan) { |
2625 | | chan = (GF_ChannelLayoutInfoBox *) gf_isom_box_new_parent(&aud_entry->child_boxes, GF_QT_BOX_TYPE_CHAN); |
2626 | | } |
2627 | | //TODO, proper channel mapping |
2628 | | chan->layout_tag = (nbChannels==2) ? 6750210 : 6553601; |
2629 | | #endif |
2630 | |
|
2631 | 0 | codec_ext = gf_isom_audio_sample_get_audio_codec_cfg_box((GF_AudioSampleEntryBox *)aud_entry); |
2632 | 0 | if (!codec_ext) return GF_OK; |
2633 | | |
2634 | 0 | if (!wave_box) { |
2635 | 0 | wave_box = gf_isom_box_new_parent(&aud_entry->child_boxes, GF_QT_BOX_TYPE_WAVE); |
2636 | 0 | } |
2637 | |
|
2638 | 0 | for (i=0; i<gf_list_count(wave_box->child_boxes); i++) { |
2639 | 0 | GF_Box *b = gf_list_get(wave_box->child_boxes, i); |
2640 | 0 | switch (b->type) { |
2641 | 0 | case GF_QT_BOX_TYPE_ENDA: |
2642 | 0 | enda = (GF_ChromaInfoBox *)b; |
2643 | 0 | break; |
2644 | 0 | case GF_QT_BOX_TYPE_FRMA: |
2645 | 0 | frma = (GF_OriginalFormatBox *)b; |
2646 | 0 | break; |
2647 | 0 | case GF_ISOM_BOX_TYPE_ESDS: |
2648 | 0 | esds = (GF_ESDBox *)b; |
2649 | 0 | break; |
2650 | 0 | case GF_ISOM_BOX_TYPE_UNKNOWN: |
2651 | 0 | if ( ((GF_UnknownBox*)b)->original_4cc == 0) |
2652 | 0 | terminator = b; |
2653 | 0 | break; |
2654 | 0 | case 0: |
2655 | 0 | terminator = b; |
2656 | 0 | break; |
2657 | 0 | } |
2658 | 0 | } |
2659 | 0 | if (!wave_box->child_boxes) wave_box->child_boxes = gf_list_new(); |
2660 | | |
2661 | | //do not use new_parent, we do this manually to ensure the order |
2662 | 0 | aud_entry->qtff_mode = old_qtff_mode ? old_qtff_mode : GF_ISOM_AUDIO_QTFF_ON_NOEXT; |
2663 | 0 | if (!frma) { |
2664 | 0 | frma = (GF_OriginalFormatBox *)gf_isom_box_new(GF_QT_BOX_TYPE_FRMA); |
2665 | 0 | } else { |
2666 | 0 | gf_list_del_item(wave_box->child_boxes, frma); |
2667 | 0 | } |
2668 | 0 | gf_list_add(wave_box->child_boxes, frma); |
2669 | |
|
2670 | 0 | if (esds) gf_list_del_item(wave_box->child_boxes, esds); |
2671 | 0 | if (!esds && (aud_entry->type==GF_ISOM_BOX_TYPE_MP4A) && ((GF_MPEGAudioSampleEntryBox*)aud_entry)->esd) { |
2672 | 0 | gf_list_del_item(entry->child_boxes, (GF_Box *) ((GF_MPEGAudioSampleEntryBox*)aud_entry)->esd); |
2673 | 0 | gf_list_add(wave_box->child_boxes, (GF_Box *) ((GF_MPEGAudioSampleEntryBox*)aud_entry)->esd); |
2674 | 0 | } |
2675 | |
|
2676 | 0 | if (!enda) { |
2677 | 0 | enda = (GF_ChromaInfoBox *)gf_isom_box_new(GF_QT_BOX_TYPE_ENDA); |
2678 | 0 | } else { |
2679 | 0 | gf_list_del_item(wave_box->child_boxes, enda); |
2680 | 0 | } |
2681 | 0 | enda->chroma=1; |
2682 | 0 | gf_list_add(wave_box->child_boxes, enda); |
2683 | |
|
2684 | 0 | if (!terminator) { |
2685 | 0 | terminator = gf_isom_box_new(0); |
2686 | 0 | } else { |
2687 | 0 | gf_list_del_item(wave_box->child_boxes, terminator); |
2688 | 0 | } |
2689 | 0 | gf_list_add(wave_box->child_boxes, terminator); |
2690 | |
|
2691 | 0 | if (aud_entry->type==GF_ISOM_BOX_TYPE_GNRA) { |
2692 | 0 | frma->data_format = ((GF_GenericAudioSampleEntryBox*) aud_entry)->EntryType; |
2693 | 0 | } else { |
2694 | 0 | frma->data_format = aud_entry->type; |
2695 | 0 | } |
2696 | |
|
2697 | 0 | return GF_OK; |
2698 | 0 | } |
2699 | | |
2700 | | GF_EXPORT |
2701 | | GF_Err gf_isom_set_audio_layout(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, GF_AudioChannelLayout *layout) |
2702 | 0 | { |
2703 | 0 | GF_Err e; |
2704 | 0 | GF_TrackBox *trak; |
2705 | 0 | GF_SampleEntryBox *entry; |
2706 | 0 | GF_AudioSampleEntryBox*aud_entry; |
2707 | 0 | GF_SampleDescriptionBox *stsd; |
2708 | 0 | GF_ChannelLayoutBox *chnl; |
2709 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2710 | 0 | if (e) return e; |
2711 | | |
2712 | 0 | if (!layout) return GF_BAD_PARAM; |
2713 | 0 | if ((layout->stream_structure & 1) && (layout->definedLayout==0) && (layout->channels_count>=64)) |
2714 | 0 | return GF_BAD_PARAM; |
2715 | | |
2716 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2717 | 0 | if (!trak) return GF_BAD_PARAM; |
2718 | | |
2719 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2720 | 0 | if (!stsd) { |
2721 | 0 | return movie->LastError = GF_ISOM_INVALID_FILE; |
2722 | 0 | } |
2723 | 0 | if (!sampleDescriptionIndex || sampleDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2724 | 0 | return movie->LastError = GF_BAD_PARAM; |
2725 | 0 | } |
2726 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, sampleDescriptionIndex - 1); |
2727 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2728 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2729 | 0 | if (!movie->keep_utc) |
2730 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2731 | |
|
2732 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_AUDIO) return GF_BAD_PARAM; |
2733 | 0 | aud_entry = (GF_AudioSampleEntryBox*) entry; |
2734 | 0 | if (aud_entry->qtff_mode) { |
2735 | 0 | u32 sr = aud_entry->samplerate_hi; |
2736 | 0 | if (aud_entry->type==GF_ISOM_BOX_TYPE_MLPA) { |
2737 | 0 | sr <<= 16; |
2738 | 0 | sr |= aud_entry->samplerate_lo; |
2739 | 0 | } |
2740 | 0 | e = gf_isom_set_audio_info(movie, trackNumber, sampleDescriptionIndex, sr, aud_entry->channel_count, (u8) aud_entry->bitspersample, GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_MPEG); |
2741 | 0 | if (e) return e; |
2742 | 0 | } |
2743 | 0 | chnl = (GF_ChannelLayoutBox *) gf_isom_box_find_child(aud_entry->child_boxes, GF_ISOM_BOX_TYPE_CHNL); |
2744 | 0 | if (!chnl) { |
2745 | 0 | chnl = (GF_ChannelLayoutBox *)gf_isom_box_new_parent(&aud_entry->child_boxes, GF_ISOM_BOX_TYPE_CHNL); |
2746 | 0 | if (!chnl) return GF_OUT_OF_MEM; |
2747 | 0 | } |
2748 | 0 | aud_entry->channel_count = layout->channels_count; |
2749 | 0 | memcpy(&chnl->layout, layout, sizeof(GF_AudioChannelLayout)); |
2750 | 0 | return GF_OK; |
2751 | 0 | } |
2752 | | |
2753 | | //set the storage mode of a file (FLAT, STREAMABLE, INTERLEAVED) |
2754 | | GF_EXPORT |
2755 | | GF_Err gf_isom_set_storage_mode(GF_ISOFile *movie, GF_ISOStorageMode storageMode) |
2756 | 0 | { |
2757 | 0 | GF_Err e; |
2758 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2759 | 0 | if (e) return e; |
2760 | | |
2761 | 0 | switch (storageMode) { |
2762 | 0 | case GF_ISOM_STORE_FLAT: |
2763 | 0 | case GF_ISOM_STORE_STREAMABLE: |
2764 | 0 | case GF_ISOM_STORE_INTERLEAVED: |
2765 | 0 | case GF_ISOM_STORE_DRIFT_INTERLEAVED: |
2766 | 0 | case GF_ISOM_STORE_TIGHT: |
2767 | 0 | case GF_ISOM_STORE_FASTSTART: |
2768 | 0 | movie->storageMode = storageMode; |
2769 | | //specifying a storage mode disables inplace rewrite |
2770 | 0 | gf_isom_disable_inplace_rewrite(movie); |
2771 | 0 | return GF_OK; |
2772 | 0 | default: |
2773 | 0 | return GF_BAD_PARAM; |
2774 | 0 | } |
2775 | 0 | } |
2776 | | |
2777 | | |
2778 | | GF_EXPORT |
2779 | | GF_Err gf_isom_enable_compression(GF_ISOFile *file, GF_ISOCompressMode compress_mode, u32 compress_flags) |
2780 | 0 | { |
2781 | 0 | if (!file) return GF_BAD_PARAM; |
2782 | 0 | file->compress_mode = compress_mode; |
2783 | 0 | file->compress_flags = compress_flags; |
2784 | 0 | return GF_OK; |
2785 | 0 | } |
2786 | | |
2787 | | GF_EXPORT |
2788 | | GF_Err gf_isom_force_64bit_chunk_offset(GF_ISOFile *file, Bool set_on) |
2789 | 0 | { |
2790 | 0 | if (!file) return GF_BAD_PARAM; |
2791 | 0 | file->force_co64 = set_on; |
2792 | 0 | return GF_OK; |
2793 | 0 | } |
2794 | | |
2795 | | |
2796 | | //update or insert a new edit segment in the track time line. Edits are used to modify |
2797 | | //the media normal timing. EditTime and EditDuration are expressed in Movie TimeScale |
2798 | | //If a segment with EditTime already exists, IT IS ERASED |
2799 | | static GF_Err gf_isom_set_edit_internal(GF_ISOFile *movie, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, u32 media_rate, GF_ISOEditType EditMode) |
2800 | 0 | { |
2801 | 0 | GF_TrackBox *trak; |
2802 | 0 | GF_EditBox *edts; |
2803 | 0 | GF_EditListBox *elst; |
2804 | 0 | GF_EdtsEntry *ent, *newEnt; |
2805 | 0 | u32 i; |
2806 | 0 | GF_Err e; |
2807 | 0 | u64 startTime; |
2808 | |
|
2809 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2810 | 0 | if (e) return e; |
2811 | | |
2812 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2813 | 0 | if (!trak) return GF_BAD_PARAM; |
2814 | | |
2815 | 0 | edts = trak->editBox; |
2816 | 0 | if (! edts) { |
2817 | 0 | edts = (GF_EditBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_EDTS); |
2818 | 0 | if (!edts) return GF_OUT_OF_MEM; |
2819 | 0 | trak_on_child_box((GF_Box*)trak, (GF_Box *)edts, GF_FALSE); |
2820 | 0 | } |
2821 | 0 | elst = edts->editList; |
2822 | 0 | if (!elst) { |
2823 | 0 | elst = (GF_EditListBox *) gf_isom_box_new_parent(&edts->child_boxes, GF_ISOM_BOX_TYPE_ELST); |
2824 | 0 | if (!elst) return GF_OUT_OF_MEM; |
2825 | 0 | edts_on_child_box((GF_Box*)edts, (GF_Box *)elst, GF_FALSE); |
2826 | 0 | } |
2827 | | |
2828 | 0 | if (trak->extl) { |
2829 | 0 | if (!trak->extl->media_timescale) trak->extl->media_timescale = movie->moov->mvhd->timeScale; |
2830 | 0 | trak->extl->flags |= GF_ISOM_EXTK_EDTS_SKIP; |
2831 | 0 | } |
2832 | 0 | startTime = 0; |
2833 | 0 | ent = NULL; |
2834 | | //get the prev entry to this startTime if any |
2835 | 0 | i=0; |
2836 | 0 | while ((ent = (GF_EdtsEntry *)gf_list_enum(elst->entryList, &i))) { |
2837 | 0 | if ( (startTime <= EditTime) && (startTime + ent->segmentDuration > EditTime) ) |
2838 | 0 | goto found; |
2839 | 0 | startTime += ent->segmentDuration; |
2840 | 0 | } |
2841 | | |
2842 | | //not found, add a new entry, insert empty one if gap |
2843 | 0 | if (!ent) { |
2844 | 0 | Bool empty_inserted = GF_FALSE; |
2845 | 0 | if (startTime != EditTime) { |
2846 | 0 | newEnt = CreateEditEntry(EditTime - startTime, 0, 0, GF_ISOM_EDIT_EMPTY); |
2847 | 0 | if (!newEnt) return GF_OUT_OF_MEM; |
2848 | 0 | empty_inserted = GF_TRUE; |
2849 | 0 | gf_list_add(elst->entryList, newEnt); |
2850 | 0 | } |
2851 | 0 | newEnt = CreateEditEntry(EditDuration, MediaTime, media_rate, EditMode); |
2852 | 0 | if (!newEnt) return GF_OUT_OF_MEM; |
2853 | 0 | gf_list_add(elst->entryList, newEnt); |
2854 | 0 | e = SetTrackDuration(trak); |
2855 | 0 | if (e) return e; |
2856 | 0 | return empty_inserted ? GF_EOS : GF_OK; |
2857 | 0 | } |
2858 | | |
2859 | 0 | startTime -= ent->segmentDuration; |
2860 | |
|
2861 | 0 | found: |
2862 | | |
2863 | | //if same time, we erase the current one... |
2864 | 0 | if (startTime == EditTime) { |
2865 | 0 | ent->segmentDuration = EditDuration; |
2866 | 0 | switch (EditMode) { |
2867 | 0 | case GF_ISOM_EDIT_EMPTY: |
2868 | 0 | ent->mediaRate = 0x10000; |
2869 | 0 | ent->mediaTime = -1; |
2870 | 0 | break; |
2871 | 0 | case GF_ISOM_EDIT_DWELL: |
2872 | 0 | ent->mediaRate = 0; |
2873 | 0 | ent->mediaTime = MediaTime; |
2874 | 0 | break; |
2875 | 0 | default: |
2876 | 0 | ent->mediaRate = media_rate; |
2877 | 0 | ent->mediaTime = MediaTime; |
2878 | 0 | break; |
2879 | 0 | } |
2880 | 0 | return SetTrackDuration(trak); |
2881 | 0 | } |
2882 | | |
2883 | | //adjust so that the prev ent leads to EntryTime |
2884 | | //Note: we don't change the next one as it is unknown to us in |
2885 | | //a lot of case (the author's changes) |
2886 | 0 | ent->segmentDuration = EditTime - startTime; |
2887 | 0 | newEnt = CreateEditEntry(EditDuration, MediaTime, media_rate, EditMode); |
2888 | 0 | if (!newEnt) return GF_OUT_OF_MEM; |
2889 | | //is it the last entry ??? |
2890 | 0 | if (i >= gf_list_count(elst->entryList) - 1) { |
2891 | | //add the new entry at the end |
2892 | 0 | gf_list_add(elst->entryList, newEnt); |
2893 | 0 | return SetTrackDuration(trak); |
2894 | 0 | } else { |
2895 | | //insert after the current entry (which is i) |
2896 | 0 | gf_list_insert(elst->entryList, newEnt, i+1); |
2897 | 0 | return SetTrackDuration(trak); |
2898 | 0 | } |
2899 | 0 | } |
2900 | | |
2901 | | GF_EXPORT |
2902 | | GF_Err gf_isom_set_edit(GF_ISOFile *movie, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, GF_ISOEditType EditMode) |
2903 | 0 | { |
2904 | 0 | return gf_isom_set_edit_internal(movie, trackNumber, EditTime, EditDuration, MediaTime, 0x10000, EditMode); |
2905 | 0 | } |
2906 | | |
2907 | | GF_EXPORT |
2908 | | GF_Err gf_isom_set_edit_with_rate(GF_ISOFile *movie, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, u32 media_rate) |
2909 | 0 | { |
2910 | 0 | return gf_isom_set_edit_internal(movie, trackNumber, EditTime, EditDuration, MediaTime, media_rate, GF_ISOM_EDIT_NORMAL); |
2911 | |
|
2912 | 0 | } |
2913 | | |
2914 | | //remove the edit segments for the whole track |
2915 | | GF_EXPORT |
2916 | | GF_Err gf_isom_remove_edits(GF_ISOFile *movie, u32 trackNumber) |
2917 | 0 | { |
2918 | 0 | GF_Err e; |
2919 | 0 | GF_TrackBox *trak; |
2920 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2921 | 0 | if (!trak) return GF_BAD_PARAM; |
2922 | | |
2923 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2924 | 0 | if (e) return e; |
2925 | | |
2926 | 0 | if (trak->extl) trak->extl->flags &= ~GF_ISOM_EXTK_EDTS_SKIP; |
2927 | 0 | if (!trak->editBox || !trak->editBox->editList) return GF_OK; |
2928 | | |
2929 | 0 | while (gf_list_count(trak->editBox->editList->entryList)) { |
2930 | 0 | GF_EdtsEntry *ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, 0); |
2931 | 0 | gf_free(ent); |
2932 | 0 | e = gf_list_rem(trak->editBox->editList->entryList, 0); |
2933 | 0 | if (e) return e; |
2934 | 0 | } |
2935 | | //then delete the GF_EditBox... |
2936 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->editBox); |
2937 | 0 | trak->editBox = NULL; |
2938 | 0 | return SetTrackDuration(trak); |
2939 | 0 | } |
2940 | | |
2941 | | |
2942 | | //remove the edit segments for the whole track |
2943 | | GF_EXPORT |
2944 | | GF_Err gf_isom_remove_edit(GF_ISOFile *movie, u32 trackNumber, u32 seg_index) |
2945 | 0 | { |
2946 | 0 | GF_Err e; |
2947 | 0 | GF_TrackBox *trak; |
2948 | 0 | GF_EdtsEntry *ent, *next_ent; |
2949 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2950 | 0 | if (!trak || !seg_index) return GF_BAD_PARAM; |
2951 | | |
2952 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2953 | 0 | if (e) return e; |
2954 | | |
2955 | 0 | if (!trak->editBox || !trak->editBox->editList) return GF_OK; |
2956 | 0 | if (gf_list_count(trak->editBox->editList->entryList)<=1) return gf_isom_remove_edits(movie, trackNumber); |
2957 | | |
2958 | 0 | ent = (GF_EdtsEntry*) gf_list_get(trak->editBox->editList->entryList, seg_index-1); |
2959 | 0 | gf_list_rem(trak->editBox->editList->entryList, seg_index-1); |
2960 | 0 | next_ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, seg_index-1); |
2961 | 0 | if (next_ent) next_ent->segmentDuration += ent->segmentDuration; |
2962 | 0 | gf_free(ent); |
2963 | 0 | return SetTrackDuration(trak); |
2964 | 0 | } |
2965 | | |
2966 | | |
2967 | | GF_EXPORT |
2968 | | GF_Err gf_isom_append_edit(GF_ISOFile *movie, u32 trackNumber, u64 EditDuration, u64 MediaTime, GF_ISOEditType EditMode) |
2969 | 0 | { |
2970 | 0 | GF_Err e; |
2971 | 0 | GF_TrackBox *trak; |
2972 | 0 | GF_EdtsEntry *ent; |
2973 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2974 | 0 | if (!trak) return GF_BAD_PARAM; |
2975 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2976 | 0 | if (e) return e; |
2977 | | |
2978 | 0 | if (!trak->editBox) { |
2979 | 0 | GF_EditBox *edts = (GF_EditBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_EDTS); |
2980 | 0 | if (!edts) return GF_OUT_OF_MEM; |
2981 | 0 | trak_on_child_box((GF_Box*)trak, (GF_Box *)edts, GF_FALSE); |
2982 | 0 | gf_assert(trak->editBox); |
2983 | 0 | } |
2984 | 0 | if (!trak->editBox->editList) { |
2985 | 0 | GF_EditListBox *elst = (GF_EditListBox *) gf_isom_box_new_parent(&trak->editBox->child_boxes, GF_ISOM_BOX_TYPE_ELST); |
2986 | 0 | if (!elst) return GF_OUT_OF_MEM; |
2987 | 0 | edts_on_child_box((GF_Box*)trak->editBox, (GF_Box *)elst, GF_FALSE); |
2988 | 0 | gf_assert(trak->editBox->editList); |
2989 | 0 | } |
2990 | 0 | ent = (GF_EdtsEntry *)gf_malloc(sizeof(GF_EdtsEntry)); |
2991 | 0 | if (!ent) return GF_OUT_OF_MEM; |
2992 | | |
2993 | 0 | ent->segmentDuration = EditDuration; |
2994 | 0 | switch (EditMode) { |
2995 | 0 | case GF_ISOM_EDIT_EMPTY: |
2996 | 0 | ent->mediaRate = 0x10000; |
2997 | 0 | ent->mediaTime = -1; |
2998 | 0 | break; |
2999 | 0 | case GF_ISOM_EDIT_DWELL: |
3000 | 0 | ent->mediaRate = 0; |
3001 | 0 | ent->mediaTime = MediaTime; |
3002 | 0 | break; |
3003 | 0 | default: |
3004 | 0 | ent->mediaRate = 0x10000; |
3005 | 0 | ent->mediaTime = MediaTime; |
3006 | 0 | break; |
3007 | 0 | } |
3008 | 0 | gf_list_add(trak->editBox->editList->entryList, ent); |
3009 | 0 | return SetTrackDuration(trak); |
3010 | 0 | } |
3011 | | |
3012 | | GF_EXPORT |
3013 | | GF_Err gf_isom_modify_edit(GF_ISOFile *movie, u32 trackNumber, u32 seg_index, u64 EditDuration, u64 MediaTime, GF_ISOEditType EditMode) |
3014 | 0 | { |
3015 | 0 | GF_Err e; |
3016 | 0 | GF_TrackBox *trak; |
3017 | 0 | GF_EdtsEntry *ent; |
3018 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
3019 | 0 | if (!trak || !seg_index) return GF_BAD_PARAM; |
3020 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3021 | 0 | if (e) return e; |
3022 | | |
3023 | 0 | if (!trak->editBox || !trak->editBox->editList) return GF_OK; |
3024 | 0 | if (gf_list_count(trak->editBox->editList->entryList)<seg_index) return GF_BAD_PARAM; |
3025 | 0 | ent = (GF_EdtsEntry*) gf_list_get(trak->editBox->editList->entryList, seg_index-1); |
3026 | |
|
3027 | 0 | ent->segmentDuration = EditDuration; |
3028 | 0 | switch (EditMode) { |
3029 | 0 | case GF_ISOM_EDIT_EMPTY: |
3030 | 0 | ent->mediaRate = 0x10000; |
3031 | 0 | ent->mediaTime = -1; |
3032 | 0 | break; |
3033 | 0 | case GF_ISOM_EDIT_DWELL: |
3034 | 0 | ent->mediaRate = 0; |
3035 | 0 | ent->mediaTime = MediaTime; |
3036 | 0 | break; |
3037 | 0 | default: |
3038 | 0 | ent->mediaRate = 0x10000; |
3039 | 0 | ent->mediaTime = MediaTime; |
3040 | 0 | break; |
3041 | 0 | } |
3042 | 0 | return SetTrackDuration(trak); |
3043 | 0 | } |
3044 | | |
3045 | | static void update_next_track_id(GF_ISOFile *movie) |
3046 | 0 | { |
3047 | 0 | GF_TrackBox *trak; |
3048 | | /*update next track ID*/ |
3049 | 0 | movie->moov->mvhd->nextTrackID = 0; |
3050 | 0 | u32 i=0; |
3051 | 0 | while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { |
3052 | 0 | if (trak->Header->trackID>movie->moov->mvhd->nextTrackID) |
3053 | 0 | movie->moov->mvhd->nextTrackID = trak->Header->trackID; |
3054 | 0 | } |
3055 | | //shall be larger than the largest track_ID in use |
3056 | 0 | movie->moov->mvhd->nextTrackID++; |
3057 | 0 | } |
3058 | | |
3059 | | //removes the desired track |
3060 | | GF_EXPORT |
3061 | | GF_Err gf_isom_remove_track(GF_ISOFile *movie, u32 trackNumber) |
3062 | 0 | { |
3063 | 0 | GF_Err e; |
3064 | 0 | GF_TrackBox *the_trak, *trak; |
3065 | 0 | GF_TrackReferenceTypeBox *tref; |
3066 | 0 | u32 i, j, k, descIndex; |
3067 | 0 | GF_ISOTrackID *newRefs; |
3068 | 0 | u8 found; |
3069 | 0 | GF_ISOSample *samp; |
3070 | 0 | the_trak = gf_isom_get_track_box(movie, trackNumber); |
3071 | 0 | if (!the_trak) return GF_BAD_PARAM; |
3072 | | |
3073 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3074 | 0 | if (e) return e; |
3075 | | |
3076 | 0 | if (the_trak && the_trak->Media && the_trak->Media->information) { |
3077 | 0 | if (the_trak->Media->information->dataHandler == movie->movieFileMap || the_trak->Media->information->dataHandler == movie->editFileMap) |
3078 | 0 | the_trak->Media->information->dataHandler = NULL; |
3079 | |
|
3080 | 0 | if (the_trak->Media->information->scalableDataHandler == movie->movieFileMap || the_trak->Media->information->scalableDataHandler == movie->editFileMap) |
3081 | 0 | the_trak->Media->information->scalableDataHandler = NULL; |
3082 | 0 | } |
3083 | |
|
3084 | 0 | if (movie->moov->iods && movie->moov->iods->descriptor) { |
3085 | 0 | GF_Descriptor *desc; |
3086 | 0 | GF_ES_ID_Inc *inc; |
3087 | 0 | GF_List *ESDs; |
3088 | 0 | desc = movie->moov->iods->descriptor; |
3089 | 0 | if (desc->tag == GF_ODF_ISOM_IOD_TAG) { |
3090 | 0 | ESDs = ((GF_IsomInitialObjectDescriptor *)desc)->ES_ID_IncDescriptors; |
3091 | 0 | } else if (desc->tag == GF_ODF_ISOM_OD_TAG) { |
3092 | 0 | ESDs = ((GF_IsomObjectDescriptor *)desc)->ES_ID_IncDescriptors; |
3093 | 0 | } else { |
3094 | 0 | return GF_ISOM_INVALID_FILE; |
3095 | 0 | } |
3096 | | |
3097 | | //remove the track ref from the root OD if any |
3098 | 0 | i=0; |
3099 | 0 | while ((inc = (GF_ES_ID_Inc *)gf_list_enum(ESDs, &i))) { |
3100 | 0 | if (inc->trackID == the_trak->Header->trackID) { |
3101 | 0 | gf_odf_desc_del((GF_Descriptor *)inc); |
3102 | 0 | i--; |
3103 | 0 | gf_list_rem(ESDs, i); |
3104 | 0 | } |
3105 | 0 | } |
3106 | 0 | } |
3107 | | |
3108 | | //remove the track from the movie |
3109 | 0 | gf_list_del_item(movie->moov->trackList, the_trak); |
3110 | | |
3111 | | //rewrite any OD tracks |
3112 | 0 | i=0; |
3113 | 0 | while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { |
3114 | 0 | if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_OD) continue; |
3115 | | |
3116 | | //this is an OD track... |
3117 | 0 | j = gf_isom_get_sample_count(movie, i); |
3118 | 0 | for (k=0; k < j; k++) { |
3119 | | //getting the sample will remove the references to the deleted track in the output OD frame |
3120 | 0 | samp = gf_isom_get_sample(movie, i, k+1, &descIndex); |
3121 | 0 | if (!samp) break; |
3122 | | //so let's update with the new OD frame ! If the sample is empty, remove it |
3123 | 0 | if (!samp->dataLength) { |
3124 | 0 | e = gf_isom_remove_sample(movie, i, k+1); |
3125 | 0 | if (e) return e; |
3126 | 0 | } else { |
3127 | 0 | e = gf_isom_update_sample(movie, i, k+1, samp, GF_TRUE); |
3128 | 0 | if (e) return e; |
3129 | 0 | } |
3130 | | //and don't forget to delete the sample |
3131 | 0 | gf_isom_sample_del(&samp); |
3132 | 0 | } |
3133 | 0 | } |
3134 | | |
3135 | | //remove the track ref from any "tref" box in all tracks, except the one to delete |
3136 | | //note that we don't touch scal references, as we don't want to rewrite AVC/HEVC samples ... |
3137 | 0 | i=0; |
3138 | 0 | while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { |
3139 | 0 | if (! trak->References || ! gf_list_count(trak->References->child_boxes)) continue; |
3140 | | |
3141 | 0 | j=0; |
3142 | 0 | while ((tref = (GF_TrackReferenceTypeBox *)gf_list_enum(trak->References->child_boxes, &j))) { |
3143 | 0 | if (tref->reference_type==GF_ISOM_REF_SCAL) |
3144 | 0 | continue; |
3145 | | |
3146 | 0 | found = 0; |
3147 | 0 | for (k=0; k<tref->trackIDCount; k++) { |
3148 | 0 | if (tref->trackIDs[k] == the_trak->Header->trackID) found++; |
3149 | 0 | } |
3150 | 0 | if (!found) continue; |
3151 | | //no more refs, remove this ref_type |
3152 | 0 | if (found == tref->trackIDCount) { |
3153 | 0 | gf_isom_box_del_parent(&trak->References->child_boxes, (GF_Box *)tref); |
3154 | 0 | j--; |
3155 | 0 | } else { |
3156 | 0 | newRefs = (GF_ISOTrackID*)gf_malloc(sizeof(GF_ISOTrackID) * (tref->trackIDCount - found)); |
3157 | 0 | if (!newRefs) return GF_OUT_OF_MEM; |
3158 | 0 | found = 0; |
3159 | 0 | for (k = 0; k < tref->trackIDCount; k++) { |
3160 | 0 | if (tref->trackIDs[k] != the_trak->Header->trackID) { |
3161 | 0 | newRefs[k-found] = tref->trackIDs[k]; |
3162 | 0 | } else { |
3163 | 0 | found++; |
3164 | 0 | } |
3165 | 0 | } |
3166 | 0 | gf_free(tref->trackIDs); |
3167 | 0 | tref->trackIDs = newRefs; |
3168 | 0 | tref->trackIDCount -= found; |
3169 | 0 | } |
3170 | 0 | } |
3171 | | //a little opt: remove the ref box if empty... |
3172 | 0 | if (! gf_list_count(trak->References->child_boxes)) { |
3173 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->References); |
3174 | 0 | trak->References = NULL; |
3175 | 0 | } |
3176 | 0 | } |
3177 | | |
3178 | 0 | gf_isom_disable_inplace_rewrite(movie); |
3179 | |
|
3180 | 0 | gf_isom_meta_track_remove(movie, the_trak, 0); |
3181 | | |
3182 | | //delete the track |
3183 | 0 | gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box *)the_trak); |
3184 | | |
3185 | | /*update next track ID*/ |
3186 | 0 | update_next_track_id(movie); |
3187 | |
|
3188 | 0 | if (!gf_list_count(movie->moov->trackList)) { |
3189 | 0 | gf_list_del_item(movie->TopBoxes, movie->moov); |
3190 | 0 | gf_isom_box_del((GF_Box *)movie->moov); |
3191 | 0 | movie->moov = NULL; |
3192 | 0 | } |
3193 | 0 | return GF_OK; |
3194 | 0 | } |
3195 | | |
3196 | | |
3197 | | GF_EXPORT |
3198 | | GF_Err gf_isom_set_copyright(GF_ISOFile *movie, const char *threeCharCode, char *notice) |
3199 | 0 | { |
3200 | 0 | GF_Err e; |
3201 | 0 | GF_CopyrightBox *ptr; |
3202 | 0 | GF_UserDataMap *map; |
3203 | 0 | u32 count, i; |
3204 | |
|
3205 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3206 | 0 | if (e) return e; |
3207 | | |
3208 | 0 | if (!notice || !threeCharCode) return GF_BAD_PARAM; |
3209 | | |
3210 | 0 | e = gf_isom_insert_moov(movie); |
3211 | 0 | if (e) return e; |
3212 | | |
3213 | 0 | if (!movie->moov->udta) { |
3214 | 0 | e = moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3215 | 0 | if (e) return e; |
3216 | 0 | } |
3217 | 0 | map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL); |
3218 | |
|
3219 | 0 | if (map) { |
3220 | | //try to find one in our language... |
3221 | 0 | count = gf_list_count(map->boxes); |
3222 | 0 | for (i=0; i<count; i++) { |
3223 | 0 | ptr = (GF_CopyrightBox*)gf_list_get(map->boxes, i); |
3224 | 0 | if (!strcmp(threeCharCode, (const char *) ptr->packedLanguageCode)) { |
3225 | 0 | gf_free(ptr->notice); |
3226 | 0 | ptr->notice = (char*)gf_malloc(sizeof(char) * (strlen(notice) + 1)); |
3227 | 0 | if (!ptr->notice) return GF_OUT_OF_MEM; |
3228 | 0 | strcpy(ptr->notice, notice); |
3229 | 0 | return GF_OK; |
3230 | 0 | } |
3231 | 0 | } |
3232 | 0 | } |
3233 | | //nope, create one |
3234 | 0 | ptr = (GF_CopyrightBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CPRT); |
3235 | 0 | if (!ptr) return GF_OUT_OF_MEM; |
3236 | | |
3237 | 0 | memcpy(ptr->packedLanguageCode, threeCharCode, 4); |
3238 | 0 | ptr->notice = (char*)gf_malloc(sizeof(char) * (strlen(notice)+1)); |
3239 | 0 | if (!ptr->notice) return GF_OUT_OF_MEM; |
3240 | 0 | strcpy(ptr->notice, notice); |
3241 | 0 | return udta_on_child_box_ex((GF_Box *)movie->moov->udta, (GF_Box *) ptr, GF_FALSE, GF_FALSE); |
3242 | 0 | } |
3243 | | |
3244 | | GF_EXPORT |
3245 | | GF_Err gf_isom_add_track_kind(GF_ISOFile *movie, u32 trackNumber, const char *schemeURI, const char *value) |
3246 | 0 | { |
3247 | 0 | GF_Err e; |
3248 | 0 | GF_KindBox *ptr; |
3249 | 0 | GF_UserDataBox *udta; |
3250 | 0 | GF_UserDataMap *map; |
3251 | 0 | u32 i, count; |
3252 | |
|
3253 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3254 | 0 | if (e) return e; |
3255 | | |
3256 | 0 | e = gf_isom_insert_moov(movie); |
3257 | 0 | if (e) return e; |
3258 | | |
3259 | 0 | if (trackNumber) { |
3260 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
3261 | 0 | if (!trak) return GF_BAD_PARAM; |
3262 | 0 | if (!trak->udta) { |
3263 | 0 | e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3264 | 0 | if (e) return e; |
3265 | 0 | } |
3266 | 0 | udta = trak->udta; |
3267 | 0 | } else { |
3268 | 0 | return GF_BAD_PARAM; |
3269 | 0 | } |
3270 | | |
3271 | | //we may get null on schemeURI if not set in the source |
3272 | 0 | if (!schemeURI) schemeURI = ""; |
3273 | |
|
3274 | 0 | map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_KIND, NULL); |
3275 | 0 | if (map) { |
3276 | 0 | count = gf_list_count(map->boxes); |
3277 | 0 | for (i=0; i<count; i++) { |
3278 | 0 | GF_Box *b = (GF_Box *)gf_list_get(map->boxes, i); |
3279 | 0 | if (b->type == GF_ISOM_BOX_TYPE_KIND) { |
3280 | 0 | GF_KindBox *kb = (GF_KindBox *)b; |
3281 | 0 | if (!strcmp(kb->schemeURI, schemeURI) && |
3282 | 0 | ((value && kb->value && !strcmp(value, kb->value)) || (!value && !kb->value))) { |
3283 | | // Already there |
3284 | 0 | return GF_OK; |
3285 | 0 | } |
3286 | 0 | if (!strcmp(kb->schemeURI, schemeURI)) { |
3287 | 0 | if (kb->value) gf_free(kb->value); |
3288 | 0 | kb->value = value ? gf_strdup(value) : NULL; |
3289 | 0 | return GF_OK; |
3290 | 0 | } |
3291 | 0 | } |
3292 | 0 | } |
3293 | 0 | } |
3294 | | |
3295 | 0 | ptr = (GF_KindBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_KIND); |
3296 | 0 | if (e) return e; |
3297 | | |
3298 | 0 | ptr->schemeURI = gf_strdup(schemeURI); |
3299 | 0 | if (value) ptr->value = gf_strdup(value); |
3300 | 0 | return udta_on_child_box_ex((GF_Box *)udta, (GF_Box *) ptr, GF_FALSE, GF_FALSE); |
3301 | 0 | } |
3302 | | |
3303 | | GF_EXPORT |
3304 | | GF_Err gf_isom_remove_track_kind(GF_ISOFile *movie, u32 trackNumber, const char *schemeURI, const char *value) |
3305 | 0 | { |
3306 | 0 | GF_Err e; |
3307 | 0 | GF_UserDataBox *udta; |
3308 | 0 | GF_UserDataMap *map; |
3309 | 0 | u32 i; |
3310 | |
|
3311 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3312 | 0 | if (e) return e; |
3313 | 0 | e = gf_isom_insert_moov(movie); |
3314 | 0 | if (e) return e; |
3315 | | |
3316 | 0 | if (trackNumber) { |
3317 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
3318 | 0 | if (!trak) return GF_BAD_PARAM; |
3319 | 0 | if (!trak->udta) { |
3320 | 0 | e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3321 | 0 | if (e) return e; |
3322 | 0 | } |
3323 | 0 | udta = trak->udta; |
3324 | 0 | } else { |
3325 | 0 | return GF_OK; |
3326 | 0 | } |
3327 | 0 | map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_KIND, NULL); |
3328 | 0 | if (map) { |
3329 | 0 | for (i=0; i<gf_list_count(map->boxes); i++) { |
3330 | 0 | GF_Box *b = (GF_Box *)gf_list_get(map->boxes, i); |
3331 | 0 | if (b->type == GF_ISOM_BOX_TYPE_KIND) { |
3332 | 0 | GF_KindBox *kb = (GF_KindBox *)b; |
3333 | 0 | if (!schemeURI || |
3334 | 0 | (!strcmp(kb->schemeURI, schemeURI) && |
3335 | 0 | ((value && kb->value && !strcmp(value, kb->value)) || (!value && !kb->value)))) { |
3336 | 0 | gf_isom_box_del_parent(&map->boxes, b); |
3337 | 0 | i--; |
3338 | 0 | } |
3339 | 0 | } |
3340 | 0 | } |
3341 | 0 | } |
3342 | 0 | return GF_OK; |
3343 | 0 | } |
3344 | | |
3345 | | GF_EXPORT |
3346 | | GF_Err gf_isom_add_chapter(GF_ISOFile *movie, u32 trackNumber, u64 timestamp, char *name) |
3347 | 0 | { |
3348 | 0 | GF_Err e; |
3349 | 0 | GF_ChapterListBox *ptr; |
3350 | 0 | u32 i, count; |
3351 | 0 | GF_ChapterEntry *ce; |
3352 | 0 | GF_UserDataBox *udta; |
3353 | 0 | GF_UserDataMap *map; |
3354 | |
|
3355 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3356 | 0 | if (e) return e; |
3357 | | |
3358 | 0 | e = gf_isom_insert_moov(movie); |
3359 | 0 | if (e) return e; |
3360 | | |
3361 | 0 | if (trackNumber) { |
3362 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
3363 | 0 | if (!trak) return GF_BAD_PARAM; |
3364 | 0 | if (!trak->udta) { |
3365 | 0 | e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3366 | 0 | if (e) return e; |
3367 | 0 | } |
3368 | 0 | udta = trak->udta; |
3369 | 0 | } else { |
3370 | 0 | if (!movie->moov->udta) { |
3371 | 0 | e = moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3372 | 0 | if (e) return e; |
3373 | 0 | } |
3374 | 0 | udta = movie->moov->udta; |
3375 | 0 | } |
3376 | | |
3377 | 0 | ptr = NULL; |
3378 | 0 | map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL); |
3379 | 0 | if (!map) { |
3380 | 0 | ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL); |
3381 | 0 | e = udta_on_child_box_ex((GF_Box *)udta, (GF_Box *) ptr, GF_FALSE, GF_FALSE); |
3382 | 0 | if (e) return e; |
3383 | 0 | map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL); |
3384 | 0 | } else { |
3385 | 0 | ptr = (GF_ChapterListBox*)gf_list_get(map->boxes, 0); |
3386 | 0 | } |
3387 | 0 | if (!map) return GF_OUT_OF_MEM; |
3388 | | |
3389 | | /*this may happen if original MP4 is not properly formatted*/ |
3390 | 0 | if (!ptr) { |
3391 | 0 | ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL); |
3392 | 0 | if (!ptr) return GF_OUT_OF_MEM; |
3393 | 0 | gf_list_add(map->boxes, ptr); |
3394 | 0 | } |
3395 | | |
3396 | 0 | GF_SAFEALLOC(ce, GF_ChapterEntry); |
3397 | 0 | if (!ce) return GF_OUT_OF_MEM; |
3398 | | |
3399 | 0 | ce->start_time = timestamp * 10000L; |
3400 | 0 | ce->name = name ? gf_strdup(name) : NULL; |
3401 | | |
3402 | | /*insert in order*/ |
3403 | 0 | count = gf_list_count(ptr->list); |
3404 | 0 | for (i=0; i<count; i++) { |
3405 | 0 | GF_ChapterEntry *ace = (GF_ChapterEntry *)gf_list_get(ptr->list, i); |
3406 | 0 | if (ace->start_time == ce->start_time) { |
3407 | 0 | if (ace->name) gf_free(ace->name); |
3408 | 0 | ace->name = ce->name; |
3409 | 0 | gf_free(ce); |
3410 | 0 | return GF_OK; |
3411 | 0 | } |
3412 | 0 | if (ace->start_time >= ce->start_time) |
3413 | 0 | return gf_list_insert(ptr->list, ce, i); |
3414 | 0 | } |
3415 | 0 | return gf_list_add(ptr->list, ce); |
3416 | 0 | } |
3417 | | |
3418 | | GF_EXPORT |
3419 | | GF_Err gf_isom_remove_chapter(GF_ISOFile *movie, u32 trackNumber, u32 index) |
3420 | 0 | { |
3421 | 0 | GF_Err e; |
3422 | 0 | GF_ChapterListBox *ptr; |
3423 | 0 | GF_ChapterEntry *ce; |
3424 | 0 | GF_UserDataBox *udta; |
3425 | 0 | GF_UserDataMap *map; |
3426 | |
|
3427 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3428 | 0 | if (e) return e; |
3429 | 0 | e = gf_isom_insert_moov(movie); |
3430 | 0 | if (e) return e; |
3431 | | |
3432 | 0 | if (trackNumber) { |
3433 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
3434 | 0 | if (!trak) return GF_BAD_PARAM; |
3435 | 0 | if (!trak->udta) return GF_OK; |
3436 | 0 | udta = trak->udta; |
3437 | 0 | } else { |
3438 | 0 | if (!movie->moov->udta) return GF_OK; |
3439 | 0 | udta = movie->moov->udta; |
3440 | 0 | } |
3441 | | |
3442 | 0 | map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL); |
3443 | 0 | if (!map) return GF_OK; |
3444 | 0 | ptr = (GF_ChapterListBox*)gf_list_get(map->boxes, 0); |
3445 | 0 | if (!ptr) return GF_OK; |
3446 | | |
3447 | 0 | if (index) { |
3448 | 0 | ce = (GF_ChapterEntry *)gf_list_get(ptr->list, index-1); |
3449 | 0 | if (!ce) return GF_BAD_PARAM; |
3450 | 0 | if (ce->name) gf_free(ce->name); |
3451 | 0 | gf_free(ce); |
3452 | 0 | gf_list_rem(ptr->list, index-1); |
3453 | 0 | } else { |
3454 | 0 | while (gf_list_count(ptr->list)) { |
3455 | 0 | ce = (GF_ChapterEntry *)gf_list_get(ptr->list, 0); |
3456 | 0 | if (ce->name) gf_free(ce->name); |
3457 | 0 | gf_free(ce); |
3458 | 0 | gf_list_rem(ptr->list, 0); |
3459 | 0 | } |
3460 | 0 | } |
3461 | 0 | if (!gf_list_count(ptr->list)) { |
3462 | 0 | gf_list_del_item(udta->recordList, map); |
3463 | 0 | gf_isom_box_array_del(map->boxes); |
3464 | 0 | gf_free(map); |
3465 | 0 | } |
3466 | 0 | return GF_OK; |
3467 | 0 | } |
3468 | | |
3469 | | #if 0 //unused |
3470 | | GF_Err gf_isom_remove_copyright(GF_ISOFile *movie, u32 index) |
3471 | | { |
3472 | | GF_Err e; |
3473 | | GF_CopyrightBox *ptr; |
3474 | | GF_UserDataMap *map; |
3475 | | u32 count; |
3476 | | |
3477 | | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3478 | | if (e) return e; |
3479 | | e = gf_isom_insert_moov(movie); |
3480 | | if (e) return e; |
3481 | | |
3482 | | if (!index) return GF_BAD_PARAM; |
3483 | | if (!movie->moov->udta) return GF_OK; |
3484 | | |
3485 | | map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL); |
3486 | | if (!map) return GF_OK; |
3487 | | |
3488 | | count = gf_list_count(map->boxes); |
3489 | | if (index>count) return GF_BAD_PARAM; |
3490 | | |
3491 | | ptr = (GF_CopyrightBox*)gf_list_get(map->boxes, index-1); |
3492 | | if (ptr) { |
3493 | | gf_list_rem(map->boxes, index-1); |
3494 | | if (ptr->notice) gf_free(ptr->notice); |
3495 | | gf_free(ptr); |
3496 | | } |
3497 | | /*last copyright, remove*/ |
3498 | | if (!gf_list_count(map->boxes)) { |
3499 | | gf_list_del_item(movie->moov->udta->recordList, map); |
3500 | | gf_list_del(map->boxes); |
3501 | | gf_free(map); |
3502 | | } |
3503 | | return GF_OK; |
3504 | | } |
3505 | | |
3506 | | GF_Err gf_isom_set_watermark(GF_ISOFile *movie, bin128 UUID, u8* data, u32 length) |
3507 | | { |
3508 | | GF_Err e; |
3509 | | GF_UnknownUUIDBox *ptr; |
3510 | | GF_UserDataMap *map; |
3511 | | |
3512 | | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3513 | | if (e) return e; |
3514 | | |
3515 | | e = gf_isom_insert_moov(movie); |
3516 | | if (e) return e; |
3517 | | if (!movie->moov->udta) { |
3518 | | e = moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA)); |
3519 | | if (e) return e; |
3520 | | } |
3521 | | |
3522 | | map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_UUID, (bin128 *) & UUID); |
3523 | | if (map) { |
3524 | | ptr = (GF_UnknownUUIDBox *)gf_list_get(map->boxes, 0); |
3525 | | if (ptr) { |
3526 | | gf_free(ptr->data); |
3527 | | ptr->data = (char*)gf_malloc(length); |
3528 | | if (!ptr->data) return GF_OUT_OF_MEM; |
3529 | | memcpy(ptr->data, data, length); |
3530 | | ptr->dataSize = length; |
3531 | | return GF_OK; |
3532 | | } |
3533 | | } |
3534 | | //nope, create one |
3535 | | ptr = (GF_UnknownUUIDBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID); |
3536 | | if (!ptr) return GF_OUT_OF_MEM; |
3537 | | |
3538 | | memcpy(ptr->uuid, UUID, 16); |
3539 | | ptr->data = (char*)gf_malloc(length); |
3540 | | if (!ptr->data) return GF_OUT_OF_MEM; |
3541 | | memcpy(ptr->data, data, length); |
3542 | | ptr->dataSize = length; |
3543 | | return udta_on_child_box_ex((GF_Box *)movie->moov->udta, (GF_Box *) ptr, GF_FALSE, GF_FALSE); |
3544 | | } |
3545 | | #endif |
3546 | | |
3547 | | //set the interleaving time of media data (INTERLEAVED mode only) |
3548 | | //InterleaveTime is in MovieTimeScale |
3549 | | GF_EXPORT |
3550 | | GF_Err gf_isom_set_interleave_time(GF_ISOFile *movie, u32 InterleaveTime) |
3551 | 0 | { |
3552 | 0 | GF_Err e; |
3553 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3554 | 0 | if (e) return e; |
3555 | | |
3556 | 0 | if (!InterleaveTime || !movie->moov) return GF_OK; |
3557 | 0 | movie->interleavingTime = InterleaveTime; |
3558 | 0 | return GF_OK; |
3559 | 0 | } |
3560 | | |
3561 | | |
3562 | | |
3563 | | //use a compact track version for sample size. This is not usually recommended |
3564 | | //except for speech codecs where the track has a lot of small samples |
3565 | | //compaction is done automatically while writing based on the track's sample sizes |
3566 | | GF_EXPORT |
3567 | | GF_Err gf_isom_use_compact_size(GF_ISOFile *movie, u32 trackNumber, Bool CompactionOn) |
3568 | 0 | { |
3569 | 0 | GF_TrackBox *trak; |
3570 | 0 | u32 i, size; |
3571 | 0 | GF_SampleSizeBox *stsz; |
3572 | 0 | GF_Err e; |
3573 | |
|
3574 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3575 | 0 | if (e) return e; |
3576 | | |
3577 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
3578 | 0 | if (!trak) return GF_BAD_PARAM; |
3579 | | |
3580 | 0 | if (!trak->Media || !trak->Media->information |
3581 | 0 | || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleSize) |
3582 | 0 | return GF_ISOM_INVALID_FILE; |
3583 | | |
3584 | 0 | stsz = trak->Media->information->sampleTable->SampleSize; |
3585 | | |
3586 | | //switch to regular table |
3587 | 0 | if (!CompactionOn) { |
3588 | 0 | if (stsz->type == GF_ISOM_BOX_TYPE_STSZ) return GF_OK; |
3589 | 0 | stsz->type = GF_ISOM_BOX_TYPE_STSZ; |
3590 | | //invalidate the sampleSize and recompute it |
3591 | 0 | stsz->sampleSize = 0; |
3592 | 0 | if (!stsz->sampleCount) return GF_OK; |
3593 | | //if the table is empty we can only assume the track is empty (no size indication) |
3594 | 0 | if (!stsz->sizes) return GF_OK; |
3595 | 0 | size = stsz->sizes[0]; |
3596 | | //check whether the sizes are all the same or not |
3597 | 0 | for (i=1; i<stsz->sampleCount; i++) { |
3598 | 0 | if (size != stsz->sizes[i]) { |
3599 | 0 | size = 0; |
3600 | 0 | break; |
3601 | 0 | } |
3602 | 0 | } |
3603 | 0 | if (size) { |
3604 | 0 | gf_free(stsz->sizes); |
3605 | 0 | stsz->sizes = NULL; |
3606 | 0 | stsz->sampleSize = size; |
3607 | 0 | } |
3608 | 0 | return GF_OK; |
3609 | 0 | } |
3610 | | |
3611 | | //switch to compact table |
3612 | 0 | if (stsz->type == GF_ISOM_BOX_TYPE_STZ2) return GF_OK; |
3613 | | //fill the table. Although it seems weird , this is needed in case of edition |
3614 | | //after the function is called. NOte however than we force regular table |
3615 | | //at write time if all samples are of same size |
3616 | 0 | if (stsz->sampleSize && stsz->sampleCount) { |
3617 | | //this is a weird table indeed ;) |
3618 | 0 | if (stsz->sizes) gf_free(stsz->sizes); |
3619 | 0 | stsz->sizes = (u32*) gf_malloc(sizeof(u32)*stsz->sampleCount); |
3620 | 0 | if (!stsz->sizes) return GF_OUT_OF_MEM; |
3621 | 0 | memset(stsz->sizes, stsz->sampleSize, sizeof(u32)); |
3622 | 0 | } |
3623 | | //set the SampleSize to 0 while the file is open |
3624 | 0 | stsz->sampleSize = 0; |
3625 | 0 | stsz->type = GF_ISOM_BOX_TYPE_STZ2; |
3626 | 0 | return GF_OK; |
3627 | 0 | } |
3628 | | |
3629 | | |
3630 | | GF_EXPORT |
3631 | | GF_Err gf_isom_disable_brand_rewrite(GF_ISOFile *movie, Bool do_disable) |
3632 | 0 | { |
3633 | 0 | if (!movie) return GF_BAD_PARAM; |
3634 | 0 | movie->disable_brand_rewrite = do_disable ? 1: 0; |
3635 | 0 | return GF_OK; |
3636 | 0 | } |
3637 | | |
3638 | | GF_EXPORT |
3639 | | GF_Err gf_isom_set_brand_info(GF_ISOFile *movie, u32 MajorBrand, u32 MinorVersion) |
3640 | 0 | { |
3641 | 0 | u32 i, *p; |
3642 | |
|
3643 | 0 | if (!movie) return GF_BAD_PARAM; |
3644 | 0 | if (movie->disable_brand_rewrite) return GF_OK; |
3645 | 0 | if (!MajorBrand) return GF_BAD_PARAM; |
3646 | | |
3647 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
3648 | 0 | if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) { |
3649 | 0 | GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3650 | 0 | if (e) return e; |
3651 | | |
3652 | 0 | e = CheckNoData(movie); |
3653 | 0 | if (e) return e; |
3654 | 0 | } |
3655 | 0 | #endif |
3656 | | |
3657 | 0 | if (!movie->brand) { |
3658 | 0 | movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP); |
3659 | 0 | if (!movie->brand) return GF_OUT_OF_MEM; |
3660 | 0 | gf_list_add(movie->TopBoxes, movie->brand); |
3661 | 0 | } |
3662 | | |
3663 | 0 | movie->brand->majorBrand = MajorBrand; |
3664 | 0 | movie->brand->minorVersion = MinorVersion; |
3665 | |
|
3666 | 0 | if (!movie->brand->altBrand) { |
3667 | 0 | movie->brand->altBrand = (u32*)gf_malloc(sizeof(u32)); |
3668 | 0 | if (!movie->brand->altBrand) return GF_OUT_OF_MEM; |
3669 | 0 | movie->brand->altBrand[0] = MajorBrand; |
3670 | 0 | movie->brand->altCount = 1; |
3671 | 0 | return GF_OK; |
3672 | 0 | } |
3673 | | |
3674 | | //if brand already present don't change anything |
3675 | 0 | for (i=0; i<movie->brand->altCount; i++) { |
3676 | 0 | if (movie->brand->altBrand[i] == MajorBrand) return GF_OK; |
3677 | 0 | } |
3678 | 0 | p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount + 1)); |
3679 | 0 | if (!p) return GF_OUT_OF_MEM; |
3680 | 0 | memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount); |
3681 | 0 | p[movie->brand->altCount] = MajorBrand; |
3682 | 0 | movie->brand->altCount += 1; |
3683 | 0 | gf_free(movie->brand->altBrand); |
3684 | 0 | movie->brand->altBrand = p; |
3685 | 0 | return GF_OK; |
3686 | 0 | } |
3687 | | |
3688 | | |
3689 | | GF_EXPORT |
3690 | | GF_Err gf_isom_modify_alternate_brand(GF_ISOFile *movie, u32 Brand, Bool AddIt) |
3691 | 0 | { |
3692 | 0 | u32 i, k, *p; |
3693 | |
|
3694 | 0 | if (!movie) return GF_BAD_PARAM; |
3695 | 0 | if (movie->disable_brand_rewrite) return GF_OK; |
3696 | 0 | if (!Brand) return GF_BAD_PARAM; |
3697 | | |
3698 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
3699 | 0 | if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) { |
3700 | 0 | GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3701 | 0 | if (e) return e; |
3702 | | |
3703 | 0 | e = CheckNoData(movie); |
3704 | 0 | if (e) return e; |
3705 | 0 | } |
3706 | 0 | #endif |
3707 | | |
3708 | 0 | if (!movie->brand && AddIt) { |
3709 | 0 | movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP); |
3710 | 0 | if (!movie->brand) return GF_OUT_OF_MEM; |
3711 | 0 | gf_list_add(movie->TopBoxes, movie->brand); |
3712 | 0 | } |
3713 | 0 | if (!AddIt && !movie->brand) return GF_OK; |
3714 | | |
3715 | | //do not mofify major one |
3716 | 0 | if (!AddIt && movie->brand->majorBrand == Brand) return GF_OK; |
3717 | | |
3718 | 0 | if (!AddIt && movie->brand->altCount == 1) { |
3719 | | //fixes it in case |
3720 | 0 | movie->brand->altBrand[0] = movie->brand->majorBrand; |
3721 | 0 | return GF_OK; |
3722 | 0 | } |
3723 | | //check for the brand |
3724 | 0 | for (i=0; i<movie->brand->altCount; i++) { |
3725 | 0 | if (movie->brand->altBrand[i] == Brand) goto found; |
3726 | 0 | } |
3727 | | //Not found |
3728 | 0 | if (!AddIt) return GF_OK; |
3729 | | //add it |
3730 | 0 | p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount + 1)); |
3731 | 0 | if (!p) return GF_OUT_OF_MEM; |
3732 | 0 | if (movie->brand->altBrand) { |
3733 | 0 | memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount); |
3734 | 0 | gf_free(movie->brand->altBrand); |
3735 | 0 | } |
3736 | 0 | p[movie->brand->altCount] = Brand; |
3737 | 0 | movie->brand->altCount += 1; |
3738 | 0 | movie->brand->altBrand = p; |
3739 | 0 | return GF_OK; |
3740 | | |
3741 | 0 | found: |
3742 | | |
3743 | | //found |
3744 | 0 | if (AddIt) return GF_OK; |
3745 | 0 | gf_assert(movie->brand->altCount>1); |
3746 | | |
3747 | | //remove it |
3748 | 0 | p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount - 1)); |
3749 | 0 | if (!p) return GF_OUT_OF_MEM; |
3750 | 0 | k = 0; |
3751 | 0 | for (i=0; i<movie->brand->altCount; i++) { |
3752 | 0 | if (movie->brand->altBrand[i] == Brand) continue; |
3753 | 0 | else { |
3754 | 0 | p[k] = movie->brand->altBrand[i]; |
3755 | 0 | k++; |
3756 | 0 | } |
3757 | 0 | } |
3758 | 0 | movie->brand->altCount -= 1; |
3759 | 0 | gf_free(movie->brand->altBrand); |
3760 | 0 | movie->brand->altBrand = p; |
3761 | 0 | return GF_OK; |
3762 | 0 | } |
3763 | | |
3764 | | GF_EXPORT |
3765 | | GF_Err gf_isom_reset_alt_brands_ex(GF_ISOFile *movie, Bool leave_empty) |
3766 | 0 | { |
3767 | 0 | u32 *p; |
3768 | |
|
3769 | 0 | if (!movie) return GF_BAD_PARAM; |
3770 | 0 | if (movie->disable_brand_rewrite) return GF_OK; |
3771 | | |
3772 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
3773 | 0 | if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) { |
3774 | 0 | GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3775 | 0 | if (e) return e; |
3776 | | |
3777 | 0 | e = CheckNoData(movie); |
3778 | 0 | if (e) return e; |
3779 | 0 | } |
3780 | 0 | #endif |
3781 | | |
3782 | 0 | if (!movie->brand) { |
3783 | 0 | movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP); |
3784 | 0 | if (!movie->brand) return GF_OUT_OF_MEM; |
3785 | 0 | gf_list_add(movie->TopBoxes, movie->brand); |
3786 | 0 | } |
3787 | 0 | gf_free(movie->brand->altBrand); |
3788 | 0 | if (leave_empty) { |
3789 | 0 | movie->brand->altCount = 0; |
3790 | 0 | movie->brand->altBrand = NULL; |
3791 | 0 | } else { |
3792 | 0 | p = (u32*)gf_malloc(sizeof(u32)); |
3793 | 0 | if (!p) return GF_OUT_OF_MEM; |
3794 | 0 | p[0] = movie->brand->majorBrand; |
3795 | 0 | movie->brand->altCount = 1; |
3796 | 0 | movie->brand->altBrand = p; |
3797 | 0 | } |
3798 | 0 | return GF_OK; |
3799 | 0 | } |
3800 | | GF_EXPORT |
3801 | | GF_Err gf_isom_reset_alt_brands(GF_ISOFile *movie) |
3802 | 0 | { |
3803 | 0 | return gf_isom_reset_alt_brands_ex(movie, GF_FALSE); |
3804 | 0 | } |
3805 | | |
3806 | | #if 0 //unused |
3807 | | GF_Err gf_isom_set_sample_padding_bits(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u8 NbBits) |
3808 | | { |
3809 | | GF_TrackBox *trak; |
3810 | | GF_Err e; |
3811 | | |
3812 | | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3813 | | if (e) return e; |
3814 | | |
3815 | | trak = gf_isom_get_track_box(movie, trackNumber); |
3816 | | if (!trak || NbBits > 7) return GF_BAD_PARAM; |
3817 | | |
3818 | | //set Padding info |
3819 | | return stbl_SetPaddingBits(trak->Media->information->sampleTable, sampleNumber, NbBits); |
3820 | | } |
3821 | | #endif |
3822 | | |
3823 | | GF_EXPORT |
3824 | | GF_Err gf_isom_remove_user_data_item(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, u32 UserDataIndex) |
3825 | 0 | { |
3826 | 0 | GF_UserDataMap *map; |
3827 | 0 | GF_Box *a; |
3828 | 0 | u32 i; |
3829 | 0 | bin128 t; |
3830 | 0 | GF_Err e; |
3831 | 0 | GF_TrackBox *trak; |
3832 | 0 | GF_UserDataBox *udta; |
3833 | |
|
3834 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3835 | 0 | if (e) return e; |
3836 | | |
3837 | 0 | if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; |
3838 | 0 | memset(t, 1, 16); |
3839 | |
|
3840 | 0 | if (trackNumber) { |
3841 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
3842 | 0 | if (!trak) return GF_BAD_PARAM; |
3843 | 0 | udta = trak->udta; |
3844 | 0 | } else { |
3845 | 0 | if (!movie->moov) return GF_BAD_PARAM; |
3846 | 0 | udta = movie->moov->udta; |
3847 | 0 | } |
3848 | 0 | if (!udta) return GF_BAD_PARAM; |
3849 | 0 | if (!UserDataIndex) return GF_BAD_PARAM; |
3850 | | |
3851 | 0 | i=0; |
3852 | 0 | while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) { |
3853 | 0 | if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found; |
3854 | 0 | else if (map->boxType == UserDataType) goto found; |
3855 | 0 | } |
3856 | | //not found |
3857 | 0 | return GF_OK; |
3858 | | |
3859 | 0 | found: |
3860 | |
|
3861 | 0 | if (UserDataIndex > gf_list_count(map->boxes) ) return GF_BAD_PARAM; |
3862 | | //delete the box |
3863 | 0 | a = (GF_Box*)gf_list_get(map->boxes, UserDataIndex-1); |
3864 | 0 | gf_isom_box_del_parent(&map->boxes, a); |
3865 | | |
3866 | | //remove the map if empty |
3867 | 0 | if (!gf_list_count(map->boxes)) { |
3868 | 0 | gf_list_rem(udta->recordList, i-1); |
3869 | 0 | gf_isom_box_array_del(map->boxes); |
3870 | 0 | gf_free(map); |
3871 | 0 | } |
3872 | | //but we keep the UDTA no matter what |
3873 | 0 | return GF_OK; |
3874 | 0 | } |
3875 | | |
3876 | | GF_EXPORT |
3877 | | GF_Err gf_isom_remove_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID) |
3878 | 0 | { |
3879 | 0 | GF_UserDataMap *map; |
3880 | 0 | u32 i; |
3881 | 0 | GF_Err e; |
3882 | 0 | bin128 t; |
3883 | 0 | GF_TrackBox *trak; |
3884 | 0 | GF_UserDataBox *udta; |
3885 | |
|
3886 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3887 | 0 | if (e) return e; |
3888 | | |
3889 | 0 | if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; |
3890 | 0 | memset(t, 1, 16); |
3891 | |
|
3892 | 0 | if (trackNumber) { |
3893 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
3894 | 0 | if (!trak) return GF_EOS; |
3895 | 0 | udta = trak->udta; |
3896 | 0 | } else { |
3897 | 0 | if (!movie->moov) return GF_BAD_PARAM; |
3898 | 0 | udta = movie->moov->udta; |
3899 | 0 | } |
3900 | | //do not return any error if no udta |
3901 | 0 | if (!udta) return GF_EOS; |
3902 | | |
3903 | 0 | i=0; |
3904 | 0 | while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) { |
3905 | 0 | if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found; |
3906 | 0 | else if (map->boxType == UserDataType) goto found; |
3907 | 0 | } |
3908 | | //not found |
3909 | 0 | return GF_OK; |
3910 | | |
3911 | 0 | found: |
3912 | |
|
3913 | 0 | gf_list_rem(udta->recordList, i-1); |
3914 | 0 | gf_isom_box_array_del(map->boxes); |
3915 | 0 | gf_free(map); |
3916 | | |
3917 | | //but we keep the UDTA no matter what |
3918 | 0 | return GF_OK; |
3919 | 0 | } |
3920 | | |
3921 | | GF_EXPORT |
3922 | | GF_Err gf_isom_add_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, u8 *data, u32 DataLength) |
3923 | 0 | { |
3924 | 0 | GF_Err e; |
3925 | 0 | GF_TrackBox *trak; |
3926 | 0 | GF_UserDataBox *udta; |
3927 | |
|
3928 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3929 | 0 | if (e) return e; |
3930 | | |
3931 | 0 | if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; |
3932 | |
|
3933 | 0 | if (trackNumber) { |
3934 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
3935 | 0 | if (!trak) return GF_BAD_PARAM; |
3936 | 0 | if (!trak->udta) trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3937 | 0 | udta = trak->udta; |
3938 | 0 | } else { |
3939 | 0 | if (!movie->moov) return GF_BAD_PARAM; |
3940 | 0 | if (!movie->moov->udta) moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3941 | 0 | udta = movie->moov->udta; |
3942 | 0 | } |
3943 | 0 | if (!udta) return GF_OUT_OF_MEM; |
3944 | | |
3945 | | //create a default box |
3946 | 0 | if (UserDataType) { |
3947 | 0 | GF_UnknownBox *a = (GF_UnknownBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UNKNOWN); |
3948 | 0 | if (!a) return GF_OUT_OF_MEM; |
3949 | 0 | a->original_4cc = UserDataType; |
3950 | 0 | if (DataLength) { |
3951 | 0 | a->data = (char*)gf_malloc(sizeof(char)*DataLength); |
3952 | 0 | if (!a->data) return GF_OUT_OF_MEM; |
3953 | 0 | memcpy(a->data, data, DataLength); |
3954 | 0 | a->dataSize = DataLength; |
3955 | 0 | } |
3956 | 0 | return udta_on_child_box_ex((GF_Box *)udta, (GF_Box *) a, GF_FALSE, GF_TRUE); |
3957 | 0 | } else if (UUID) { |
3958 | 0 | GF_UnknownUUIDBox *a = (GF_UnknownUUIDBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID); |
3959 | 0 | if (!a) return GF_OUT_OF_MEM; |
3960 | 0 | memcpy(a->uuid, UUID, 16); |
3961 | 0 | if (DataLength) { |
3962 | 0 | a->data = (char*)gf_malloc(sizeof(char)*DataLength); |
3963 | 0 | if (!a->data) return GF_OUT_OF_MEM; |
3964 | 0 | memcpy(a->data, data, DataLength); |
3965 | 0 | a->dataSize = DataLength; |
3966 | 0 | } |
3967 | 0 | return udta_on_child_box_ex((GF_Box *)udta, (GF_Box *) a, GF_FALSE, GF_TRUE); |
3968 | 0 | } else { |
3969 | 0 | return GF_BAD_PARAM; |
3970 | 0 | } |
3971 | 0 | return GF_OK; |
3972 | 0 | } |
3973 | | |
3974 | | GF_EXPORT |
3975 | | GF_Err gf_isom_add_user_data_boxes(GF_ISOFile *movie, u32 trackNumber, u8 *data, u32 DataLength) |
3976 | 0 | { |
3977 | 0 | GF_Err e; |
3978 | 0 | GF_TrackBox *trak; |
3979 | 0 | GF_UserDataBox *udta; |
3980 | 0 | GF_BitStream *bs; |
3981 | |
|
3982 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3983 | 0 | if (e) return e; |
3984 | | |
3985 | 0 | if (trackNumber) { |
3986 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
3987 | 0 | if (!trak) return GF_BAD_PARAM; |
3988 | 0 | if (!trak->udta) trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3989 | 0 | udta = trak->udta; |
3990 | 0 | } else { |
3991 | 0 | if (!movie->moov) return GF_BAD_PARAM; |
3992 | 0 | if (!movie->moov->udta) moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3993 | 0 | udta = movie->moov->udta; |
3994 | 0 | } |
3995 | 0 | if (!udta) return GF_OUT_OF_MEM; |
3996 | | |
3997 | 0 | bs = gf_bs_new(data, DataLength, GF_BITSTREAM_READ); |
3998 | 0 | while (gf_bs_available(bs)) { |
3999 | 0 | GF_Box *a; |
4000 | 0 | e = gf_isom_box_parse(&a, bs); |
4001 | 0 | if (e) break; |
4002 | 0 | e = udta_on_child_box_ex((GF_Box *)udta, a, GF_FALSE, GF_FALSE); |
4003 | 0 | if (e) break; |
4004 | 0 | } |
4005 | 0 | gf_bs_del(bs); |
4006 | 0 | return e; |
4007 | 0 | } |
4008 | | |
4009 | | GF_EXPORT |
4010 | | GF_Err gf_isom_clone_pl_indications(GF_ISOFile *orig, GF_ISOFile *dest) |
4011 | 0 | { |
4012 | 0 | GF_IsomInitialObjectDescriptor *iod_d; |
4013 | 0 | if (!orig || !dest) return GF_BAD_PARAM; |
4014 | 0 | if (!orig->moov->iods || !orig->moov->iods->descriptor) return GF_OK; |
4015 | 0 | if (orig->moov->iods->descriptor->tag != GF_ODF_ISOM_IOD_TAG) return GF_OK; |
4016 | | |
4017 | 0 | AddMovieIOD(dest->moov, 1); |
4018 | 0 | gf_odf_desc_del((GF_Descriptor *)dest->moov->iods->descriptor); |
4019 | 0 | gf_odf_desc_copy((GF_Descriptor *)orig->moov->iods->descriptor, (GF_Descriptor **)&dest->moov->iods->descriptor); |
4020 | 0 | iod_d = (GF_IsomInitialObjectDescriptor *) dest->moov->iods->descriptor; |
4021 | 0 | while (gf_list_count(iod_d->ES_ID_IncDescriptors)) { |
4022 | 0 | GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_IncDescriptors, 0); |
4023 | 0 | gf_list_rem(iod_d->ES_ID_IncDescriptors, 0); |
4024 | 0 | gf_odf_desc_del(d); |
4025 | 0 | } |
4026 | 0 | while (gf_list_count(iod_d->ES_ID_RefDescriptors)) { |
4027 | 0 | GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_RefDescriptors, 0); |
4028 | 0 | gf_list_rem(iod_d->ES_ID_RefDescriptors, 0); |
4029 | 0 | gf_odf_desc_del(d); |
4030 | 0 | } |
4031 | 0 | return GF_OK; |
4032 | 0 | } |
4033 | | |
4034 | | GF_EXPORT |
4035 | | GF_Err gf_isom_clone_box(GF_Box *src, GF_Box **dst) |
4036 | 0 | { |
4037 | 0 | GF_Err e; |
4038 | 0 | u8 *data; |
4039 | 0 | u32 data_size; |
4040 | 0 | GF_BitStream *bs; |
4041 | |
|
4042 | 0 | if (*dst) { |
4043 | 0 | gf_isom_box_del(*dst); |
4044 | 0 | *dst=NULL; |
4045 | 0 | } |
4046 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4047 | 0 | if (!bs) return GF_OUT_OF_MEM; |
4048 | 0 | e = gf_isom_box_size( (GF_Box *) src); |
4049 | 0 | if (!e) e = gf_isom_box_write((GF_Box *) src, bs); |
4050 | 0 | gf_bs_get_content(bs, &data, &data_size); |
4051 | 0 | gf_bs_del(bs); |
4052 | 0 | if (e) { |
4053 | 0 | if (data) gf_free(data); |
4054 | 0 | return e; |
4055 | 0 | } |
4056 | 0 | bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); |
4057 | 0 | if (!bs) { |
4058 | 0 | if (data) gf_free(data); |
4059 | 0 | return GF_OUT_OF_MEM; |
4060 | 0 | } |
4061 | 0 | e = gf_isom_box_parse(dst, bs); |
4062 | 0 | gf_bs_del(bs); |
4063 | 0 | gf_free(data); |
4064 | 0 | return e; |
4065 | 0 | } |
4066 | | |
4067 | | #if 0 //unused |
4068 | | /*clones the entire movie file to destination. Tracks can be cloned if clone_tracks is set, in which case hint tracks can be |
4069 | | kept if keep_hint_tracks is set |
4070 | | if keep_pssh, all pssh boxes will be kept |
4071 | | fragment information (mvex) is not kept*/ |
4072 | | GF_Err gf_isom_clone_movie(GF_ISOFile *orig_file, GF_ISOFile *dest_file, Bool clone_tracks, Bool keep_hint_tracks, Bool keep_pssh) |
4073 | | { |
4074 | | GF_Err e; |
4075 | | u32 i; |
4076 | | GF_Box *box; |
4077 | | |
4078 | | e = gf_isom_can_access_movie(dest_file, GF_ISOM_OPEN_WRITE); |
4079 | | if (e) return e; |
4080 | | |
4081 | | if (orig_file->brand) { |
4082 | | gf_list_del_item(dest_file->TopBoxes, dest_file->brand); |
4083 | | gf_isom_box_del((GF_Box *)dest_file->brand); |
4084 | | dest_file->brand = NULL; |
4085 | | gf_isom_clone_box((GF_Box *)orig_file->brand, (GF_Box **)&dest_file->brand); |
4086 | | if (dest_file->brand) gf_list_add(dest_file->TopBoxes, dest_file->brand); |
4087 | | } |
4088 | | |
4089 | | if (orig_file->meta) { |
4090 | | gf_list_del_item(dest_file->TopBoxes, dest_file->meta); |
4091 | | gf_isom_box_del((GF_Box *)dest_file->meta); |
4092 | | dest_file->meta = NULL; |
4093 | | /*fixme - check imports*/ |
4094 | | gf_isom_clone_box((GF_Box *)orig_file->meta, (GF_Box **)&dest_file->meta); |
4095 | | if (dest_file->meta) gf_list_add(dest_file->TopBoxes, dest_file->meta); |
4096 | | } |
4097 | | if (orig_file->moov) { |
4098 | | u32 i, dstTrack; |
4099 | | GF_Box *iods; |
4100 | | GF_List *tracks = gf_list_new(); |
4101 | | GF_List *old_tracks = orig_file->moov->trackList; |
4102 | | orig_file->moov->trackList = tracks; |
4103 | | iods = (GF_Box*)orig_file->moov->iods; |
4104 | | orig_file->moov->iods = NULL; |
4105 | | e = gf_isom_clone_box((GF_Box *)orig_file->moov, (GF_Box **)&dest_file->moov); |
4106 | | if (e) { |
4107 | | gf_list_del(tracks); |
4108 | | orig_file->moov->trackList = old_tracks; |
4109 | | return e; |
4110 | | } |
4111 | | orig_file->moov->trackList = old_tracks; |
4112 | | gf_list_del(tracks); |
4113 | | orig_file->moov->iods = (GF_ObjectDescriptorBox*)iods; |
4114 | | gf_list_add(dest_file->TopBoxes, dest_file->moov); |
4115 | | |
4116 | | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
4117 | | if (dest_file->moov->mvex) { |
4118 | | gf_isom_box_del_parent(&dest_file->moov->child_boxes, (GF_Box *)dest_file->moov->mvex); |
4119 | | dest_file->moov->mvex = NULL; |
4120 | | } |
4121 | | #endif |
4122 | | |
4123 | | if (clone_tracks) { |
4124 | | for (i=0; i<gf_list_count(orig_file->moov->trackList); i++) { |
4125 | | GF_TrackBox *trak = (GF_TrackBox*)gf_list_get( orig_file->moov->trackList, i); |
4126 | | if (!trak) continue; |
4127 | | if (keep_hint_tracks || (trak->Media->handler->handlerType != GF_ISOM_MEDIA_HINT)) { |
4128 | | e = gf_isom_clone_track(orig_file, i+1, dest_file, 0, &dstTrack); |
4129 | | if (e) return e; |
4130 | | } |
4131 | | } |
4132 | | if (iods) |
4133 | | gf_isom_clone_box((GF_Box *)orig_file->moov->iods, (GF_Box **)dest_file->moov->iods); |
4134 | | } else { |
4135 | | dest_file->moov->mvhd->nextTrackID = 1; |
4136 | | gf_isom_clone_pl_indications(orig_file, dest_file); |
4137 | | } |
4138 | | dest_file->moov->mov = dest_file; |
4139 | | } |
4140 | | |
4141 | | if (!keep_pssh) { |
4142 | | i=0; |
4143 | | while ((box = (GF_Box*)gf_list_get(dest_file->moov->child_boxes, i++))) { |
4144 | | if (box->type == GF_ISOM_BOX_TYPE_PSSH) { |
4145 | | i--; |
4146 | | gf_isom_box_del_parent(&dest_file->moov->child_boxes, box); |
4147 | | } |
4148 | | } |
4149 | | } |
4150 | | |
4151 | | //duplicate other boxes |
4152 | | i=0; |
4153 | | while ((box = (GF_Box*)gf_list_get(orig_file->TopBoxes, i++))) { |
4154 | | switch(box->type) { |
4155 | | case GF_ISOM_BOX_TYPE_MOOV: |
4156 | | case GF_ISOM_BOX_TYPE_META: |
4157 | | case GF_ISOM_BOX_TYPE_MDAT: |
4158 | | case GF_ISOM_BOX_TYPE_FTYP: |
4159 | | case GF_ISOM_BOX_TYPE_PDIN: |
4160 | | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
4161 | | case GF_ISOM_BOX_TYPE_STYP: |
4162 | | case GF_ISOM_BOX_TYPE_SIDX: |
4163 | | case GF_ISOM_BOX_TYPE_SSIX: |
4164 | | case GF_ISOM_BOX_TYPE_MOOF: |
4165 | | #endif |
4166 | | case GF_ISOM_BOX_TYPE_JP: |
4167 | | break; |
4168 | | |
4169 | | case GF_ISOM_BOX_TYPE_PSSH: |
4170 | | if (!keep_pssh) |
4171 | | break; |
4172 | | |
4173 | | default: |
4174 | | { |
4175 | | GF_Box *box2 = NULL; |
4176 | | gf_isom_clone_box(box, &box2); |
4177 | | gf_list_add(dest_file->TopBoxes, box2); |
4178 | | } |
4179 | | break; |
4180 | | } |
4181 | | } |
4182 | | |
4183 | | return GF_OK; |
4184 | | } |
4185 | | #endif |
4186 | | |
4187 | | |
4188 | | GF_EXPORT |
4189 | | GF_Err gf_isom_get_raw_user_data(GF_ISOFile *file, u8 **output, u32 *output_size) |
4190 | 0 | { |
4191 | 0 | GF_BitStream *bs; |
4192 | 0 | GF_Err e; |
4193 | 0 | GF_Box *b; |
4194 | 0 | u32 i; |
4195 | |
|
4196 | 0 | *output = NULL; |
4197 | 0 | *output_size = 0; |
4198 | 0 | if (!file || !file->moov || (!file->moov->udta && !file->moov->child_boxes)) return GF_OK; |
4199 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4200 | |
|
4201 | 0 | if (file->moov->udta) { |
4202 | 0 | e = gf_isom_box_size( (GF_Box *) file->moov->udta); |
4203 | 0 | if (e) goto exit; |
4204 | 0 | e = gf_isom_box_write((GF_Box *) file->moov->udta, bs); |
4205 | 0 | if (e) goto exit; |
4206 | 0 | } |
4207 | 0 | e = GF_OK; |
4208 | 0 | i=0; |
4209 | 0 | while ((b = gf_list_enum(file->moov->child_boxes, &i))) { |
4210 | 0 | switch (b->type) { |
4211 | 0 | case GF_ISOM_BOX_TYPE_TRAK: |
4212 | 0 | case GF_ISOM_BOX_TYPE_MVHD: |
4213 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
4214 | 0 | case GF_ISOM_BOX_TYPE_MVEX: |
4215 | 0 | #endif |
4216 | 0 | case GF_ISOM_BOX_TYPE_IODS: |
4217 | 0 | case GF_ISOM_BOX_TYPE_META: |
4218 | 0 | continue; |
4219 | 0 | } |
4220 | 0 | e = gf_isom_box_size( (GF_Box *) b); |
4221 | 0 | if (e) goto exit; |
4222 | 0 | e = gf_isom_box_write((GF_Box *) b, bs); |
4223 | 0 | if (e) goto exit; |
4224 | 0 | } |
4225 | | |
4226 | 0 | gf_bs_get_content(bs, output, output_size); |
4227 | |
|
4228 | 0 | exit: |
4229 | 0 | gf_bs_del(bs); |
4230 | 0 | return e; |
4231 | 0 | } |
4232 | | |
4233 | | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
4234 | | static GF_Err gf_isom_get_trex_props(GF_ISOFile *file, GF_TrackBox *trak, GF_TrackExtendsBox **trex, GF_TrackExtensionPropertiesBox **trexprop) |
4235 | 0 | { |
4236 | 0 | u32 i; |
4237 | 0 | if (!file->moov->mvex) return GF_NOT_FOUND; |
4238 | 0 | *trex = NULL; |
4239 | 0 | for (i=0; i<gf_list_count(file->moov->mvex->TrackExList); i++) { |
4240 | 0 | *trex = gf_list_get(file->moov->mvex->TrackExList, i); |
4241 | 0 | if ((*trex)->trackID == trak->Header->trackID) break; |
4242 | 0 | *trex = NULL; |
4243 | 0 | } |
4244 | 0 | if (! *trex) return GF_NOT_FOUND; |
4245 | | |
4246 | 0 | for (i=0; i<gf_list_count(file->moov->mvex->TrackExPropList); i++) { |
4247 | 0 | *trexprop = gf_list_get(file->moov->mvex->TrackExPropList, i); |
4248 | 0 | if ((*trexprop)->trackID== trak->Header->trackID) break; |
4249 | 0 | *trexprop = NULL; |
4250 | 0 | } |
4251 | 0 | return GF_OK; |
4252 | 0 | } |
4253 | | #endif |
4254 | | |
4255 | | GF_EXPORT |
4256 | | GF_Err gf_isom_get_track_template(GF_ISOFile *file, u32 track, u8 **output, u32 *output_size) |
4257 | 0 | { |
4258 | 0 | GF_TrackBox *trak; |
4259 | 0 | GF_BitStream *bs; |
4260 | 0 | GF_DataReferenceBox *dref; |
4261 | 0 | GF_SampleTableBox *stbl, *stbl_temp; |
4262 | 0 | GF_SampleEncryptionBox *senc; |
4263 | 0 | GF_List *gpac_internals = NULL; |
4264 | 0 | u32 i, count; |
4265 | |
|
4266 | 0 | *output = NULL; |
4267 | 0 | *output_size = 0; |
4268 | | /*get orig sample desc and clone it*/ |
4269 | 0 | trak = gf_isom_get_track_box(file, track); |
4270 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
4271 | | |
4272 | | //don't serialize dref |
4273 | 0 | dref = NULL; |
4274 | 0 | if (trak->Media->information->dataInformation) { |
4275 | 0 | dref = trak->Media->information->dataInformation->dref; |
4276 | 0 | trak->Media->information->dataInformation->dref = NULL; |
4277 | 0 | gf_list_del_item(trak->Media->information->dataInformation->child_boxes, dref); |
4278 | 0 | } |
4279 | | |
4280 | | //don't serialize stbl but create a temp one |
4281 | 0 | stbl_temp = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL); |
4282 | 0 | if (!stbl_temp->child_boxes) stbl_temp->child_boxes = gf_list_new(); |
4283 | 0 | stbl = trak->Media->information->sampleTable; |
4284 | 0 | gf_list_del_item(trak->Media->information->child_boxes, stbl); |
4285 | |
|
4286 | 0 | trak->Media->information->sampleTable = stbl_temp; |
4287 | 0 | gf_list_add(trak->Media->information->child_boxes, stbl_temp); |
4288 | | |
4289 | | /*do not clone sampleDescription table but create an empty one*/ |
4290 | 0 | stbl_temp->SampleDescription = (GF_SampleDescriptionBox *) gf_isom_box_new_parent(&stbl_temp->child_boxes, GF_ISOM_BOX_TYPE_STSD); |
4291 | | |
4292 | | /*clone sampleGroups description tables if any*/ |
4293 | 0 | stbl_temp->sampleGroupsDescription = stbl->sampleGroupsDescription; |
4294 | 0 | count = gf_list_count(stbl->sampleGroupsDescription); |
4295 | 0 | for (i=0;i<count; i++) { |
4296 | 0 | GF_SampleGroupDescriptionBox *b = gf_list_get(stbl->sampleGroupsDescription, i); |
4297 | | //don't add our internal sample groups |
4298 | 0 | if (b->grouping_type==GF_4CC('E','M','S','G')) |
4299 | 0 | continue; |
4300 | 0 | if (b->grouping_type==GF_4CC('P','S','S','H')) |
4301 | 0 | continue; |
4302 | 0 | gf_list_add(stbl_temp->child_boxes, b); |
4303 | 0 | } |
4304 | | /*clone CompositionToDecode table, we may remove it later*/ |
4305 | 0 | stbl_temp->CompositionToDecode = stbl->CompositionToDecode; |
4306 | 0 | gf_list_add(stbl_temp->child_boxes, stbl->CompositionToDecode); |
4307 | | |
4308 | |
|
4309 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
4310 | | //get CSLG from trex if not in stbl |
4311 | 0 | GF_CompositionToDecodeBox *trex_cslg=NULL; |
4312 | 0 | if (!stbl_temp->CompositionToDecode) { |
4313 | 0 | GF_TrackExtendsBox *trex=NULL; |
4314 | 0 | GF_TrackExtensionPropertiesBox *trexprop=NULL; |
4315 | 0 | gf_isom_get_trex_props(file, trak, &trex, &trexprop); |
4316 | 0 | if (trexprop) { |
4317 | 0 | trex_cslg = (GF_CompositionToDecodeBox *) gf_isom_box_find_child(trexprop->child_boxes, GF_ISOM_BOX_TYPE_CSLG); |
4318 | 0 | if (trex_cslg) { |
4319 | 0 | stbl_temp->CompositionToDecode = trex_cslg; |
4320 | 0 | gf_list_add(stbl_temp->child_boxes, trex_cslg); |
4321 | 0 | } |
4322 | 0 | } |
4323 | 0 | } |
4324 | 0 | #endif |
4325 | |
|
4326 | 0 | count = gf_list_count(trak->child_boxes); |
4327 | 0 | for (i=0;i<count; i++) { |
4328 | 0 | GF_UnknownBox *b = gf_list_get(trak->child_boxes, i); |
4329 | 0 | if (b->type != GF_ISOM_BOX_TYPE_UNKNOWN) continue; |
4330 | 0 | if (b->original_4cc==GF_ISOM_BOX_TYPE_GDAT) { |
4331 | 0 | if (!gpac_internals) gpac_internals = gf_list_new(); |
4332 | 0 | gf_list_add(gpac_internals, b); |
4333 | 0 | gf_list_rem(trak->child_boxes, i); |
4334 | 0 | i--; |
4335 | 0 | count--; |
4336 | 0 | } |
4337 | 0 | } |
4338 | | |
4339 | | //don't serialize senc |
4340 | 0 | senc = trak->sample_encryption; |
4341 | 0 | if (senc) { |
4342 | 0 | gf_assert(trak->child_boxes); |
4343 | 0 | gf_list_del_item(trak->child_boxes, senc); |
4344 | 0 | trak->sample_encryption = NULL; |
4345 | 0 | } |
4346 | |
|
4347 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4348 | |
|
4349 | 0 | gf_isom_box_size( (GF_Box *) trak); |
4350 | 0 | gf_isom_box_write((GF_Box *) trak, bs); |
4351 | 0 | gf_bs_get_content(bs, output, output_size); |
4352 | 0 | gf_bs_del(bs); |
4353 | | |
4354 | | //restore our pointers |
4355 | 0 | if (dref) { |
4356 | 0 | trak->Media->information->dataInformation->dref = dref; |
4357 | 0 | gf_list_add(trak->Media->information->dataInformation->child_boxes, dref); |
4358 | 0 | } |
4359 | 0 | trak->Media->information->sampleTable = stbl; |
4360 | 0 | gf_list_add(trak->Media->information->child_boxes, stbl); |
4361 | 0 | gf_list_del_item(trak->Media->information->child_boxes, stbl_temp); |
4362 | 0 | if (senc) { |
4363 | 0 | trak->sample_encryption = senc; |
4364 | 0 | gf_list_add(trak->child_boxes, senc); |
4365 | 0 | } |
4366 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
4367 | 0 | if (trex_cslg) { |
4368 | 0 | stbl_temp->CompositionToDecode = NULL; |
4369 | 0 | gf_list_del_item(stbl_temp->child_boxes, trex_cslg); |
4370 | 0 | } |
4371 | 0 | #endif |
4372 | |
|
4373 | 0 | stbl_temp->sampleGroupsDescription = NULL; |
4374 | 0 | count = gf_list_count(stbl->sampleGroupsDescription); |
4375 | 0 | for (i=0;i<count; i++) { |
4376 | 0 | GF_Box *b = gf_list_get(stbl->sampleGroupsDescription, i); |
4377 | 0 | gf_list_del_item(stbl_temp->child_boxes, b); |
4378 | 0 | } |
4379 | |
|
4380 | 0 | stbl_temp->CompositionToDecode = NULL; |
4381 | 0 | gf_list_del_item(stbl_temp->child_boxes, stbl->CompositionToDecode); |
4382 | 0 | gf_isom_box_del((GF_Box *)stbl_temp); |
4383 | |
|
4384 | 0 | if (gpac_internals) { |
4385 | 0 | gf_list_transfer(trak->child_boxes, gpac_internals); |
4386 | 0 | gf_list_del(gpac_internals); |
4387 | 0 | } |
4388 | 0 | return GF_OK; |
4389 | |
|
4390 | 0 | } |
4391 | | |
4392 | | GF_EXPORT |
4393 | | GF_Err gf_isom_get_trex_template(GF_ISOFile *file, u32 track, u8 **output, u32 *output_size) |
4394 | 0 | { |
4395 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
4396 | 0 | GF_TrackBox *trak; |
4397 | 0 | GF_BitStream *bs; |
4398 | 0 | GF_TrackExtendsBox *trex = NULL; |
4399 | 0 | GF_TrackExtensionPropertiesBox *trexprop = NULL; |
4400 | |
|
4401 | 0 | *output = NULL; |
4402 | 0 | *output_size = 0; |
4403 | | /*get orig sample desc and clone it*/ |
4404 | 0 | trak = gf_isom_get_track_box(file, track); |
4405 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
4406 | 0 | if (!file->moov->mvex) return GF_NOT_FOUND; |
4407 | 0 | GF_Err e = gf_isom_get_trex_props(file, trak, &trex, &trexprop); |
4408 | 0 | if (e) return e; |
4409 | | |
4410 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4411 | 0 | gf_isom_box_size( (GF_Box *) trex); |
4412 | 0 | gf_isom_box_write((GF_Box *) trex, bs); |
4413 | |
|
4414 | 0 | if (trexprop) { |
4415 | 0 | gf_isom_box_size( (GF_Box *) trexprop); |
4416 | 0 | gf_isom_box_write((GF_Box *) trexprop, bs); |
4417 | 0 | } |
4418 | 0 | gf_bs_get_content(bs, output, output_size); |
4419 | 0 | gf_bs_del(bs); |
4420 | |
|
4421 | | #else |
4422 | | *output = NULL; |
4423 | | *output_size = 0; |
4424 | | #endif |
4425 | 0 | return GF_OK; |
4426 | 0 | } |
4427 | | |
4428 | | GF_EXPORT |
4429 | | GF_Err gf_isom_get_stsd_template(GF_ISOFile *file, u32 track, u32 stsd_idx, u8 **output, u32 *output_size) |
4430 | 0 | { |
4431 | 0 | GF_TrackBox *trak; |
4432 | 0 | GF_BitStream *bs; |
4433 | 0 | GF_Box *ent; |
4434 | |
|
4435 | 0 | *output = NULL; |
4436 | 0 | *output_size = 0; |
4437 | | /*get orig sample desc and clone it*/ |
4438 | 0 | trak = gf_isom_get_track_box(file, track); |
4439 | 0 | if (!trak || !trak->Media || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription) return GF_BAD_PARAM; |
4440 | | |
4441 | 0 | if (stsd_idx) { |
4442 | 0 | ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, stsd_idx-1); |
4443 | 0 | if (!ent) return GF_BAD_PARAM; |
4444 | 0 | } else { |
4445 | 0 | ent = (GF_Box*) trak->Media->information->sampleTable->SampleDescription; |
4446 | 0 | } |
4447 | | |
4448 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4449 | |
|
4450 | 0 | gf_isom_box_size(ent); |
4451 | 0 | gf_isom_box_write(ent, bs); |
4452 | 0 | gf_bs_get_content(bs, output, output_size); |
4453 | 0 | gf_bs_del(bs); |
4454 | 0 | return GF_OK; |
4455 | 0 | } |
4456 | | |
4457 | | |
4458 | | GF_EXPORT |
4459 | | GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *dest_file, GF_ISOTrackCloneFlags flags, u32 *dest_track) |
4460 | 0 | { |
4461 | 0 | GF_TrackBox *trak, *new_tk; |
4462 | 0 | GF_BitStream *bs; |
4463 | 0 | u8 *data; |
4464 | 0 | const u8 *buffer; |
4465 | 0 | u32 data_size; |
4466 | 0 | u32 i, count; |
4467 | 0 | GF_Err e; |
4468 | 0 | GF_SampleTableBox *stbl=NULL, *stbl_temp=NULL; |
4469 | 0 | GF_SampleEncryptionBox *senc=NULL; |
4470 | |
|
4471 | 0 | e = gf_isom_can_access_movie(dest_file, GF_ISOM_OPEN_WRITE); |
4472 | 0 | if (e) return e; |
4473 | 0 | e = gf_isom_insert_moov(dest_file); |
4474 | 0 | if (e) return e; |
4475 | | |
4476 | | /*get orig sample desc and clone it*/ |
4477 | 0 | trak = gf_isom_get_track_box(orig_file, orig_track); |
4478 | 0 | if (!trak) return GF_BAD_PARAM; |
4479 | 0 | if (!trak->Media && !trak->extl) return GF_BAD_PARAM; |
4480 | | |
4481 | | //for non-external tracks only |
4482 | 0 | if (trak->Media) { |
4483 | 0 | stbl = trak->Media->information->sampleTable; |
4484 | 0 | stbl_temp = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL); |
4485 | 0 | if (!stbl_temp->child_boxes) stbl_temp->child_boxes = gf_list_new(); |
4486 | |
|
4487 | 0 | trak->Media->information->sampleTable = stbl_temp; |
4488 | 0 | gf_list_add(trak->Media->information->child_boxes, stbl_temp); |
4489 | 0 | gf_list_del_item(trak->Media->information->child_boxes, stbl); |
4490 | |
|
4491 | 0 | if (!stbl_temp->child_boxes) stbl_temp->child_boxes = gf_list_new(); |
4492 | | |
4493 | | /*clone sampleDescription table*/ |
4494 | 0 | stbl_temp->SampleDescription = stbl->SampleDescription; |
4495 | 0 | gf_list_add(stbl_temp->child_boxes, stbl->SampleDescription); |
4496 | | /*also clone sampleGroups description tables if any*/ |
4497 | 0 | stbl_temp->sampleGroupsDescription = stbl->sampleGroupsDescription; |
4498 | 0 | count = gf_list_count(stbl->sampleGroupsDescription); |
4499 | 0 | for (i=0; i<count; i++) { |
4500 | 0 | GF_Box *b = gf_list_get(stbl->sampleGroupsDescription, i); |
4501 | 0 | gf_list_add(stbl_temp->child_boxes, b); |
4502 | 0 | } |
4503 | | /*clone CompositionToDecode table, we may remove it later*/ |
4504 | 0 | stbl_temp->CompositionToDecode = stbl->CompositionToDecode; |
4505 | 0 | gf_list_add(stbl_temp->child_boxes, stbl->CompositionToDecode); |
4506 | |
|
4507 | 0 | senc = trak->sample_encryption; |
4508 | 0 | if (senc) { |
4509 | 0 | gf_assert(trak->child_boxes); |
4510 | 0 | gf_list_del_item(trak->child_boxes, senc); |
4511 | 0 | trak->sample_encryption = NULL; |
4512 | 0 | } |
4513 | 0 | } |
4514 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4515 | |
|
4516 | 0 | gf_isom_box_size( (GF_Box *) trak); |
4517 | 0 | gf_isom_box_write((GF_Box *) trak, bs); |
4518 | 0 | gf_bs_get_content(bs, &data, &data_size); |
4519 | 0 | gf_bs_del(bs); |
4520 | 0 | bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); |
4521 | 0 | if (flags & GF_ISOM_CLONE_TRACK_NO_QT) |
4522 | 0 | gf_bs_set_cookie(bs, GF_ISOM_BS_COOKIE_QT_CONV | GF_ISOM_BS_COOKIE_CLONE_TRACK); |
4523 | 0 | else |
4524 | 0 | gf_bs_set_cookie(bs, GF_ISOM_BS_COOKIE_CLONE_TRACK); |
4525 | |
|
4526 | 0 | e = gf_isom_box_parse((GF_Box **) &new_tk, bs); |
4527 | 0 | gf_bs_del(bs); |
4528 | 0 | gf_free(data); |
4529 | |
|
4530 | 0 | if (trak->Media) { |
4531 | 0 | trak->Media->information->sampleTable = stbl; |
4532 | 0 | gf_list_del_item(trak->Media->information->child_boxes, stbl_temp); |
4533 | 0 | gf_list_add(trak->Media->information->child_boxes, stbl); |
4534 | |
|
4535 | 0 | if (senc) { |
4536 | 0 | trak->sample_encryption = senc; |
4537 | 0 | gf_list_add(trak->child_boxes, senc); |
4538 | 0 | } |
4539 | 0 | gf_list_del_item(stbl_temp->child_boxes, stbl_temp->SampleDescription); |
4540 | 0 | stbl_temp->SampleDescription = NULL; |
4541 | |
|
4542 | 0 | count = gf_list_count(stbl->sampleGroupsDescription); |
4543 | 0 | for (i=0; i<count; i++) { |
4544 | 0 | GF_Box *b = gf_list_get(stbl->sampleGroupsDescription, i); |
4545 | 0 | gf_list_del_item(stbl_temp->child_boxes, b); |
4546 | 0 | } |
4547 | 0 | stbl_temp->sampleGroupsDescription = NULL; |
4548 | |
|
4549 | 0 | gf_list_del_item(stbl_temp->child_boxes, stbl_temp->CompositionToDecode); |
4550 | 0 | stbl_temp->CompositionToDecode = NULL; |
4551 | 0 | gf_isom_box_del((GF_Box *)stbl_temp); |
4552 | 0 | } |
4553 | |
|
4554 | 0 | if (e) { |
4555 | 0 | if (new_tk) gf_isom_box_del((GF_Box *)new_tk); |
4556 | 0 | return e; |
4557 | 0 | } |
4558 | | |
4559 | 0 | gf_isom_disable_inplace_rewrite(dest_file); |
4560 | |
|
4561 | 0 | if (trak->Media) { |
4562 | | /*create default boxes*/ |
4563 | 0 | stbl = new_tk->Media->information->sampleTable; |
4564 | 0 | stbl->ChunkOffset = gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STCO); |
4565 | 0 | if (!stbl->ChunkOffset) return GF_OUT_OF_MEM; |
4566 | 0 | stbl->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSZ); |
4567 | 0 | if (!stbl->SampleSize) return GF_OUT_OF_MEM; |
4568 | 0 | stbl->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSC); |
4569 | 0 | if (!stbl->SampleToChunk) return GF_OUT_OF_MEM; |
4570 | 0 | stbl->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STTS); |
4571 | 0 | if (!stbl->TimeToSample) return GF_OUT_OF_MEM; |
4572 | 0 | } |
4573 | | |
4574 | 0 | if (flags & GF_ISOM_CLONE_TRACK_DROP_ID) |
4575 | 0 | new_tk->Header->trackID = 0; |
4576 | | |
4577 | | /*check trackID validity before adding track*/ |
4578 | 0 | if (!new_tk->Header->trackID || gf_isom_get_track_by_id(dest_file, new_tk->Header->trackID)) { |
4579 | 0 | u32 ID = 1; |
4580 | 0 | while (1) { |
4581 | 0 | if (RequestTrack(dest_file->moov, ID)) break; |
4582 | 0 | ID += 1; |
4583 | 0 | if (ID == 0xFFFFFFFF) break; |
4584 | 0 | } |
4585 | 0 | new_tk->Header->trackID = ID; |
4586 | 0 | } |
4587 | 0 | if (!dest_file->moov->child_boxes) dest_file->moov->child_boxes = gf_list_new(); |
4588 | 0 | gf_list_add(dest_file->moov->child_boxes, new_tk); |
4589 | 0 | moov_on_child_box((GF_Box*)dest_file->moov, (GF_Box *)new_tk, GF_FALSE); |
4590 | | |
4591 | | /*set originalID*/ |
4592 | 0 | new_tk->originalID = trak->Header->trackID; |
4593 | | /*set originalFile*/ |
4594 | 0 | buffer = gf_isom_get_filename(orig_file); |
4595 | 0 | new_tk->originalFile = gf_crc_32(buffer, (u32) strlen(buffer)); |
4596 | | |
4597 | | /*rewrite edit list segmentDuration to new movie timescale*/ |
4598 | 0 | if (dest_file->moov->mvhd->timeScale != orig_file->moov->mvhd->timeScale) { |
4599 | 0 | Double ts_scale = dest_file->moov->mvhd->timeScale; |
4600 | 0 | ts_scale /= orig_file->moov->mvhd->timeScale; |
4601 | 0 | new_tk->Header->duration = (u64) (new_tk->Header->duration * ts_scale); |
4602 | 0 | if (new_tk->editBox && new_tk->editBox->editList) { |
4603 | 0 | count = gf_list_count(new_tk->editBox->editList->entryList); |
4604 | 0 | for (i=0; i<count; i++) { |
4605 | 0 | GF_EdtsEntry *ent = (GF_EdtsEntry *)gf_list_get(new_tk->editBox->editList->entryList, i); |
4606 | 0 | ent->segmentDuration = (u64) (ent->segmentDuration * ts_scale); |
4607 | 0 | } |
4608 | 0 | } |
4609 | 0 | } |
4610 | |
|
4611 | 0 | if (trak->Media) { |
4612 | 0 | if (flags & GF_ISOM_CLONE_RESET_DURATION) |
4613 | 0 | new_tk->Media->mediaHeader->duration = 0; |
4614 | |
|
4615 | 0 | if (!new_tk->Media->information->dataInformation->dref) return GF_BAD_PARAM; |
4616 | | |
4617 | | /*reset data ref*/ |
4618 | 0 | if (! (flags & GF_ISOM_CLONE_TRACK_KEEP_DREF) ) { |
4619 | 0 | GF_SampleEntryBox *entry; |
4620 | 0 | Bool use_alis = GF_FALSE; |
4621 | 0 | if (! (flags & GF_ISOM_CLONE_TRACK_NO_QT)) { |
4622 | 0 | GF_Box *b = gf_list_get(new_tk->Media->information->dataInformation->dref->child_boxes, 0); |
4623 | 0 | if (b && (b->type==GF_QT_BOX_TYPE_ALIS)) |
4624 | 0 | use_alis = GF_TRUE; |
4625 | 0 | } |
4626 | 0 | gf_isom_box_array_del(new_tk->Media->information->dataInformation->dref->child_boxes); |
4627 | 0 | new_tk->Media->information->dataInformation->dref->child_boxes = gf_list_new(); |
4628 | | /*update data ref*/ |
4629 | 0 | entry = (GF_SampleEntryBox*)gf_list_get(new_tk->Media->information->sampleTable->SampleDescription->child_boxes, 0); |
4630 | 0 | if (entry) { |
4631 | 0 | u32 dref; |
4632 | 0 | Media_CreateDataRef(dest_file, new_tk->Media->information->dataInformation->dref, use_alis ? "alis" : NULL, NULL, &dref); |
4633 | 0 | entry->dataReferenceIndex = dref; |
4634 | 0 | } |
4635 | 0 | } else { |
4636 | 0 | for (i=0; i<gf_list_count(new_tk->Media->information->dataInformation->dref->child_boxes); i++) { |
4637 | 0 | GF_DataEntryBox *dref_entry = (GF_DataEntryBox *)gf_list_get(new_tk->Media->information->dataInformation->dref->child_boxes, i); |
4638 | 0 | if (dref_entry->flags & 1) { |
4639 | 0 | dref_entry->flags &= ~1; |
4640 | 0 | e = Media_SetDrefURL((GF_DataEntryURLBox *)dref_entry, orig_file->fileName, dest_file->finalName); |
4641 | 0 | if (e) return e; |
4642 | 0 | } |
4643 | 0 | } |
4644 | 0 | } |
4645 | 0 | } |
4646 | | |
4647 | | //purge all 'gpac' boxes at track level |
4648 | 0 | for (i=0; i<gf_list_count(new_tk->child_boxes); i++) { |
4649 | 0 | GF_UnknownBox *b = (GF_UnknownBox *)gf_list_get(new_tk->child_boxes, i); |
4650 | 0 | if (b->type != GF_ISOM_BOX_TYPE_UNKNOWN) continue; |
4651 | 0 | if (b->original_4cc==GF_ISOM_BOX_TYPE_GDAT) { |
4652 | 0 | gf_list_rem(new_tk->child_boxes, i); |
4653 | 0 | i--; |
4654 | 0 | gf_isom_box_del((GF_Box*)b); |
4655 | 0 | } |
4656 | 0 | } |
4657 | |
|
4658 | 0 | *dest_track = gf_list_count(dest_file->moov->trackList); |
4659 | |
|
4660 | 0 | if (dest_file->moov->mvhd->nextTrackID <= new_tk->Header->trackID) |
4661 | 0 | dest_file->moov->mvhd->nextTrackID = new_tk->Header->trackID+1; |
4662 | | |
4663 | | /*if it contains IAMF, add the iamf brand to ftyp*/ |
4664 | 0 | if (gf_isom_get_media_subtype(dest_file, new_tk->Header->trackID, 1) == GF_ISOM_SUBTYPE_IAMF) { |
4665 | 0 | gf_isom_modify_alternate_brand(dest_file, GF_ISOM_BRAND_IAMF, GF_TRUE); |
4666 | 0 | } |
4667 | |
|
4668 | 0 | return GF_OK; |
4669 | 0 | } |
4670 | | |
4671 | | #if 0 |
4672 | | /*clones all sampleDescription entries in new track, after an optional reset of existing entries*/ |
4673 | | GF_Err gf_isom_clone_sample_descriptions(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, Bool reset_existing) |
4674 | | { |
4675 | | u32 i; |
4676 | | GF_TrackBox *dst_trak, *src_trak; |
4677 | | GF_Err e = gf_isom_can_access_movie(the_file, GF_ISOM_OPEN_WRITE); |
4678 | | if (e) return e; |
4679 | | |
4680 | | dst_trak = gf_isom_get_track_box(the_file, trackNumber); |
4681 | | if (!dst_trak || !dst_trak->Media) return GF_BAD_PARAM; |
4682 | | src_trak = gf_isom_get_track_box(orig_file, orig_track); |
4683 | | if (!src_trak || !src_trak->Media) return GF_BAD_PARAM; |
4684 | | |
4685 | | if (reset_existing) { |
4686 | | gf_isom_box_array_del(dst_trak->Media->information->sampleTable->SampleDescription->child_boxes); |
4687 | | dst_trak->Media->information->sampleTable->SampleDescription->child_boxes = gf_list_new(); |
4688 | | } |
4689 | | |
4690 | | for (i=0; i<gf_list_count(src_trak->Media->information->sampleTable->SampleDescription->child_boxes); i++) { |
4691 | | u32 outDesc; |
4692 | | e = gf_isom_clone_sample_description(the_file, trackNumber, orig_file, orig_track, i+1, NULL, NULL, &outDesc); |
4693 | | if (e) break; |
4694 | | } |
4695 | | return e; |
4696 | | } |
4697 | | #endif |
4698 | | |
4699 | | |
4700 | | GF_EXPORT |
4701 | | GF_Err gf_isom_clone_sample_description(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, u32 orig_desc_index, const char *URLname, const char *URNname, u32 *outDescriptionIndex) |
4702 | 0 | { |
4703 | 0 | GF_TrackBox *trak; |
4704 | 0 | GF_BitStream *bs; |
4705 | 0 | u8 *data; |
4706 | 0 | u32 data_size; |
4707 | 0 | GF_Box *entry; |
4708 | 0 | GF_Err e; |
4709 | 0 | u32 dataRefIndex; |
4710 | 0 | u32 mtype; |
4711 | 0 | u32 internal_type; |
4712 | |
|
4713 | 0 | e = gf_isom_can_access_movie(the_file, GF_ISOM_OPEN_WRITE); |
4714 | 0 | if (e) return e; |
4715 | | |
4716 | | /*get orig sample desc and clone it*/ |
4717 | 0 | trak = gf_isom_get_track_box(orig_file, orig_track); |
4718 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
4719 | | |
4720 | 0 | entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, orig_desc_index-1); |
4721 | 0 | if (!entry) return GF_BAD_PARAM; |
4722 | 0 | internal_type = ((GF_SampleEntryBox *)entry)->internal_type; |
4723 | | |
4724 | | |
4725 | | // for generic entries, force the entrytype to unknown to avoid allocating potentially disallowed box types |
4726 | 0 | u32 generic_entry_type = 0; |
4727 | 0 | if (entry->type == GF_ISOM_BOX_TYPE_GNRV) { |
4728 | 0 | generic_entry_type = ((GF_GenericVisualSampleEntryBox *) entry)->EntryType; |
4729 | 0 | ((GF_GenericVisualSampleEntryBox *) entry)->EntryType = GF_ISOM_BOX_TYPE_UNKNOWN; |
4730 | 0 | } else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) { |
4731 | 0 | generic_entry_type = ((GF_GenericAudioSampleEntryBox *) entry)->EntryType; |
4732 | 0 | ((GF_GenericAudioSampleEntryBox *) entry)->EntryType = GF_ISOM_BOX_TYPE_UNKNOWN; |
4733 | 0 | } else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) { |
4734 | 0 | generic_entry_type = ((GF_GenericSampleEntryBox *) entry)->EntryType; |
4735 | 0 | ((GF_GenericSampleEntryBox *) entry)->EntryType = GF_ISOM_BOX_TYPE_UNKNOWN; |
4736 | 0 | } |
4737 | |
|
4738 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4739 | |
|
4740 | 0 | gf_isom_box_size(entry); |
4741 | 0 | gf_isom_box_write(entry, bs); |
4742 | 0 | gf_bs_get_content(bs, &data, &data_size); |
4743 | 0 | gf_bs_del(bs); |
4744 | 0 | bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); |
4745 | | |
4746 | | // restore the original entrytype before losing the entry pointer |
4747 | 0 | if (entry->type == GF_ISOM_BOX_TYPE_GNRV) { |
4748 | 0 | ((GF_GenericVisualSampleEntryBox *) entry)->EntryType = generic_entry_type; |
4749 | 0 | } else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) { |
4750 | 0 | ((GF_GenericAudioSampleEntryBox *) entry)->EntryType = generic_entry_type; |
4751 | 0 | } else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) { |
4752 | 0 | ((GF_GenericSampleEntryBox *) entry)->EntryType = generic_entry_type; |
4753 | 0 | } |
4754 | | |
4755 | |
|
4756 | 0 | e = gf_isom_box_parse(&entry, bs); |
4757 | 0 | gf_bs_del(bs); |
4758 | 0 | gf_free(data); |
4759 | 0 | if (e) return e; |
4760 | | |
4761 | 0 | if (entry->type==GF_ISOM_BOX_TYPE_UNKNOWN) { |
4762 | 0 | GF_UnknownBox *ubox = (GF_UnknownBox*)entry; |
4763 | 0 | if (internal_type == GF_ISOM_SAMPLE_ENTRY_VIDEO) { |
4764 | 0 | GF_GenericVisualSampleEntryBox *ve = (GF_GenericVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRV); |
4765 | 0 | ve->EntryType = generic_entry_type ? generic_entry_type : ubox->original_4cc; |
4766 | 0 | ve->data = ubox->data; |
4767 | 0 | ve->data_size = ubox->dataSize; |
4768 | 0 | entry = (GF_Box *) ve; |
4769 | 0 | } |
4770 | 0 | else if (internal_type == GF_ISOM_SAMPLE_ENTRY_AUDIO) { |
4771 | 0 | GF_GenericAudioSampleEntryBox *ae = (GF_GenericAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRA); |
4772 | 0 | ae->EntryType = generic_entry_type ? generic_entry_type : ubox->original_4cc; |
4773 | 0 | ae->data = ubox->data; |
4774 | 0 | ae->data_size = ubox->dataSize; |
4775 | 0 | entry = (GF_Box *) ae; |
4776 | 0 | } |
4777 | 0 | else { |
4778 | 0 | GF_GenericSampleEntryBox *ge = (GF_GenericSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRM); |
4779 | 0 | ge->EntryType = generic_entry_type ? generic_entry_type : ubox->original_4cc; |
4780 | 0 | ge->data = ubox->data; |
4781 | 0 | ge->data_size = ubox->dataSize; |
4782 | 0 | entry = (GF_Box *) ge; |
4783 | 0 | } |
4784 | 0 | ubox->data = NULL; |
4785 | 0 | ubox->dataSize = 0; |
4786 | 0 | gf_isom_box_del((GF_Box *)ubox); |
4787 | 0 | } |
4788 | | |
4789 | | /*get new track and insert clone*/ |
4790 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
4791 | 0 | if (!trak || !trak->Media) goto exit; |
4792 | | |
4793 | | /*get or create the data ref*/ |
4794 | 0 | e = Media_FindDataRef(trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex); |
4795 | 0 | if (e) goto exit; |
4796 | 0 | if (!dataRefIndex) { |
4797 | 0 | e = Media_CreateDataRef(the_file, trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex); |
4798 | 0 | if (e) goto exit; |
4799 | 0 | } |
4800 | 0 | if (!the_file->keep_utc) |
4801 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
4802 | | /*overwrite dref*/ |
4803 | 0 | ((GF_SampleEntryBox *)entry)->dataReferenceIndex = dataRefIndex; |
4804 | 0 | e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, entry); |
4805 | 0 | *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes); |
4806 | | |
4807 | | /*also clone track w/h info*/ |
4808 | 0 | mtype = gf_isom_get_media_type(the_file, trackNumber); |
4809 | 0 | if (gf_isom_is_video_handler_type(mtype) ) { |
4810 | 0 | gf_isom_set_visual_info(the_file, trackNumber, (*outDescriptionIndex), ((GF_VisualSampleEntryBox*)entry)->Width, ((GF_VisualSampleEntryBox*)entry)->Height); |
4811 | 0 | } |
4812 | 0 | return e; |
4813 | | |
4814 | 0 | exit: |
4815 | 0 | gf_isom_box_del(entry); |
4816 | 0 | return e; |
4817 | 0 | } |
4818 | | |
4819 | | GF_EXPORT |
4820 | | GF_Err gf_isom_new_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, const char *URLname, const char *URNname, GF_GenericSampleDescription *udesc, u32 *outDescriptionIndex) |
4821 | 0 | { |
4822 | 0 | GF_TrackBox *trak; |
4823 | 0 | GF_Err e; |
4824 | 0 | u8 **wrap_data; |
4825 | 0 | u32 *wrap_size; |
4826 | 0 | u32 dataRefIndex; |
4827 | |
|
4828 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
4829 | 0 | if (e) return e; |
4830 | | |
4831 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
4832 | 0 | if (!trak || !trak->Media || !udesc) return GF_BAD_PARAM; |
4833 | | |
4834 | | //get or create the data ref |
4835 | 0 | e = Media_FindDataRef(trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex); |
4836 | 0 | if (e) return e; |
4837 | 0 | if (!dataRefIndex) { |
4838 | 0 | e = Media_CreateDataRef(movie, trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex); |
4839 | 0 | if (e) return e; |
4840 | 0 | } |
4841 | 0 | if (!movie->keep_utc) |
4842 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
4843 | |
|
4844 | 0 | if (gf_isom_is_video_handler_type(trak->Media->handler->handlerType)) { |
4845 | 0 | GF_GenericVisualSampleEntryBox *entry; |
4846 | | //create a new entry |
4847 | 0 | entry = (GF_GenericVisualSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRV); |
4848 | 0 | if (!entry) return GF_OUT_OF_MEM; |
4849 | | |
4850 | 0 | if (!udesc->codec_tag) { |
4851 | 0 | entry->EntryType = GF_ISOM_BOX_TYPE_UUID; |
4852 | 0 | memcpy(entry->uuid, udesc->UUID, sizeof(bin128)); |
4853 | 0 | } else { |
4854 | 0 | entry->EntryType = udesc->codec_tag; |
4855 | 0 | } |
4856 | 0 | if (entry->EntryType == 0) { |
4857 | 0 | gf_isom_box_del((GF_Box *)entry); |
4858 | 0 | return GF_NOT_SUPPORTED; |
4859 | 0 | } |
4860 | | |
4861 | 0 | entry->dataReferenceIndex = dataRefIndex; |
4862 | 0 | entry->vendor = udesc->vendor_code; |
4863 | 0 | entry->version = udesc->version; |
4864 | 0 | entry->revision = udesc->revision; |
4865 | 0 | entry->temporal_quality = udesc->temporal_quality; |
4866 | 0 | entry->spatial_quality = udesc->spatial_quality; |
4867 | 0 | entry->Width = udesc->width; |
4868 | 0 | entry->Height = udesc->height; |
4869 | 0 | strncpy(entry->compressor_name, udesc->compressor_name, GF_ARRAY_LENGTH(entry->compressor_name)); |
4870 | 0 | entry->compressor_name[ GF_ARRAY_LENGTH(entry->compressor_name) - 1] = 0; |
4871 | 0 | entry->color_table_index = -1; |
4872 | 0 | entry->frames_per_sample = 1; |
4873 | 0 | entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000; |
4874 | 0 | entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000; |
4875 | 0 | entry->bit_depth = udesc->depth ? udesc->depth : 0x18; |
4876 | 0 | wrap_data = &entry->data; |
4877 | 0 | wrap_size = &entry->data_size; |
4878 | 0 | e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, entry); |
4879 | 0 | } |
4880 | 0 | else if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_AUDIO) { |
4881 | 0 | GF_GenericAudioSampleEntryBox *gena; |
4882 | | //create a new entry |
4883 | 0 | gena = (GF_GenericAudioSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRA); |
4884 | 0 | if (!gena) return GF_OUT_OF_MEM; |
4885 | | |
4886 | 0 | if (!udesc->codec_tag) { |
4887 | 0 | gena->EntryType = GF_ISOM_BOX_TYPE_UUID; |
4888 | 0 | memcpy(gena->uuid, udesc->UUID, sizeof(bin128)); |
4889 | 0 | } else { |
4890 | 0 | gena->EntryType = udesc->codec_tag; |
4891 | 0 | } |
4892 | 0 | if (gena->EntryType == 0) { |
4893 | 0 | gf_isom_box_del((GF_Box *)gena); |
4894 | 0 | return GF_NOT_SUPPORTED; |
4895 | 0 | } |
4896 | | |
4897 | 0 | gena->dataReferenceIndex = dataRefIndex; |
4898 | 0 | gena->vendor = udesc->vendor_code; |
4899 | 0 | gena->version = udesc->version; |
4900 | 0 | gena->revision = udesc->revision; |
4901 | 0 | gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16; |
4902 | 0 | gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2; |
4903 | 0 | gena->samplerate_hi = udesc->samplerate; |
4904 | 0 | gena->samplerate_lo = 0; |
4905 | 0 | gena->qtff_mode = udesc->is_qtff ? GF_ISOM_AUDIO_QTFF_ON_NOEXT : GF_ISOM_AUDIO_QTFF_NONE; |
4906 | 0 | if (gena->EntryType==GF_QT_SUBTYPE_LPCM) { |
4907 | 0 | gena->version = 2; |
4908 | 0 | gena->qtff_mode = GF_ISOM_AUDIO_QTFF_ON_EXT_VALID; |
4909 | 0 | GF_BitStream *bs = gf_bs_new(gena->extensions, 36, GF_BITSTREAM_WRITE); |
4910 | 0 | gf_bs_write_u32(bs, 72); |
4911 | 0 | gf_bs_write_double(bs, udesc->samplerate); |
4912 | 0 | gf_bs_write_u32(bs, udesc->nb_channels); |
4913 | 0 | gf_bs_write_u32(bs, 0x7F000000); |
4914 | 0 | gf_bs_write_u32(bs, gena->bitspersample); |
4915 | 0 | gf_bs_write_u32(bs, udesc->lpcm_flags); |
4916 | 0 | gf_bs_write_u32(bs, udesc->nb_channels*gena->bitspersample/8); //constBytesPerAudioPacket |
4917 | 0 | gf_bs_write_u32(bs, 1); //constLPCMFramesPerAudioPacket |
4918 | 0 | gf_bs_del(bs); |
4919 | 0 | gena->revision = 0; |
4920 | 0 | gena->vendor = 0; |
4921 | 0 | gena->channel_count = 3; |
4922 | 0 | gena->bitspersample = 16; |
4923 | 0 | gena->compression_id = 0xFFFE; |
4924 | 0 | gena->packet_size = 0; |
4925 | 0 | gena->samplerate_hi = 1; |
4926 | 0 | } else if (udesc->is_qtff) { |
4927 | 0 | GF_Box *b = gf_isom_box_new_parent(&gena->child_boxes, GF_QT_BOX_TYPE_WAVE); |
4928 | 0 | GF_ChromaInfoBox *enda = (GF_ChromaInfoBox*) gf_isom_box_new_parent(&b->child_boxes, GF_QT_BOX_TYPE_ENDA); |
4929 | 0 | ((GF_ChromaInfoBox *)enda)->chroma = (udesc->lpcm_flags & (1<<1)) ? 0 : 1; |
4930 | |
|
4931 | 0 | GF_UnknownBox *term = (GF_UnknownBox*) gf_isom_box_new_parent(&b->child_boxes, GF_ISOM_BOX_TYPE_UNKNOWN); |
4932 | 0 | if (term) term->original_4cc = 0; |
4933 | 0 | } |
4934 | |
|
4935 | 0 | wrap_data = &gena->data; |
4936 | 0 | wrap_size = &gena->data_size; |
4937 | 0 | e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, gena); |
4938 | 0 | } |
4939 | 0 | else { |
4940 | 0 | GF_GenericSampleEntryBox *genm; |
4941 | | //create a new entry |
4942 | 0 | genm = (GF_GenericSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRM); |
4943 | 0 | if (!genm) return GF_OUT_OF_MEM; |
4944 | | |
4945 | 0 | if (!udesc->codec_tag) { |
4946 | 0 | genm->EntryType = GF_ISOM_BOX_TYPE_UUID; |
4947 | 0 | memcpy(genm->uuid, udesc->UUID, sizeof(bin128)); |
4948 | 0 | } else { |
4949 | 0 | genm->EntryType = udesc->codec_tag; |
4950 | 0 | } |
4951 | 0 | if (genm->EntryType == 0) { |
4952 | 0 | gf_isom_box_del((GF_Box *)genm); |
4953 | 0 | return GF_NOT_SUPPORTED; |
4954 | 0 | } |
4955 | 0 | genm->dataReferenceIndex = dataRefIndex; |
4956 | 0 | wrap_data = &genm->data; |
4957 | 0 | wrap_size = &genm->data_size; |
4958 | 0 | e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, genm); |
4959 | 0 | } |
4960 | 0 | *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes); |
4961 | |
|
4962 | 0 | if (udesc->extension_buf && udesc->extension_buf_size) { |
4963 | 0 | GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4964 | 0 | if (udesc->ext_box_wrap) { |
4965 | 0 | gf_bs_write_u32(bs, 8+udesc->extension_buf_size); |
4966 | 0 | gf_bs_write_u32(bs, udesc->ext_box_wrap); |
4967 | 0 | } |
4968 | 0 | gf_bs_write_data(bs, udesc->extension_buf, udesc->extension_buf_size); |
4969 | 0 | gf_bs_get_content(bs, wrap_data, wrap_size); |
4970 | 0 | gf_bs_del(bs); |
4971 | 0 | } |
4972 | 0 | return e; |
4973 | 0 | } |
4974 | | |
4975 | | //use carefully. Very useful when you made a lot of changes (IPMP, IPI, OCI, ...) |
4976 | | //THIS WILL REPLACE THE WHOLE DESCRIPTOR ... |
4977 | | #if 0 //unused |
4978 | | /*change the data field of an unknown sample description*/ |
4979 | | GF_Err gf_isom_change_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_GenericSampleDescription *udesc) |
4980 | | { |
4981 | | GF_TrackBox *trak; |
4982 | | GF_Err e; |
4983 | | GF_GenericVisualSampleEntryBox *entry; |
4984 | | |
4985 | | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
4986 | | if (e) return e; |
4987 | | |
4988 | | trak = gf_isom_get_track_box(movie, trackNumber); |
4989 | | if (!trak || !trak->Media || !StreamDescriptionIndex) return GF_BAD_PARAM; |
4990 | | |
4991 | | entry = (GF_GenericVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex-1); |
4992 | | if (!entry) return GF_BAD_PARAM; |
4993 | | if (entry->type == GF_ISOM_BOX_TYPE_GNRV) { |
4994 | | entry->vendor = udesc->vendor_code; |
4995 | | entry->version = udesc->version; |
4996 | | entry->revision = udesc->revision; |
4997 | | entry->temporal_quality = udesc->temporal_quality; |
4998 | | entry->spatial_quality = udesc->spatial_quality; |
4999 | | entry->Width = udesc->width; |
5000 | | entry->Height = udesc->height; |
5001 | | strcpy(entry->compressor_name, udesc->compressor_name); |
5002 | | entry->color_table_index = -1; |
5003 | | entry->frames_per_sample = 1; |
5004 | | entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000; |
5005 | | entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000; |
5006 | | entry->bit_depth = udesc->depth ? udesc->depth : 0x18; |
5007 | | if (entry->data) gf_free(entry->data); |
5008 | | entry->data = NULL; |
5009 | | entry->data_size = 0; |
5010 | | if (udesc->extension_buf && udesc->extension_buf_size) { |
5011 | | entry->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size); |
5012 | | if (!entry->data) { |
5013 | | gf_isom_box_del((GF_Box *) entry); |
5014 | | return GF_OUT_OF_MEM; |
5015 | | } |
5016 | | memcpy(entry->data, udesc->extension_buf, udesc->extension_buf_size); |
5017 | | entry->data_size = udesc->extension_buf_size; |
5018 | | } |
5019 | | return GF_OK; |
5020 | | } else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) { |
5021 | | GF_GenericAudioSampleEntryBox *gena = (GF_GenericAudioSampleEntryBox *)entry; |
5022 | | gena->vendor = udesc->vendor_code; |
5023 | | gena->version = udesc->version; |
5024 | | gena->revision = udesc->revision; |
5025 | | gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16; |
5026 | | gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2; |
5027 | | gena->samplerate_hi = udesc->samplerate; |
5028 | | gena->samplerate_lo = 0; |
5029 | | if (gena->data) gf_free(gena->data); |
5030 | | gena->data = NULL; |
5031 | | gena->data_size = 0; |
5032 | | |
5033 | | if (udesc->extension_buf && udesc->extension_buf_size) { |
5034 | | gena->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size); |
5035 | | if (!gena->data) { |
5036 | | gf_isom_box_del((GF_Box *) gena); |
5037 | | return GF_OUT_OF_MEM; |
5038 | | } |
5039 | | memcpy(gena->data, udesc->extension_buf, udesc->extension_buf_size); |
5040 | | gena->data_size = udesc->extension_buf_size; |
5041 | | } |
5042 | | return GF_OK; |
5043 | | } else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) { |
5044 | | GF_GenericSampleEntryBox *genm = (GF_GenericSampleEntryBox *)entry; |
5045 | | if (genm->data) gf_free(genm->data); |
5046 | | genm->data = NULL; |
5047 | | genm->data_size = 0; |
5048 | | |
5049 | | if (udesc->extension_buf && udesc->extension_buf_size) { |
5050 | | genm->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size); |
5051 | | if (!genm->data) { |
5052 | | gf_isom_box_del((GF_Box *) genm); |
5053 | | return GF_OUT_OF_MEM; |
5054 | | } |
5055 | | memcpy(genm->data, udesc->extension_buf, udesc->extension_buf_size); |
5056 | | genm->data_size = udesc->extension_buf_size; |
5057 | | } |
5058 | | return GF_OK; |
5059 | | } |
5060 | | return GF_BAD_PARAM; |
5061 | | } |
5062 | | #endif |
5063 | | |
5064 | | #if 0 |
5065 | | /*removes given stream description*/ |
5066 | | GF_Err gf_isom_remove_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 streamDescIndex) |
5067 | | { |
5068 | | GF_TrackBox *trak; |
5069 | | GF_Err e; |
5070 | | GF_Box *entry; |
5071 | | |
5072 | | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
5073 | | if (e) return e; |
5074 | | trak = gf_isom_get_track_box(movie, trackNumber); |
5075 | | if (!trak || !trak->Media || !streamDescIndex) return GF_BAD_PARAM; |
5076 | | entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, streamDescIndex-1); |
5077 | | if (!entry) return GF_BAD_PARAM; |
5078 | | gf_list_rem(trak->Media->information->sampleTable->SampleDescription->child_boxes, streamDescIndex-1); |
5079 | | gf_isom_box_del(entry); |
5080 | | return GF_OK; |
5081 | | } |
5082 | | #endif |
5083 | | |
5084 | | //sets a track reference |
5085 | | GF_EXPORT |
5086 | | GF_Err gf_isom_set_track_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, GF_ISOTrackID ReferencedTrackID) |
5087 | 0 | { |
5088 | 0 | GF_Err e; |
5089 | 0 | GF_TrackBox *trak; |
5090 | 0 | GF_TrackReferenceBox *tref; |
5091 | 0 | GF_TrackReferenceTypeBox *dpnd; |
5092 | |
|
5093 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
5094 | 0 | if (!trak) return GF_BAD_PARAM; |
5095 | | |
5096 | | //no tref, create one |
5097 | 0 | tref = trak->References; |
5098 | 0 | if (!tref) { |
5099 | 0 | tref = (GF_TrackReferenceBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TREF); |
5100 | 0 | if (!tref) return GF_OUT_OF_MEM; |
5101 | 0 | e = trak_on_child_box((GF_Box*)trak, (GF_Box *) tref, GF_FALSE); |
5102 | 0 | if (e) return e; |
5103 | 0 | } |
5104 | | //find a ref of the given type |
5105 | 0 | e = Track_FindRef(trak, referenceType, &dpnd); |
5106 | 0 | if (e) return e; |
5107 | | |
5108 | 0 | if (!dpnd) { |
5109 | 0 | dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT); |
5110 | 0 | if (!dpnd) return GF_OUT_OF_MEM; |
5111 | 0 | dpnd->reference_type = referenceType; |
5112 | 0 | } |
5113 | | //add the ref |
5114 | 0 | return reftype_AddRefTrack(dpnd, ReferencedTrackID, NULL); |
5115 | 0 | } |
5116 | | |
5117 | | GF_EXPORT |
5118 | | GF_Err gf_isom_purge_track_reference(GF_ISOFile *the_file, u32 trackNumber) |
5119 | 0 | { |
5120 | 0 | GF_TrackBox *trak; |
5121 | 0 | GF_TrackReferenceTypeBox *ref; |
5122 | 0 | u32 i=0; |
5123 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
5124 | 0 | if (!trak) return GF_BAD_PARAM; |
5125 | | |
5126 | | //no tref, nothing to remove |
5127 | 0 | if (!trak->References) return GF_OK; |
5128 | | |
5129 | 0 | while ((ref = gf_list_enum(trak->References->child_boxes, &i))) { |
5130 | 0 | u32 k; |
5131 | 0 | if (!ref->reference_type) continue; |
5132 | | |
5133 | 0 | for (k=0; k<ref->trackIDCount; k++) { |
5134 | 0 | u32 tk = gf_isom_get_track_by_id(the_file, ref->trackIDs[k]); |
5135 | 0 | if (!tk) { |
5136 | 0 | memmove(&ref->trackIDs[k], &ref->trackIDs[k+1], ref->trackIDCount-k-1); |
5137 | 0 | k--; |
5138 | 0 | ref->trackIDCount--; |
5139 | 0 | } |
5140 | 0 | } |
5141 | 0 | if (!ref->trackIDCount) { |
5142 | 0 | i--; |
5143 | 0 | gf_isom_box_del_parent(&trak->References->child_boxes, (GF_Box *) ref); |
5144 | 0 | } |
5145 | 0 | } |
5146 | 0 | if (!trak->References->child_boxes || !gf_list_count(trak->References->child_boxes)) { |
5147 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *) trak->References); |
5148 | 0 | trak->References = NULL; |
5149 | 0 | } |
5150 | 0 | return GF_OK; |
5151 | 0 | } |
5152 | | |
5153 | | //sets a track reference |
5154 | | GF_EXPORT |
5155 | | GF_Err gf_isom_remove_track_references(GF_ISOFile *the_file, u32 trackNumber) |
5156 | 0 | { |
5157 | 0 | GF_TrackBox *trak; |
5158 | |
|
5159 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
5160 | 0 | if (!trak) return GF_BAD_PARAM; |
5161 | | |
5162 | 0 | if (trak->References) { |
5163 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->References); |
5164 | 0 | trak->References = NULL; |
5165 | 0 | } |
5166 | 0 | return GF_OK; |
5167 | 0 | } |
5168 | | |
5169 | | GF_EXPORT |
5170 | | GF_Err gf_isom_remove_track_reference(GF_ISOFile *isom_file, u32 trackNumber, u32 ref_type) |
5171 | 0 | { |
5172 | 0 | GF_TrackBox *trak; |
5173 | 0 | u32 i=0; |
5174 | 0 | GF_TrackReferenceTypeBox *ref; |
5175 | 0 | trak = gf_isom_get_track_box(isom_file, trackNumber); |
5176 | 0 | if (!trak) return GF_BAD_PARAM; |
5177 | | |
5178 | 0 | if (!trak->References) return GF_OK; |
5179 | 0 | while ((ref = gf_list_enum(trak->References->child_boxes, &i))) { |
5180 | 0 | if (ref->reference_type == ref_type) { |
5181 | 0 | gf_isom_box_del_parent(&trak->References->child_boxes, (GF_Box *)ref); |
5182 | 0 | break; |
5183 | 0 | } |
5184 | 0 | } |
5185 | 0 | if (!gf_list_count(trak->References->child_boxes)) { |
5186 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->References); |
5187 | 0 | trak->References = NULL; |
5188 | 0 | } |
5189 | 0 | return GF_OK; |
5190 | |
|
5191 | 0 | } |
5192 | | |
5193 | | //changes track ID |
5194 | | GF_EXPORT |
5195 | | GF_Err gf_isom_set_track_id(GF_ISOFile *movie, u32 trackNumber, GF_ISOTrackID trackID) |
5196 | 0 | { |
5197 | 0 | GF_TrackReferenceTypeBox *ref; |
5198 | 0 | GF_TrackBox *trak, *a_trak; |
5199 | 0 | u32 i, j, k; |
5200 | |
|
5201 | 0 | if (!movie) return GF_BAD_PARAM; |
5202 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
5203 | 0 | if (trak && (trak->Header->trackID==trackID)) return GF_OK; |
5204 | 0 | a_trak = gf_isom_get_track_from_id(movie->moov, trackID); |
5205 | 0 | if (!trak || a_trak) return GF_BAD_PARAM; |
5206 | | |
5207 | | /*rewrite all dependencies*/ |
5208 | 0 | i=0; |
5209 | 0 | while ((a_trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) { |
5210 | 0 | if (!a_trak->References) continue; |
5211 | 0 | j=0; |
5212 | 0 | while ((ref = (GF_TrackReferenceTypeBox *)gf_list_enum(a_trak->References->child_boxes, &j))) { |
5213 | 0 | for (k=0; k<ref->trackIDCount; k++) { |
5214 | 0 | if (ref->trackIDs[k]==trak->Header->trackID) { |
5215 | 0 | ref->trackIDs[k] = trackID; |
5216 | 0 | break; |
5217 | 0 | } |
5218 | 0 | } |
5219 | 0 | } |
5220 | 0 | } |
5221 | | |
5222 | | /*and update IOD if any*/ |
5223 | 0 | if (movie->moov->iods && movie->moov->iods->descriptor) { |
5224 | 0 | GF_ES_ID_Inc *inc; |
5225 | 0 | GF_IsomObjectDescriptor *od = (GF_IsomObjectDescriptor *)movie->moov->iods->descriptor; |
5226 | |
|
5227 | 0 | i=0; |
5228 | 0 | while ((inc = (GF_ES_ID_Inc*)gf_list_enum(od->ES_ID_IncDescriptors, &i))) { |
5229 | 0 | if (inc->trackID==trak->Header->trackID) inc->trackID = trackID; |
5230 | 0 | } |
5231 | 0 | } |
5232 | 0 | trak->Header->trackID = trackID; |
5233 | 0 | update_next_track_id(movie); |
5234 | 0 | return GF_OK; |
5235 | 0 | } |
5236 | | |
5237 | | /*force to rewrite all dependencies when the trackID of referenced track changes*/ |
5238 | | GF_EXPORT |
5239 | | GF_Err gf_isom_rewrite_track_dependencies(GF_ISOFile *movie, u32 trackNumber) |
5240 | 0 | { |
5241 | 0 | GF_TrackReferenceTypeBox *ref; |
5242 | 0 | GF_TrackBox *trak, *a_trak; |
5243 | 0 | u32 i, k; |
5244 | |
|
5245 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
5246 | 0 | if (!trak) |
5247 | 0 | return GF_BAD_PARAM; |
5248 | 0 | if (!trak->References) |
5249 | 0 | return GF_OK; |
5250 | | |
5251 | 0 | i=0; |
5252 | 0 | while ((ref = (GF_TrackReferenceTypeBox *)gf_list_enum(trak->References->child_boxes, &i))) { |
5253 | 0 | for (k=0; k < ref->trackIDCount; k++) { |
5254 | 0 | a_trak = gf_isom_get_track_from_original_id(movie->moov, ref->trackIDs[k], trak->originalFile); |
5255 | 0 | if (a_trak) { |
5256 | 0 | ref->trackIDs[k] = a_trak->Header->trackID; |
5257 | 0 | } else { |
5258 | 0 | a_trak = gf_isom_get_track_from_id(movie->moov, ref->trackIDs[k]); |
5259 | | /*we should have a track with no original ID (not imported) - should we rewrite the dependency ?*/ |
5260 | 0 | if (! a_trak || a_trak->originalID) return GF_BAD_PARAM; |
5261 | 0 | } |
5262 | 0 | } |
5263 | 0 | } |
5264 | | |
5265 | 0 | return GF_OK; |
5266 | 0 | } |
5267 | | |
5268 | | #if 0 //unused |
5269 | | |
5270 | | /*! changes the sample description index of a sample |
5271 | | \param isom_file the destination ISO file |
5272 | | \param trackNumber the destination track |
5273 | | \param sampleNum the target sample number |
5274 | | \param fnewSampleDescIndex the new sample description index to assign to the sample |
5275 | | \return error if any |
5276 | | */ |
5277 | | GF_EXPORT |
5278 | | GF_Err gf_isom_change_sample_desc_index(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 newSampleDescIndex) |
5279 | | { |
5280 | | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5281 | | if (!trak || !sample_number || !newSampleDescIndex) return GF_BAD_PARAM; |
5282 | | if (!trak->is_unpacked) { |
5283 | | unpack_track(trak); |
5284 | | } |
5285 | | if (!trak->Media->information->sampleTable->SampleToChunk) return GF_BAD_PARAM; |
5286 | | if (trak->Media->information->sampleTable->SampleToChunk->nb_entries < sample_number) return GF_BAD_PARAM; |
5287 | | trak->Media->information->sampleTable->SampleToChunk->entries[sample_number-1].sampleDescriptionIndex = newSampleDescIndex; |
5288 | | return GF_OK; |
5289 | | } |
5290 | | |
5291 | | /*modify CTS offset of a given sample (used for B-frames) - MUST be called in unpack mode only*/ |
5292 | | GF_EXPORT |
5293 | | GF_Err gf_isom_modify_cts_offset(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 offset) |
5294 | | { |
5295 | | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5296 | | if (!trak) return GF_BAD_PARAM; |
5297 | | if (!trak->Media->information->sampleTable->CompositionOffset) return GF_BAD_PARAM; |
5298 | | if (!trak->Media->information->sampleTable->CompositionOffset->unpack_mode) return GF_BAD_PARAM; |
5299 | | /*we're in unpack mode: one entry per sample*/ |
5300 | | trak->Media->information->sampleTable->CompositionOffset->entries[sample_number - 1].decodingOffset = offset; |
5301 | | return GF_OK; |
5302 | | } |
5303 | | #endif |
5304 | | |
5305 | | GF_EXPORT |
5306 | | GF_Err gf_isom_shift_cts_offset(GF_ISOFile *the_file, u32 trackNumber, s32 offset_shift) |
5307 | 0 | { |
5308 | 0 | u32 i; |
5309 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5310 | 0 | if (!trak) return GF_BAD_PARAM; |
5311 | 0 | if (!trak->Media->information->sampleTable->CompositionOffset) return GF_BAD_PARAM; |
5312 | 0 | if (!trak->Media->information->sampleTable->CompositionOffset->unpack_mode) return GF_BAD_PARAM; |
5313 | | |
5314 | 0 | GF_CompositionOffsetBox *ctso = trak->Media->information->sampleTable->CompositionOffset; |
5315 | 0 | for (i=0; i<ctso->nb_entries; i++) { |
5316 | 0 | s64 new_ts = ctso->entries[i].decodingOffset; |
5317 | 0 | new_ts -= offset_shift; |
5318 | | /*we're in unpack mode: one entry per sample*/ |
5319 | 0 | ctso->entries[i].decodingOffset = (s32) new_ts; |
5320 | 0 | } |
5321 | 0 | if (trak->Media->mediaHeader->duration >= -offset_shift) { |
5322 | 0 | s64 new_dur = trak->Media->mediaHeader->duration; |
5323 | 0 | new_dur -= offset_shift; |
5324 | 0 | if (new_dur<0) new_dur = 0; |
5325 | 0 | trak->Media->mediaHeader->duration = (u32) new_dur; |
5326 | 0 | } |
5327 | 0 | return GF_OK; |
5328 | 0 | } |
5329 | | |
5330 | | #if 0 //unused |
5331 | | GF_Err gf_isom_remove_cts_info(GF_ISOFile *the_file, u32 trackNumber) |
5332 | | { |
5333 | | GF_SampleTableBox *stbl; |
5334 | | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5335 | | if (!trak) return GF_BAD_PARAM; |
5336 | | |
5337 | | stbl = trak->Media->information->sampleTable; |
5338 | | if (!stbl->CompositionOffset) return GF_OK; |
5339 | | |
5340 | | gf_isom_box_del_parent(&stbl->child_boxes, (GF_Box *)stbl->CompositionOffset); |
5341 | | stbl->CompositionOffset = NULL; |
5342 | | return GF_OK; |
5343 | | } |
5344 | | #endif |
5345 | | |
5346 | | GF_EXPORT |
5347 | | GF_Err gf_isom_set_cts_packing(GF_ISOFile *the_file, u32 trackNumber, Bool unpack) |
5348 | 0 | { |
5349 | 0 | GF_Err e; |
5350 | 0 | GF_Err stbl_repackCTS(GF_CompositionOffsetBox *ctts); |
5351 | 0 | GF_Err stbl_unpackCTS(GF_SampleTableBox *stbl); |
5352 | 0 | GF_SampleTableBox *stbl; |
5353 | |
|
5354 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5355 | 0 | if (!trak) return GF_BAD_PARAM; |
5356 | | |
5357 | 0 | stbl = trak->Media->information->sampleTable; |
5358 | 0 | if (unpack) { |
5359 | 0 | if (!stbl->CompositionOffset) { |
5360 | 0 | stbl->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_CTTS); |
5361 | 0 | if (!stbl->CompositionOffset) return GF_OUT_OF_MEM; |
5362 | 0 | } |
5363 | 0 | e = stbl_unpackCTS(stbl); |
5364 | 0 | } else { |
5365 | 0 | if (!stbl->CompositionOffset) return GF_OK; |
5366 | 0 | e = stbl_repackCTS(stbl->CompositionOffset); |
5367 | 0 | } |
5368 | 0 | if (e) return e; |
5369 | 0 | return SetTrackDuration(trak); |
5370 | 0 | } |
5371 | | |
5372 | | GF_EXPORT |
5373 | | GF_Err gf_isom_set_track_matrix(GF_ISOFile *the_file, u32 trackNumber, s32 matrix[9]) |
5374 | 0 | { |
5375 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5376 | 0 | if (!trak || !trak->Header) return GF_BAD_PARAM; |
5377 | 0 | memcpy(trak->Header->matrix, matrix, sizeof(trak->Header->matrix)); |
5378 | 0 | return GF_OK; |
5379 | 0 | } |
5380 | | |
5381 | | GF_EXPORT |
5382 | | GF_Err gf_isom_set_track_layout_info(GF_ISOFile *the_file, u32 trackNumber, u32 width, u32 height, s32 translation_x, s32 translation_y, s16 layer) |
5383 | 0 | { |
5384 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5385 | 0 | if (!trak || !trak->Header) return GF_BAD_PARAM; |
5386 | 0 | trak->Header->width = width; |
5387 | 0 | trak->Header->height = height; |
5388 | 0 | trak->Header->matrix[6] = translation_x; |
5389 | 0 | trak->Header->matrix[7] = translation_y; |
5390 | 0 | trak->Header->layer = layer; |
5391 | 0 | return GF_OK; |
5392 | 0 | } |
5393 | | |
5394 | | GF_EXPORT |
5395 | | GF_Err gf_isom_set_media_timescale(GF_ISOFile *the_file, u32 trackNumber, u32 newTS, u32 new_tsinc, u32 force_rescale_type) |
5396 | 0 | { |
5397 | 0 | Double scale; |
5398 | 0 | u32 old_ts_inc=0; |
5399 | 0 | u32 old_timescale; |
5400 | 0 | GF_TrackBox *trak; |
5401 | 0 | GF_SampleTableBox *stbl; |
5402 | |
|
5403 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
5404 | 0 | if (!trak || !trak->Media || !trak->Media->mediaHeader) return GF_BAD_PARAM; |
5405 | 0 | if ((trak->Media->mediaHeader->timeScale==newTS) && !new_tsinc) |
5406 | 0 | return GF_OK; //nothing to do |
5407 | | |
5408 | 0 | if (!newTS) newTS = trak->Media->mediaHeader->timeScale; |
5409 | 0 | scale = newTS; |
5410 | 0 | scale /= trak->Media->mediaHeader->timeScale; |
5411 | 0 | old_timescale = trak->Media->mediaHeader->timeScale; |
5412 | 0 | trak->Media->mediaHeader->timeScale = newTS; |
5413 | |
|
5414 | 0 | stbl = trak->Media->information->sampleTable; |
5415 | 0 | if (new_tsinc) { |
5416 | 0 | u32 i; |
5417 | 0 | if (!stbl->TimeToSample || !stbl->TimeToSample->nb_entries) |
5418 | 0 | return GF_BAD_PARAM; |
5419 | | |
5420 | 0 | for (i=0; i<stbl->TimeToSample->nb_entries; i++) { |
5421 | 0 | if (!old_ts_inc) |
5422 | 0 | old_ts_inc = stbl->TimeToSample->entries[i].sampleDelta; |
5423 | 0 | else if (old_ts_inc<stbl->TimeToSample->entries[i].sampleDelta) |
5424 | 0 | old_ts_inc = stbl->TimeToSample->entries[i].sampleDelta; |
5425 | 0 | } |
5426 | |
|
5427 | 0 | if ((old_timescale==newTS) && (old_ts_inc==new_tsinc) && (force_rescale_type!=2) ) |
5428 | 0 | return GF_EOS; |
5429 | | |
5430 | 0 | if (!force_rescale_type) |
5431 | 0 | force_rescale_type = 1; |
5432 | 0 | else if (force_rescale_type==2) { |
5433 | 0 | gf_free(stbl->TimeToSample->entries); |
5434 | 0 | stbl->TimeToSample->alloc_size = 1; |
5435 | 0 | stbl->TimeToSample->nb_entries = 1; |
5436 | 0 | stbl->TimeToSample->entries = gf_malloc(sizeof(GF_SttsEntry)); |
5437 | 0 | stbl->TimeToSample->entries[0].sampleDelta = new_tsinc; |
5438 | 0 | stbl->TimeToSample->entries[0].sampleCount = stbl->SampleSize->sampleCount; |
5439 | 0 | } |
5440 | | |
5441 | |
|
5442 | 0 | for (i=0; i<stbl->TimeToSample->nb_entries; i++) { |
5443 | 0 | stbl->TimeToSample->entries[i].sampleDelta = new_tsinc; |
5444 | 0 | } |
5445 | |
|
5446 | 0 | if (stbl->CompositionOffset) { |
5447 | 0 | for (i=0; i<stbl->CompositionOffset->nb_entries; i++) { |
5448 | 0 | u32 old_offset = stbl->CompositionOffset->entries[i].decodingOffset; |
5449 | 0 | if (force_rescale_type==2) { |
5450 | 0 | u32 val = old_offset ; |
5451 | | //get number of TS delta |
5452 | 0 | old_offset /= old_ts_inc; |
5453 | 0 | if (old_offset * old_ts_inc < val) |
5454 | 0 | old_offset++; |
5455 | 0 | old_offset *= new_tsinc; |
5456 | 0 | } else { |
5457 | 0 | old_offset *= new_tsinc; |
5458 | 0 | old_offset /= old_ts_inc; |
5459 | 0 | } |
5460 | 0 | stbl->CompositionOffset->entries[i].decodingOffset = old_offset; |
5461 | 0 | } |
5462 | 0 | } |
5463 | |
|
5464 | 0 | #define RESCALE_TSVAL(_tsval) {\ |
5465 | 0 | s64 val = ((s64) _tsval) * new_tsinc;\ |
5466 | 0 | val /= old_ts_inc;\ |
5467 | 0 | _tsval = (s32) val;\ |
5468 | 0 | } |
5469 | |
|
5470 | 0 | if (stbl->CompositionToDecode) { |
5471 | 0 | RESCALE_TSVAL(stbl->CompositionToDecode->compositionEndTime) |
5472 | 0 | RESCALE_TSVAL(stbl->CompositionToDecode->compositionStartTime) |
5473 | 0 | RESCALE_TSVAL(stbl->CompositionToDecode->compositionToDTSShift) |
5474 | 0 | RESCALE_TSVAL(stbl->CompositionToDecode->greatestDecodeToDisplayDelta) |
5475 | 0 | RESCALE_TSVAL(stbl->CompositionToDecode->leastDecodeToDisplayDelta) |
5476 | 0 | } |
5477 | 0 | if (trak->editBox) { |
5478 | 0 | GF_EdtsEntry *ent; |
5479 | 0 | i=0; |
5480 | 0 | while ((ent = (GF_EdtsEntry*)gf_list_enum(trak->editBox->editList->entryList, &i))) { |
5481 | 0 | RESCALE_TSVAL(ent->mediaTime) |
5482 | 0 | } |
5483 | 0 | } |
5484 | 0 | #undef RESCALE_TSVAL |
5485 | | //force recompute of duration |
5486 | 0 | trak->Media->mediaHeader->duration=0; |
5487 | 0 | return SetTrackDuration(trak); |
5488 | 0 | } |
5489 | | |
5490 | | //rescale timings |
5491 | 0 | u32 i, k, idx, last_delta; |
5492 | 0 | u64 cur_dts; |
5493 | 0 | u64*DTSs = NULL; |
5494 | 0 | s64*CTSs = NULL; |
5495 | |
|
5496 | 0 | if (trak->editBox) { |
5497 | 0 | GF_EdtsEntry *ent; |
5498 | 0 | i=0; |
5499 | 0 | while ((ent = (GF_EdtsEntry*)gf_list_enum(trak->editBox->editList->entryList, &i))) { |
5500 | | //only update if media time is >=0 (neg means empty edit) |
5501 | 0 | if (ent->mediaTime>=0) |
5502 | 0 | ent->mediaTime = (u32) (scale*ent->mediaTime); |
5503 | 0 | } |
5504 | 0 | } |
5505 | 0 | if (! stbl || !stbl->TimeToSample || !stbl->TimeToSample->nb_entries) { |
5506 | 0 | return SetTrackDuration(trak); |
5507 | 0 | } |
5508 | | |
5509 | 0 | idx = 0; |
5510 | 0 | cur_dts = 0; |
5511 | | //unpack the DTSs |
5512 | 0 | DTSs = (u64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount) ); |
5513 | 0 | if (!DTSs) return GF_OUT_OF_MEM; |
5514 | | |
5515 | 0 | CTSs = NULL; |
5516 | 0 | if (stbl->CompositionOffset) { |
5517 | 0 | CTSs = (s64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount) ); |
5518 | 0 | if (!CTSs) return GF_OUT_OF_MEM; |
5519 | 0 | } |
5520 | | |
5521 | 0 | for (i=0; i<stbl->TimeToSample->nb_entries; i++) { |
5522 | 0 | for (k=0; k<stbl->TimeToSample->entries[i].sampleCount; k++) { |
5523 | 0 | cur_dts += stbl->TimeToSample->entries[i].sampleDelta; |
5524 | 0 | DTSs[idx] = (u64) (cur_dts * scale); |
5525 | |
|
5526 | 0 | if (stbl->CompositionOffset) { |
5527 | 0 | s32 cts_o; |
5528 | 0 | stbl_GetSampleCTS(stbl->CompositionOffset, idx+1, &cts_o); |
5529 | 0 | CTSs[idx] = (s64) ( ((s64) cur_dts + cts_o) * scale); |
5530 | 0 | } |
5531 | 0 | idx++; |
5532 | 0 | } |
5533 | 0 | } |
5534 | 0 | last_delta = (u32) (stbl->TimeToSample->entries[stbl->TimeToSample->nb_entries-1].sampleDelta * scale); |
5535 | | |
5536 | | //repack DTS |
5537 | 0 | if (stbl->SampleSize->sampleCount) { |
5538 | 0 | stbl->TimeToSample->entries = gf_realloc(stbl->TimeToSample->entries, sizeof(GF_SttsEntry)*stbl->SampleSize->sampleCount); |
5539 | 0 | memset(stbl->TimeToSample->entries, 0, sizeof(GF_SttsEntry)*stbl->SampleSize->sampleCount); |
5540 | 0 | stbl->TimeToSample->entries[0].sampleDelta = (u32) DTSs[0]; |
5541 | 0 | stbl->TimeToSample->entries[0].sampleCount = 1; |
5542 | 0 | idx=0; |
5543 | 0 | for (i=1; i< stbl->SampleSize->sampleCount - 1; i++) { |
5544 | 0 | if (DTSs[i+1] - DTSs[i] == stbl->TimeToSample->entries[idx].sampleDelta) { |
5545 | 0 | stbl->TimeToSample->entries[idx].sampleCount++; |
5546 | 0 | } else { |
5547 | 0 | idx++; |
5548 | 0 | stbl->TimeToSample->entries[idx].sampleDelta = (u32) ( DTSs[i+1] - DTSs[i] ); |
5549 | 0 | stbl->TimeToSample->entries[idx].sampleCount=1; |
5550 | 0 | } |
5551 | 0 | } |
5552 | 0 | if (stbl->SampleSize->sampleCount > 1) { |
5553 | | //add the sample delta for the last sample |
5554 | 0 | if (stbl->TimeToSample->entries[idx].sampleDelta == last_delta) { |
5555 | 0 | stbl->TimeToSample->entries[idx].sampleCount++; |
5556 | 0 | } else { |
5557 | 0 | idx++; |
5558 | 0 | stbl->TimeToSample->entries[idx].sampleDelta = last_delta; |
5559 | 0 | stbl->TimeToSample->entries[idx].sampleCount=1; |
5560 | 0 | } |
5561 | |
|
5562 | 0 | stbl->TimeToSample->nb_entries = idx+1; |
5563 | 0 | stbl->TimeToSample->entries = gf_realloc(stbl->TimeToSample->entries, sizeof(GF_SttsEntry)*stbl->TimeToSample->nb_entries); |
5564 | 0 | } |
5565 | 0 | } |
5566 | |
|
5567 | 0 | if (CTSs && stbl->SampleSize->sampleCount>0) { |
5568 | | //repack CTS |
5569 | 0 | stbl->CompositionOffset->entries = gf_realloc(stbl->CompositionOffset->entries, sizeof(GF_DttsEntry)*stbl->SampleSize->sampleCount); |
5570 | 0 | memset(stbl->CompositionOffset->entries, 0, sizeof(GF_DttsEntry)*stbl->SampleSize->sampleCount); |
5571 | 0 | stbl->CompositionOffset->entries[0].decodingOffset = (s32) (CTSs[0] - DTSs[0]); |
5572 | 0 | stbl->CompositionOffset->entries[0].sampleCount = 1; |
5573 | 0 | idx=0; |
5574 | 0 | for (i=1; i< stbl->SampleSize->sampleCount; i++) { |
5575 | 0 | s32 cts_o = (s32) (CTSs[i] - DTSs[i]); |
5576 | 0 | if (cts_o == stbl->CompositionOffset->entries[idx].decodingOffset) { |
5577 | 0 | stbl->CompositionOffset->entries[idx].sampleCount++; |
5578 | 0 | } else { |
5579 | 0 | idx++; |
5580 | 0 | stbl->CompositionOffset->entries[idx].decodingOffset = cts_o; |
5581 | 0 | stbl->CompositionOffset->entries[idx].sampleCount=1; |
5582 | 0 | } |
5583 | 0 | } |
5584 | 0 | stbl->CompositionOffset->nb_entries = idx+1; |
5585 | 0 | stbl->CompositionOffset->entries = gf_realloc(stbl->CompositionOffset->entries, sizeof(GF_DttsEntry)*stbl->CompositionOffset->nb_entries); |
5586 | |
|
5587 | 0 | gf_free(CTSs); |
5588 | 0 | } |
5589 | 0 | gf_free(DTSs); |
5590 | |
|
5591 | 0 | if (stbl->CompositionToDecode) { |
5592 | 0 | stbl->CompositionToDecode->compositionEndTime = (s32) (stbl->CompositionToDecode->compositionEndTime * scale); |
5593 | 0 | stbl->CompositionToDecode->compositionStartTime = (s32)(stbl->CompositionToDecode->compositionStartTime * scale); |
5594 | 0 | stbl->CompositionToDecode->compositionToDTSShift = (s32)(stbl->CompositionToDecode->compositionToDTSShift * scale); |
5595 | 0 | stbl->CompositionToDecode->greatestDecodeToDisplayDelta = (s32)(stbl->CompositionToDecode->greatestDecodeToDisplayDelta * scale); |
5596 | 0 | stbl->CompositionToDecode->leastDecodeToDisplayDelta = (s32)(stbl->CompositionToDecode->leastDecodeToDisplayDelta * scale); |
5597 | 0 | } |
5598 | |
|
5599 | 0 | return SetTrackDuration(trak); |
5600 | 0 | } |
5601 | | |
5602 | | GF_EXPORT |
5603 | | Bool gf_isom_box_equal(GF_Box *a, GF_Box *b) |
5604 | 0 | { |
5605 | 0 | Bool ret; |
5606 | 0 | u8 *data1, *data2; |
5607 | 0 | u32 data1_size, data2_size; |
5608 | 0 | GF_BitStream *bs; |
5609 | |
|
5610 | 0 | if (a == b) return GF_TRUE; |
5611 | 0 | if (!a || !b) return GF_FALSE; |
5612 | | //do NOT check size, they could be set/not set at this point |
5613 | | |
5614 | 0 | data1 = data2 = NULL; |
5615 | |
|
5616 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
5617 | 0 | gf_isom_box_size(a); |
5618 | 0 | gf_isom_box_write(a, bs); |
5619 | 0 | gf_bs_get_content(bs, &data1, &data1_size); |
5620 | 0 | gf_bs_del(bs); |
5621 | |
|
5622 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
5623 | 0 | gf_isom_box_size(b); |
5624 | 0 | gf_isom_box_write(b, bs); |
5625 | 0 | gf_bs_get_content(bs, &data2, &data2_size); |
5626 | 0 | gf_bs_del(bs); |
5627 | |
|
5628 | 0 | ret = GF_FALSE; |
5629 | 0 | if (data1_size == data2_size) { |
5630 | 0 | ret = (memcmp(data1, data2, sizeof(char)*data1_size) == 0) ? GF_TRUE : GF_FALSE; |
5631 | 0 | } |
5632 | 0 | gf_free(data1); |
5633 | 0 | gf_free(data2); |
5634 | 0 | return ret; |
5635 | 0 | } |
5636 | | |
5637 | | static u32 base_sample_entry_type(u32 type) |
5638 | 0 | { |
5639 | 0 | if (type==GF_ISOM_SUBTYPE_DVH1) return GF_ISOM_SUBTYPE_HVC1; |
5640 | 0 | if (type==GF_ISOM_SUBTYPE_DVHE) return GF_ISOM_SUBTYPE_HEV1; |
5641 | 0 | if (type==GF_ISOM_SUBTYPE_DVA1) return GF_ISOM_SUBTYPE_AVC_H264; |
5642 | 0 | if (type==GF_ISOM_SUBTYPE_DVAV) return GF_ISOM_SUBTYPE_AVC3_H264; |
5643 | 0 | if (type==GF_ISOM_SUBTYPE_DAV1) return GF_ISOM_SUBTYPE_AV01; |
5644 | 0 | return type; |
5645 | 0 | } |
5646 | | |
5647 | | GF_EXPORT |
5648 | | Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, u32 sdesc_index1, GF_ISOFile *f2, u32 tk2, u32 sdesc_index2) |
5649 | 0 | { |
5650 | 0 | u32 i, count; |
5651 | 0 | GF_TrackBox *trak1, *trak2; |
5652 | 0 | GF_ESD *esd1, *esd2; |
5653 | 0 | Bool need_memcmp, ret; |
5654 | 0 | GF_Box *a, *b; |
5655 | | |
5656 | | /*get orig sample desc and clone it*/ |
5657 | 0 | trak1 = gf_isom_get_track_box(f1, tk1); |
5658 | 0 | if (!trak1 || !trak1->Media) return GF_FALSE; |
5659 | 0 | trak2 = gf_isom_get_track_box(f2, tk2); |
5660 | 0 | if (!trak2 || !trak2->Media) return GF_FALSE; |
5661 | | |
5662 | 0 | if (trak1->Media->handler->handlerType != trak2->Media->handler->handlerType) return GF_FALSE; |
5663 | 0 | count = gf_list_count(trak1->Media->information->sampleTable->SampleDescription->child_boxes); |
5664 | 0 | if (count != gf_list_count(trak2->Media->information->sampleTable->SampleDescription->child_boxes)) { |
5665 | 0 | if (!sdesc_index1 && !sdesc_index2) return GF_FALSE; |
5666 | 0 | } |
5667 | | |
5668 | 0 | need_memcmp = GF_TRUE; |
5669 | 0 | for (i=0; i<count; i++) { |
5670 | 0 | u32 type1, type2; |
5671 | 0 | GF_SampleEntryBox *ent1 = (GF_SampleEntryBox *)gf_list_get(trak1->Media->information->sampleTable->SampleDescription->child_boxes, i); |
5672 | 0 | GF_SampleEntryBox *ent2 = (GF_SampleEntryBox *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->child_boxes, i); |
5673 | |
|
5674 | 0 | if (sdesc_index1) ent1 = (GF_SampleEntryBox *)gf_list_get(trak1->Media->information->sampleTable->SampleDescription->child_boxes, sdesc_index1 - 1); |
5675 | 0 | if (sdesc_index2) ent2 = (GF_SampleEntryBox *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->child_boxes, sdesc_index2 - 1); |
5676 | |
|
5677 | 0 | if (!ent1 || !ent2) return GF_FALSE; |
5678 | 0 | if (ent1->internal_type != ent2->internal_type) return GF_FALSE; |
5679 | 0 | type1 = base_sample_entry_type(ent1->type); |
5680 | 0 | type2 = base_sample_entry_type(ent2->type); |
5681 | 0 | if (type1 != type2) return GF_FALSE; |
5682 | | |
5683 | 0 | switch (ent1->type) { |
5684 | | /*for MPEG-4 streams, only compare decSpecInfo (bitrate may not be the same but that's not an issue)*/ |
5685 | 0 | case GF_ISOM_BOX_TYPE_MP4S: |
5686 | 0 | case GF_ISOM_BOX_TYPE_MP4A: |
5687 | 0 | case GF_ISOM_BOX_TYPE_MP4V: |
5688 | 0 | case GF_ISOM_BOX_TYPE_ENCA: |
5689 | 0 | case GF_ISOM_BOX_TYPE_ENCV: |
5690 | 0 | case GF_ISOM_BOX_TYPE_RESV: |
5691 | 0 | case GF_ISOM_BOX_TYPE_ENCS: |
5692 | 0 | Media_GetESD(trak1->Media, sdesc_index1 ? sdesc_index1 : i+1, &esd1, GF_TRUE); |
5693 | 0 | Media_GetESD(trak2->Media, sdesc_index2 ? sdesc_index2 : i+1, &esd2, GF_TRUE); |
5694 | 0 | if (!esd1 || !esd2 || !esd1->decoderConfig || !esd2->decoderConfig) continue; |
5695 | 0 | need_memcmp = GF_FALSE; |
5696 | 0 | if (esd1->decoderConfig->streamType != esd2->decoderConfig->streamType) return GF_FALSE; |
5697 | 0 | if (esd1->decoderConfig->objectTypeIndication != esd2->decoderConfig->objectTypeIndication) return GF_FALSE; |
5698 | 0 | if (!esd1->decoderConfig->decoderSpecificInfo && esd2->decoderConfig->decoderSpecificInfo) return GF_FALSE; |
5699 | 0 | if (esd1->decoderConfig->decoderSpecificInfo && !esd2->decoderConfig->decoderSpecificInfo) return GF_FALSE; |
5700 | 0 | if (!esd1->decoderConfig->decoderSpecificInfo || !esd2->decoderConfig->decoderSpecificInfo) continue; |
5701 | 0 | if (esd1->decoderConfig->decoderSpecificInfo->dataLength != esd2->decoderConfig->decoderSpecificInfo->dataLength) |
5702 | 0 | return GF_FALSE; |
5703 | 0 | if (memcmp(esd1->decoderConfig->decoderSpecificInfo->data, esd2->decoderConfig->decoderSpecificInfo->data, sizeof(char)*esd1->decoderConfig->decoderSpecificInfo->dataLength)!=0) return GF_FALSE; |
5704 | 0 | break; |
5705 | 0 | case GF_ISOM_BOX_TYPE_HVT1: |
5706 | 0 | return GF_TRUE; |
5707 | 0 | case GF_ISOM_BOX_TYPE_AVC1: |
5708 | 0 | case GF_ISOM_BOX_TYPE_AVC2: |
5709 | 0 | case GF_ISOM_BOX_TYPE_AVC3: |
5710 | 0 | case GF_ISOM_BOX_TYPE_AVC4: |
5711 | 0 | case GF_ISOM_BOX_TYPE_SVC1: |
5712 | 0 | case GF_ISOM_BOX_TYPE_MVC1: |
5713 | 0 | case GF_ISOM_BOX_TYPE_HVC1: |
5714 | 0 | case GF_ISOM_BOX_TYPE_HEV1: |
5715 | 0 | case GF_ISOM_BOX_TYPE_HVC2: |
5716 | 0 | case GF_ISOM_BOX_TYPE_HEV2: |
5717 | 0 | case GF_ISOM_BOX_TYPE_LHE1: |
5718 | 0 | case GF_ISOM_BOX_TYPE_LHV1: |
5719 | 0 | case GF_ISOM_BOX_TYPE_AV01: |
5720 | 0 | case GF_ISOM_BOX_TYPE_VVC1: |
5721 | 0 | case GF_ISOM_BOX_TYPE_VVI1: |
5722 | 0 | case GF_ISOM_BOX_TYPE_DVHE: |
5723 | 0 | case GF_ISOM_BOX_TYPE_DVH1: |
5724 | 0 | case GF_ISOM_BOX_TYPE_DVA1: |
5725 | 0 | case GF_ISOM_BOX_TYPE_DVAV: |
5726 | 0 | case GF_ISOM_BOX_TYPE_DAV1: |
5727 | 0 | { |
5728 | 0 | GF_MPEGVisualSampleEntryBox *avc1 = (GF_MPEGVisualSampleEntryBox *)ent1; |
5729 | 0 | GF_MPEGVisualSampleEntryBox *avc2 = (GF_MPEGVisualSampleEntryBox *)ent2; |
5730 | |
|
5731 | 0 | if (avc1->hevc_config) |
5732 | 0 | a = (GF_Box *) avc1->hevc_config; |
5733 | 0 | else if (avc1->lhvc_config) |
5734 | 0 | a = (GF_Box *) avc1->lhvc_config; |
5735 | 0 | else if (avc1->svc_config) |
5736 | 0 | a = (GF_Box *) avc1->svc_config; |
5737 | 0 | else if (avc1->mvc_config) |
5738 | 0 | a = (GF_Box *) avc1->mvc_config; |
5739 | 0 | else if (avc1->av1_config) |
5740 | 0 | a = (GF_Box *)avc1->av1_config; |
5741 | 0 | else if (avc1->vvc_config) |
5742 | 0 | a = (GF_Box *)avc1->vvc_config; |
5743 | 0 | else if (avc1->vp_config) |
5744 | 0 | a = (GF_Box *)avc1->vp_config; |
5745 | 0 | else if (avc1->cfg_3gpp) |
5746 | 0 | a = (GF_Box *)avc1->cfg_3gpp; |
5747 | 0 | else |
5748 | 0 | a = (GF_Box *) avc1->avc_config; |
5749 | |
|
5750 | 0 | if (avc2->hevc_config) |
5751 | 0 | b = (GF_Box *) avc2->hevc_config; |
5752 | 0 | else if (avc2->lhvc_config) |
5753 | 0 | b = (GF_Box *) avc2->lhvc_config; |
5754 | 0 | else if (avc2->svc_config) |
5755 | 0 | b = (GF_Box *) avc2->svc_config; |
5756 | 0 | else if (avc2->mvc_config) |
5757 | 0 | b = (GF_Box *) avc2->mvc_config; |
5758 | 0 | else if (avc2->av1_config) |
5759 | 0 | b = (GF_Box *)avc2->av1_config; |
5760 | 0 | else if (avc2->vvc_config) |
5761 | 0 | b = (GF_Box *)avc2->vvc_config; |
5762 | 0 | else if (avc2->vp_config) |
5763 | 0 | b = (GF_Box *)avc2->vp_config; |
5764 | 0 | else if (avc2->cfg_3gpp) |
5765 | 0 | b = (GF_Box *)avc2->cfg_3gpp; |
5766 | 0 | else |
5767 | 0 | b = (GF_Box *) avc2->avc_config; |
5768 | |
|
5769 | 0 | Bool res = gf_isom_box_equal(a,b); |
5770 | 0 | if (!res) return GF_FALSE; |
5771 | | |
5772 | | //check dovi config disabled for now |
5773 | | //res = gf_isom_box_equal((GF_Box*)avc1->dovi_config, (GF_Box*)avc2->dovi_config); |
5774 | | //if (!res) return GF_FALSE; |
5775 | 0 | return GF_TRUE; |
5776 | 0 | } |
5777 | 0 | break; |
5778 | 0 | case GF_ISOM_BOX_TYPE_LSR1: |
5779 | 0 | { |
5780 | 0 | GF_LASeRSampleEntryBox *lsr1 = (GF_LASeRSampleEntryBox *)ent1; |
5781 | 0 | GF_LASeRSampleEntryBox *lsr2 = (GF_LASeRSampleEntryBox *)ent2; |
5782 | 0 | if (lsr1->lsr_config && lsr2->lsr_config |
5783 | 0 | && lsr1->lsr_config->hdr && lsr2->lsr_config->hdr |
5784 | 0 | && (lsr1->lsr_config->hdr_size==lsr2->lsr_config->hdr_size) |
5785 | 0 | && !memcmp(lsr1->lsr_config->hdr, lsr2->lsr_config->hdr, lsr2->lsr_config->hdr_size) |
5786 | 0 | ) { |
5787 | 0 | return GF_TRUE; |
5788 | 0 | } |
5789 | 0 | return GF_FALSE; |
5790 | 0 | } |
5791 | 0 | break; |
5792 | 0 | #ifndef GPAC_DISABLE_VTT |
5793 | 0 | case GF_ISOM_BOX_TYPE_WVTT: |
5794 | 0 | { |
5795 | 0 | GF_WebVTTSampleEntryBox *wvtt1 = (GF_WebVTTSampleEntryBox *)ent1; |
5796 | 0 | GF_WebVTTSampleEntryBox *wvtt2 = (GF_WebVTTSampleEntryBox *)ent2; |
5797 | 0 | if (wvtt1->config && wvtt2->config && |
5798 | 0 | (wvtt1->config->string && wvtt2->config->string && !strcmp(wvtt1->config->string, wvtt2->config->string))) { |
5799 | 0 | return GF_TRUE; |
5800 | 0 | } |
5801 | 0 | return GF_FALSE; |
5802 | 0 | } |
5803 | 0 | break; |
5804 | 0 | #endif |
5805 | 0 | case GF_ISOM_BOX_TYPE_STPP: |
5806 | 0 | { |
5807 | 0 | GF_MetaDataSampleEntryBox *stpp1 = (GF_MetaDataSampleEntryBox *)ent1; |
5808 | 0 | GF_MetaDataSampleEntryBox *stpp2 = (GF_MetaDataSampleEntryBox *)ent2; |
5809 | 0 | if (stpp1->xml_namespace && stpp2->xml_namespace && !strcmp(stpp1->xml_namespace, stpp2->xml_namespace)) { |
5810 | 0 | return GF_TRUE; |
5811 | 0 | } |
5812 | 0 | return GF_FALSE; |
5813 | 0 | } |
5814 | 0 | break; |
5815 | 0 | case GF_ISOM_BOX_TYPE_SBTT: |
5816 | 0 | { |
5817 | 0 | return GF_FALSE; |
5818 | 0 | } |
5819 | 0 | break; |
5820 | 0 | case GF_ISOM_BOX_TYPE_STXT: |
5821 | 0 | { |
5822 | 0 | GF_MetaDataSampleEntryBox *stxt1 = (GF_MetaDataSampleEntryBox *)ent1; |
5823 | 0 | GF_MetaDataSampleEntryBox *stxt2 = (GF_MetaDataSampleEntryBox *)ent2; |
5824 | 0 | if (stxt1->mime_type && stxt2->mime_type && |
5825 | 0 | ( (!stxt1->config && !stxt2->config) || |
5826 | 0 | (stxt1->config && stxt2->config && stxt1->config->config && stxt2->config->config && |
5827 | 0 | !strcmp(stxt1->config->config, stxt2->config->config)))) { |
5828 | 0 | return GF_TRUE; |
5829 | 0 | } |
5830 | 0 | return GF_FALSE; |
5831 | 0 | } |
5832 | 0 | case GF_ISOM_BOX_TYPE_MP3: |
5833 | 0 | case GF_QT_SUBTYPE_RAW_AUD: |
5834 | 0 | case GF_QT_SUBTYPE_TWOS: |
5835 | 0 | case GF_QT_SUBTYPE_SOWT: |
5836 | 0 | case GF_QT_SUBTYPE_FL32: |
5837 | 0 | case GF_QT_SUBTYPE_FL64: |
5838 | 0 | case GF_QT_SUBTYPE_IN24: |
5839 | 0 | case GF_QT_SUBTYPE_IN32: |
5840 | 0 | case GF_QT_SUBTYPE_ULAW: |
5841 | 0 | case GF_QT_SUBTYPE_ALAW: |
5842 | 0 | case GF_QT_SUBTYPE_ADPCM: |
5843 | 0 | case GF_QT_SUBTYPE_IMA_ADPCM: |
5844 | 0 | case GF_QT_SUBTYPE_DVCA: |
5845 | 0 | case GF_QT_SUBTYPE_QDMC: |
5846 | 0 | case GF_QT_SUBTYPE_QDMC2: |
5847 | 0 | case GF_QT_SUBTYPE_QCELP: |
5848 | 0 | case GF_QT_SUBTYPE_kMP3: |
5849 | 0 | case GF_QT_SUBTYPE_APCH: |
5850 | 0 | case GF_QT_SUBTYPE_APCO: |
5851 | 0 | case GF_QT_SUBTYPE_APCN: |
5852 | 0 | case GF_QT_SUBTYPE_APCS: |
5853 | 0 | case GF_QT_SUBTYPE_AP4X: |
5854 | 0 | case GF_QT_SUBTYPE_AP4H: |
5855 | 0 | case GF_QT_SUBTYPE_RAW_VID: |
5856 | 0 | case GF_QT_SUBTYPE_YUYV: |
5857 | 0 | case GF_QT_SUBTYPE_UYVY: |
5858 | 0 | case GF_QT_SUBTYPE_YUV444: |
5859 | 0 | case GF_QT_SUBTYPE_YUVA444: |
5860 | 0 | case GF_QT_SUBTYPE_YUV422_10: |
5861 | 0 | case GF_QT_SUBTYPE_YUV444_10: |
5862 | 0 | case GF_QT_SUBTYPE_YUV422_16: |
5863 | 0 | case GF_QT_SUBTYPE_YUV420: |
5864 | 0 | case GF_QT_SUBTYPE_I420: |
5865 | 0 | case GF_QT_SUBTYPE_IYUV: |
5866 | 0 | case GF_QT_SUBTYPE_YV12: |
5867 | 0 | case GF_QT_SUBTYPE_YVYU: |
5868 | 0 | case GF_QT_SUBTYPE_RGBA: |
5869 | 0 | case GF_QT_SUBTYPE_ABGR: |
5870 | 0 | default: |
5871 | 0 | if (ent1->internal_type == GF_ISOM_SAMPLE_ENTRY_VIDEO) { |
5872 | 0 | GF_VisualSampleEntryBox *vent1 = (GF_VisualSampleEntryBox *) ent1; |
5873 | 0 | GF_VisualSampleEntryBox *vent2 = (GF_VisualSampleEntryBox *) ent2; |
5874 | 0 | if (vent1->Width != vent2->Width) return GF_FALSE; |
5875 | 0 | if (vent1->Height != vent2->Height) return GF_FALSE; |
5876 | 0 | } |
5877 | 0 | else if (ent1->internal_type == GF_ISOM_SAMPLE_ENTRY_AUDIO) { |
5878 | 0 | GF_AudioSampleEntryBox *aent1 = (GF_AudioSampleEntryBox *) ent1; |
5879 | 0 | GF_AudioSampleEntryBox *aent2 = (GF_AudioSampleEntryBox *) ent2; |
5880 | 0 | if (aent1->samplerate_hi != aent2->samplerate_hi) return GF_FALSE; |
5881 | 0 | if (aent1->samplerate_lo != aent2->samplerate_lo) return GF_FALSE; |
5882 | 0 | if (aent1->channel_count != aent2->channel_count) return GF_FALSE; |
5883 | 0 | } |
5884 | 0 | return GF_TRUE; |
5885 | 0 | } |
5886 | | |
5887 | 0 | if (sdesc_index1 && sdesc_index2) break; |
5888 | 0 | } |
5889 | 0 | if (!need_memcmp) return GF_TRUE; |
5890 | 0 | a = (GF_Box *)trak1->Media->information->sampleTable->SampleDescription; |
5891 | 0 | b = (GF_Box *)trak2->Media->information->sampleTable->SampleDescription; |
5892 | | //we ignore all bitrate boxes when comparing the box, disable their writing |
5893 | 0 | gf_isom_registry_disable(GF_ISOM_BOX_TYPE_BTRT, GF_TRUE); |
5894 | 0 | ret = gf_isom_box_equal(a,b); |
5895 | | //re-enable btrt writing |
5896 | 0 | gf_isom_registry_disable(GF_ISOM_BOX_TYPE_BTRT, GF_FALSE); |
5897 | |
|
5898 | 0 | return ret; |
5899 | 0 | } |
5900 | | |
5901 | | GF_EXPORT |
5902 | | u64 gf_isom_estimate_size(GF_ISOFile *movie) |
5903 | 0 | { |
5904 | 0 | GF_Err e; |
5905 | 0 | GF_Box *a; |
5906 | 0 | u32 i, count; |
5907 | 0 | u64 mdat_size; |
5908 | 0 | if (!movie || !movie->moov) return 0; |
5909 | | |
5910 | 0 | mdat_size = 0; |
5911 | 0 | count = gf_list_count(movie->moov->trackList); |
5912 | 0 | for (i=0; i<count; i++) { |
5913 | 0 | mdat_size += gf_isom_get_media_data_size(movie, i+1); |
5914 | 0 | } |
5915 | 0 | if (mdat_size) { |
5916 | 0 | mdat_size += 8; |
5917 | 0 | if (mdat_size > 0xFFFFFFFF) mdat_size += 8; |
5918 | 0 | } |
5919 | |
|
5920 | 0 | i=0; |
5921 | 0 | while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { |
5922 | 0 | e = gf_isom_box_size(a); |
5923 | 0 | if (e == GF_OK) |
5924 | 0 | mdat_size += a->size; |
5925 | 0 | } |
5926 | 0 | return mdat_size; |
5927 | 0 | } |
5928 | | |
5929 | | |
5930 | | //set shadowing on/off |
5931 | | #if 0 //unused |
5932 | | GF_Err gf_isom_remove_sync_shadows(GF_ISOFile *movie, u32 trackNumber) |
5933 | | { |
5934 | | GF_TrackBox *trak; |
5935 | | GF_SampleTableBox *stbl; |
5936 | | |
5937 | | if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; |
5938 | | trak = gf_isom_get_track_box(movie, trackNumber); |
5939 | | if (!trak) return GF_BAD_PARAM; |
5940 | | |
5941 | | stbl = trak->Media->information->sampleTable; |
5942 | | if (stbl->ShadowSync) { |
5943 | | gf_isom_box_del_parent(&stbl->child_boxes, (GF_Box *) stbl->ShadowSync); |
5944 | | stbl->ShadowSync = NULL; |
5945 | | } |
5946 | | return GF_OK; |
5947 | | } |
5948 | | |
5949 | | /*Use this function to do the shadowing if you use shadowing. |
5950 | | the sample to be shadowed MUST be a non-sync sample (ignored if not) |
5951 | | the sample shadowing must be a Sync sample (error if not)*/ |
5952 | | GF_Err gf_isom_set_sync_shadow(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u32 syncSample) |
5953 | | { |
5954 | | GF_TrackBox *trak; |
5955 | | GF_SampleTableBox *stbl; |
5956 | | GF_ISOSAPType isRAP; |
5957 | | GF_Err e; |
5958 | | |
5959 | | if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; |
5960 | | trak = gf_isom_get_track_box(movie, trackNumber); |
5961 | | if (!trak || !sampleNumber || !syncSample) return GF_BAD_PARAM; |
5962 | | |
5963 | | stbl = trak->Media->information->sampleTable; |
5964 | | if (!stbl->ShadowSync) { |
5965 | | stbl->ShadowSync = (GF_ShadowSyncBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSH); |
5966 | | if (!stbl->ShadowSync) return GF_OUT_OF_MEM; |
5967 | | } |
5968 | | |
5969 | | //if no sync, skip |
5970 | | if (!stbl->SyncSample) return GF_OK; |
5971 | | //else set the sync shadow. |
5972 | | //if the sample is sync, ignore |
5973 | | e = stbl_GetSampleRAP(stbl->SyncSample, sampleNumber, &isRAP, NULL, NULL); |
5974 | | if (e) return e; |
5975 | | if (isRAP) return GF_OK; |
5976 | | //if the shadowing sample is not sync, error |
5977 | | e = stbl_GetSampleRAP(stbl->SyncSample, syncSample, &isRAP, NULL, NULL); |
5978 | | if (e) return e; |
5979 | | if (!isRAP) return GF_BAD_PARAM; |
5980 | | |
5981 | | return stbl_SetSyncShadow(stbl->ShadowSync, sampleNumber, syncSample); |
5982 | | } |
5983 | | #endif |
5984 | | |
5985 | | //set the GroupID of a track (only used for interleaving) |
5986 | | GF_EXPORT |
5987 | | GF_Err gf_isom_set_track_interleaving_group(GF_ISOFile *movie, u32 trackNumber, u32 GroupID) |
5988 | 0 | { |
5989 | 0 | GF_TrackBox *trak; |
5990 | |
|
5991 | 0 | if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE; |
5992 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
5993 | 0 | if (!trak || !GroupID) return GF_BAD_PARAM; |
5994 | | |
5995 | 0 | trak->Media->information->sampleTable->groupID = GroupID; |
5996 | 0 | return GF_OK; |
5997 | 0 | } |
5998 | | |
5999 | | |
6000 | | //set the Priority of a track within a Group (only used for tight interleaving) |
6001 | | //Priority ranges from 1 to 9 |
6002 | | GF_EXPORT |
6003 | | GF_Err gf_isom_set_track_priority_in_group(GF_ISOFile *movie, u32 trackNumber, u32 Priority) |
6004 | 0 | { |
6005 | 0 | GF_TrackBox *trak; |
6006 | |
|
6007 | 0 | if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE; |
6008 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
6009 | 0 | if (!trak || !Priority) return GF_BAD_PARAM; |
6010 | | |
6011 | 0 | trak->Media->information->sampleTable->trackPriority = Priority > 255 ? 255 : Priority; |
6012 | 0 | return GF_OK; |
6013 | 0 | } |
6014 | | |
6015 | | //set the max SamplesPerChunk (for file optimization) |
6016 | | GF_EXPORT |
6017 | | GF_Err gf_isom_hint_max_chunk_size(GF_ISOFile *movie, u32 trackNumber, u32 maxChunkSize) |
6018 | 0 | { |
6019 | 0 | GF_TrackBox *trak; |
6020 | |
|
6021 | 0 | if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; |
6022 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
6023 | 0 | if (!trak || !maxChunkSize) return GF_BAD_PARAM; |
6024 | | |
6025 | 0 | trak->Media->information->sampleTable->MaxChunkSize = maxChunkSize; |
6026 | 0 | return GF_OK; |
6027 | 0 | } |
6028 | | |
6029 | | |
6030 | | //set the max SamplesPerChunk (for file optimization) |
6031 | | GF_EXPORT |
6032 | | GF_Err gf_isom_hint_max_chunk_duration(GF_ISOFile *movie, u32 trackNumber, u32 maxChunkDur) |
6033 | 0 | { |
6034 | 0 | GF_TrackBox *trak; |
6035 | |
|
6036 | 0 | if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; |
6037 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
6038 | 0 | if (!trak) return GF_BAD_PARAM; |
6039 | | |
6040 | 0 | trak->Media->information->sampleTable->MaxChunkDur = maxChunkDur; |
6041 | 0 | return GF_OK; |
6042 | 0 | } |
6043 | | |
6044 | | |
6045 | | GF_EXPORT |
6046 | | GF_Err gf_isom_set_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, const GF_SLConfig *slConfig) |
6047 | 0 | { |
6048 | 0 | GF_TrackBox *trak; |
6049 | 0 | GF_SampleEntryBox *entry; |
6050 | 0 | GF_Err e; |
6051 | 0 | GF_SLConfig **slc; |
6052 | 0 | GF_ESDBox *esds; |
6053 | |
|
6054 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
6055 | 0 | if (!trak) return GF_BAD_PARAM; |
6056 | | |
6057 | 0 | e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL); |
6058 | 0 | if (e) return e; |
6059 | | |
6060 | | //we must be sure we are not using a remote ESD |
6061 | 0 | switch (entry->type) { |
6062 | 0 | case GF_ISOM_BOX_TYPE_MP4S: |
6063 | 0 | esds = ((GF_MPEGSampleEntryBox *)entry)->esd; |
6064 | 0 | if (!esds || !esds->desc || !esds->desc->slConfig || (esds->desc->slConfig->predefined != SLPredef_MP4)) |
6065 | 0 | return GF_ISOM_INVALID_FILE; |
6066 | 0 | slc = & ((GF_MPEGSampleEntryBox *)entry)->slc; |
6067 | 0 | break; |
6068 | 0 | case GF_ISOM_BOX_TYPE_MP4A: |
6069 | 0 | esds = ((GF_MPEGAudioSampleEntryBox *)entry)->esd; |
6070 | 0 | if (!esds || !esds->desc || !esds->desc->slConfig || (esds->desc->slConfig->predefined != SLPredef_MP4)) |
6071 | 0 | return GF_ISOM_INVALID_FILE; |
6072 | 0 | slc = & ((GF_MPEGAudioSampleEntryBox *)entry)->slc; |
6073 | 0 | break; |
6074 | 0 | case GF_ISOM_BOX_TYPE_MP4V: |
6075 | 0 | esds = ((GF_MPEGVisualSampleEntryBox *)entry)->esd; |
6076 | 0 | if (!esds || !esds->desc || !esds->desc->slConfig || (esds->desc->slConfig->predefined != SLPredef_MP4)) |
6077 | 0 | return GF_ISOM_INVALID_FILE; |
6078 | 0 | slc = & ((GF_MPEGVisualSampleEntryBox *)entry)->slc; |
6079 | 0 | break; |
6080 | 0 | default: |
6081 | 0 | return GF_OK; |
6082 | 0 | } |
6083 | | |
6084 | 0 | if (*slc) { |
6085 | 0 | gf_odf_desc_del((GF_Descriptor *)*slc); |
6086 | 0 | *slc = NULL; |
6087 | 0 | } |
6088 | 0 | if (!slConfig) return GF_OK; |
6089 | | //finally duplicate the SL |
6090 | 0 | return gf_odf_desc_copy((GF_Descriptor *) slConfig, (GF_Descriptor **) slc); |
6091 | 0 | } |
6092 | | |
6093 | | #if 0 //unused |
6094 | | GF_Err gf_isom_get_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig **slConfig) |
6095 | | { |
6096 | | GF_TrackBox *trak; |
6097 | | GF_SampleEntryBox *entry; |
6098 | | GF_Err e; |
6099 | | GF_SLConfig *slc; |
6100 | | |
6101 | | trak = gf_isom_get_track_box(the_file, trackNumber); |
6102 | | if (!trak) return GF_BAD_PARAM; |
6103 | | |
6104 | | e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL); |
6105 | | if (e) return e; |
6106 | | |
6107 | | //we must be sure we are not using a remote ESD |
6108 | | slc = NULL; |
6109 | | *slConfig = NULL; |
6110 | | switch (entry->type) { |
6111 | | case GF_ISOM_BOX_TYPE_MP4S: |
6112 | | if (((GF_MPEGSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; |
6113 | | slc = ((GF_MPEGSampleEntryBox *)entry)->slc; |
6114 | | break; |
6115 | | case GF_ISOM_BOX_TYPE_MP4A: |
6116 | | if (((GF_MPEGAudioSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; |
6117 | | slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc; |
6118 | | break; |
6119 | | case GF_ISOM_BOX_TYPE_MP4V: |
6120 | | if (((GF_MPEGVisualSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; |
6121 | | slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc; |
6122 | | break; |
6123 | | default: |
6124 | | return GF_BAD_PARAM; |
6125 | | } |
6126 | | |
6127 | | if (!slc) return GF_OK; |
6128 | | //finally duplicate the SL |
6129 | | return gf_odf_desc_copy((GF_Descriptor *) slc, (GF_Descriptor **) slConfig); |
6130 | | } |
6131 | | |
6132 | | u32 gf_isom_get_track_group(GF_ISOFile *the_file, u32 trackNumber) |
6133 | | { |
6134 | | GF_TrackBox *trak; |
6135 | | trak = gf_isom_get_track_box(the_file, trackNumber); |
6136 | | if (!trak) return 0; |
6137 | | return trak->Media->information->sampleTable->groupID; |
6138 | | } |
6139 | | |
6140 | | u32 gf_isom_get_track_priority_in_group(GF_ISOFile *the_file, u32 trackNumber) |
6141 | | { |
6142 | | GF_TrackBox *trak; |
6143 | | trak = gf_isom_get_track_box(the_file, trackNumber); |
6144 | | if (!trak) return 0; |
6145 | | return trak->Media->information->sampleTable->trackPriority; |
6146 | | } |
6147 | | #endif |
6148 | | |
6149 | | |
6150 | | GF_EXPORT |
6151 | | GF_Err gf_isom_make_interleave_ex(GF_ISOFile *file, GF_Fraction *fTimeInSec) |
6152 | 0 | { |
6153 | 0 | GF_Err e; |
6154 | 0 | u64 itime; |
6155 | 0 | if (!file || !fTimeInSec->den || (fTimeInSec->num<=0)) return GF_BAD_PARAM; |
6156 | | |
6157 | 0 | itime = (u64) fTimeInSec->num; |
6158 | 0 | itime *= gf_isom_get_timescale(file); |
6159 | 0 | itime /= fTimeInSec->den; |
6160 | 0 | if (file->storageMode==GF_ISOM_STORE_FASTSTART) { |
6161 | 0 | return gf_isom_set_interleave_time(file, (u32) itime); |
6162 | 0 | } |
6163 | 0 | if (gf_isom_get_mode(file) < GF_ISOM_OPEN_EDIT) return GF_BAD_PARAM; |
6164 | 0 | e = gf_isom_set_storage_mode(file, GF_ISOM_STORE_DRIFT_INTERLEAVED); |
6165 | 0 | if (e) return e; |
6166 | 0 | return gf_isom_set_interleave_time(file, (u32) itime); |
6167 | 0 | } |
6168 | | |
6169 | | GF_EXPORT |
6170 | | GF_Err gf_isom_make_interleave(GF_ISOFile *file, Double TimeInSec) |
6171 | 0 | { |
6172 | 0 | GF_Fraction f; |
6173 | 0 | f.num = (s32) (TimeInSec * 1000); |
6174 | 0 | f.den = 1000; |
6175 | 0 | return gf_isom_make_interleave_ex(file, &f); |
6176 | |
|
6177 | 0 | } |
6178 | | GF_EXPORT |
6179 | | GF_Err gf_isom_set_handler_name(GF_ISOFile *the_file, u32 trackNumber, const char *nameUTF8) |
6180 | 0 | { |
6181 | 0 | GF_TrackBox *trak; |
6182 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
6183 | 0 | if (!trak) return GF_BAD_PARAM; |
6184 | 0 | if (trak->Media->handler->nameUTF8) gf_free(trak->Media->handler->nameUTF8); |
6185 | 0 | trak->Media->handler->nameUTF8 = NULL; |
6186 | |
|
6187 | 0 | if (!nameUTF8) return GF_OK; |
6188 | | |
6189 | 0 | if (!strnicmp(nameUTF8, "file://", 7)) { |
6190 | 0 | u8 BOM[4]; |
6191 | 0 | FILE *f = gf_fopen(nameUTF8+7, "rb"); |
6192 | 0 | u64 size; |
6193 | 0 | if (!f) return GF_URL_ERROR; |
6194 | 0 | size = gf_fsize(f); |
6195 | 0 | if (3!=gf_fread(BOM, 3, f)) { |
6196 | 0 | gf_fclose(f); |
6197 | 0 | return GF_CORRUPTED_DATA; |
6198 | 0 | } |
6199 | | /*skip BOM if any*/ |
6200 | 0 | if ((BOM[0]==0xEF) && (BOM[1]==0xBB) && (BOM[2]==0xBF)) size -= 3; |
6201 | 0 | else if ((BOM[0]==0xEF) || (BOM[0]==0xFF)) { |
6202 | 0 | gf_fclose(f); |
6203 | 0 | return GF_BAD_PARAM; |
6204 | 0 | } |
6205 | 0 | else gf_fseek(f, 0, SEEK_SET); |
6206 | 0 | trak->Media->handler->nameUTF8 = (char*)gf_malloc(sizeof(char)*(size_t)(size+1)); |
6207 | 0 | if (!trak->Media->handler->nameUTF8) { |
6208 | 0 | gf_fclose(f); |
6209 | 0 | return GF_OUT_OF_MEM; |
6210 | 0 | } |
6211 | 0 | size = gf_fread(trak->Media->handler->nameUTF8, (size_t)size, f); |
6212 | 0 | trak->Media->handler->nameUTF8[size] = 0; |
6213 | 0 | gf_fclose(f); |
6214 | 0 | } else { |
6215 | 0 | u32 i, j, len; |
6216 | 0 | char szOrig[1024], szLine[1024]; |
6217 | 0 | strcpy(szOrig, nameUTF8); |
6218 | 0 | j=0; |
6219 | 0 | len = (u32) strlen(szOrig); |
6220 | 0 | for (i=0; i<len; i++) { |
6221 | 0 | if (szOrig[i] & 0x80) { |
6222 | | /*non UTF8 (likely some win-CP)*/ |
6223 | 0 | if ( (szOrig[i+1] & 0xc0) != 0x80) { |
6224 | 0 | szLine[j] = 0xc0 | ( (szOrig[i] >> 6) & 0x3 ); |
6225 | 0 | j++; |
6226 | 0 | szOrig[i] &= 0xbf; |
6227 | 0 | } |
6228 | | /*UTF8 2 bytes char */ |
6229 | 0 | else if ( (szOrig[i] & 0xe0) == 0xc0) { |
6230 | 0 | szLine[j] = szOrig[i]; |
6231 | 0 | i++; |
6232 | 0 | j++; |
6233 | 0 | } |
6234 | | /*UTF8 3 bytes char */ |
6235 | 0 | else if ( (szOrig[i] & 0xf0) == 0xe0) { |
6236 | 0 | szLine[j] = szOrig[i]; |
6237 | 0 | i++; |
6238 | 0 | j++; |
6239 | 0 | szLine[j] = szOrig[i]; |
6240 | 0 | i++; |
6241 | 0 | j++; |
6242 | 0 | } |
6243 | | /*UTF8 4 bytes char */ |
6244 | 0 | else if ( (szOrig[i] & 0xf8) == 0xf0) { |
6245 | 0 | szLine[j] = szOrig[i]; |
6246 | 0 | i++; |
6247 | 0 | j++; |
6248 | 0 | szLine[j] = szOrig[i]; |
6249 | 0 | i++; |
6250 | 0 | j++; |
6251 | 0 | szLine[j] = szOrig[i]; |
6252 | 0 | i++; |
6253 | 0 | j++; |
6254 | 0 | } |
6255 | 0 | } |
6256 | 0 | szLine[j] = szOrig[i]; |
6257 | 0 | j++; |
6258 | 0 | } |
6259 | 0 | szLine[j] = 0; |
6260 | 0 | trak->Media->handler->nameUTF8 = gf_strdup(szLine); |
6261 | 0 | } |
6262 | 0 | return GF_OK; |
6263 | 0 | } |
6264 | | |
6265 | | #if 0 //unused |
6266 | | /*clones root OD from input to output file, without copying root OD track references*/ |
6267 | | GF_Err gf_isom_clone_root_od(GF_ISOFile *input, GF_ISOFile *output) |
6268 | | { |
6269 | | GF_List *esds; |
6270 | | GF_Err e; |
6271 | | u32 i; |
6272 | | GF_Descriptor *desc; |
6273 | | |
6274 | | e = gf_isom_remove_root_od(output); |
6275 | | if (e) return e; |
6276 | | if (!input->moov || !input->moov->iods || !input->moov->iods->descriptor) return GF_OK; |
6277 | | e = gf_isom_insert_moov(output); |
6278 | | if (e) return e; |
6279 | | e = AddMovieIOD(output->moov, 0); |
6280 | | if (e) return e; |
6281 | | if (output->moov->iods->descriptor) gf_odf_desc_del(output->moov->iods->descriptor); |
6282 | | output->moov->iods->descriptor = NULL; |
6283 | | gf_odf_desc_copy(input->moov->iods->descriptor, &output->moov->iods->descriptor); |
6284 | | |
6285 | | switch (output->moov->iods->descriptor->tag) { |
6286 | | case GF_ODF_ISOM_IOD_TAG: |
6287 | | esds = ((GF_IsomInitialObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors; |
6288 | | break; |
6289 | | case GF_ODF_ISOM_OD_TAG: |
6290 | | esds = ((GF_IsomObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors; |
6291 | | break; |
6292 | | default: |
6293 | | return GF_ISOM_INVALID_FILE; |
6294 | | } |
6295 | | |
6296 | | //get the desc |
6297 | | i=0; |
6298 | | while ((desc = (GF_Descriptor*)gf_list_enum(esds, &i))) { |
6299 | | gf_odf_desc_del(desc); |
6300 | | gf_list_rem(esds, i-1); |
6301 | | } |
6302 | | return GF_OK; |
6303 | | } |
6304 | | #endif |
6305 | | |
6306 | | GF_EXPORT |
6307 | | GF_Err gf_isom_set_media_type(GF_ISOFile *movie, u32 trackNumber, u32 new_type) |
6308 | 0 | { |
6309 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
6310 | 0 | if (!trak || !new_type) return GF_BAD_PARAM; |
6311 | 0 | trak->Media->handler->handlerType = new_type; |
6312 | 0 | return GF_OK; |
6313 | 0 | } |
6314 | | |
6315 | | GF_EXPORT |
6316 | | GF_Err gf_isom_set_media_subtype(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, u32 new_type) |
6317 | 0 | { |
6318 | 0 | GF_SampleEntryBox*entry; |
6319 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
6320 | 0 | if (!trak || !sampleDescriptionIndex || !new_type) return GF_BAD_PARAM; |
6321 | | |
6322 | 0 | entry = (GF_SampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex - 1); |
6323 | 0 | if (!entry) return GF_BAD_PARAM; |
6324 | 0 | if (entry->type==GF_ISOM_BOX_TYPE_GNRV) { |
6325 | 0 | ((GF_GenericVisualSampleEntryBox *)entry)->EntryType = new_type; |
6326 | 0 | } else if (entry->type==GF_ISOM_BOX_TYPE_GNRA) { |
6327 | 0 | ((GF_GenericAudioSampleEntryBox *)entry)->EntryType = new_type; |
6328 | 0 | } else if (entry->type==GF_ISOM_BOX_TYPE_GNRM) { |
6329 | 0 | ((GF_GenericSampleEntryBox *)entry)->EntryType = new_type; |
6330 | 0 | } else { |
6331 | 0 | entry->type = new_type; |
6332 | 0 | } |
6333 | 0 | return GF_OK; |
6334 | 0 | } |
6335 | | |
6336 | | |
6337 | | #if 0 //unused |
6338 | | GF_Err gf_isom_set_JPEG2000(GF_ISOFile *mov, Bool set_on) |
6339 | | { |
6340 | | if (!mov) return GF_BAD_PARAM; |
6341 | | mov->is_jp2 = set_on; |
6342 | | return GF_OK; |
6343 | | } |
6344 | | #endif |
6345 | | |
6346 | | GF_Err gf_isom_remove_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID) |
6347 | 0 | { |
6348 | 0 | u32 i, count; |
6349 | 0 | GF_List *list; |
6350 | |
|
6351 | 0 | if (trackNumber==(u32) -1) { |
6352 | 0 | if (!movie) return GF_BAD_PARAM; |
6353 | 0 | list = movie->TopBoxes; |
6354 | 0 | } else if (trackNumber) { |
6355 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
6356 | 0 | if (!trak) return GF_BAD_PARAM; |
6357 | 0 | list = trak->child_boxes; |
6358 | 0 | } else { |
6359 | 0 | if (!movie) return GF_BAD_PARAM; |
6360 | 0 | list = movie->moov->child_boxes; |
6361 | 0 | } |
6362 | | |
6363 | 0 | count = list ? gf_list_count(list) : 0; |
6364 | 0 | for (i=0; i<count; i++) { |
6365 | 0 | GF_UnknownUUIDBox *uuid = (GF_UnknownUUIDBox *)gf_list_get(list, i); |
6366 | 0 | if (uuid->type != GF_ISOM_BOX_TYPE_UUID) continue; |
6367 | 0 | if (memcmp(UUID, uuid->uuid, sizeof(bin128))) continue; |
6368 | 0 | gf_list_rem(list, i); |
6369 | 0 | i--; |
6370 | 0 | count--; |
6371 | 0 | gf_isom_box_del((GF_Box*)uuid); |
6372 | 0 | } |
6373 | 0 | return GF_OK; |
6374 | 0 | } |
6375 | | |
6376 | | GF_EXPORT |
6377 | | GF_Err gf_isom_add_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID, const u8 *data, u32 data_size) |
6378 | 0 | { |
6379 | 0 | GF_List *list; |
6380 | 0 | u32 btype; |
6381 | 0 | GF_Box *box; |
6382 | 0 | GF_UnknownUUIDBox *uuidb; |
6383 | |
|
6384 | 0 | if (data_size && !data) return GF_BAD_PARAM; |
6385 | 0 | if (trackNumber==(u32) -1) { |
6386 | 0 | if (!movie) return GF_BAD_PARAM; |
6387 | 0 | list = movie->TopBoxes; |
6388 | 0 | } else if (trackNumber) { |
6389 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
6390 | 0 | if (!trak) return GF_BAD_PARAM; |
6391 | 0 | if (!trak->child_boxes) trak->child_boxes = gf_list_new(); |
6392 | 0 | list = trak->child_boxes; |
6393 | 0 | } else { |
6394 | 0 | if (!movie) return GF_BAD_PARAM; |
6395 | 0 | if (!movie->moov) { |
6396 | 0 | GF_Err e = gf_isom_insert_moov(movie); |
6397 | 0 | if (e) return e; |
6398 | 0 | } |
6399 | 0 | if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new(); |
6400 | 0 | list = movie->moov->child_boxes; |
6401 | 0 | } |
6402 | 0 | btype = gf_isom_solve_uuid_box((char *) UUID); |
6403 | 0 | if (!btype) btype = GF_ISOM_BOX_TYPE_UUID; |
6404 | 0 | box = gf_isom_box_new(btype); |
6405 | 0 | if (!box) return GF_OUT_OF_MEM; |
6406 | 0 | uuidb = (GF_UnknownUUIDBox*)box; |
6407 | 0 | uuidb->internal_4cc = gf_isom_solve_uuid_box((char *) UUID); |
6408 | 0 | if ((char *) UUID) |
6409 | 0 | memcpy(uuidb->uuid, UUID, sizeof(bin128)); |
6410 | 0 | uuidb->dataSize = data_size; |
6411 | 0 | if (data_size) { |
6412 | 0 | uuidb->data = (char*)gf_malloc(sizeof(char)*data_size); |
6413 | 0 | if (!uuidb->data) return GF_OUT_OF_MEM; |
6414 | 0 | memcpy(uuidb->data, data, sizeof(char)*data_size); |
6415 | 0 | } |
6416 | 0 | gf_list_add(list, uuidb); |
6417 | 0 | return GF_OK; |
6418 | 0 | } |
6419 | | |
6420 | | |
6421 | | GF_EXPORT |
6422 | | GF_Err gf_isom_apple_set_tag_ex(GF_ISOFile *mov, GF_ISOiTunesTag tag, const u8 *data, u32 data_len, u64 int_val, u32 int_val2, const char *in_cust_name, const char *in_cust_mean, u32 locale) |
6423 | 0 | { |
6424 | 0 | GF_Err e; |
6425 | 0 | GF_ItemListBox *ilst; |
6426 | 0 | GF_MetaBox *meta; |
6427 | 0 | GF_ListItemBox *info; |
6428 | 0 | u32 btype=0, i, itype; |
6429 | 0 | s32 tag_idx; |
6430 | 0 | u32 n=0, d=0; |
6431 | 0 | u8 loc_data[10]; |
6432 | 0 | u32 int_flags = 0x15; |
6433 | 0 | GF_DataBox *dbox; |
6434 | 0 | char *cust_mean=NULL, *cust_name=NULL; |
6435 | |
|
6436 | 0 | if (in_cust_name || in_cust_mean) tag = GF_4CC('c','u','s','t'); |
6437 | |
|
6438 | 0 | e = gf_isom_can_access_movie(mov, GF_ISOM_OPEN_WRITE); |
6439 | 0 | if (e) return e; |
6440 | | |
6441 | 0 | tag_idx = gf_itags_find_by_itag(tag); |
6442 | 0 | if (tag_idx<0) { |
6443 | 0 | itype = GF_ITAG_STR; |
6444 | 0 | } else { |
6445 | 0 | itype = gf_itags_get_type(tag_idx); |
6446 | 0 | } |
6447 | 0 | meta = (GF_MetaBox *) gf_isom_create_meta_extensions(mov, GF_FALSE); |
6448 | 0 | if (!meta) return GF_BAD_PARAM; |
6449 | 0 | if (mov->brand->majorBrand == GF_4CC_CSTR("qt ")) |
6450 | 0 | meta->write_qt = 1; |
6451 | |
|
6452 | 0 | ilst = gf_isom_locate_box(meta->child_boxes, GF_ISOM_BOX_TYPE_ILST, NULL); |
6453 | 0 | if (!ilst) { |
6454 | 0 | ilst = (GF_ItemListBox *) gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_ILST); |
6455 | 0 | } |
6456 | |
|
6457 | 0 | if (tag==GF_ISOM_ITUNE_RESET) { |
6458 | 0 | gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) ilst); |
6459 | | //if last, delete udta - we may still have a handler box remaining |
6460 | 0 | if ((gf_list_count(meta->child_boxes) <= 1) && (gf_list_count(mov->moov->udta->recordList)==1)) { |
6461 | 0 | gf_isom_box_del_parent(&mov->moov->child_boxes, (GF_Box *) mov->moov->udta); |
6462 | 0 | mov->moov->udta = NULL; |
6463 | 0 | } |
6464 | 0 | return GF_OK; |
6465 | 0 | } |
6466 | | |
6467 | 0 | if (tag==GF_ISOM_ITUNE_GENRE) { |
6468 | 0 | if (!int_val && data) { |
6469 | 0 | int_val = gf_id3_get_genre_tag(data); |
6470 | 0 | if (int_val) { |
6471 | 0 | data = NULL; |
6472 | 0 | data_len = 0; |
6473 | 0 | itype = GF_ITAG_INT16; |
6474 | 0 | int_flags = 0; |
6475 | 0 | } |
6476 | 0 | } |
6477 | 0 | btype = data ? GF_ISOM_ITUNE_GENRE_USER : GF_ISOM_ITUNE_GENRE; |
6478 | 0 | } else if (tag==GF_4CC('c','u','s','t') ) { |
6479 | 0 | if (in_cust_name || in_cust_mean) { |
6480 | 0 | if (in_cust_mean && in_cust_mean[0]) |
6481 | 0 | cust_mean = gf_strdup(in_cust_mean); |
6482 | 0 | if (in_cust_name && in_cust_name[0]) |
6483 | 0 | cust_name = gf_strdup(in_cust_name); |
6484 | 0 | btype = GF_ISOM_BOX_TYPE_iTunesSpecificInfo; |
6485 | 0 | } else { |
6486 | 0 | char *sep = strchr(data, ','); |
6487 | 0 | if (sep) { |
6488 | 0 | sep[0] = 0; |
6489 | 0 | if (data[0]) |
6490 | 0 | cust_mean = gf_strdup(data); |
6491 | 0 | sep[0] = ','; |
6492 | 0 | sep++; |
6493 | |
|
6494 | 0 | char *sep2 = strchr(sep+1, ','); |
6495 | 0 | if (sep2) { |
6496 | 0 | sep2[0] = 0; |
6497 | 0 | if (sep[0]) |
6498 | 0 | cust_name = gf_strdup(sep); |
6499 | 0 | sep2[0] = ','; |
6500 | 0 | data_len -= (u32) (sep2-(char*)data) +1; |
6501 | 0 | data = sep2+1; |
6502 | 0 | } else { |
6503 | 0 | data_len -= (u32) (sep-(char*)data)+1; |
6504 | 0 | data = sep+1; |
6505 | 0 | } |
6506 | 0 | btype = GF_ISOM_BOX_TYPE_iTunesSpecificInfo; |
6507 | 0 | } |
6508 | 0 | } |
6509 | 0 | } else { |
6510 | 0 | btype = tag; |
6511 | 0 | } |
6512 | | /*remove tag*/ |
6513 | 0 | i = 0; |
6514 | 0 | while ((info = (GF_ListItemBox*)gf_list_enum(ilst->child_boxes, &i))) { |
6515 | 0 | if (info->type==GF_ISOM_BOX_TYPE_iTunesSpecificInfo) { |
6516 | |
|
6517 | 0 | if (info->name && cust_name && !strcmp(info->name->string, cust_name)) {} |
6518 | 0 | else if (!info->name && !cust_name) {} |
6519 | 0 | else continue; |
6520 | | |
6521 | 0 | if (info->mean && cust_mean && !strcmp(info->mean->string, cust_mean)) {} |
6522 | 0 | else if (!info->mean && !cust_mean) {} |
6523 | 0 | else continue; |
6524 | | |
6525 | 0 | gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *) info); |
6526 | 0 | info = NULL; |
6527 | 0 | break; |
6528 | 0 | } |
6529 | 0 | if (info->type==btype) { |
6530 | 0 | gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *) info); |
6531 | 0 | info = NULL; |
6532 | 0 | break; |
6533 | 0 | } |
6534 | 0 | if (info->type==GF_ISOM_BOX_TYPE_UNKNOWN) { |
6535 | 0 | GF_UnknownBox *u = (GF_UnknownBox *) info; |
6536 | 0 | if (u->original_4cc==btype) { |
6537 | 0 | gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *) info); |
6538 | 0 | info = NULL; |
6539 | 0 | break; |
6540 | 0 | } |
6541 | 0 | } |
6542 | 0 | } |
6543 | |
|
6544 | 0 | if (!data && data_len) { |
6545 | 0 | if (!gf_list_count(ilst->child_boxes) ) |
6546 | 0 | gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) ilst); |
6547 | 0 | if (cust_mean) gf_free(cust_mean); |
6548 | 0 | if (cust_name) gf_free(cust_name); |
6549 | 0 | return GF_OK; |
6550 | 0 | } |
6551 | | |
6552 | | //watch out for cprt, we don't want to create a regular cprt box |
6553 | 0 | if (btype==GF_ISOM_ITUNE_COPYRIGHT) { |
6554 | 0 | info = (GF_ListItemBox *)gf_isom_box_new(GF_ISOM_ITUNE_TOOL); |
6555 | 0 | info->type = GF_ISOM_ITUNE_COPYRIGHT; |
6556 | 0 | } else { |
6557 | 0 | info = (GF_ListItemBox *)gf_isom_box_new(btype); |
6558 | 0 | } |
6559 | 0 | if (info == NULL) { |
6560 | 0 | if (cust_mean) gf_free(cust_mean); |
6561 | 0 | if (cust_name) gf_free(cust_name); |
6562 | 0 | return GF_OUT_OF_MEM; |
6563 | 0 | } |
6564 | | |
6565 | 0 | dbox = (GF_DataBox *)gf_isom_box_new_parent(&info->child_boxes, GF_ISOM_BOX_TYPE_DATA); |
6566 | 0 | if (!dbox) { |
6567 | 0 | gf_isom_box_del((GF_Box *)info); |
6568 | 0 | if (cust_mean) gf_free(cust_mean); |
6569 | 0 | if (cust_name) gf_free(cust_name); |
6570 | 0 | return GF_OUT_OF_MEM; |
6571 | 0 | } |
6572 | 0 | dbox->locale = locale; |
6573 | |
|
6574 | 0 | if (info->type!=GF_ISOM_BOX_TYPE_UNKNOWN) { |
6575 | 0 | info->data = dbox; |
6576 | |
|
6577 | 0 | if (cust_name) { |
6578 | 0 | info->name = (GF_NameBox *)gf_isom_box_new_parent(&info->child_boxes, GF_QT_BOX_TYPE_NAME); |
6579 | 0 | info->name->string = cust_name; |
6580 | 0 | } |
6581 | 0 | if (cust_mean) { |
6582 | 0 | info->mean = (GF_NameBox *)gf_isom_box_new_parent(&info->child_boxes, GF_QT_BOX_TYPE_MEAN); |
6583 | 0 | info->mean->string = cust_mean; |
6584 | 0 | } |
6585 | 0 | } |
6586 | |
|
6587 | 0 | switch (itype) { |
6588 | 0 | case GF_ITAG_FRAC6: |
6589 | 0 | case GF_ITAG_FRAC8: |
6590 | 0 | if (data && data_len) { |
6591 | 0 | if (sscanf(data, "%u/%u", &n, &d) != 2) { |
6592 | 0 | n = d = 0; |
6593 | 0 | if (sscanf(data, "%u", &n) != 1) |
6594 | 0 | n = 0; |
6595 | 0 | } |
6596 | 0 | } else { |
6597 | 0 | n = (u32) int_val; |
6598 | 0 | d = int_val2; |
6599 | 0 | } |
6600 | 0 | if (n) { |
6601 | 0 | memset(loc_data, 0, sizeof(char) * 8); |
6602 | 0 | data_len = (itype == GF_ITAG_FRAC6) ? 6 : 8; |
6603 | 0 | loc_data[3] = n; |
6604 | 0 | loc_data[2] = n >> 8; |
6605 | 0 | loc_data[5] = d; |
6606 | 0 | loc_data[4] = d >> 8; |
6607 | 0 | data = loc_data; |
6608 | 0 | } else { |
6609 | 0 | data = NULL; |
6610 | 0 | } |
6611 | 0 | dbox->flags = 0x15; |
6612 | 0 | break; |
6613 | 0 | case GF_ITAG_BOOL: |
6614 | 0 | loc_data[0] = 0; |
6615 | 0 | if (data && data_len) { |
6616 | 0 | if ( !strcmp(data, "yes") || !strcmp(data, "1") || !strcmp(data, "true")) |
6617 | 0 | loc_data[0] = 1; |
6618 | 0 | } else { |
6619 | 0 | loc_data[0] = int_val ? 1 : 0; |
6620 | 0 | } |
6621 | 0 | data = loc_data; |
6622 | 0 | data_len = 1; |
6623 | 0 | dbox->flags = int_flags; |
6624 | 0 | break; |
6625 | 0 | case GF_ITAG_INT8: |
6626 | 0 | loc_data[0] = 0; |
6627 | 0 | if (data && data_len) int_val = atoi(data); |
6628 | 0 | loc_data[0] = (u8) int_val; |
6629 | 0 | data = loc_data; |
6630 | 0 | data_len = 1; |
6631 | 0 | dbox->flags = int_flags; |
6632 | 0 | break; |
6633 | 0 | case GF_ITAG_INT16: |
6634 | 0 | loc_data[0] = 0; |
6635 | 0 | if (data && data_len) int_val = atoi(data); |
6636 | 0 | loc_data[1] = (u8) int_val; |
6637 | 0 | loc_data[0] = (u8) (int_val>>8); |
6638 | 0 | data = loc_data; |
6639 | 0 | data_len = 2; |
6640 | 0 | dbox->flags = int_flags; |
6641 | 0 | break; |
6642 | 0 | case GF_ITAG_INT32: |
6643 | 0 | loc_data[0] = 0; |
6644 | 0 | if (data && data_len) int_val = atoi(data); |
6645 | 0 | loc_data[3] = (u8) int_val; |
6646 | 0 | loc_data[2] = (u8) (int_val>>8); |
6647 | 0 | loc_data[1] = (u8) (int_val>>16); |
6648 | 0 | loc_data[0] = (u8) (int_val>>24); |
6649 | 0 | data = loc_data; |
6650 | 0 | data_len = 4; |
6651 | 0 | dbox->flags = int_flags; |
6652 | 0 | break; |
6653 | 0 | case GF_ITAG_INT64: |
6654 | 0 | loc_data[0] = 0; |
6655 | 0 | if (data && data_len) sscanf(data, LLU, &int_val); |
6656 | 0 | loc_data[7] = (u8) int_val; |
6657 | 0 | loc_data[6] = (u8) (int_val>>8); |
6658 | 0 | loc_data[5] = (u8) (int_val>>16); |
6659 | 0 | loc_data[4] = (u8) (int_val>>24); |
6660 | 0 | loc_data[3] = (u8) (int_val>>32); |
6661 | 0 | loc_data[2] = (u8) (int_val>>40); |
6662 | 0 | loc_data[1] = (u8) (int_val>>48); |
6663 | 0 | loc_data[0] = (u8) (int_val>>56); |
6664 | 0 | data = loc_data; |
6665 | 0 | data_len = 8; |
6666 | 0 | dbox->flags = int_flags; |
6667 | 0 | break; |
6668 | 0 | default: |
6669 | 0 | dbox->flags = 1; |
6670 | 0 | break; |
6671 | 0 | } |
6672 | | |
6673 | 0 | if (!data) return GF_BAD_PARAM; |
6674 | | |
6675 | | |
6676 | 0 | if (tag==GF_ISOM_ITUNE_COVER_ART) { |
6677 | 0 | info->data->flags = 0; |
6678 | | /*check for PNG sig*/ |
6679 | 0 | if ((data_len>4) && (data[0] == 0x89) && (data[1] == 0x50) && (data[2] == 0x4E) && (data[3] == 0x47) ) { |
6680 | 0 | info->data->flags = 14; |
6681 | 0 | } |
6682 | | //JPG and JFIF - do not check second tag type |
6683 | 0 | else if ((data_len>4) && (data[0] == 0xFF) && (data[1] == 0xD8) && (data[2] == 0xFF) /*&& ((data[3] == 0xE0) || (data[3] == 0xDB))*/ ) { |
6684 | 0 | info->data->flags = 13; |
6685 | 0 | } |
6686 | | //GIF |
6687 | 0 | else if ((data_len>3) && (data[0] == 'G') && (data[1] == 'I') && (data[2] == 'F') ) { |
6688 | 0 | info->data->flags = 12; |
6689 | 0 | } |
6690 | 0 | } |
6691 | |
|
6692 | 0 | dbox->dataSize = data_len; |
6693 | 0 | dbox->data = (char*)gf_malloc(sizeof(char)*data_len); |
6694 | 0 | if (!dbox->data) return GF_OUT_OF_MEM; |
6695 | 0 | memcpy(dbox->data, data, sizeof(char)*data_len); |
6696 | |
|
6697 | 0 | if (!info && !gf_list_count(ilst->child_boxes) ) { |
6698 | 0 | gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) ilst); |
6699 | 0 | return GF_OK; |
6700 | 0 | } |
6701 | 0 | if (!ilst->child_boxes) ilst->child_boxes = gf_list_new(); |
6702 | |
|
6703 | 0 | return gf_list_add(ilst->child_boxes, info); |
6704 | 0 | } |
6705 | | |
6706 | | GF_EXPORT |
6707 | | GF_Err gf_isom_apple_set_tag(GF_ISOFile *mov, GF_ISOiTunesTag tag, const u8 *data, u32 data_len, u64 int_val, u32 int_val2) |
6708 | 0 | { |
6709 | 0 | return gf_isom_apple_set_tag_ex(mov, tag, data, data_len, int_val, int_val2, NULL, NULL, 0); |
6710 | 0 | } |
6711 | | |
6712 | | #include <gpac/utf.h> |
6713 | | |
6714 | | GF_EXPORT |
6715 | | GF_Err gf_isom_wma_set_tag(GF_ISOFile *mov, char *name, char *value) |
6716 | 0 | { |
6717 | 0 | GF_Err e; |
6718 | 0 | GF_XtraTag *tag=NULL; |
6719 | 0 | u32 count, i; |
6720 | 0 | GF_XtraBox *xtra; |
6721 | |
|
6722 | 0 | e = gf_isom_can_access_movie(mov, GF_ISOM_OPEN_WRITE); |
6723 | 0 | if (e) return e; |
6724 | | |
6725 | 0 | gf_isom_create_meta_extensions(mov, GF_FALSE); |
6726 | |
|
6727 | 0 | xtra = (GF_XtraBox *) gf_isom_create_meta_extensions(mov, GF_TRUE); |
6728 | 0 | if (!xtra) return GF_BAD_PARAM; |
6729 | | |
6730 | 0 | count = gf_list_count(xtra->tags); |
6731 | 0 | for (i=0; i<count; i++) { |
6732 | 0 | tag = gf_list_get(xtra->tags, i); |
6733 | 0 | if (name && tag->name && !strcmp(tag->name, name)) { |
6734 | |
|
6735 | 0 | } else { |
6736 | 0 | tag = NULL; |
6737 | 0 | continue; |
6738 | 0 | } |
6739 | | |
6740 | 0 | if (!value) { |
6741 | 0 | gf_list_rem(xtra->tags, i); |
6742 | 0 | gf_free(tag->name); |
6743 | 0 | if (tag->prop_value) gf_free(tag->prop_value); |
6744 | 0 | gf_free(tag); |
6745 | 0 | return GF_OK; |
6746 | 0 | } |
6747 | 0 | gf_free(tag->prop_value); |
6748 | 0 | tag->prop_value = NULL; |
6749 | 0 | break; |
6750 | 0 | } |
6751 | 0 | if (!tag) { |
6752 | 0 | if (!name) return GF_OK; |
6753 | | |
6754 | 0 | GF_SAFEALLOC(tag, GF_XtraTag); |
6755 | 0 | tag->name = gf_strdup(name); |
6756 | 0 | tag->prop_type = 0; |
6757 | 0 | tag->flags = 1; |
6758 | 0 | gf_list_add(xtra->tags, tag); |
6759 | 0 | } |
6760 | | |
6761 | 0 | u32 len = (u32) strlen(value); |
6762 | 0 | tag->prop_value = gf_malloc(sizeof(u16) * ((len/2)*2+2) ); |
6763 | 0 | memset(tag->prop_value, 0, sizeof(u16) * ((len/2)*2+2) ); |
6764 | 0 | if (len) { |
6765 | 0 | u32 _len = gf_utf8_mbstowcs((u16 *) tag->prop_value, len+1, (const char **) &value); |
6766 | 0 | if (_len == GF_UTF8_FAIL) _len = 0; |
6767 | 0 | tag->prop_value[2 * _len] = 0; |
6768 | 0 | tag->prop_value[2 * _len + 1] = 0; |
6769 | 0 | tag->prop_size = 2 * _len + 2; |
6770 | 0 | } else { |
6771 | 0 | tag->prop_size = 2; |
6772 | 0 | } |
6773 | 0 | return GF_OK; |
6774 | 0 | } |
6775 | | |
6776 | | GF_EXPORT |
6777 | | GF_Err gf_isom_set_qt_key(GF_ISOFile *movie, GF_QT_UDTAKey *key) |
6778 | 0 | { |
6779 | 0 | GF_Err e; |
6780 | 0 | GF_MetaBox *meta; |
6781 | 0 | GF_ItemListBox *ilst; |
6782 | 0 | GF_MetaKeysBox *keys; |
6783 | 0 | u32 i, nb_keys; |
6784 | |
|
6785 | 0 | if (!movie) return GF_BAD_PARAM; |
6786 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
6787 | 0 | if (e) { |
6788 | 0 | gf_isom_set_last_error(movie, e); |
6789 | 0 | return 0; |
6790 | 0 | } |
6791 | 0 | e = gf_isom_insert_moov(movie); |
6792 | 0 | if (e) return e; |
6793 | | |
6794 | 0 | meta = (GF_MetaBox *) gf_isom_create_meta_extensions(movie, 2); |
6795 | 0 | if (!meta) return GF_BAD_PARAM; |
6796 | | |
6797 | 0 | keys = (GF_MetaKeysBox *) gf_isom_locate_box(meta->child_boxes, GF_ISOM_BOX_TYPE_KEYS, NULL); |
6798 | 0 | if (!keys) { |
6799 | 0 | keys = (GF_MetaKeysBox *) gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_KEYS); |
6800 | 0 | meta->keys = keys; |
6801 | 0 | } |
6802 | 0 | ilst = (GF_ItemListBox *) gf_isom_locate_box(meta->child_boxes, GF_ISOM_BOX_TYPE_ILST, NULL); |
6803 | 0 | if (!ilst) { |
6804 | 0 | ilst = (GF_ItemListBox *) gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_ILST); |
6805 | 0 | } |
6806 | 0 | if (!keys || !ilst) return GF_OUT_OF_MEM; |
6807 | | |
6808 | 0 | nb_keys = gf_list_count(keys->keys); |
6809 | 0 | if (!key) { |
6810 | 0 | gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) keys); |
6811 | 0 | for (i=0; i<gf_list_count(ilst->child_boxes); i++) { |
6812 | 0 | GF_ListItemBox *info = gf_list_get(ilst->child_boxes, i); |
6813 | 0 | if (info->type <= nb_keys) { |
6814 | 0 | gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *) info); |
6815 | 0 | i--; |
6816 | 0 | } |
6817 | 0 | } |
6818 | 0 | if (!gf_list_count(ilst->child_boxes)) { |
6819 | 0 | gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) ilst); |
6820 | | //if last, delete udta - we may still have a handler box remaining |
6821 | 0 | if ((gf_list_count(meta->child_boxes) <= 1) && (gf_list_count(movie->moov->udta->recordList)==1)) { |
6822 | 0 | gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box *) movie->moov->udta); |
6823 | 0 | movie->moov->udta = NULL; |
6824 | 0 | } |
6825 | 0 | } |
6826 | 0 | return GF_OK; |
6827 | 0 | } |
6828 | | //locate key |
6829 | 0 | GF_MetaKey *o_key = NULL; |
6830 | 0 | u32 ksize = (u32) strlen(key->name); |
6831 | 0 | for (i=0; i<nb_keys; i++) { |
6832 | 0 | o_key = gf_list_get(keys->keys, i); |
6833 | 0 | if ((o_key->ns == key->ns) && (o_key->size==ksize) && !strcmp(o_key->data, key->name)) |
6834 | 0 | break; |
6835 | 0 | o_key = NULL; |
6836 | 0 | } |
6837 | 0 | if (!o_key) { |
6838 | 0 | if (key->type==GF_QT_KEY_REMOVE) return GF_OK; |
6839 | 0 | GF_SAFEALLOC(o_key, GF_MetaKey); |
6840 | 0 | o_key->ns = key->ns; |
6841 | 0 | o_key->data = gf_strdup(key->name); |
6842 | 0 | o_key->size = ksize; |
6843 | 0 | gf_list_add(keys->keys, o_key); |
6844 | 0 | } |
6845 | 0 | u32 key_idx = gf_list_find(keys->keys, o_key)+1; |
6846 | 0 | GF_UnknownBox *info=NULL; |
6847 | 0 | for (i=0; i<gf_list_count(ilst->child_boxes); i++) { |
6848 | 0 | info = gf_list_get(ilst->child_boxes, i); |
6849 | 0 | if (info->original_4cc == key_idx) break; |
6850 | 0 | info = NULL; |
6851 | 0 | } |
6852 | |
|
6853 | 0 | if (key->type==GF_QT_KEY_REMOVE) { |
6854 | 0 | gf_list_del_item(keys->keys, o_key); |
6855 | 0 | if (o_key->data) gf_free(o_key->data); |
6856 | 0 | gf_free(o_key); |
6857 | 0 | for (i=0; i<gf_list_count(ilst->child_boxes); i++) { |
6858 | 0 | info = gf_list_get(ilst->child_boxes, i); |
6859 | 0 | if (info->type!=GF_ISOM_BOX_TYPE_UNKNOWN) continue; |
6860 | 0 | if (info->original_4cc==key_idx) { |
6861 | 0 | gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *)info); |
6862 | 0 | i--; |
6863 | 0 | continue; |
6864 | 0 | } |
6865 | 0 | if (info->original_4cc>key_idx) { |
6866 | 0 | info->original_4cc--; |
6867 | 0 | } |
6868 | 0 | } |
6869 | 0 | return GF_OK; |
6870 | 0 | } |
6871 | 0 | if (!info) { |
6872 | 0 | info = (GF_UnknownBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_UNKNOWN); |
6873 | 0 | if (!info) return GF_OUT_OF_MEM; |
6874 | 0 | if (!ilst->child_boxes) ilst->child_boxes = gf_list_new(); |
6875 | 0 | gf_list_add(ilst->child_boxes, info); |
6876 | 0 | } |
6877 | | |
6878 | 0 | GF_DataBox *dbox = (GF_DataBox *) gf_isom_box_find_child(info->child_boxes, GF_ISOM_BOX_TYPE_DATA); |
6879 | 0 | if (!dbox) { |
6880 | 0 | dbox = (GF_DataBox *)gf_isom_box_new_parent(&info->child_boxes, GF_ISOM_BOX_TYPE_DATA); |
6881 | 0 | if (!dbox) { |
6882 | 0 | gf_isom_box_del((GF_Box *)info); |
6883 | 0 | return GF_OUT_OF_MEM; |
6884 | 0 | } |
6885 | 0 | } |
6886 | 0 | u32 nb_bits=0; |
6887 | 0 | info->original_4cc = key_idx; |
6888 | 0 | dbox->version = 0; |
6889 | 0 | dbox->flags = key->type; |
6890 | | //serialize |
6891 | 0 | GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
6892 | |
|
6893 | 0 | switch (key->type) { |
6894 | 0 | case GF_QT_KEY_UTF8: |
6895 | 0 | case GF_QT_KEY_UTF8_SORT: |
6896 | 0 | if (key->value.string) |
6897 | 0 | gf_bs_write_data(bs, key->value.string, (u32) strlen(key->value.string)); |
6898 | 0 | break; |
6899 | | |
6900 | 0 | case GF_QT_KEY_SIGNED_VSIZE: |
6901 | 0 | if (ABS(key->value.sint)<=0x7F) nb_bits=8; |
6902 | 0 | else if (ABS(key->value.sint)<=0x7FFF) nb_bits=16; |
6903 | 0 | else if (ABS(key->value.sint)<=0x7FFFFF) nb_bits=24; |
6904 | 0 | else nb_bits = 32; |
6905 | 0 | gf_bs_write_int(bs, (s32) key->value.sint, nb_bits); |
6906 | 0 | break; |
6907 | 0 | case GF_QT_KEY_UNSIGNED_VSIZE: |
6908 | 0 | if (key->value.uint<=0xFF) nb_bits=8; |
6909 | 0 | else if (key->value.uint<=0xFFFF) nb_bits=16; |
6910 | 0 | else if (key->value.uint<=0xFFFFFF) nb_bits=24; |
6911 | 0 | else nb_bits = 32; |
6912 | 0 | gf_bs_write_int(bs, (u32) key->value.uint, nb_bits); |
6913 | 0 | break; |
6914 | 0 | case GF_QT_KEY_FLOAT: |
6915 | 0 | gf_bs_write_float(bs, (Float) key->value.number); |
6916 | 0 | break; |
6917 | 0 | case GF_QT_KEY_DOUBLE: |
6918 | 0 | gf_bs_write_double(bs, key->value.number); |
6919 | 0 | break; |
6920 | 0 | case GF_QT_KEY_SIGNED_8: |
6921 | 0 | gf_bs_write_int(bs, (s32) key->value.sint, 8); |
6922 | 0 | break; |
6923 | 0 | case GF_QT_KEY_SIGNED_16: |
6924 | 0 | gf_bs_write_int(bs, (s32) key->value.sint, 16); |
6925 | 0 | break; |
6926 | 0 | case GF_QT_KEY_SIGNED_32: |
6927 | 0 | gf_bs_write_int(bs, (s32) key->value.sint, 32); |
6928 | 0 | break; |
6929 | 0 | case GF_QT_KEY_SIGNED_64: |
6930 | 0 | gf_bs_write_long_int(bs, key->value.sint, 64); |
6931 | 0 | break; |
6932 | 0 | case GF_QT_KEY_POINTF: |
6933 | 0 | case GF_QT_KEY_SIZEF: |
6934 | 0 | gf_bs_write_float(bs, key->value.pos_size.x); |
6935 | 0 | gf_bs_write_float(bs, key->value.pos_size.y); |
6936 | 0 | break; |
6937 | 0 | case GF_QT_KEY_RECTF: |
6938 | 0 | gf_bs_write_float(bs, key->value.rect.x); |
6939 | 0 | gf_bs_write_float(bs, key->value.rect.y); |
6940 | 0 | gf_bs_write_float(bs, key->value.rect.w); |
6941 | 0 | gf_bs_write_float(bs, key->value.rect.h); |
6942 | 0 | break; |
6943 | | |
6944 | 0 | case GF_QT_KEY_UNSIGNED_8: |
6945 | 0 | gf_bs_write_int(bs, (s32) key->value.uint, 8); |
6946 | 0 | break; |
6947 | 0 | case GF_QT_KEY_UNSIGNED_16: |
6948 | 0 | gf_bs_write_int(bs, (s32) key->value.uint, 16); |
6949 | 0 | break; |
6950 | 0 | case GF_QT_KEY_UNSIGNED_32: |
6951 | 0 | gf_bs_write_int(bs, (s32) key->value.uint, 32); |
6952 | 0 | break; |
6953 | 0 | case GF_QT_KEY_UNSIGNED_64: |
6954 | 0 | gf_bs_write_long_int(bs, key->value.uint, 64); |
6955 | 0 | break; |
6956 | 0 | case GF_QT_KEY_MATRIXF: |
6957 | 0 | for (i=0; i<9; i++) |
6958 | 0 | gf_bs_write_float(bs, (Float) key->value.matrix[i] ); |
6959 | 0 | break; |
6960 | | |
6961 | 0 | case GF_QT_KEY_OPAQUE: |
6962 | 0 | case GF_QT_KEY_UTF16_BE: |
6963 | 0 | case GF_QT_KEY_JIS: |
6964 | 0 | case GF_QT_KEY_UTF16_SORT: |
6965 | 0 | case GF_QT_KEY_JPEG: |
6966 | 0 | case GF_QT_KEY_PNG: |
6967 | 0 | case GF_QT_KEY_BMP: |
6968 | 0 | case GF_QT_KEY_METABOX: |
6969 | 0 | default: |
6970 | 0 | gf_bs_write_data(bs, key->value.data.data, key->value.data.data_len); |
6971 | 0 | break; |
6972 | 0 | } |
6973 | | //write extra 0 at end, not serialized |
6974 | 0 | gf_bs_write_u8(bs, 0); |
6975 | 0 | if (dbox->data) gf_free(dbox->data); |
6976 | |
|
6977 | 0 | gf_bs_get_content(bs, &dbox->data, &i); |
6978 | 0 | if (i) i--; |
6979 | 0 | dbox->dataSize = i; |
6980 | 0 | gf_bs_del(bs); |
6981 | 0 | return GF_OK; |
6982 | 0 | } |
6983 | | |
6984 | | |
6985 | | GF_EXPORT |
6986 | | GF_Err gf_isom_set_alternate_group_id(GF_ISOFile *movie, u32 trackNumber, u32 groupId) |
6987 | 0 | { |
6988 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
6989 | 0 | if (!trak) return GF_BAD_PARAM; |
6990 | 0 | trak->Header->alternate_group = groupId; |
6991 | 0 | return GF_OK; |
6992 | 0 | } |
6993 | | |
6994 | | |
6995 | | GF_EXPORT |
6996 | | GF_Err gf_isom_set_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, u32 trackRefGroup, Bool is_switch_group, u32 *switchGroupID, u32 *criteriaList, u32 criteriaListCount) |
6997 | 0 | { |
6998 | 0 | GF_TrackSelectionBox *tsel; |
6999 | 0 | GF_TrackBox *trak; |
7000 | 0 | GF_UserDataMap *map; |
7001 | 0 | GF_Err e; |
7002 | 0 | u32 alternateGroupID = 0; |
7003 | 0 | u32 next_switch_group_id = 0; |
7004 | |
|
7005 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
7006 | 0 | if (!trak || !switchGroupID) return GF_BAD_PARAM; |
7007 | | |
7008 | | |
7009 | 0 | if (trackRefGroup) { |
7010 | 0 | GF_TrackBox *trak_ref = gf_isom_get_track_box(movie, trackRefGroup); |
7011 | 0 | if (trak_ref != trak) { |
7012 | 0 | if (!trak_ref || !trak_ref->Header->alternate_group) { |
7013 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Track %d has not an alternate group - skipping\n", trak_ref ? trak_ref->Header->trackID : 0)); |
7014 | 0 | return GF_BAD_PARAM; |
7015 | 0 | } |
7016 | 0 | alternateGroupID = trak_ref->Header->alternate_group; |
7017 | 0 | } else { |
7018 | 0 | alternateGroupID = trak->Header->alternate_group; |
7019 | 0 | } |
7020 | 0 | } |
7021 | 0 | if (!alternateGroupID) { |
7022 | | /*there is a function for this ....*/ |
7023 | 0 | if (trak->Header->alternate_group && !is_switch_group) { |
7024 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Track %d has already an alternate group - skipping\n", trak->Header->trackID)); |
7025 | 0 | return GF_BAD_PARAM; |
7026 | 0 | } |
7027 | 0 | alternateGroupID = gf_isom_get_next_alternate_group_id(movie); |
7028 | 0 | } |
7029 | | |
7030 | 0 | if (is_switch_group) { |
7031 | 0 | u32 i=0; |
7032 | 0 | while (i< gf_isom_get_track_count(movie) ) { |
7033 | | //locate first available ID |
7034 | 0 | GF_TrackBox *a_trak = gf_isom_get_track_box(movie, i+1); |
7035 | |
|
7036 | 0 | if (a_trak->udta) { |
7037 | 0 | u32 j, count; |
7038 | 0 | map = udta_getEntry(a_trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); |
7039 | 0 | if (map) { |
7040 | 0 | count = gf_list_count(map->boxes); |
7041 | 0 | for (j=0; j<count; j++) { |
7042 | 0 | tsel = (GF_TrackSelectionBox*)gf_list_get(map->boxes, j); |
7043 | |
|
7044 | 0 | if (*switchGroupID) { |
7045 | 0 | if (tsel->switchGroup==next_switch_group_id) { |
7046 | 0 | if (a_trak->Header->alternate_group != alternateGroupID) return GF_BAD_PARAM; |
7047 | 0 | } |
7048 | 0 | } else { |
7049 | 0 | if (tsel->switchGroup && (tsel->switchGroup>=next_switch_group_id) ) |
7050 | 0 | next_switch_group_id = tsel->switchGroup; |
7051 | 0 | } |
7052 | 0 | } |
7053 | 0 | } |
7054 | |
|
7055 | 0 | } |
7056 | 0 | i++; |
7057 | 0 | } |
7058 | 0 | if (! *switchGroupID) *switchGroupID = next_switch_group_id+1; |
7059 | 0 | } |
7060 | | |
7061 | 0 | if (!trak->Header->alternate_group) |
7062 | 0 | trak->Header->alternate_group = alternateGroupID; |
7063 | |
|
7064 | 0 | tsel = NULL; |
7065 | 0 | if (*switchGroupID) { |
7066 | 0 | if (!trak->udta) { |
7067 | 0 | e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
7068 | 0 | if (e) return e; |
7069 | 0 | } |
7070 | | |
7071 | 0 | map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); |
7072 | | |
7073 | | /*locate tsel box with no switch group*/ |
7074 | 0 | if (map) { |
7075 | 0 | u32 j, count = gf_list_count(map->boxes); |
7076 | 0 | for (j=0; j<count; j++) { |
7077 | 0 | tsel = (GF_TrackSelectionBox*)gf_list_get(map->boxes, j); |
7078 | 0 | if (tsel->switchGroup == *switchGroupID) break; |
7079 | 0 | tsel = NULL; |
7080 | 0 | } |
7081 | 0 | } |
7082 | 0 | if (!tsel) { |
7083 | 0 | tsel = (GF_TrackSelectionBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TSEL); |
7084 | 0 | if (!tsel) return GF_OUT_OF_MEM; |
7085 | 0 | e = udta_on_child_box_ex((GF_Box *)trak->udta, (GF_Box *) tsel, GF_FALSE, GF_TRUE); |
7086 | 0 | if (e) return e; |
7087 | 0 | } |
7088 | | |
7089 | 0 | tsel->switchGroup = *switchGroupID; |
7090 | 0 | tsel->attributeListCount = criteriaListCount; |
7091 | 0 | if (tsel->attributeList) gf_free(tsel->attributeList); |
7092 | 0 | tsel->attributeList = (u32*)gf_malloc(sizeof(u32)*criteriaListCount); |
7093 | 0 | if (!tsel->attributeList) return GF_OUT_OF_MEM; |
7094 | 0 | memcpy(tsel->attributeList, criteriaList, sizeof(u32)*criteriaListCount); |
7095 | 0 | } |
7096 | 0 | return GF_OK; |
7097 | 0 | } |
7098 | | |
7099 | | void reset_tsel_box(GF_TrackBox *trak) |
7100 | 0 | { |
7101 | 0 | GF_UserDataMap *map; |
7102 | 0 | trak->Header->alternate_group = 0; |
7103 | 0 | map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); |
7104 | 0 | if (map) { |
7105 | 0 | gf_list_del_item(trak->udta->recordList, map); |
7106 | 0 | gf_isom_box_array_del(map->boxes); |
7107 | 0 | gf_free(map); |
7108 | 0 | } |
7109 | |
|
7110 | 0 | } |
7111 | | |
7112 | | GF_EXPORT |
7113 | | GF_Err gf_isom_reset_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, Bool reset_all_group) |
7114 | 0 | { |
7115 | 0 | GF_TrackBox *trak; |
7116 | 0 | u32 alternateGroupID = 0; |
7117 | |
|
7118 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
7119 | 0 | if (!trak) return GF_BAD_PARAM; |
7120 | 0 | if (!trak->Header->alternate_group) return GF_OK; |
7121 | | |
7122 | 0 | alternateGroupID = trak->Header->alternate_group; |
7123 | 0 | if (reset_all_group) { |
7124 | 0 | u32 i=0; |
7125 | 0 | while (i< gf_isom_get_track_count(movie) ) { |
7126 | | //locate first available ID |
7127 | 0 | GF_TrackBox *a_trak = gf_isom_get_track_box(movie, i+1); |
7128 | 0 | if (a_trak->Header->alternate_group == alternateGroupID) reset_tsel_box(a_trak); |
7129 | 0 | i++; |
7130 | 0 | } |
7131 | 0 | } else { |
7132 | 0 | reset_tsel_box(trak); |
7133 | 0 | } |
7134 | 0 | return GF_OK; |
7135 | 0 | } |
7136 | | |
7137 | | |
7138 | | GF_EXPORT |
7139 | | GF_Err gf_isom_reset_switch_parameters(GF_ISOFile *movie) |
7140 | 0 | { |
7141 | 0 | u32 i=0; |
7142 | 0 | while (i< gf_isom_get_track_count(movie) ) { |
7143 | | //locate first available ID |
7144 | 0 | GF_TrackBox *a_trak = gf_isom_get_track_box(movie, i+1); |
7145 | 0 | reset_tsel_box(a_trak); |
7146 | 0 | i++; |
7147 | 0 | } |
7148 | 0 | return GF_OK; |
7149 | 0 | } |
7150 | | |
7151 | | |
7152 | | GF_Err gf_isom_add_subsample(GF_ISOFile *movie, u32 track, u32 sampleNumber, u32 flags, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable) |
7153 | 0 | { |
7154 | 0 | u32 i, count; |
7155 | 0 | GF_SubSampleInformationBox *sub_samples; |
7156 | 0 | GF_TrackBox *trak; |
7157 | 0 | GF_Err e; |
7158 | |
|
7159 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7160 | 0 | if (e) return e; |
7161 | | |
7162 | 0 | trak = gf_isom_get_track_box(movie, track); |
7163 | 0 | if (!trak || !trak->Media || !trak->Media->information->sampleTable) |
7164 | 0 | return GF_BAD_PARAM; |
7165 | | |
7166 | 0 | if (!trak->Media->information->sampleTable->sub_samples) { |
7167 | 0 | trak->Media->information->sampleTable->sub_samples=gf_list_new(); |
7168 | 0 | } |
7169 | |
|
7170 | 0 | sub_samples = NULL; |
7171 | 0 | count = gf_list_count(trak->Media->information->sampleTable->sub_samples); |
7172 | 0 | for (i=0; i<count; i++) { |
7173 | 0 | sub_samples = gf_list_get(trak->Media->information->sampleTable->sub_samples, i); |
7174 | 0 | if (sub_samples->flags==flags) break; |
7175 | 0 | sub_samples = NULL; |
7176 | 0 | } |
7177 | 0 | if (!sub_samples) { |
7178 | 0 | sub_samples = (GF_SubSampleInformationBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_SUBS); |
7179 | 0 | if (!sub_samples) return GF_OUT_OF_MEM; |
7180 | 0 | gf_list_add(trak->Media->information->sampleTable->sub_samples, sub_samples); |
7181 | 0 | sub_samples->version = (subSampleSize>0xFFFF) ? 1 : 0; |
7182 | 0 | sub_samples->flags = flags; |
7183 | 0 | } |
7184 | 0 | return gf_isom_add_subsample_info(sub_samples, sampleNumber, subSampleSize, priority, reserved, discardable); |
7185 | 0 | } |
7186 | | |
7187 | | |
7188 | | GF_EXPORT |
7189 | | GF_Err gf_isom_set_rvc_config(GF_ISOFile *movie, u32 track, u32 sampleDescriptionIndex, u16 rvc_predefined, char *mime, u8 *data, u32 size) |
7190 | 0 | { |
7191 | 0 | GF_MPEGVisualSampleEntryBox *entry; |
7192 | 0 | GF_Err e; |
7193 | 0 | GF_TrackBox *trak; |
7194 | |
|
7195 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7196 | 0 | if (e) return e; |
7197 | | |
7198 | 0 | trak = gf_isom_get_track_box(movie, track); |
7199 | 0 | if (!trak) return GF_BAD_PARAM; |
7200 | | |
7201 | | |
7202 | 0 | entry = (GF_MPEGVisualSampleEntryBox *) gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex-1); |
7203 | 0 | if (!entry ) return GF_BAD_PARAM; |
7204 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
7205 | | |
7206 | 0 | GF_RVCConfigurationBox *rvcc = (GF_RVCConfigurationBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_RVCC); |
7207 | 0 | if (rvcc && rvcc->rvc_meta_idx) { |
7208 | 0 | gf_isom_remove_meta_item(movie, GF_FALSE, track, rvcc->rvc_meta_idx, GF_FALSE, NULL); |
7209 | 0 | rvcc->rvc_meta_idx = 0; |
7210 | 0 | } |
7211 | |
|
7212 | 0 | if (!rvcc) { |
7213 | 0 | rvcc = (GF_RVCConfigurationBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_RVCC); |
7214 | 0 | if (!rvcc) return GF_OUT_OF_MEM; |
7215 | 0 | } |
7216 | 0 | rvcc->predefined_rvc_config = rvc_predefined; |
7217 | 0 | if (!rvc_predefined) { |
7218 | 0 | u32 it_id=0; |
7219 | 0 | e = gf_isom_set_meta_type(movie, GF_FALSE, track, GF_META_TYPE_RVCI); |
7220 | 0 | if (e) return e; |
7221 | 0 | gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_ISO2, GF_TRUE); |
7222 | 0 | e = gf_isom_add_meta_item_memory(movie, GF_FALSE, track, "rvcconfig.xml", &it_id, GF_META_ITEM_TYPE_MIME, mime, NULL, NULL, data, size, NULL); |
7223 | 0 | if (e) return e; |
7224 | 0 | rvcc->rvc_meta_idx = gf_isom_get_meta_item_count(movie, GF_FALSE, track); |
7225 | 0 | } |
7226 | 0 | return GF_OK; |
7227 | 0 | } |
7228 | | |
7229 | | /*for now not exported*/ |
7230 | | /*expands sampleGroup table for the given grouping type and sample_number. If sample_number is 0, just appends an entry at the end of the table*/ |
7231 | | static GF_Err gf_isom_add_sample_group_entry(GF_List *sampleGroups, u32 sample_number, GF_SampleGroupDescriptionBox *sgdesc, u32 grouping_type_parameter, u32 sampleGroupDescriptionIndex, GF_List *parent, GF_SampleTableBox *stbl) |
7232 | 0 | { |
7233 | 0 | GF_SampleGroupBox *sgroup = NULL; |
7234 | 0 | u32 i, count, last_sample_in_entry; |
7235 | 0 | Bool all_samples = GF_FALSE; |
7236 | 0 | gf_assert(sampleGroups); |
7237 | 0 | if (!sgdesc) return GF_BAD_PARAM; |
7238 | 0 | count = gf_list_count(sampleGroups); |
7239 | 0 | for (i=0; i<count; i++) { |
7240 | 0 | sgroup = (GF_SampleGroupBox*)gf_list_get(sampleGroups, i); |
7241 | 0 | if (sgroup->grouping_type==sgdesc->grouping_type) break; |
7242 | 0 | sgroup = NULL; |
7243 | 0 | } |
7244 | 0 | if (!sgroup) { |
7245 | 0 | sgroup = (GF_SampleGroupBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SBGP); |
7246 | 0 | if (!sgroup) return GF_OUT_OF_MEM; |
7247 | 0 | sgroup->grouping_type = sgdesc->grouping_type; |
7248 | 0 | sgroup->grouping_type_parameter = grouping_type_parameter; |
7249 | | // gf_list_add(sampleGroups, sgroup); |
7250 | | //crude patch to align old arch and filters |
7251 | 0 | gf_list_insert(sampleGroups, sgroup, 0); |
7252 | 0 | gf_assert(parent); |
7253 | 0 | gf_list_add(parent, sgroup); |
7254 | 0 | } |
7255 | 0 | u32 def_insert_value = (sgdesc && (sgdesc->version==2)) ? sgdesc->default_description_index : 0; |
7256 | | |
7257 | | /*used in fragments, means we are adding the last sample*/ |
7258 | 0 | if (!sample_number) { |
7259 | 0 | sample_number = 1; |
7260 | 0 | if (sgroup->entry_count) { |
7261 | 0 | for (i=0; i<sgroup->entry_count; i++) { |
7262 | 0 | sample_number += sgroup->sample_entries[i].sample_count; |
7263 | 0 | } |
7264 | 0 | } |
7265 | 0 | } else if (sample_number==(u32) -1) { |
7266 | 0 | all_samples = GF_TRUE; |
7267 | 0 | sample_number = 1; |
7268 | 0 | } |
7269 | |
|
7270 | 0 | if (!sgroup->entry_count) { |
7271 | 0 | u32 idx = 0; |
7272 | 0 | sgroup->entry_count = (sample_number>1) ? 2 : 1; |
7273 | 0 | sgroup->sample_entries = (GF_SampleGroupEntry*)gf_malloc(sizeof(GF_SampleGroupEntry) * sgroup->entry_count ); |
7274 | 0 | if (!sgroup->sample_entries) return GF_OUT_OF_MEM; |
7275 | 0 | if (sample_number>1) { |
7276 | 0 | sgroup->sample_entries[0].sample_count = sample_number-1; |
7277 | 0 | sgroup->sample_entries[0].group_description_index = sampleGroupDescriptionIndex ? def_insert_value : 1; |
7278 | 0 | idx = 1; |
7279 | 0 | } |
7280 | 0 | sgroup->sample_entries[idx].sample_count = 1; |
7281 | 0 | sgroup->sample_entries[idx].group_description_index = sampleGroupDescriptionIndex; |
7282 | 0 | if (all_samples && stbl) { |
7283 | 0 | sgroup->sample_entries[idx].sample_count = stbl->SampleSize->sampleCount; |
7284 | 0 | } |
7285 | 0 | return GF_OK; |
7286 | 0 | } |
7287 | 0 | if (all_samples && stbl) { |
7288 | 0 | sgroup->entry_count = 1; |
7289 | 0 | sgroup->sample_entries[0].group_description_index = sampleGroupDescriptionIndex; |
7290 | 0 | sgroup->sample_entries[0].sample_count = stbl->SampleSize->sampleCount; |
7291 | 0 | return GF_OK; |
7292 | 0 | } |
7293 | 0 | last_sample_in_entry = 0; |
7294 | 0 | for (i=0; i<sgroup->entry_count; i++) { |
7295 | | /*TODO*/ |
7296 | 0 | if (last_sample_in_entry + sgroup->sample_entries[i].sample_count > sample_number) |
7297 | 0 | return GF_NOT_SUPPORTED; |
7298 | 0 | last_sample_in_entry += sgroup->sample_entries[i].sample_count; |
7299 | 0 | } |
7300 | | |
7301 | 0 | if (last_sample_in_entry == sample_number) { |
7302 | 0 | if (sgroup->sample_entries[sgroup->entry_count-1].group_description_index==sampleGroupDescriptionIndex) |
7303 | 0 | return GF_OK; |
7304 | 0 | else |
7305 | 0 | return GF_NOT_SUPPORTED; |
7306 | 0 | } |
7307 | | |
7308 | 0 | if ((sgroup->sample_entries[sgroup->entry_count-1].group_description_index==sampleGroupDescriptionIndex) && (last_sample_in_entry+1==sample_number)) { |
7309 | 0 | sgroup->sample_entries[sgroup->entry_count-1].sample_count++; |
7310 | 0 | return GF_OK; |
7311 | 0 | } |
7312 | | /*last entry was an empty desc (no group associated), just add the number of samples missing until new one, then add new one*/ |
7313 | 0 | if (! sgroup->sample_entries[sgroup->entry_count-1].group_description_index) { |
7314 | 0 | sgroup->sample_entries[sgroup->entry_count-1].sample_count += sample_number - 1 - last_sample_in_entry; |
7315 | 0 | sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 1) ); |
7316 | 0 | sgroup->sample_entries[sgroup->entry_count].sample_count = 1; |
7317 | 0 | sgroup->sample_entries[sgroup->entry_count].group_description_index = sampleGroupDescriptionIndex; |
7318 | 0 | sgroup->entry_count++; |
7319 | 0 | return GF_OK; |
7320 | 0 | } |
7321 | | /*we are adding a sample with no desc, add entry at the end*/ |
7322 | 0 | if (!sampleGroupDescriptionIndex || (sample_number - 1 - last_sample_in_entry==0) ) { |
7323 | 0 | sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 1) ); |
7324 | 0 | sgroup->sample_entries[sgroup->entry_count].sample_count = 1; |
7325 | 0 | sgroup->sample_entries[sgroup->entry_count].group_description_index = sampleGroupDescriptionIndex; |
7326 | 0 | sgroup->entry_count++; |
7327 | 0 | return GF_OK; |
7328 | 0 | } |
7329 | | /*need to insert two entries ...*/ |
7330 | 0 | sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 2) ); |
7331 | |
|
7332 | 0 | sgroup->sample_entries[sgroup->entry_count].sample_count = sample_number - 1 - last_sample_in_entry; |
7333 | 0 | sgroup->sample_entries[sgroup->entry_count].group_description_index = def_insert_value; |
7334 | |
|
7335 | 0 | sgroup->sample_entries[sgroup->entry_count+1].sample_count = 1; |
7336 | 0 | sgroup->sample_entries[sgroup->entry_count+1].group_description_index = sampleGroupDescriptionIndex; |
7337 | 0 | sgroup->entry_count+=2; |
7338 | 0 | return GF_OK; |
7339 | 0 | } |
7340 | | |
7341 | | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7342 | | static GF_SampleGroupDescriptionBox *get_sgdp(GF_SampleTableBox *stbl, GF_TrackFragmentBox *traf, u32 grouping_type, Bool *is_traf_sgdp) |
7343 | | #else |
7344 | | static GF_SampleGroupDescriptionBox *get_sgdp(GF_SampleTableBox *stbl, void *traf, u32 grouping_type, Bool *is_traf_sgdp) |
7345 | | #endif /* GPAC_DISABLE_ISOM_FRAGMENTS */ |
7346 | 0 | { |
7347 | 0 | GF_List *groupList=NULL; |
7348 | 0 | GF_List **parent=NULL; |
7349 | 0 | GF_SampleGroupDescriptionBox *sgdesc=NULL; |
7350 | 0 | u32 count, i; |
7351 | |
|
7352 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7353 | 0 | if (!stbl && traf && traf->trex->track->Media->information->sampleTable) |
7354 | 0 | stbl = traf->trex->track->Media->information->sampleTable; |
7355 | 0 | #endif |
7356 | 0 | if (stbl) { |
7357 | 0 | if (!stbl->sampleGroupsDescription && !traf) |
7358 | 0 | stbl->sampleGroupsDescription = gf_list_new(); |
7359 | |
|
7360 | 0 | groupList = stbl->sampleGroupsDescription; |
7361 | 0 | if (is_traf_sgdp) *is_traf_sgdp = GF_FALSE; |
7362 | 0 | parent = &stbl->child_boxes; |
7363 | |
|
7364 | 0 | count = gf_list_count(groupList); |
7365 | 0 | for (i=0; i<count; i++) { |
7366 | 0 | sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(groupList, i); |
7367 | 0 | if (sgdesc->grouping_type==grouping_type) break; |
7368 | 0 | sgdesc = NULL; |
7369 | 0 | } |
7370 | 0 | } |
7371 | |
|
7372 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7373 | | /*look in stbl or traf for sample sampleGroupsDescription*/ |
7374 | 0 | if (!sgdesc && traf) { |
7375 | 0 | if (!traf->sampleGroupsDescription) |
7376 | 0 | traf->sampleGroupsDescription = gf_list_new(); |
7377 | 0 | groupList = traf->sampleGroupsDescription; |
7378 | 0 | parent = &traf->child_boxes; |
7379 | 0 | if (is_traf_sgdp) *is_traf_sgdp = GF_TRUE; |
7380 | |
|
7381 | 0 | count = gf_list_count(groupList); |
7382 | 0 | for (i=0; i<count; i++) { |
7383 | 0 | sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(groupList, i); |
7384 | 0 | if (sgdesc->grouping_type==grouping_type) break; |
7385 | 0 | sgdesc = NULL; |
7386 | 0 | } |
7387 | 0 | } |
7388 | 0 | #endif |
7389 | |
|
7390 | 0 | if (!sgdesc) { |
7391 | 0 | sgdesc = (GF_SampleGroupDescriptionBox *) gf_isom_box_new_parent(parent, GF_ISOM_BOX_TYPE_SGPD); |
7392 | 0 | if (!sgdesc) return NULL; |
7393 | 0 | sgdesc->grouping_type = grouping_type; |
7394 | 0 | gf_assert(groupList); |
7395 | 0 | gf_list_add(groupList, sgdesc); |
7396 | 0 | } |
7397 | 0 | return sgdesc; |
7398 | 0 | } |
7399 | | |
7400 | | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7401 | | static GF_Err gf_isom_set_sample_group_info_ex(GF_SampleTableBox *stbl, GF_TrackFragmentBox *traf, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *udta, void *(*sg_create_entry)(void *udta), Bool (*sg_compare_entry)(void *udta, void *entry)) |
7402 | | #else |
7403 | | static GF_Err gf_isom_set_sample_group_info_ex(GF_SampleTableBox *stbl, void *traf, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *udta, void *(*sg_create_entry)(void *udta), Bool (*sg_compare_entry)(void *udta, void *entry)) |
7404 | | #endif /* GPAC_DISABLE_ISOM_FRAGMENTS */ |
7405 | 0 | { |
7406 | 0 | GF_List *groupList, *parent; |
7407 | 0 | void *entry; |
7408 | 0 | Bool is_traf_sgpd; |
7409 | 0 | GF_SampleGroupDescriptionBox *sgdesc = NULL; |
7410 | 0 | u32 i, entry_idx; |
7411 | |
|
7412 | 0 | if (!stbl && !traf) return GF_BAD_PARAM; |
7413 | | |
7414 | 0 | sgdesc = get_sgdp(stbl, traf, grouping_type, &is_traf_sgpd); |
7415 | 0 | if (!sgdesc) return GF_OUT_OF_MEM; |
7416 | | |
7417 | 0 | entry = NULL; |
7418 | 0 | if (sg_compare_entry) { |
7419 | 0 | for (i=0; i<gf_list_count(sgdesc->group_descriptions); i++) { |
7420 | 0 | entry = gf_list_get(sgdesc->group_descriptions, i); |
7421 | 0 | if (sg_compare_entry(udta, entry)) break; |
7422 | 0 | entry = NULL; |
7423 | 0 | } |
7424 | 0 | } |
7425 | 0 | if (!entry && sg_create_entry) { |
7426 | 0 | entry = sg_create_entry(udta); |
7427 | 0 | if (!entry) return GF_IO_ERR; |
7428 | 0 | if (traf && !is_traf_sgpd) { |
7429 | 0 | sgdesc = get_sgdp(NULL, traf, grouping_type, &is_traf_sgpd); |
7430 | 0 | } |
7431 | 0 | gf_list_add(sgdesc->group_descriptions, entry); |
7432 | 0 | } |
7433 | 0 | if (!entry) |
7434 | 0 | entry_idx = 0; |
7435 | 0 | else |
7436 | 0 | entry_idx = 1 + gf_list_find(sgdesc->group_descriptions, entry); |
7437 | | |
7438 | | /*look in stbl or traf for sample sampleGroups*/ |
7439 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7440 | 0 | if (traf) { |
7441 | 0 | if (!traf->sampleGroups) |
7442 | 0 | traf->sampleGroups = gf_list_new(); |
7443 | 0 | groupList = traf->sampleGroups; |
7444 | 0 | parent = traf->child_boxes; |
7445 | 0 | if (entry_idx && is_traf_sgpd) |
7446 | 0 | entry_idx |= 0x10000; |
7447 | 0 | } else |
7448 | 0 | #endif |
7449 | 0 | { |
7450 | 0 | if (!stbl->sampleGroups) |
7451 | 0 | stbl->sampleGroups = gf_list_new(); |
7452 | 0 | groupList = stbl->sampleGroups; |
7453 | 0 | parent = stbl->child_boxes; |
7454 | 0 | } |
7455 | |
|
7456 | 0 | return gf_isom_add_sample_group_entry(groupList, sample_number, sgdesc, grouping_type_parameter, entry_idx, parent, stbl); |
7457 | 0 | } |
7458 | | |
7459 | | static GF_Err gf_isom_set_sample_group_info_internal(GF_ISOFile *movie, u32 track, u32 trafID, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *udta, void *(*sg_create_entry)(void *udta), Bool (*sg_compare_entry)(void *udta, void *entry)) |
7460 | 0 | { |
7461 | 0 | GF_Err e; |
7462 | 0 | GF_TrackBox *trak=NULL; |
7463 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7464 | 0 | GF_TrackFragmentBox *traf=NULL; |
7465 | 0 | #endif |
7466 | 0 | if (!trafID && (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) { |
7467 | 0 | trak = gf_isom_get_track_box(movie, track); |
7468 | 0 | if (!trak) return GF_BAD_PARAM; |
7469 | 0 | trafID = trak->Header->trackID; |
7470 | 0 | } |
7471 | | |
7472 | 0 | if (trafID) { |
7473 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7474 | 0 | if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) |
7475 | 0 | return GF_BAD_PARAM; |
7476 | | |
7477 | 0 | traf = gf_isom_get_traf(movie, trafID); |
7478 | | #else |
7479 | | return GF_NOT_SUPPORTED; |
7480 | | #endif |
7481 | |
|
7482 | 0 | } else if (track) { |
7483 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7484 | 0 | if (e) return e; |
7485 | | |
7486 | 0 | trak = gf_isom_get_track_box(movie, track); |
7487 | 0 | if (!trak) return GF_BAD_PARAM; |
7488 | 0 | } |
7489 | | |
7490 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7491 | 0 | return gf_isom_set_sample_group_info_ex(trak ? trak->Media->information->sampleTable : NULL, traf, sample_number, grouping_type, grouping_type_parameter, udta, sg_create_entry, sg_compare_entry); |
7492 | | #else |
7493 | | return gf_isom_set_sample_group_info_ex(trak->Media->information->sampleTable, NULL, sample_number, grouping_type, grouping_type_parameter, udta, sg_create_entry, sg_compare_entry); |
7494 | | #endif |
7495 | |
|
7496 | 0 | } |
7497 | | |
7498 | | void *sgpd_parse_entry(GF_SampleGroupDescriptionBox *p, GF_BitStream *bs, s32 bytes_in_box, u32 entry_size, u32 *total_bytes); |
7499 | | |
7500 | | GF_Err gf_isom_add_sample_group_info_internal(GF_ISOFile *movie, u32 track, u32 grouping_type, void *data, u32 data_size, u32 sgpd_flags, u32 *sampleGroupDescriptionIndex, Bool *is_traf_sgpd, Bool check_access, Bool *use_default, GF_SampleGroupDescriptionBox **out_sgdesc) |
7501 | 0 | { |
7502 | 0 | GF_Err e; |
7503 | 0 | GF_TrackBox *trak=NULL; |
7504 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7505 | 0 | GF_TrackFragmentBox *traf=NULL; |
7506 | | #else |
7507 | | void *traf=NULL; |
7508 | | #endif |
7509 | 0 | u32 trafID=0; |
7510 | 0 | GF_DefaultSampleGroupDescriptionEntry *entry=NULL; |
7511 | 0 | GF_SampleGroupDescriptionBox *sgdesc = NULL; |
7512 | 0 | Bool is_default = sgpd_flags & 0x80000000; |
7513 | |
|
7514 | 0 | if (sampleGroupDescriptionIndex) *sampleGroupDescriptionIndex = 0; |
7515 | |
|
7516 | 0 | if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) { |
7517 | 0 | trak = gf_isom_get_track_box(movie, track); |
7518 | 0 | if (!trak) return GF_BAD_PARAM; |
7519 | 0 | trafID = trak->Header->trackID; |
7520 | 0 | } |
7521 | | |
7522 | 0 | if (trafID) { |
7523 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7524 | 0 | if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) |
7525 | 0 | return GF_BAD_PARAM; |
7526 | | |
7527 | 0 | traf = gf_isom_get_traf(movie, trafID); |
7528 | | #else |
7529 | | return GF_NOT_SUPPORTED; |
7530 | | #endif |
7531 | 0 | } else { |
7532 | 0 | if (check_access) { |
7533 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7534 | 0 | if (e) return e; |
7535 | 0 | } |
7536 | | |
7537 | 0 | trak = gf_isom_get_track_box(movie, track); |
7538 | 0 | } |
7539 | 0 | if (!trak) return GF_BAD_PARAM; |
7540 | | |
7541 | | //get sample group desc for this grouping type |
7542 | 0 | sgdesc = get_sgdp(trak->Media->information->sampleTable, traf, grouping_type, is_traf_sgpd); |
7543 | 0 | if (!sgdesc) return GF_OUT_OF_MEM; |
7544 | | //first time we create the sample group description, set flags |
7545 | 0 | if (!gf_list_count(sgdesc->group_descriptions) && !traf) { |
7546 | 0 | if (sgpd_flags&1) sgdesc->flags |= 1; |
7547 | 0 | if (sgpd_flags&2) sgdesc->flags |= 2; |
7548 | 0 | if (sgpd_flags&0x40000000) sgdesc->version=3; |
7549 | 0 | } |
7550 | | |
7551 | |
|
7552 | 0 | GF_BitStream *bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); |
7553 | 0 | u32 bytes; |
7554 | 0 | entry = sgpd_parse_entry(sgdesc, bs, data_size, data_size, &bytes); |
7555 | 0 | gf_bs_del(bs); |
7556 | 0 | if (!entry) return GF_NON_COMPLIANT_BITSTREAM; |
7557 | | |
7558 | 0 | if (out_sgdesc) *out_sgdesc = sgdesc; |
7559 | | |
7560 | | |
7561 | | //find the same entry |
7562 | 0 | u32 k; |
7563 | 0 | for (k=0; k<gf_list_count(sgdesc->group_descriptions); k++) { |
7564 | 0 | void *sgde_dst = gf_list_get(sgdesc->group_descriptions, k); |
7565 | 0 | if (gf_isom_is_identical_sgpd(entry, sgde_dst, sgdesc->grouping_type)) { |
7566 | 0 | if (sampleGroupDescriptionIndex) *sampleGroupDescriptionIndex = k+1; |
7567 | 0 | sgpd_del_entry(sgdesc->grouping_type, entry, sgdesc->is_opaque); |
7568 | 0 | if (use_default) { |
7569 | 0 | u32 idx = k+1; |
7570 | 0 | if (is_traf_sgpd && *is_traf_sgpd) idx |= 0x10000; |
7571 | 0 | *use_default = (sgdesc->default_description_index==idx) ? GF_TRUE : GF_FALSE; |
7572 | 0 | } |
7573 | 0 | return GF_OK; |
7574 | 0 | } |
7575 | 0 | } |
7576 | | |
7577 | 0 | if (traf && ! *is_traf_sgpd) { |
7578 | 0 | sgdesc = get_sgdp(NULL, traf, grouping_type, is_traf_sgpd); |
7579 | 0 | if (!sgdesc) return GF_OUT_OF_MEM; |
7580 | 0 | } |
7581 | 0 | if (out_sgdesc) *out_sgdesc = sgdesc; |
7582 | |
|
7583 | 0 | e = gf_list_add(sgdesc->group_descriptions, entry); |
7584 | 0 | if (e) { |
7585 | 0 | sgpd_del_entry(sgdesc->grouping_type, entry, sgdesc->is_opaque); |
7586 | 0 | return e; |
7587 | 0 | } |
7588 | | |
7589 | | #if 0 |
7590 | | if (grouping_type==GF_ISOM_SAMPLE_GROUP_OINF) { |
7591 | | GF_OperatingPointsInformation *ptr = gf_isom_oinf_new_entry(); |
7592 | | GF_BitStream *bs=gf_bs_new(data, data_size, GF_BITSTREAM_READ); |
7593 | | e = gf_isom_oinf_read_entry(ptr, bs); |
7594 | | gf_bs_del(bs); |
7595 | | if (e) { |
7596 | | gf_isom_oinf_del_entry(ptr); |
7597 | | return e; |
7598 | | } |
7599 | | //not in track, create new sgdp |
7600 | | if (traf && ! *is_traf_sgpd) { |
7601 | | sgdesc = get_sgdp(NULL, traf, grouping_type, is_traf_sgpd); |
7602 | | if (!sgdesc) return GF_OUT_OF_MEM; |
7603 | | } |
7604 | | e = gf_list_add(sgdesc->group_descriptions, ptr); |
7605 | | if (e) return e; |
7606 | | entry = (GF_DefaultSampleGroupDescriptionEntry *) ptr; |
7607 | | } else if (grouping_type==GF_ISOM_SAMPLE_GROUP_LINF) { |
7608 | | GF_LHVCLayerInformation *ptr = gf_isom_linf_new_entry(); |
7609 | | GF_BitStream *bs=gf_bs_new(data, data_size, GF_BITSTREAM_READ); |
7610 | | e = gf_isom_linf_read_entry(ptr, bs); |
7611 | | gf_bs_del(bs); |
7612 | | if (e) { |
7613 | | gf_isom_linf_del_entry(ptr); |
7614 | | return e; |
7615 | | } |
7616 | | |
7617 | | if (traf && ! *is_traf_sgpd) { |
7618 | | sgdesc = get_sgdp(NULL, traf, grouping_type, is_traf_sgpd); |
7619 | | if (!sgdesc) return GF_OUT_OF_MEM; |
7620 | | } |
7621 | | |
7622 | | e = gf_list_add(sgdesc->group_descriptions, ptr); |
7623 | | if (e) return e; |
7624 | | entry = (GF_DefaultSampleGroupDescriptionEntry *) ptr; |
7625 | | } else { |
7626 | | u32 i, count=gf_list_count(sgdesc->group_descriptions); |
7627 | | for (i=0; i<count; i++) { |
7628 | | GF_DefaultSampleGroupDescriptionEntry *ent = gf_list_get(sgdesc->group_descriptions, i); |
7629 | | if ((ent->length == data_size) && !memcmp(ent->data, data, data_size)) { |
7630 | | entry = ent; |
7631 | | break; |
7632 | | } |
7633 | | entry=NULL; |
7634 | | } |
7635 | | if (!entry) { |
7636 | | GF_SAFEALLOC(entry, GF_DefaultSampleGroupDescriptionEntry); |
7637 | | if (!entry) return GF_OUT_OF_MEM; |
7638 | | entry->data = (u8*)gf_malloc(sizeof(char) * data_size); |
7639 | | if (!entry->data) { |
7640 | | gf_free(entry); |
7641 | | return GF_OUT_OF_MEM; |
7642 | | } |
7643 | | entry->length = data_size; |
7644 | | memcpy(entry->data, data, sizeof(char) * data_size); |
7645 | | |
7646 | | if (traf && ! *is_traf_sgpd) { |
7647 | | sgdesc = get_sgdp(NULL, traf, grouping_type, is_traf_sgpd); |
7648 | | if (!sgdesc) return GF_OUT_OF_MEM; |
7649 | | } |
7650 | | |
7651 | | e = gf_list_add(sgdesc->group_descriptions, entry); |
7652 | | if (e) { |
7653 | | gf_free(entry->data); |
7654 | | gf_free(entry); |
7655 | | return e; |
7656 | | } |
7657 | | } |
7658 | | } |
7659 | | #endif |
7660 | | |
7661 | | |
7662 | 0 | if (is_default && !sgdesc->default_description_index) { |
7663 | 0 | sgdesc->default_description_index = 1 + gf_list_find(sgdesc->group_descriptions, entry); |
7664 | 0 | if (sgdesc->version < 2) sgdesc->version = 2; |
7665 | 0 | if (is_traf_sgpd && *is_traf_sgpd) { |
7666 | 0 | sgdesc->default_description_index |= 0x10000; |
7667 | 0 | } |
7668 | 0 | } |
7669 | 0 | u32 grp_idx = 1 + gf_list_find(sgdesc->group_descriptions, entry); |
7670 | 0 | if (sampleGroupDescriptionIndex) *sampleGroupDescriptionIndex = grp_idx; |
7671 | 0 | if (use_default) { |
7672 | 0 | if (*is_traf_sgpd) |
7673 | 0 | grp_idx |= 0x10000; |
7674 | 0 | *use_default = (sgdesc->default_description_index==grp_idx) ? GF_TRUE : GF_FALSE; |
7675 | 0 | } |
7676 | 0 | return GF_OK; |
7677 | 0 | } |
7678 | | GF_EXPORT |
7679 | | GF_Err gf_isom_add_sample_group_info(GF_ISOFile *movie, u32 track, u32 grouping_type, void *data, u32 data_size, Bool is_default, u32 *sampleGroupDescriptionIndex) |
7680 | 0 | { |
7681 | 0 | return gf_isom_add_sample_group_info_internal(movie, track, grouping_type, data, data_size, is_default ? 0x80000000 : 0, sampleGroupDescriptionIndex, NULL, GF_TRUE, NULL, NULL); |
7682 | 0 | } |
7683 | | |
7684 | | GF_Err gf_isom_set_sample_group_description_internal(GF_ISOFile *movie, u32 track, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *data, u32 data_size, Bool check_access, u32 sgpd_flags) |
7685 | 0 | { |
7686 | 0 | u32 sampleGroupDescriptionIndex, trafID=0; |
7687 | 0 | GF_Err e; |
7688 | 0 | GF_SampleGroupDescriptionBox *sgdesc=NULL; |
7689 | 0 | Bool is_traf_sgpd, use_default=GF_FALSE; |
7690 | 0 | GF_List *groupList=NULL, *parent=NULL; |
7691 | |
|
7692 | 0 | e = gf_isom_add_sample_group_info_internal(movie, track, grouping_type, data, data_size, sgpd_flags, &sampleGroupDescriptionIndex, &is_traf_sgpd, check_access, &use_default, &sgdesc); |
7693 | 0 | if (e) return e; |
7694 | 0 | if (use_default) return GF_OK; |
7695 | | |
7696 | 0 | GF_SampleTableBox *stbl=NULL; |
7697 | 0 | GF_TrackBox *trak=NULL; |
7698 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7699 | 0 | GF_TrackFragmentBox *traf=NULL; |
7700 | 0 | #endif |
7701 | 0 | if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) { |
7702 | 0 | trak = gf_isom_get_track_box(movie, track); |
7703 | 0 | if (!trak) return GF_BAD_PARAM; |
7704 | 0 | trafID = trak->Header->trackID; |
7705 | 0 | } |
7706 | | |
7707 | 0 | if (trafID) { |
7708 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7709 | 0 | if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) |
7710 | 0 | return GF_BAD_PARAM; |
7711 | | |
7712 | 0 | traf = gf_isom_get_traf(movie, trafID); |
7713 | | #else |
7714 | | return GF_NOT_SUPPORTED; |
7715 | | #endif |
7716 | |
|
7717 | 0 | } else if (track) { |
7718 | 0 | if (check_access) { |
7719 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7720 | 0 | if (e) return e; |
7721 | 0 | } |
7722 | | |
7723 | 0 | trak = gf_isom_get_track_box(movie, track); |
7724 | 0 | if (!trak) return GF_BAD_PARAM; |
7725 | 0 | } |
7726 | | |
7727 | | /*look in stbl or traf for sample sampleGroups*/ |
7728 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7729 | 0 | if (traf) { |
7730 | 0 | if (!traf->sampleGroups) |
7731 | 0 | traf->sampleGroups = gf_list_new(); |
7732 | 0 | groupList = traf->sampleGroups; |
7733 | 0 | parent = traf->child_boxes; |
7734 | 0 | if (sampleGroupDescriptionIndex && is_traf_sgpd) |
7735 | 0 | sampleGroupDescriptionIndex |= 0x10000; |
7736 | 0 | } else |
7737 | 0 | #endif |
7738 | 0 | { |
7739 | 0 | stbl = trak->Media->information->sampleTable; |
7740 | 0 | if (!stbl->sampleGroups) |
7741 | 0 | stbl->sampleGroups = gf_list_new(); |
7742 | 0 | groupList = stbl->sampleGroups; |
7743 | 0 | parent = stbl->child_boxes; |
7744 | 0 | } |
7745 | |
|
7746 | 0 | return gf_isom_add_sample_group_entry(groupList, sample_number, sgdesc, grouping_type_parameter, sampleGroupDescriptionIndex, parent, stbl); |
7747 | |
|
7748 | 0 | } |
7749 | | |
7750 | | GF_Err gf_isom_set_sample_group_description(GF_ISOFile *movie, u32 track, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *data, u32 data_size, u32 sgpd_flags) |
7751 | 0 | { |
7752 | 0 | return gf_isom_set_sample_group_description_internal(movie, track, sample_number, grouping_type, grouping_type_parameter, data, data_size, GF_TRUE, sgpd_flags); |
7753 | 0 | } |
7754 | | |
7755 | | GF_EXPORT |
7756 | | GF_Err gf_isom_remove_sample_group(GF_ISOFile *movie, u32 track, u32 grouping_type) |
7757 | 0 | { |
7758 | 0 | GF_Err e; |
7759 | 0 | GF_TrackBox *trak; |
7760 | 0 | u32 count, i; |
7761 | |
|
7762 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7763 | 0 | if (e) return e; |
7764 | | |
7765 | 0 | trak = gf_isom_get_track_box(movie, track); |
7766 | 0 | if (!trak) return GF_BAD_PARAM; |
7767 | | |
7768 | 0 | if (trak->Media->information->sampleTable->sampleGroupsDescription) { |
7769 | 0 | count = gf_list_count(trak->Media->information->sampleTable->sampleGroupsDescription); |
7770 | 0 | for (i=0; i<count; i++) { |
7771 | 0 | GF_SampleGroupDescriptionBox *sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(trak->Media->information->sampleTable->sampleGroupsDescription, i); |
7772 | 0 | if (sgdesc->grouping_type==grouping_type) { |
7773 | 0 | gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box*)sgdesc); |
7774 | 0 | gf_list_rem(trak->Media->information->sampleTable->sampleGroupsDescription, i); |
7775 | 0 | i--; |
7776 | 0 | count--; |
7777 | 0 | } |
7778 | 0 | } |
7779 | 0 | } |
7780 | 0 | if (trak->Media->information->sampleTable->sampleGroups) { |
7781 | 0 | count = gf_list_count(trak->Media->information->sampleTable->sampleGroups); |
7782 | 0 | for (i=0; i<count; i++) { |
7783 | 0 | GF_SampleGroupBox *sgroup = gf_list_get(trak->Media->information->sampleTable->sampleGroups, i); |
7784 | 0 | if (sgroup->grouping_type==grouping_type) { |
7785 | 0 | gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box*)sgroup); |
7786 | 0 | gf_list_rem(trak->Media->information->sampleTable->sampleGroups, i); |
7787 | 0 | i--; |
7788 | 0 | count--; |
7789 | 0 | } |
7790 | 0 | } |
7791 | 0 | } |
7792 | 0 | return GF_OK; |
7793 | 0 | } |
7794 | | |
7795 | | GF_EXPORT |
7796 | | GF_Err gf_isom_add_sample_info(GF_ISOFile *movie, u32 track, u32 sample_number, u32 grouping_type, u32 sampleGroupDescriptionIndex, u32 grouping_type_parameter) |
7797 | 0 | { |
7798 | 0 | GF_Err e; |
7799 | 0 | GF_TrackBox *trak; |
7800 | 0 | GF_List *groupList; |
7801 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7802 | 0 | if (e) return e; |
7803 | | |
7804 | 0 | trak = gf_isom_get_track_box(movie, track); |
7805 | 0 | if (!trak) return GF_BAD_PARAM; |
7806 | | |
7807 | 0 | if (!trak->Media->information->sampleTable->sampleGroups) |
7808 | 0 | trak->Media->information->sampleTable->sampleGroups = gf_list_new(); |
7809 | | |
7810 | |
|
7811 | 0 | GF_SampleGroupDescriptionBox *sgdesc = get_sgdp(trak->Media->information->sampleTable, NULL, grouping_type, NULL); |
7812 | 0 | if (!sgdesc) return GF_BAD_PARAM; |
7813 | | |
7814 | 0 | groupList = trak->Media->information->sampleTable->sampleGroups; |
7815 | 0 | return gf_isom_add_sample_group_entry(groupList, sample_number, sgdesc, grouping_type_parameter, sampleGroupDescriptionIndex, trak->Media->information->sampleTable->child_boxes, trak->Media->information->sampleTable); |
7816 | 0 | } |
7817 | | |
7818 | | |
7819 | | void *sg_rap_create_entry(void *udta) |
7820 | 0 | { |
7821 | 0 | GF_VisualRandomAccessEntry *entry; |
7822 | 0 | u32 *num_leading_samples = (u32 *) udta; |
7823 | 0 | gf_assert(udta); |
7824 | 0 | GF_SAFEALLOC(entry, GF_VisualRandomAccessEntry); |
7825 | 0 | if (!entry) return NULL; |
7826 | 0 | entry->num_leading_samples = *num_leading_samples; |
7827 | 0 | entry->num_leading_samples_known = entry->num_leading_samples ? 1 : 0; |
7828 | 0 | return entry; |
7829 | 0 | } |
7830 | | |
7831 | | Bool sg_rap_compare_entry(void *udta, void *entry) |
7832 | 0 | { |
7833 | 0 | u32 *num_leading_samples = (u32 *) udta; |
7834 | 0 | if (*num_leading_samples == ((GF_VisualRandomAccessEntry *)entry)->num_leading_samples) return GF_TRUE; |
7835 | 0 | return GF_FALSE; |
7836 | 0 | } |
7837 | | |
7838 | | GF_EXPORT |
7839 | | GF_Err gf_isom_set_sample_rap_group(GF_ISOFile *movie, u32 track, u32 sample_number, Bool is_rap, u32 num_leading_samples) |
7840 | 0 | { |
7841 | 0 | return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_RAP, 0, &num_leading_samples, is_rap ? sg_rap_create_entry : NULL, is_rap ? sg_rap_compare_entry : NULL); |
7842 | 0 | } |
7843 | | |
7844 | | GF_Err gf_isom_fragment_set_sample_rap_group(GF_ISOFile *movie, GF_ISOTrackID trackID, u32 sample_number_in_frag, Bool is_rap, u32 num_leading_samples) |
7845 | 0 | { |
7846 | 0 | return gf_isom_set_sample_group_info_internal(movie, 0, trackID, sample_number_in_frag, GF_ISOM_SAMPLE_GROUP_RAP, 0, &num_leading_samples, is_rap ? sg_rap_create_entry : NULL, is_rap ? sg_rap_compare_entry : NULL); |
7847 | 0 | } |
7848 | | |
7849 | | |
7850 | | void *sg_av1s_create_entry(void *udta) |
7851 | 0 | { |
7852 | 0 | GF_AV1SwitchingEntry *entry; |
7853 | 0 | GF_SAFEALLOC(entry, GF_AV1SwitchingEntry); |
7854 | 0 | if (!entry) return NULL; |
7855 | 0 | return entry; |
7856 | 0 | } |
7857 | | |
7858 | | Bool sg_av1s_compare_entry(void *udta, void *entry) |
7859 | 0 | { |
7860 | 0 | return GF_TRUE; |
7861 | 0 | } |
7862 | | |
7863 | | GF_EXPORT |
7864 | | GF_Err gf_isom_set_sample_av1_switch_frame_group(GF_ISOFile *movie, u32 track, u32 sample_number, Bool is_switch_Frame) |
7865 | 0 | { |
7866 | 0 | return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_AV1S, 0, NULL, is_switch_Frame ? sg_av1s_create_entry : NULL, is_switch_Frame ? sg_av1s_compare_entry : NULL); |
7867 | 0 | } |
7868 | | |
7869 | | GF_Err gf_isom_fragment_set_sample_av1_switch_frame_group(GF_ISOFile *movie, GF_ISOTrackID trackID, u32 sample_number_in_frag, Bool is_switch_Frame) |
7870 | 0 | { |
7871 | 0 | return gf_isom_set_sample_group_info_internal(movie, 0, trackID, sample_number_in_frag, GF_ISOM_SAMPLE_GROUP_AV1S, 0, NULL, is_switch_Frame ? sg_av1s_create_entry : NULL, is_switch_Frame ? sg_av1s_compare_entry : NULL); |
7872 | 0 | } |
7873 | | |
7874 | | |
7875 | | |
7876 | | void *sg_roll_create_entry(void *udta) |
7877 | 0 | { |
7878 | 0 | GF_RollRecoveryEntry *entry; |
7879 | 0 | s16 *roll_distance = (s16 *) udta; |
7880 | 0 | GF_SAFEALLOC(entry, GF_RollRecoveryEntry); |
7881 | 0 | if (!entry) return NULL; |
7882 | 0 | entry->roll_distance = *roll_distance; |
7883 | 0 | return entry; |
7884 | 0 | } |
7885 | | |
7886 | | Bool sg_roll_compare_entry(void *udta, void *entry) |
7887 | 0 | { |
7888 | 0 | s16 *roll_distance = (s16 *) udta; |
7889 | 0 | if (*roll_distance == ((GF_RollRecoveryEntry *)entry)->roll_distance) return GF_TRUE; |
7890 | 0 | return GF_FALSE; |
7891 | 0 | } |
7892 | | |
7893 | | GF_EXPORT |
7894 | | GF_Err gf_isom_set_sample_roll_group(GF_ISOFile *movie, u32 track, u32 sample_number, GF_ISOSampleRollType roll_type, s16 roll_distance) |
7895 | 0 | { |
7896 | 0 | u32 grp_type = (roll_type>=GF_ISOM_SAMPLE_PREROLL) ? GF_ISOM_SAMPLE_GROUP_PROL : GF_ISOM_SAMPLE_GROUP_ROLL; |
7897 | 0 | if (roll_type==GF_ISOM_SAMPLE_PREROLL_NONE) |
7898 | 0 | roll_type = 0; |
7899 | |
|
7900 | 0 | return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, grp_type, 0, &roll_distance, roll_type ? sg_roll_create_entry : NULL, roll_type ? sg_roll_compare_entry : NULL); |
7901 | 0 | } |
7902 | | |
7903 | | GF_EXPORT |
7904 | | GF_Err gf_isom_fragment_set_sample_roll_group(GF_ISOFile *movie, GF_ISOTrackID trackID, u32 sample_number_in_frag, GF_ISOSampleRollType roll_type, s16 roll_distance) |
7905 | 0 | { |
7906 | 0 | u32 grp_type = (roll_type>=GF_ISOM_SAMPLE_PREROLL) ? GF_ISOM_SAMPLE_GROUP_PROL : GF_ISOM_SAMPLE_GROUP_ROLL; |
7907 | 0 | if (roll_type==GF_ISOM_SAMPLE_PREROLL_NONE) |
7908 | 0 | roll_type = 0; |
7909 | |
|
7910 | 0 | return gf_isom_set_sample_group_info_internal(movie, 0, trackID, sample_number_in_frag, grp_type, 0, &roll_distance, roll_type ? sg_roll_create_entry : NULL, roll_type ? sg_roll_compare_entry : NULL); |
7911 | 0 | } |
7912 | | |
7913 | | |
7914 | | void *sg_encryption_create_entry(void *udta) |
7915 | 0 | { |
7916 | 0 | GF_CENCSampleEncryptionGroupEntry *entry, *from_entry; |
7917 | 0 | GF_SAFEALLOC(entry, GF_CENCSampleEncryptionGroupEntry); |
7918 | 0 | if (!entry) return NULL; |
7919 | 0 | from_entry = (GF_CENCSampleEncryptionGroupEntry *)udta; |
7920 | 0 | memcpy(entry, from_entry, sizeof(GF_CENCSampleEncryptionGroupEntry) ); |
7921 | 0 | entry->key_info = gf_malloc(sizeof(u8) * entry->key_info_size); |
7922 | 0 | if (!entry->key_info) { |
7923 | 0 | gf_free(entry); |
7924 | 0 | return NULL; |
7925 | 0 | } |
7926 | 0 | memcpy(entry->key_info, from_entry->key_info, entry->key_info_size); |
7927 | 0 | return entry; |
7928 | 0 | } |
7929 | | |
7930 | | Bool sg_encryption_compare_entry(void *udta, void *_entry) |
7931 | 0 | { |
7932 | 0 | GF_CENCSampleEncryptionGroupEntry *entry = (GF_CENCSampleEncryptionGroupEntry *)_entry; |
7933 | 0 | GF_CENCSampleEncryptionGroupEntry *with_entry = (GF_CENCSampleEncryptionGroupEntry *)udta; |
7934 | |
|
7935 | 0 | if (entry->IsProtected != with_entry->IsProtected) return GF_FALSE; |
7936 | 0 | if (entry->skip_byte_block != with_entry->skip_byte_block) return GF_FALSE; |
7937 | 0 | if (entry->crypt_byte_block != with_entry->crypt_byte_block) return GF_FALSE; |
7938 | 0 | if (entry->key_info_size != with_entry->key_info_size) return GF_FALSE; |
7939 | | |
7940 | 0 | if (!memcmp(entry->key_info, with_entry->key_info, with_entry->key_info_size)) |
7941 | 0 | return GF_TRUE; |
7942 | 0 | return GF_FALSE; |
7943 | 0 | } |
7944 | | |
7945 | | |
7946 | | /*sample encryption information group can be in stbl or traf*/ |
7947 | | GF_EXPORT |
7948 | | GF_Err gf_isom_set_sample_cenc_group(GF_ISOFile *movie, u32 track, u32 sample_number, u8 isEncrypted, u32 crypt_byte_block, u32 skip_byte_block, u8 *key_info, u32 key_info_size) |
7949 | 0 | { |
7950 | 0 | GF_CENCSampleEncryptionGroupEntry entry; |
7951 | 0 | if (!key_info || (key_info_size<19)) |
7952 | 0 | return GF_BAD_PARAM; |
7953 | | |
7954 | 0 | memset(&entry, 0, sizeof(GF_CENCSampleEncryptionGroupEntry)); |
7955 | 0 | entry.crypt_byte_block = crypt_byte_block; |
7956 | 0 | entry.skip_byte_block = skip_byte_block; |
7957 | 0 | entry.IsProtected = isEncrypted; |
7958 | 0 | entry.key_info = key_info; |
7959 | 0 | entry.key_info_size = key_info_size; |
7960 | |
|
7961 | 0 | return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_SEIG, 0, &entry, sg_encryption_create_entry, sg_encryption_compare_entry); |
7962 | 0 | } |
7963 | | |
7964 | | |
7965 | | |
7966 | | GF_EXPORT |
7967 | | GF_Err gf_isom_set_sample_cenc_default_group(GF_ISOFile *movie, u32 track, u32 sample_number) |
7968 | 0 | { |
7969 | 0 | return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_SEIG, 0, NULL, NULL, NULL); |
7970 | 0 | } |
7971 | | |
7972 | | GF_Err gf_isom_force_ctts(GF_ISOFile *file, u32 track) |
7973 | 0 | { |
7974 | 0 | GF_TrackBox *trak; |
7975 | 0 | GF_Err e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
7976 | 0 | if (e) return e; |
7977 | 0 | trak = gf_isom_get_track_box(file, track); |
7978 | 0 | if (!trak) return GF_BAD_PARAM; |
7979 | 0 | if (trak->Media->information->sampleTable->CompositionOffset) return GF_OK; |
7980 | | |
7981 | 0 | trak->Media->information->sampleTable->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_CTTS); |
7982 | 0 | if (!trak->Media->information->sampleTable->CompositionOffset) return GF_OUT_OF_MEM; |
7983 | 0 | trak->Media->information->sampleTable->CompositionOffset->nb_entries = 1; |
7984 | 0 | trak->Media->information->sampleTable->CompositionOffset->entries = gf_malloc(sizeof(GF_DttsEntry)); |
7985 | 0 | trak->Media->information->sampleTable->CompositionOffset->entries[0].decodingOffset = 0; |
7986 | 0 | trak->Media->information->sampleTable->CompositionOffset->entries[0].sampleCount = trak->Media->information->sampleTable->SampleSize->sampleCount; |
7987 | 0 | return GF_OK; |
7988 | 0 | } |
7989 | | |
7990 | | GF_EXPORT |
7991 | | GF_Err gf_isom_set_ctts_v1(GF_ISOFile *file, u32 track, u32 ctts_shift) |
7992 | 0 | { |
7993 | 0 | u32 i, shift; |
7994 | 0 | u64 duration; |
7995 | 0 | GF_CompositionOffsetBox *ctts; |
7996 | 0 | GF_CompositionToDecodeBox *cslg; |
7997 | 0 | s32 leastCTTS, greatestCTTS; |
7998 | 0 | GF_TrackBox *trak; |
7999 | 0 | GF_Err e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8000 | 0 | if (e) return e; |
8001 | | |
8002 | 0 | trak = gf_isom_get_track_box(file, track); |
8003 | 0 | if (!trak) return GF_BAD_PARAM; |
8004 | | |
8005 | 0 | ctts = trak->Media->information->sampleTable->CompositionOffset; |
8006 | 0 | if (ctts->version) { |
8007 | 0 | shift = ctts_shift; |
8008 | 0 | } else { |
8009 | 0 | shift = ctts->nb_entries ? ctts->entries[0].decodingOffset : 0; |
8010 | 0 | } |
8011 | 0 | leastCTTS = GF_INT_MAX; |
8012 | 0 | greatestCTTS = 0; |
8013 | 0 | for (i=0; i<ctts->nb_entries; i++) { |
8014 | 0 | if (!ctts->version) |
8015 | 0 | ctts->entries[i].decodingOffset -= shift; |
8016 | |
|
8017 | 0 | if ((s32)ctts->entries[i].decodingOffset < leastCTTS) |
8018 | 0 | leastCTTS = ctts->entries[i].decodingOffset; |
8019 | 0 | if ((s32)ctts->entries[i].decodingOffset > greatestCTTS) |
8020 | 0 | greatestCTTS = ctts->entries[i].decodingOffset; |
8021 | 0 | } |
8022 | 0 | if (!ctts->version) { |
8023 | 0 | ctts->version = 1; |
8024 | | //if we had edit lists, shift all media times by the given amount |
8025 | 0 | if (trak->editBox && trak->editBox->editList) { |
8026 | 0 | for (i=0; i<gf_list_count(trak->editBox->editList->entryList); i++) { |
8027 | 0 | GF_EdtsEntry *ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, i); |
8028 | | //empty edit |
8029 | 0 | if (ent->mediaTime<0) continue; |
8030 | 0 | if (ent->mediaTime>=shift) ent->mediaTime -= shift; |
8031 | 0 | else ent->mediaTime = 0; |
8032 | | //no offset and last entry, trash edit |
8033 | 0 | if (!ent->mediaTime && (gf_list_count(trak->editBox->editList->entryList)==1)) { |
8034 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->editBox); |
8035 | 0 | trak->editBox = NULL; |
8036 | 0 | break; |
8037 | 0 | } |
8038 | 0 | } |
8039 | 0 | SetTrackDuration(trak); |
8040 | 0 | } |
8041 | 0 | } |
8042 | |
|
8043 | 0 | if (!trak->Media->information->sampleTable->CompositionToDecode) { |
8044 | 0 | trak->Media->information->sampleTable->CompositionToDecode = (GF_CompositionToDecodeBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_CSLG); |
8045 | 0 | if (!trak->Media->information->sampleTable->CompositionToDecode) |
8046 | 0 | return GF_OUT_OF_MEM; |
8047 | 0 | } |
8048 | | |
8049 | 0 | cslg = trak->Media->information->sampleTable->CompositionToDecode; |
8050 | 0 | if (cslg) { |
8051 | 0 | cslg->compositionToDTSShift = shift; |
8052 | 0 | cslg->leastDecodeToDisplayDelta = leastCTTS; |
8053 | 0 | cslg->greatestDecodeToDisplayDelta = greatestCTTS; |
8054 | 0 | cslg->compositionStartTime = 0; |
8055 | | /*for our use case (first CTS set to 0), the composition end time is the media duration if it fits on 32 bits*/ |
8056 | 0 | duration = gf_isom_get_media_duration(file, track); |
8057 | 0 | cslg->compositionEndTime = (duration<0x7FFFFFFF) ? (s32) duration : 0; |
8058 | 0 | } |
8059 | |
|
8060 | 0 | gf_isom_modify_alternate_brand(file, GF_ISOM_BRAND_ISO4, GF_TRUE); |
8061 | 0 | return GF_OK; |
8062 | 0 | } |
8063 | | |
8064 | | static GF_Err gf_isom_set_ctts_v0(GF_ISOFile *file, GF_TrackBox *trak) |
8065 | 0 | { |
8066 | 0 | u32 i; |
8067 | 0 | s32 shift; |
8068 | 0 | GF_CompositionOffsetBox *ctts; |
8069 | 0 | GF_CompositionToDecodeBox *cslg; |
8070 | |
|
8071 | 0 | ctts = trak->Media->information->sampleTable->CompositionOffset; |
8072 | |
|
8073 | 0 | if (!trak->Media->information->sampleTable->CompositionToDecode) |
8074 | 0 | { |
8075 | 0 | shift = 0; |
8076 | 0 | for (i=0; i<ctts->nb_entries; i++) { |
8077 | 0 | if (-ctts->entries[i].decodingOffset > shift) |
8078 | 0 | shift = -ctts->entries[i].decodingOffset; |
8079 | 0 | } |
8080 | 0 | if (shift > 0) |
8081 | 0 | { |
8082 | 0 | for (i=0; i<ctts->nb_entries; i++) { |
8083 | 0 | s64 new_ts = ctts->entries[i].decodingOffset; |
8084 | 0 | new_ts += shift; |
8085 | 0 | ctts->entries[i].decodingOffset = (s32) new_ts; |
8086 | 0 | } |
8087 | 0 | } |
8088 | 0 | } |
8089 | 0 | else |
8090 | 0 | { |
8091 | 0 | cslg = trak->Media->information->sampleTable->CompositionToDecode; |
8092 | 0 | shift = (s32) cslg->compositionToDTSShift; |
8093 | 0 | for (i=0; i<ctts->nb_entries; i++) { |
8094 | 0 | s64 new_ts = ctts->entries[i].decodingOffset; |
8095 | 0 | new_ts += shift; |
8096 | 0 | ctts->entries[i].decodingOffset = (s32) new_ts; |
8097 | 0 | } |
8098 | 0 | gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box *)cslg); |
8099 | 0 | trak->Media->information->sampleTable->CompositionToDecode = NULL; |
8100 | 0 | } |
8101 | 0 | if (shift>0) { |
8102 | | //no edits, insert one |
8103 | 0 | if (! trak->editBox) { |
8104 | 0 | u64 dur = trak->Media->mediaHeader->duration; |
8105 | 0 | dur *= file->moov->mvhd->timeScale; |
8106 | 0 | dur /= trak->Media->mediaHeader->timeScale; |
8107 | 0 | gf_isom_set_edit(file, gf_list_find(file->moov->trackList, trak)+1, 0, dur, shift, GF_ISOM_EDIT_NORMAL); |
8108 | 0 | } else { |
8109 | | //otherwise shift media times in all entries |
8110 | 0 | for (i=0; i<gf_list_count(trak->editBox->editList->entryList); i++) { |
8111 | 0 | GF_EdtsEntry *ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, i); |
8112 | | //empty edit |
8113 | 0 | if (ent->mediaTime<0) continue; |
8114 | 0 | ent->mediaTime += shift; |
8115 | 0 | } |
8116 | 0 | SetTrackDuration(trak); |
8117 | 0 | } |
8118 | 0 | } |
8119 | 0 | ctts->version = 0; |
8120 | 0 | gf_isom_modify_alternate_brand(file, GF_ISOM_BRAND_ISO4, GF_FALSE); |
8121 | 0 | return GF_OK; |
8122 | 0 | } |
8123 | | |
8124 | | GF_EXPORT |
8125 | | GF_Err gf_isom_set_composition_offset_mode(GF_ISOFile *file, u32 track, Bool use_negative_offsets) |
8126 | 0 | { |
8127 | 0 | GF_Err e; |
8128 | 0 | GF_TrackBox *trak; |
8129 | 0 | GF_CompositionOffsetBox *ctts; |
8130 | |
|
8131 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8132 | 0 | if (e) return e; |
8133 | | |
8134 | 0 | trak = gf_isom_get_track_box(file, track); |
8135 | 0 | if (!trak) return GF_BAD_PARAM; |
8136 | | |
8137 | 0 | ctts = trak->Media->information->sampleTable->CompositionOffset; |
8138 | 0 | if (!ctts) { |
8139 | 0 | if (!use_negative_offsets && trak->Media->information->sampleTable->CompositionToDecode) { |
8140 | 0 | gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box *)trak->Media->information->sampleTable->CompositionToDecode); |
8141 | 0 | trak->Media->information->sampleTable->CompositionToDecode = NULL; |
8142 | 0 | } |
8143 | 0 | return GF_OK; |
8144 | 0 | } |
8145 | | |
8146 | 0 | if (use_negative_offsets) { |
8147 | 0 | return gf_isom_set_ctts_v1(file, track, 0); |
8148 | 0 | } else { |
8149 | 0 | if (ctts->version==0) return GF_OK; |
8150 | 0 | return gf_isom_set_ctts_v0(file, trak); |
8151 | 0 | } |
8152 | 0 | } |
8153 | | |
8154 | | #if 0 //unused |
8155 | | GF_Err gf_isom_set_sync_table(GF_ISOFile *file, u32 track) |
8156 | | { |
8157 | | GF_Err e; |
8158 | | GF_TrackBox *trak; |
8159 | | |
8160 | | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8161 | | if (e) return e; |
8162 | | |
8163 | | trak = gf_isom_get_track_box(file, track); |
8164 | | if (!trak) return GF_BAD_PARAM; |
8165 | | |
8166 | | if (!trak->Media->information->sampleTable->SyncSample) { |
8167 | | trak->Media->information->sampleTable->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STSS); |
8168 | | |
8169 | | if (!trak->Media->information->sampleTable->SyncSample) |
8170 | | return GF_OUT_OF_MEM; |
8171 | | } |
8172 | | return GF_OK; |
8173 | | } |
8174 | | #endif |
8175 | | |
8176 | | GF_Err gf_isom_set_sample_flags(GF_ISOFile *file, u32 track, u32 sampleNumber, u32 isLeading, u32 dependsOn, u32 dependedOn, u32 redundant) |
8177 | 0 | { |
8178 | 0 | GF_Err e; |
8179 | 0 | GF_TrackBox *trak; |
8180 | |
|
8181 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8182 | 0 | if (e) return e; |
8183 | | |
8184 | 0 | trak = gf_isom_get_track_box(file, track); |
8185 | 0 | if (!trak) return GF_BAD_PARAM; |
8186 | 0 | return stbl_SetDependencyType(trak->Media->information->sampleTable, sampleNumber, isLeading, dependsOn, dependedOn, redundant); |
8187 | 0 | } |
8188 | | |
8189 | | #if 0 //unused |
8190 | | GF_Err gf_isom_sample_set_dep_info(GF_ISOFile *file, u32 track, u32 sampleNumber, u32 isLeading, u32 dependsOn, u32 dependedOn, u32 redundant) |
8191 | | { |
8192 | | GF_TrackBox *trak; |
8193 | | |
8194 | | trak = gf_isom_get_track_box(file, track); |
8195 | | if (!trak) return GF_BAD_PARAM; |
8196 | | |
8197 | | return stbl_AddDependencyType(trak->Media->information->sampleTable, sampleNumber, isLeading, dependsOn, dependedOn, redundant); |
8198 | | } |
8199 | | #endif |
8200 | | |
8201 | | GF_EXPORT |
8202 | | GF_Err gf_isom_copy_sample_info(GF_ISOFile *dst, u32 dst_track, GF_ISOFile *src, u32 src_track, u32 sampleNumber) |
8203 | 0 | { |
8204 | 0 | u32 i, count, idx, dst_sample_num, subs_flags; |
8205 | 0 | GF_SubSampleInfoEntry *sub_sample; |
8206 | 0 | GF_Err e; |
8207 | 0 | GF_TrackBox *src_trak, *dst_trak; |
8208 | |
|
8209 | 0 | src_trak = gf_isom_get_track_box(src, src_track); |
8210 | 0 | if (!src_trak) return GF_BAD_PARAM; |
8211 | | |
8212 | 0 | dst_trak = gf_isom_get_track_box(dst, dst_track); |
8213 | 0 | if (!dst_trak) return GF_BAD_PARAM; |
8214 | | |
8215 | 0 | dst_sample_num = dst_trak->Media->information->sampleTable->SampleSize->sampleCount; |
8216 | | |
8217 | | /*modify depends flags*/ |
8218 | 0 | if (src_trak->Media->information->sampleTable->SampleDep) { |
8219 | 0 | u32 isLeading, dependsOn, dependedOn, redundant; |
8220 | |
|
8221 | 0 | isLeading = dependsOn = dependedOn = redundant = 0; |
8222 | |
|
8223 | 0 | e = stbl_GetSampleDepType(src_trak->Media->information->sampleTable->SampleDep, sampleNumber, &isLeading, &dependsOn, &dependedOn, &redundant); |
8224 | 0 | if (e) return e; |
8225 | | |
8226 | 0 | e = stbl_AppendDependencyType(dst_trak->Media->information->sampleTable, isLeading, dependsOn, dependedOn, redundant); |
8227 | 0 | if (e) return e; |
8228 | 0 | } |
8229 | | |
8230 | | /*copy subsample info if any*/ |
8231 | 0 | idx=1; |
8232 | 0 | while (gf_isom_get_subsample_types(src, src_track, idx, &subs_flags)) { |
8233 | 0 | GF_SubSampleInformationBox *dst_subs=NULL; |
8234 | 0 | idx++; |
8235 | |
|
8236 | 0 | if ( ! gf_isom_sample_get_subsample_entry(src, src_track, sampleNumber, subs_flags, &sub_sample)) |
8237 | 0 | continue; |
8238 | | |
8239 | | /*create subsample if needed*/ |
8240 | 0 | if (!dst_trak->Media->information->sampleTable->sub_samples) { |
8241 | 0 | dst_trak->Media->information->sampleTable->sub_samples = gf_list_new(); |
8242 | 0 | } |
8243 | 0 | count = gf_list_count(dst_trak->Media->information->sampleTable->sub_samples); |
8244 | 0 | for (i=0; i<count; i++) { |
8245 | 0 | dst_subs = gf_list_get(dst_trak->Media->information->sampleTable->sub_samples, i); |
8246 | 0 | if (dst_subs->flags==subs_flags) break; |
8247 | 0 | dst_subs=NULL; |
8248 | 0 | } |
8249 | 0 | if (!dst_subs) { |
8250 | 0 | dst_subs = (GF_SubSampleInformationBox *) gf_isom_box_new_parent(&dst_trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_SUBS); |
8251 | 0 | if (!dst_subs) return GF_OUT_OF_MEM; |
8252 | 0 | dst_subs->version=0; |
8253 | 0 | dst_subs->flags = subs_flags; |
8254 | 0 | gf_list_add(dst_trak->Media->information->sampleTable->sub_samples, dst_subs); |
8255 | 0 | } |
8256 | | |
8257 | 0 | count = gf_list_count(sub_sample->SubSamples); |
8258 | 0 | for (i=0; i<count; i++) { |
8259 | 0 | GF_SubSampleEntry *entry = (GF_SubSampleEntry*)gf_list_get(sub_sample->SubSamples, i); |
8260 | 0 | e = gf_isom_add_subsample_info(dst_subs, dst_sample_num, entry->subsample_size, entry->subsample_priority, entry->reserved, entry->discardable); |
8261 | 0 | if (e) return e; |
8262 | 0 | } |
8263 | 0 | } |
8264 | | |
8265 | | /*copy sampleToGroup info if any*/ |
8266 | 0 | count = 0; |
8267 | 0 | if (src_trak->Media->information->sampleTable->sampleGroups) |
8268 | 0 | count = gf_list_count(src_trak->Media->information->sampleTable->sampleGroups); |
8269 | |
|
8270 | 0 | for (i=0; i<count; i++) { |
8271 | 0 | GF_SampleGroupBox *sg; |
8272 | 0 | u32 j, k, default_index; |
8273 | 0 | u32 first_sample_in_entry, last_sample_in_entry, group_desc_index_src, group_desc_index_dst; |
8274 | 0 | first_sample_in_entry = 1; |
8275 | |
|
8276 | 0 | sg = (GF_SampleGroupBox*)gf_list_get(src_trak->Media->information->sampleTable->sampleGroups, i); |
8277 | 0 | for (j=0; j<sg->entry_count; j++) { |
8278 | 0 | GF_SampleGroupDescriptionBox *sgd_dst = NULL; |
8279 | 0 | last_sample_in_entry = first_sample_in_entry + sg->sample_entries[j].sample_count - 1; |
8280 | 0 | if ((sampleNumber<first_sample_in_entry) || (sampleNumber>last_sample_in_entry)) { |
8281 | 0 | first_sample_in_entry = last_sample_in_entry+1; |
8282 | 0 | continue; |
8283 | 0 | } |
8284 | | |
8285 | 0 | if (!dst_trak->Media->information->sampleTable->sampleGroups) |
8286 | 0 | dst_trak->Media->information->sampleTable->sampleGroups = gf_list_new(); |
8287 | |
|
8288 | 0 | group_desc_index_src = group_desc_index_dst = sg->sample_entries[j].group_description_index; |
8289 | |
|
8290 | 0 | if (group_desc_index_src) { |
8291 | 0 | GF_SampleGroupDescriptionBox *sgd_src; |
8292 | 0 | void *sgde_src, *sgde_dst; |
8293 | |
|
8294 | 0 | group_desc_index_dst = 0; |
8295 | | //check that the sample group description exists !! |
8296 | 0 | sgde_src = gf_isom_get_sample_group_info_entry(src, src_trak, sg->grouping_type, sg->sample_entries[j].group_description_index, &default_index, &sgd_src); |
8297 | |
|
8298 | 0 | if (!sgde_src) break; |
8299 | | |
8300 | 0 | if (!dst_trak->Media->information->sampleTable->sampleGroupsDescription) |
8301 | 0 | dst_trak->Media->information->sampleTable->sampleGroupsDescription = gf_list_new(); |
8302 | |
|
8303 | 0 | sgd_dst = NULL; |
8304 | 0 | for (k=0; k< gf_list_count(dst_trak->Media->information->sampleTable->sampleGroupsDescription); k++) { |
8305 | 0 | sgd_dst = gf_list_get(dst_trak->Media->information->sampleTable->sampleGroupsDescription, k); |
8306 | 0 | if (sgd_dst->grouping_type==sgd_src->grouping_type) break; |
8307 | 0 | sgd_dst = NULL; |
8308 | 0 | } |
8309 | 0 | if (!sgd_dst) { |
8310 | 0 | gf_isom_clone_box( (GF_Box *) sgd_src, (GF_Box **) &sgd_dst); |
8311 | 0 | if (!sgd_dst) return GF_OUT_OF_MEM; |
8312 | 0 | gf_list_add(dst_trak->Media->information->sampleTable->sampleGroupsDescription, sgd_dst); |
8313 | 0 | gf_list_add(dst_trak->Media->information->sampleTable->child_boxes, sgd_dst); |
8314 | 0 | } |
8315 | | |
8316 | | //find the same entry |
8317 | 0 | for (k=0; k<gf_list_count(sgd_dst->group_descriptions); k++) { |
8318 | 0 | sgde_dst = gf_list_get(sgd_dst->group_descriptions, k); |
8319 | 0 | if (gf_isom_is_identical_sgpd(sgde_src, sgde_dst, sgd_src->grouping_type)) { |
8320 | 0 | group_desc_index_dst = k+1; |
8321 | 0 | break; |
8322 | 0 | } |
8323 | 0 | } |
8324 | 0 | if (!group_desc_index_dst) { |
8325 | 0 | GF_SampleGroupDescriptionBox *cloned=NULL; |
8326 | 0 | gf_isom_clone_box( (GF_Box *) sgd_src, (GF_Box **) &cloned); |
8327 | 0 | if (!cloned) return GF_OUT_OF_MEM; |
8328 | 0 | sgde_dst = gf_list_get(cloned->group_descriptions, group_desc_index_dst); |
8329 | 0 | gf_list_rem(cloned->group_descriptions, group_desc_index_dst); |
8330 | 0 | gf_isom_box_del( (GF_Box *) cloned); |
8331 | 0 | gf_list_add(sgd_dst->group_descriptions, sgde_dst); |
8332 | 0 | group_desc_index_dst = gf_list_count(sgd_dst->group_descriptions); |
8333 | 0 | } |
8334 | 0 | } else { |
8335 | 0 | gf_isom_get_sample_group_info_entry(dst, dst_trak, sg->grouping_type, 1, NULL, &sgd_dst); |
8336 | 0 | if (!sgd_dst) continue; |
8337 | 0 | } |
8338 | | |
8339 | | /*found our sample, add it to trak->sampleGroups*/ |
8340 | 0 | e = gf_isom_add_sample_group_entry(dst_trak->Media->information->sampleTable->sampleGroups, dst_sample_num, sgd_dst, sg->grouping_type_parameter, group_desc_index_dst, dst_trak->Media->information->sampleTable->child_boxes, NULL); |
8341 | 0 | if (e) return e; |
8342 | 0 | break; |
8343 | 0 | } |
8344 | 0 | } |
8345 | | |
8346 | | //copy auxiliary info |
8347 | 0 | count = gf_list_count(src_trak->Media->information->sampleTable->sai_sizes); |
8348 | 0 | for (i=0; i<count; i++) { |
8349 | 0 | u32 j; |
8350 | 0 | GF_SampleAuxiliaryInfoOffsetBox *saio = NULL; |
8351 | 0 | GF_SampleAuxiliaryInfoSizeBox *saiz = gf_list_get(src_trak->Media->information->sampleTable->sai_sizes, i); |
8352 | |
|
8353 | 0 | switch (saiz->aux_info_type) { |
8354 | 0 | case GF_ISOM_CENC_SCHEME: |
8355 | 0 | case GF_ISOM_CBC_SCHEME: |
8356 | 0 | case GF_ISOM_CENS_SCHEME: |
8357 | 0 | case GF_ISOM_CBCS_SCHEME: |
8358 | 0 | case GF_ISOM_PIFF_SCHEME: |
8359 | 0 | case 0: |
8360 | 0 | continue; |
8361 | 0 | default: |
8362 | 0 | break; |
8363 | 0 | } |
8364 | | //no aux sample associated |
8365 | 0 | if (saiz->sample_count<sampleNumber) continue; |
8366 | | //no size associated |
8367 | 0 | if (!saiz->default_sample_info_size && !saiz->sample_info_size[sampleNumber-1]) continue; |
8368 | | |
8369 | 0 | for (j=0; j<gf_list_count(src_trak->Media->information->sampleTable->sai_offsets); j++) { |
8370 | 0 | saio = gf_list_get(src_trak->Media->information->sampleTable->sai_offsets, j); |
8371 | 0 | if ((saio->aux_info_type==saiz->aux_info_type) && (saio->aux_info_type_parameter==saiz->aux_info_type_parameter)) break; |
8372 | 0 | saio=NULL; |
8373 | 0 | } |
8374 | 0 | if (!saio) continue; |
8375 | 0 | if (!saio->offsets && !saio->sai_data) continue; |
8376 | | |
8377 | 0 | u64 offset = saio->offsets ? saio->offsets[0] : 0; |
8378 | 0 | u32 nb_saio = saio->entry_count; |
8379 | 0 | if ((nb_saio>1) && (saio->entry_count != saiz->sample_count)) continue; |
8380 | | |
8381 | 0 | u32 size; |
8382 | |
|
8383 | 0 | if (nb_saio == 1) { |
8384 | 0 | for (j=0; j < sampleNumber-1; j++) { |
8385 | 0 | size = saiz->default_sample_info_size ? saiz->default_sample_info_size : saiz->sample_info_size[j]; |
8386 | 0 | offset += size; |
8387 | 0 | } |
8388 | 0 | } else { |
8389 | 0 | offset = saio->offsets[sampleNumber-1]; |
8390 | 0 | } |
8391 | |
|
8392 | 0 | size = saiz->default_sample_info_size ? saiz->default_sample_info_size : saiz->sample_info_size[j]; |
8393 | |
|
8394 | 0 | if (saio->sai_data) { |
8395 | 0 | e = gf_isom_add_sample_aux_info_internal(dst_trak, NULL, j+1, saiz->aux_info_type, saiz->aux_info_type_parameter, saio->sai_data->data + offset, size); |
8396 | 0 | } else { |
8397 | 0 | u8 *sai = gf_malloc(size); |
8398 | 0 | if (!sai) return GF_OUT_OF_MEM; |
8399 | | |
8400 | 0 | u64 cur_position = gf_bs_get_position(src_trak->moov->mov->movieFileMap->bs); |
8401 | 0 | gf_bs_seek(src_trak->moov->mov->movieFileMap->bs, offset); |
8402 | |
|
8403 | 0 | gf_bs_read_data(src_trak->moov->mov->movieFileMap->bs, sai, size); |
8404 | 0 | gf_bs_seek(src_trak->moov->mov->movieFileMap->bs, cur_position); |
8405 | 0 | e = gf_isom_add_sample_aux_info_internal(dst_trak, NULL, j+1, saiz->aux_info_type, saiz->aux_info_type_parameter, sai, size); |
8406 | 0 | gf_free(sai); |
8407 | 0 | } |
8408 | | |
8409 | 0 | if (e) { |
8410 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] Failed to clone sai data: %s\n", gf_error_to_string(e) )); |
8411 | 0 | } |
8412 | 0 | } |
8413 | 0 | return GF_OK; |
8414 | 0 | } |
8415 | | |
8416 | | GF_EXPORT |
8417 | | GF_Err gf_isom_text_set_display_flags(GF_ISOFile *file, u32 track, u32 desc_index, u32 flags, GF_TextFlagsMode op_type) |
8418 | 0 | { |
8419 | 0 | u32 i; |
8420 | 0 | GF_Err e; |
8421 | 0 | GF_TrackBox *trak; |
8422 | |
|
8423 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8424 | 0 | if (e) return e; |
8425 | | |
8426 | 0 | trak = gf_isom_get_track_box(file, track); |
8427 | 0 | if (!trak) return GF_BAD_PARAM; |
8428 | | |
8429 | 0 | for (i=0; i < gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes); i++) { |
8430 | 0 | GF_Tx3gSampleEntryBox *txt; |
8431 | 0 | if (desc_index && (i+1 != desc_index)) continue; |
8432 | | |
8433 | 0 | txt = (GF_Tx3gSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, i); |
8434 | 0 | if (txt->type != GF_ISOM_BOX_TYPE_TX3G) continue; |
8435 | | |
8436 | 0 | switch (op_type) { |
8437 | 0 | case GF_ISOM_TEXT_FLAGS_TOGGLE: |
8438 | 0 | txt->displayFlags |= flags; |
8439 | 0 | break; |
8440 | 0 | case GF_ISOM_TEXT_FLAGS_UNTOGGLE: |
8441 | 0 | txt->displayFlags &= ~flags; |
8442 | 0 | break; |
8443 | 0 | default: |
8444 | 0 | txt->displayFlags = flags; |
8445 | 0 | break; |
8446 | 0 | } |
8447 | 0 | } |
8448 | 0 | return GF_OK; |
8449 | |
|
8450 | 0 | } |
8451 | | |
8452 | | |
8453 | | GF_EXPORT |
8454 | | GF_Err gf_isom_update_duration(GF_ISOFile *movie) |
8455 | 0 | { |
8456 | 0 | u32 i; |
8457 | 0 | u64 maxDur; |
8458 | 0 | GF_TrackBox *trak; |
8459 | |
|
8460 | 0 | if (!movie || !movie->moov) return GF_BAD_PARAM; |
8461 | | |
8462 | | //if file was open in Write or Edit mode, recompute the duration |
8463 | | //the duration of a movie is the MaxDuration of all the tracks... |
8464 | | |
8465 | 0 | maxDur = 0; |
8466 | 0 | i=0; |
8467 | 0 | while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { |
8468 | 0 | if( (movie->LastError = SetTrackDuration(trak)) ) return movie->LastError; |
8469 | 0 | if (trak->Header && (trak->Header->duration > maxDur)) |
8470 | 0 | maxDur = trak->Header->duration; |
8471 | 0 | } |
8472 | 0 | movie->moov->mvhd->duration = maxDur; |
8473 | |
|
8474 | 0 | return GF_OK; |
8475 | 0 | } |
8476 | | |
8477 | | GF_EXPORT |
8478 | | GF_Err gf_isom_update_edit_list_duration(GF_ISOFile *file, u32 track) |
8479 | 0 | { |
8480 | 0 | u32 i; |
8481 | 0 | u64 trackDuration; |
8482 | 0 | GF_EdtsEntry *ent; |
8483 | 0 | GF_Err e; |
8484 | 0 | GF_TrackBox *trak; |
8485 | |
|
8486 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8487 | 0 | if (e) return e; |
8488 | | |
8489 | 0 | trak = gf_isom_get_track_box(file, track); |
8490 | 0 | if (!trak) return GF_BAD_PARAM; |
8491 | | |
8492 | | |
8493 | | //the total duration is the media duration: adjust it in case... |
8494 | 0 | e = Media_SetDuration(trak); |
8495 | 0 | if (e) return e; |
8496 | | |
8497 | | //assert the timeScales are non-NULL |
8498 | 0 | if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) return GF_ISOM_INVALID_FILE; |
8499 | 0 | trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale; |
8500 | | |
8501 | | //if we have an edit list, the duration is the sum of all the editList |
8502 | | //entries' duration (always expressed in MovieTimeScale) |
8503 | 0 | if (trak->editBox && trak->editBox->editList) { |
8504 | 0 | u64 editListDuration = 0; |
8505 | 0 | GF_EditListBox *elst = trak->editBox->editList; |
8506 | 0 | i=0; |
8507 | 0 | while ((ent = (GF_EdtsEntry*)gf_list_enum(elst->entryList, &i))) { |
8508 | 0 | if ((ent->mediaTime>=0) && (ent->mediaRate==0x10000) && (ent->segmentDuration > trackDuration)) |
8509 | 0 | ent->segmentDuration = trackDuration; |
8510 | |
|
8511 | 0 | if (!ent->segmentDuration) { |
8512 | 0 | u64 diff; |
8513 | 0 | ent->segmentDuration = trackDuration; |
8514 | 0 | if (ent->mediaTime>0) { |
8515 | 0 | diff = ent->mediaTime; |
8516 | 0 | diff *= trak->moov->mvhd->timeScale; |
8517 | 0 | diff /= trak->Media->mediaHeader->timeScale; |
8518 | 0 | if (diff < ent->segmentDuration) |
8519 | 0 | ent->segmentDuration -= diff; |
8520 | | /* |
8521 | | else |
8522 | | diff = 0; |
8523 | | */ |
8524 | 0 | } |
8525 | 0 | } |
8526 | 0 | if ((ent->mediaTime>=0) && ((u64) ent->mediaTime>=trak->Media->mediaHeader->duration)) { |
8527 | 0 | ent->mediaTime = trak->Media->mediaHeader->duration; |
8528 | 0 | } |
8529 | 0 | editListDuration += ent->segmentDuration; |
8530 | 0 | } |
8531 | 0 | trackDuration = editListDuration; |
8532 | 0 | } |
8533 | 0 | if (!trackDuration) { |
8534 | 0 | trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale; |
8535 | 0 | } |
8536 | 0 | trak->Header->duration = trackDuration; |
8537 | |
|
8538 | 0 | return GF_OK; |
8539 | |
|
8540 | 0 | } |
8541 | | |
8542 | | |
8543 | | GF_EXPORT |
8544 | 0 | GF_Err gf_isom_clone_pssh(GF_ISOFile *output, GF_ISOFile *input, Bool in_moof) { |
8545 | 0 | GF_Box *a; |
8546 | 0 | u32 i; |
8547 | 0 | i = 0; |
8548 | |
|
8549 | 0 | while ((a = (GF_Box *)gf_list_enum(input->moov->child_boxes, &i))) { |
8550 | 0 | if (a->type == GF_ISOM_BOX_TYPE_PSSH) { |
8551 | 0 | GF_List **child_boxes = &output->moov->child_boxes; |
8552 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
8553 | 0 | if (in_moof) |
8554 | 0 | child_boxes = &output->moof->child_boxes; |
8555 | 0 | #endif |
8556 | |
|
8557 | 0 | GF_ProtectionSystemHeaderBox *pssh = (GF_ProtectionSystemHeaderBox *)gf_isom_box_new_parent(child_boxes, GF_ISOM_BOX_TYPE_PSSH); |
8558 | 0 | if (!pssh) return GF_OUT_OF_MEM; |
8559 | 0 | memmove(pssh->SystemID, ((GF_ProtectionSystemHeaderBox *)a)->SystemID, 16); |
8560 | 0 | if (((GF_ProtectionSystemHeaderBox *)a)->KIDs && ((GF_ProtectionSystemHeaderBox *)a)->KID_count > 0) { |
8561 | 0 | pssh->KID_count = ((GF_ProtectionSystemHeaderBox *)a)->KID_count; |
8562 | 0 | pssh->KIDs = (bin128 *)gf_malloc(pssh->KID_count*sizeof(bin128)); |
8563 | 0 | if (!pssh->KIDs) return GF_OUT_OF_MEM; |
8564 | 0 | memmove(pssh->KIDs, ((GF_ProtectionSystemHeaderBox *)a)->KIDs, pssh->KID_count*sizeof(bin128)); |
8565 | 0 | } |
8566 | 0 | pssh->private_data_size = ((GF_ProtectionSystemHeaderBox *)a)->private_data_size; |
8567 | 0 | if (!pssh->private_data_size) { |
8568 | 0 | pssh->private_data = NULL; |
8569 | 0 | } else { |
8570 | 0 | pssh->private_data = (u8 *)gf_malloc(pssh->private_data_size*sizeof(char)); |
8571 | 0 | if (!pssh->private_data) return GF_OUT_OF_MEM; |
8572 | 0 | memmove(pssh->private_data, ((GF_ProtectionSystemHeaderBox *)a)->private_data, pssh->private_data_size); |
8573 | 0 | } |
8574 | 0 | } |
8575 | 0 | } |
8576 | 0 | return GF_OK; |
8577 | 0 | } |
8578 | | |
8579 | | GF_EXPORT |
8580 | | GF_Err gf_isom_set_track_group(GF_ISOFile *file, u32 track_number, u32 track_group_id, u32 group_type, Bool do_add) |
8581 | 0 | { |
8582 | 0 | u32 i, j; |
8583 | 0 | GF_TrackGroupTypeBox *trgt; |
8584 | 0 | GF_Err e; |
8585 | 0 | GF_TrackBox *trak; |
8586 | |
|
8587 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8588 | 0 | if (e) return e; |
8589 | | |
8590 | 0 | trak = gf_isom_get_track_box(file, track_number); |
8591 | 0 | if (!trak) return GF_BAD_PARAM; |
8592 | 0 | if (!trak->groups) trak->groups = (GF_TrackGroupBox*) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TRGR); |
8593 | 0 | if (!trak->groups) return GF_OUT_OF_MEM; |
8594 | | |
8595 | 0 | for (j=0; j<gf_list_count(file->moov->trackList); j++) { |
8596 | 0 | GF_TrackBox *a_trak = gf_list_get(file->moov->trackList, j); |
8597 | 0 | if (!a_trak->groups) continue; |
8598 | | |
8599 | 0 | for (i=0; i<gf_list_count(a_trak->groups->groups); i++) { |
8600 | 0 | trgt = gf_list_get(a_trak->groups->groups, i); |
8601 | |
|
8602 | 0 | if (trgt->track_group_id==track_group_id) { |
8603 | 0 | if (trgt->group_type != group_type) { |
8604 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("A track with same group ID is already defined for different group type %s\n", gf_4cc_to_str(trgt->group_type) )); |
8605 | 0 | return GF_BAD_PARAM; |
8606 | 0 | } |
8607 | 0 | if (a_trak==trak) { |
8608 | 0 | if (!do_add) { |
8609 | 0 | gf_list_rem(trak->groups->groups, i); |
8610 | 0 | gf_isom_box_del_parent(&trak->groups->child_boxes, (GF_Box *)trgt); |
8611 | 0 | } |
8612 | 0 | return GF_OK; |
8613 | 0 | } |
8614 | 0 | } |
8615 | 0 | } |
8616 | 0 | } |
8617 | | //not found, add new group |
8618 | 0 | trgt = (GF_TrackGroupTypeBox*) gf_isom_box_new_parent(&trak->groups->child_boxes, GF_ISOM_BOX_TYPE_TRGT); |
8619 | 0 | if (!trgt) return GF_OUT_OF_MEM; |
8620 | 0 | trgt->track_group_id = track_group_id; |
8621 | 0 | trgt->group_type = group_type; |
8622 | 0 | return gf_list_add(trak->groups->groups, trgt); |
8623 | 0 | } |
8624 | | |
8625 | | GF_EXPORT |
8626 | | GF_Err gf_isom_set_nalu_length_field(GF_ISOFile *file, u32 track, u32 StreamDescriptionIndex, u32 nalu_size_length) |
8627 | 0 | { |
8628 | 0 | GF_Err e; |
8629 | 0 | GF_TrackBox *trak; |
8630 | 0 | GF_SampleEntryBox *entry; |
8631 | 0 | GF_MPEGVisualSampleEntryBox *ve; |
8632 | 0 | GF_SampleDescriptionBox *stsd; |
8633 | |
|
8634 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8635 | 0 | if (e) return e; |
8636 | | |
8637 | 0 | trak = gf_isom_get_track_box(file, track); |
8638 | 0 | if (!trak) return GF_BAD_PARAM; |
8639 | | |
8640 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
8641 | 0 | if (!stsd || !StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
8642 | 0 | return GF_BAD_PARAM; |
8643 | 0 | } |
8644 | | |
8645 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
8646 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
8647 | 0 | if (!entry || ! gf_isom_is_nalu_based_entry(trak->Media, entry)) { |
8648 | 0 | return GF_BAD_PARAM; |
8649 | 0 | } |
8650 | | |
8651 | 0 | ve = (GF_MPEGVisualSampleEntryBox*)entry; |
8652 | 0 | if (ve->avc_config) ve->avc_config->config->nal_unit_size = nalu_size_length; |
8653 | 0 | if (ve->svc_config) ve->svc_config->config->nal_unit_size = nalu_size_length; |
8654 | 0 | if (ve->hevc_config) ve->hevc_config->config->nal_unit_size = nalu_size_length; |
8655 | 0 | if (ve->lhvc_config) ve->lhvc_config->config->nal_unit_size = nalu_size_length; |
8656 | 0 | if (ve->vvc_config) ve->vvc_config->config->nal_unit_size = nalu_size_length; |
8657 | 0 | return GF_OK; |
8658 | 0 | } |
8659 | | |
8660 | | GF_EXPORT |
8661 | | GF_Err gf_isom_set_sample_group_in_traf(GF_ISOFile *file) |
8662 | 0 | { |
8663 | 0 | GF_Err e; |
8664 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8665 | 0 | if (e) return e; |
8666 | | |
8667 | 0 | file->sample_groups_in_traf = GF_TRUE; |
8668 | 0 | return GF_OK; |
8669 | 0 | } |
8670 | | |
8671 | | GF_EXPORT |
8672 | | void gf_isom_set_progress_callback(GF_ISOFile *file, void (*progress_cbk)(void *udta, u64 nb_done, u64 nb_total), void *progress_cbk_udta) |
8673 | 0 | { |
8674 | 0 | if (file) { |
8675 | 0 | file->progress_cbk = progress_cbk; |
8676 | 0 | file->progress_cbk_udta = progress_cbk_udta; |
8677 | |
|
8678 | 0 | } |
8679 | 0 | } |
8680 | | |
8681 | | GF_Err gf_isom_update_video_sample_entry_fields(GF_ISOFile *file, u32 track, u32 stsd_idx, u16 revision, u32 vendor, u32 temporalQ, u32 spatialQ, u32 horiz_res, u32 vert_res, u16 frames_per_sample, const char *compressor_name, s16 color_table_index) |
8682 | 0 | { |
8683 | |
|
8684 | 0 | GF_TrackBox *trak; |
8685 | 0 | GF_MPEGVisualSampleEntryBox *vid_ent; |
8686 | | |
8687 | | /*get orig sample desc and clone it*/ |
8688 | 0 | trak = gf_isom_get_track_box(file, track); |
8689 | 0 | if (!trak || !stsd_idx) return GF_BAD_PARAM; |
8690 | | |
8691 | 0 | if (!trak->Media || !trak->Media->handler || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription) |
8692 | 0 | return GF_ISOM_INVALID_FILE; |
8693 | | |
8694 | 0 | switch (trak->Media->handler->handlerType) { |
8695 | 0 | case GF_ISOM_MEDIA_VISUAL: |
8696 | 0 | case GF_ISOM_MEDIA_AUXV: |
8697 | 0 | case GF_ISOM_MEDIA_PICT: |
8698 | 0 | break; |
8699 | 0 | default: |
8700 | 0 | return GF_BAD_PARAM; |
8701 | 0 | } |
8702 | 0 | vid_ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, stsd_idx-1); |
8703 | 0 | if (!vid_ent) |
8704 | 0 | return GF_BAD_PARAM; |
8705 | 0 | if (vid_ent->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) |
8706 | 0 | return GF_ISOM_INVALID_FILE; |
8707 | | |
8708 | 0 | vid_ent->revision = revision; |
8709 | 0 | vid_ent->vendor = vendor; |
8710 | 0 | vid_ent->temporal_quality = temporalQ; |
8711 | 0 | vid_ent->spatial_quality = spatialQ; |
8712 | 0 | vid_ent->horiz_res = horiz_res; |
8713 | 0 | vid_ent->vert_res = vert_res; |
8714 | 0 | vid_ent->frames_per_sample = frames_per_sample; |
8715 | 0 | if (compressor_name) |
8716 | 0 | strncpy(vid_ent->compressor_name, compressor_name, 32); |
8717 | |
|
8718 | 0 | vid_ent->color_table_index = color_table_index; |
8719 | 0 | return GF_OK; |
8720 | 0 | } |
8721 | | |
8722 | | GF_EXPORT |
8723 | | GF_Err gf_isom_update_sample_description_from_template(GF_ISOFile *file, u32 track, u32 sampleDescriptionIndex, u8 *data, u32 size) |
8724 | 0 | { |
8725 | 0 | GF_BitStream *bs; |
8726 | 0 | GF_TrackBox *trak; |
8727 | 0 | GF_Box *ent, *tpl_ent; |
8728 | 0 | GF_Err e; |
8729 | | /*get orig sample desc and clone it*/ |
8730 | 0 | trak = gf_isom_get_track_box(file, track); |
8731 | 0 | if (!trak || !sampleDescriptionIndex) return GF_BAD_PARAM; |
8732 | | |
8733 | 0 | if (!trak->Media || !trak->Media->handler || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription) |
8734 | 0 | return GF_ISOM_INVALID_FILE; |
8735 | | |
8736 | 0 | ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex-1); |
8737 | 0 | if (!ent) return GF_BAD_PARAM; |
8738 | | |
8739 | 0 | bs = gf_bs_new(data, size, GF_BITSTREAM_READ); |
8740 | | // e = gf_isom_box_parse(&tpl_ent, bs); |
8741 | 0 | e = gf_isom_box_parse_ex(&tpl_ent, bs, GF_ISOM_BOX_TYPE_STSD, GF_FALSE, 0); |
8742 | 0 | gf_bs_del(bs); |
8743 | 0 | if (e) return e; |
8744 | | |
8745 | 0 | while (gf_list_count(tpl_ent->child_boxes)) { |
8746 | 0 | u32 j=0; |
8747 | 0 | Bool found = GF_FALSE; |
8748 | 0 | GF_Box *abox = gf_list_pop_front(tpl_ent->child_boxes); |
8749 | |
|
8750 | 0 | switch (abox->type) { |
8751 | 0 | case GF_ISOM_BOX_TYPE_SINF: |
8752 | 0 | case GF_ISOM_BOX_TYPE_RINF: |
8753 | 0 | case GF_ISOM_BOX_TYPE_BTRT: |
8754 | 0 | found = GF_TRUE; |
8755 | 0 | break; |
8756 | 0 | } |
8757 | | |
8758 | 0 | if (found) { |
8759 | 0 | gf_isom_box_del(abox); |
8760 | 0 | continue; |
8761 | 0 | } |
8762 | | |
8763 | 0 | if (!ent->child_boxes) ent->child_boxes = gf_list_new(); |
8764 | 0 | for (j=0; j<gf_list_count(ent->child_boxes); j++) { |
8765 | 0 | GF_Box *b = gf_list_get(ent->child_boxes, j); |
8766 | 0 | if (b->type == abox->type) { |
8767 | 0 | found = GF_TRUE; |
8768 | 0 | break; |
8769 | 0 | } |
8770 | 0 | } |
8771 | 0 | if (!found) { |
8772 | 0 | gf_list_add(ent->child_boxes, abox); |
8773 | 0 | } else { |
8774 | 0 | gf_isom_box_del(abox); |
8775 | 0 | } |
8776 | 0 | } |
8777 | 0 | gf_isom_box_del(tpl_ent); |
8778 | | |
8779 | | //patch for old export |
8780 | 0 | GF_Box *abox = gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_SINF); |
8781 | 0 | if (abox) { |
8782 | 0 | gf_list_del_item(ent->child_boxes, abox); |
8783 | 0 | gf_list_add(ent->child_boxes, abox); |
8784 | 0 | } |
8785 | 0 | return GF_OK; |
8786 | 0 | } |
8787 | | |
8788 | | |
8789 | | #include <gpac/xml.h> |
8790 | | GF_EXPORT |
8791 | | GF_Err gf_isom_apply_box_patch(GF_ISOFile *file, GF_ISOTrackID globalTrackID, const char *box_patch_filename, Bool for_fragments) |
8792 | 0 | { |
8793 | 0 | GF_Err e; |
8794 | 0 | GF_DOMParser *dom; |
8795 | 0 | u32 i; |
8796 | 0 | GF_XMLNode *root; |
8797 | 0 | u8 *box_data=NULL; |
8798 | 0 | u32 box_data_size; |
8799 | 0 | if (!file || !box_patch_filename) return GF_BAD_PARAM; |
8800 | | #ifdef GPAC_DISABLE_ISOM_FRAGMENTS |
8801 | | if (for_fragments) return GF_NOT_SUPPORTED; |
8802 | | #endif |
8803 | 0 | dom = gf_xml_dom_new(); |
8804 | 0 | if (strstr(box_patch_filename, "<?xml")) { |
8805 | 0 | e = gf_xml_dom_parse_string(dom, (char *) box_patch_filename); |
8806 | 0 | } else { |
8807 | 0 | e = gf_xml_dom_parse(dom, box_patch_filename, NULL, NULL); |
8808 | 0 | } |
8809 | 0 | if (e) goto err_exit; |
8810 | | |
8811 | 0 | root = gf_xml_dom_get_root(dom); |
8812 | 0 | if (!root || strcmp(root->name, "GPACBOXES")) { |
8813 | 0 | e = GF_NON_COMPLIANT_BITSTREAM; |
8814 | 0 | goto err_exit; |
8815 | 0 | } |
8816 | | |
8817 | | //compute size of each child boxes to freeze the order |
8818 | 0 | if (for_fragments) { |
8819 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
8820 | 0 | u32 count = file->moof ? gf_list_count(file->moof->child_boxes) : 0; |
8821 | 0 | for (i=0; i<count; i++) { |
8822 | 0 | GF_Box *box = gf_list_get(file->moof->child_boxes, i); |
8823 | 0 | if (!(box->internal_flags & GF_ISOM_ORDER_FREEZE)) { |
8824 | 0 | gf_isom_box_size(box); |
8825 | 0 | gf_isom_box_freeze_order(box); |
8826 | 0 | } |
8827 | 0 | } |
8828 | 0 | #endif |
8829 | 0 | } else { |
8830 | 0 | for (i=0; i<gf_list_count(file->TopBoxes); i++) { |
8831 | 0 | GF_Box *box = gf_list_get(file->TopBoxes, i); |
8832 | 0 | if (!(box->internal_flags & GF_ISOM_ORDER_FREEZE)) { |
8833 | 0 | gf_isom_box_size(box); |
8834 | 0 | gf_isom_box_freeze_order(box); |
8835 | 0 | } |
8836 | 0 | } |
8837 | 0 | } |
8838 | |
|
8839 | 0 | for (i=0; i<gf_list_count(root->content); i++) { |
8840 | 0 | u32 j; |
8841 | 0 | u32 path_len; |
8842 | 0 | Bool essential_prop=GF_FALSE; |
8843 | 0 | u32 trackID=globalTrackID; |
8844 | 0 | u32 item_id=trackID; |
8845 | 0 | Bool is_frag_box; |
8846 | 0 | char *box_path=NULL; |
8847 | 0 | GF_Box *parent_box = NULL; |
8848 | 0 | GF_XMLNode *box_edit = gf_list_get(root->content, i); |
8849 | 0 | if (!box_edit->name || strcmp(box_edit->name, "Box")) continue; |
8850 | | |
8851 | 0 | for (j=0; j<gf_list_count(box_edit->attributes);j++) { |
8852 | 0 | GF_XMLAttribute *att = gf_list_get(box_edit->attributes, j); |
8853 | 0 | if (!strcmp(att->name, "path")) box_path = att->value; |
8854 | 0 | else if (!strcmp(att->name, "essential")) { |
8855 | 0 | if (!strcmp(att->value, "yes") || !strcmp(att->value, "true") || !strcmp(att->value, "1")) { |
8856 | 0 | essential_prop=GF_TRUE; |
8857 | 0 | } |
8858 | 0 | } |
8859 | 0 | else if (!strcmp(att->name, "itemID")) |
8860 | 0 | item_id = atoi(att->value); |
8861 | 0 | else if (!globalTrackID && !strcmp(att->name, "trackID")) |
8862 | 0 | trackID = atoi(att->value); |
8863 | 0 | } |
8864 | |
|
8865 | 0 | if (!box_path) continue; |
8866 | 0 | path_len = (u32) strlen(box_path); |
8867 | |
|
8868 | 0 | is_frag_box = !strncmp(box_path, "traf", 4) ? GF_TRUE : GF_FALSE; |
8869 | |
|
8870 | 0 | if (for_fragments && !is_frag_box) continue; |
8871 | 0 | else if (!for_fragments && is_frag_box) continue; |
8872 | | |
8873 | 0 | gf_xml_parse_bit_sequence(box_edit, box_patch_filename, &box_data, &box_data_size); |
8874 | 0 | if (box_data_size && (box_data_size<4) ) { |
8875 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Wrong BS specification for box, shall either be empty or at least 4 bytes for box type\n")); |
8876 | 0 | e = GF_NON_COMPLIANT_BITSTREAM; |
8877 | 0 | goto err_exit; |
8878 | 0 | } |
8879 | | |
8880 | 0 | while (box_path && (path_len>=4)) { |
8881 | 0 | u32 parent_list_box_type; |
8882 | 0 | GF_List **parent_list; |
8883 | 0 | u32 box_type = GF_4CC(box_path[0],box_path[1],box_path[2],box_path[3]); |
8884 | 0 | GF_Box *box=NULL; |
8885 | 0 | GF_BitStream *bs; |
8886 | 0 | s32 insert_pos = -1; |
8887 | 0 | box_path+=4; |
8888 | 0 | path_len-=4; |
8889 | |
|
8890 | 0 | if (!parent_box) { |
8891 | 0 | box=gf_isom_box_find_child(file->TopBoxes, box_type); |
8892 | 0 | if (!box) { |
8893 | 0 | if (box_type==GF_ISOM_BOX_TYPE_TRAK) { |
8894 | 0 | if (trackID) { |
8895 | 0 | box = (GF_Box *) gf_isom_get_track_box(file, gf_isom_get_track_by_id(file, trackID) ); |
8896 | 0 | } |
8897 | 0 | if (!box && gf_list_count(file->moov->trackList)==1) { |
8898 | 0 | box = gf_list_get(file->moov->trackList, 0); |
8899 | 0 | } |
8900 | 0 | } |
8901 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
8902 | 0 | else if (box_type==GF_ISOM_BOX_TYPE_TRAF) { |
8903 | 0 | if (trackID) { |
8904 | 0 | box = (GF_Box *) gf_isom_get_traf(file, trackID); |
8905 | 0 | } |
8906 | 0 | if (!box && file->moof && gf_list_count(file->moof->TrackList)==1) { |
8907 | 0 | box = gf_list_get(file->moof->TrackList, 0); |
8908 | 0 | } |
8909 | 0 | } |
8910 | 0 | #endif |
8911 | 0 | } |
8912 | 0 | if (!box) { |
8913 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Cannot locate box type %s at root or as track\n", gf_4cc_to_str(box_type) )); |
8914 | 0 | e = GF_BAD_PARAM; |
8915 | 0 | goto err_exit; |
8916 | 0 | } |
8917 | 0 | } else { |
8918 | 0 | box = gf_isom_box_find_child(parent_box->child_boxes, box_type); |
8919 | 0 | if (!box) { |
8920 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Cannot locate box type %s at child of %s\n", gf_4cc_to_str(box_type), gf_4cc_to_str(parent_box->type))); |
8921 | 0 | e = GF_BAD_PARAM; |
8922 | 0 | goto err_exit; |
8923 | 0 | } |
8924 | 0 | } |
8925 | | // '.' is child access |
8926 | 0 | if (path_len && (box_path[0]=='.')) { |
8927 | 0 | box_path += 1; |
8928 | 0 | path_len-=1; |
8929 | 0 | parent_box = box; |
8930 | 0 | continue; |
8931 | 0 | } |
8932 | | |
8933 | 0 | if (parent_box && !parent_box->child_boxes) parent_box->child_boxes = gf_list_new(); |
8934 | 0 | parent_list = parent_box ? &parent_box->child_boxes : &file->TopBoxes; |
8935 | 0 | parent_list_box_type = parent_box ? parent_box->type : 0; |
8936 | | |
8937 | | // '+' is append after, '-' is insert before |
8938 | 0 | if (path_len && ((box_path[0]=='-') || (box_path[0]=='+')) ) { |
8939 | 0 | s32 idx = gf_list_find(*parent_list, box); |
8940 | 0 | if (idx<0) { |
8941 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Invalid index for path %s\n", box_path)); |
8942 | 0 | e = GF_NON_COMPLIANT_BITSTREAM; |
8943 | 0 | goto err_exit; |
8944 | 0 | } |
8945 | 0 | if (box_path[0]=='+') insert_pos = idx+1; |
8946 | 0 | else insert_pos = idx; |
8947 | 0 | } |
8948 | 0 | else if (path_len) { |
8949 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Invalid path %s, expecting either '-', '+' or '.' as separators\n", box_path)); |
8950 | 0 | e = GF_NON_COMPLIANT_BITSTREAM; |
8951 | 0 | goto err_exit; |
8952 | 0 | } |
8953 | | |
8954 | 0 | if (!box_data) { |
8955 | 0 | if (insert_pos>=0) { |
8956 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[ISOBMFF] Invalid path %s for box removal, ignoring position\n", box_path)); |
8957 | 0 | } |
8958 | 0 | switch (box->type) { |
8959 | 0 | case GF_ISOM_BOX_TYPE_MOOV: |
8960 | 0 | file->moov = NULL; |
8961 | 0 | break; |
8962 | 0 | case GF_ISOM_BOX_TYPE_MDAT: |
8963 | 0 | file->mdat = NULL; |
8964 | 0 | break; |
8965 | 0 | case GF_ISOM_BOX_TYPE_PDIN: |
8966 | 0 | file->pdin = NULL; |
8967 | 0 | break; |
8968 | 0 | case GF_ISOM_BOX_TYPE_FTYP: |
8969 | 0 | file->brand = NULL; |
8970 | 0 | break; |
8971 | 0 | case GF_ISOM_BOX_TYPE_META: |
8972 | 0 | if ((GF_Box *) file->meta == box) file->meta = NULL; |
8973 | 0 | break; |
8974 | 0 | } |
8975 | 0 | if (parent_box) { |
8976 | 0 | gf_isom_box_remove_from_parent(parent_box, box); |
8977 | 0 | } |
8978 | 0 | gf_isom_box_del_parent(parent_list, box); |
8979 | 0 | } else { |
8980 | 0 | u32 size; |
8981 | |
|
8982 | 0 | bs = gf_bs_new(box_data, box_data_size, GF_BITSTREAM_READ); |
8983 | 0 | size = gf_bs_read_u32(bs); |
8984 | 0 | if (size != box_data_size) { |
8985 | 0 | GF_UnknownBox *new_box = (GF_UnknownBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UNKNOWN); |
8986 | 0 | new_box->original_4cc = size; |
8987 | 0 | new_box->dataSize = (u32) gf_bs_available(bs); |
8988 | 0 | new_box->data = gf_malloc(sizeof(u8)*new_box->dataSize); |
8989 | 0 | gf_bs_read_data(bs, new_box->data, new_box->dataSize); |
8990 | 0 | if (insert_pos<0) { |
8991 | 0 | gf_list_add(box->child_boxes, new_box); |
8992 | 0 | insert_pos = gf_list_find(box->child_boxes, new_box); |
8993 | 0 | } else { |
8994 | 0 | gf_list_insert(*parent_list, new_box, insert_pos); |
8995 | 0 | } |
8996 | |
|
8997 | 0 | if (parent_box && (parent_box->type==GF_ISOM_BOX_TYPE_IPRP)) { |
8998 | 0 | GF_ItemPropertyAssociationBox *ipma = (GF_ItemPropertyAssociationBox *) gf_isom_box_find_child(parent_box->child_boxes, GF_ISOM_BOX_TYPE_IPMA); |
8999 | 0 | if (!item_id) { |
9000 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[ISOBMFF] Inserting box in ipco without itemID, no association added\n")); |
9001 | 0 | } else if (ipma) { |
9002 | 0 | u32 nb_asso, k, insert_pos; |
9003 | 0 | GF_ItemPropertyAssociationEntry *entry = NULL; |
9004 | 0 | nb_asso = gf_list_count(ipma->entries); |
9005 | 0 | insert_pos = 0; |
9006 | 0 | for (k=0; k<nb_asso;k++) { |
9007 | 0 | entry = gf_list_get(ipma->entries, k); |
9008 | 0 | if (entry->item_id==item_id) break; |
9009 | | // item ids must appear in increasing order |
9010 | 0 | if (item_id>entry->item_id) ++insert_pos; |
9011 | 0 | entry = NULL; |
9012 | 0 | } |
9013 | 0 | if (!entry) { |
9014 | 0 | GF_SAFEALLOC(entry, GF_ItemPropertyAssociationEntry); |
9015 | 0 | if (!entry) return GF_OUT_OF_MEM; |
9016 | 0 | gf_list_insert(ipma->entries, entry, insert_pos); |
9017 | 0 | entry->item_id = item_id; |
9018 | 0 | } |
9019 | 0 | entry->associations = gf_realloc(entry->associations, sizeof(GF_ItemPropertyAssociationSlot) * (entry->nb_associations+1)); |
9020 | 0 | entry->associations[entry->nb_associations].essential = essential_prop; |
9021 | 0 | entry->associations[entry->nb_associations].index = 1+insert_pos; |
9022 | 0 | entry->nb_associations++; |
9023 | 0 | } |
9024 | 0 | } |
9025 | 0 | } else { |
9026 | 0 | u32 box_idx = 0; |
9027 | |
|
9028 | 0 | gf_bs_seek(bs, 0); |
9029 | 0 | while (gf_bs_available(bs)) { |
9030 | 0 | GF_Box *new_box; |
9031 | 0 | e = gf_isom_box_parse_ex(&new_box, bs, (insert_pos<0) ? box->type : parent_list_box_type, parent_box ? GF_FALSE : GF_TRUE, 0); |
9032 | 0 | if (e) { |
9033 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] failed to parse box\n", box_path)); |
9034 | 0 | gf_bs_del(bs); |
9035 | 0 | goto err_exit; |
9036 | 0 | } |
9037 | 0 | if (insert_pos<0) { |
9038 | 0 | gf_list_add(box->child_boxes, new_box); |
9039 | 0 | } else { |
9040 | 0 | gf_list_insert(*parent_list, new_box, insert_pos+box_idx); |
9041 | 0 | box_idx++; |
9042 | 0 | } |
9043 | 0 | } |
9044 | 0 | } |
9045 | 0 | gf_bs_del(bs); |
9046 | |
|
9047 | 0 | } |
9048 | 0 | gf_free(box_data); |
9049 | 0 | box_data = NULL; |
9050 | 0 | box_path = NULL; |
9051 | 0 | } |
9052 | 0 | } |
9053 | | |
9054 | 0 | err_exit: |
9055 | |
|
9056 | 0 | gf_xml_dom_del(dom); |
9057 | 0 | if (box_data) gf_free(box_data); |
9058 | 0 | return e; |
9059 | 0 | } |
9060 | | |
9061 | | GF_EXPORT |
9062 | | GF_Err gf_isom_set_track_magic(GF_ISOFile *movie, u32 trackNumber, u64 magic) |
9063 | 0 | { |
9064 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
9065 | 0 | if (!trak) return GF_BAD_PARAM; |
9066 | 0 | trak->magic = magic; |
9067 | 0 | return GF_OK; |
9068 | 0 | } |
9069 | | |
9070 | | GF_EXPORT |
9071 | | GF_Err gf_isom_set_track_index(GF_ISOFile *movie, u32 trackNumber, u32 index) |
9072 | 0 | { |
9073 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
9074 | 0 | if (!trak) return GF_BAD_PARAM; |
9075 | 0 | trak->mux_index = index; |
9076 | 0 | if (index) |
9077 | 0 | movie->tracks_use_mux_index ++; |
9078 | 0 | else if (movie->tracks_use_mux_index) |
9079 | 0 | movie->tracks_use_mux_index --; |
9080 | 0 | return GF_OK; |
9081 | 0 | } |
9082 | | |
9083 | | GF_EXPORT |
9084 | | GF_Err gf_isom_set_ipod_compatible(GF_ISOFile *the_file, u32 trackNumber) |
9085 | 0 | { |
9086 | 0 | GF_TrackBox *trak; |
9087 | 0 | GF_Err e; |
9088 | 0 | GF_MPEGVisualSampleEntryBox *entry; |
9089 | |
|
9090 | 0 | e = gf_isom_can_access_movie(the_file, GF_ISOM_OPEN_WRITE); |
9091 | 0 | if (e) return e; |
9092 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
9093 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
9094 | 0 | entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, 0); |
9095 | 0 | if (!entry) return GF_OK; |
9096 | 0 | switch (entry->type) { |
9097 | 0 | case GF_ISOM_BOX_TYPE_AVC1: |
9098 | 0 | case GF_ISOM_BOX_TYPE_AVC2: |
9099 | 0 | case GF_ISOM_BOX_TYPE_AVC3: |
9100 | 0 | case GF_ISOM_BOX_TYPE_AVC4: |
9101 | 0 | case GF_ISOM_BOX_TYPE_SVC1: |
9102 | 0 | case GF_ISOM_BOX_TYPE_MVC1: |
9103 | 0 | case GF_ISOM_BOX_TYPE_HVC1: |
9104 | 0 | case GF_ISOM_BOX_TYPE_HEV1: |
9105 | 0 | case GF_ISOM_BOX_TYPE_HVT1: |
9106 | 0 | break; |
9107 | 0 | default: |
9108 | 0 | return GF_OK; |
9109 | 0 | } |
9110 | | |
9111 | 0 | if (!entry->ipod_ext) { |
9112 | 0 | entry->ipod_ext = (GF_UnknownUUIDBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_UUID); |
9113 | 0 | if (!entry->ipod_ext) return GF_OUT_OF_MEM; |
9114 | 0 | } |
9115 | 0 | memcpy(entry->ipod_ext->uuid, GF_ISOM_IPOD_EXT, sizeof(u8)*16); |
9116 | 0 | entry->ipod_ext->dataSize = 4; |
9117 | 0 | entry->ipod_ext->data = gf_malloc(sizeof(u8)*4); |
9118 | 0 | if (!entry->ipod_ext->data) return GF_OUT_OF_MEM; |
9119 | 0 | memset(entry->ipod_ext->data, 0, sizeof(u8)*4); |
9120 | 0 | return GF_OK; |
9121 | 0 | } |
9122 | | |
9123 | | GF_EXPORT |
9124 | | Bool gf_isom_is_inplace_rewrite(GF_ISOFile *movie) |
9125 | 0 | { |
9126 | 0 | if (!movie) return GF_FALSE; |
9127 | 0 | if (!movie->no_inplace_rewrite) { |
9128 | | //things where added to the file, no inplace rewrite |
9129 | 0 | if (movie->editFileMap && gf_bs_get_size(movie->editFileMap->bs)) |
9130 | 0 | movie->no_inplace_rewrite = GF_TRUE; |
9131 | | //block redirect (used by mp4mx), no inplace rewrite |
9132 | 0 | else if (movie->on_block_out || (movie->finalName && !strcmp(movie->finalName, "_gpac_isobmff_redirect"))) |
9133 | 0 | movie->no_inplace_rewrite = GF_TRUE; |
9134 | | //stdout redirect, no inplace rewrite |
9135 | 0 | else if (movie->finalName && !strcmp(movie->finalName, "std")) |
9136 | 0 | movie->no_inplace_rewrite = GF_TRUE; |
9137 | | //new file, no inplace rewrite |
9138 | 0 | else if (!movie->fileName) |
9139 | 0 | movie->no_inplace_rewrite = GF_TRUE; |
9140 | 0 | } |
9141 | 0 | if (movie->no_inplace_rewrite) return GF_FALSE; |
9142 | | |
9143 | 0 | return GF_TRUE; |
9144 | 0 | } |
9145 | | |
9146 | | GF_EXPORT |
9147 | | void gf_isom_disable_inplace_rewrite(GF_ISOFile *movie) |
9148 | 0 | { |
9149 | 0 | if (movie) |
9150 | 0 | movie->no_inplace_rewrite = GF_TRUE; |
9151 | 0 | } |
9152 | | |
9153 | | GF_EXPORT |
9154 | | GF_Err gf_isom_set_y3d_info(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, GF_ISOM_Y3D_Info *info) |
9155 | 0 | { |
9156 | 0 | GF_Err e; |
9157 | 0 | u32 proj_type; |
9158 | 0 | GF_SampleEntryBox *ent; |
9159 | 0 | GF_TrackBox *trak; |
9160 | |
|
9161 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
9162 | 0 | if (e) return e; |
9163 | | |
9164 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
9165 | 0 | if (!trak || !trak->Media || !info) return GF_BAD_PARAM; |
9166 | | |
9167 | 0 | ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex-1); |
9168 | 0 | if (!ent) return GF_BAD_PARAM; |
9169 | | |
9170 | 0 | if (info->projection_type > GF_PROJ360_EQR) return GF_NOT_SUPPORTED; |
9171 | | |
9172 | 0 | GF_Stereo3DBox *st3d = (GF_Stereo3DBox *) gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_ST3D); |
9173 | 0 | if (st3d) { |
9174 | 0 | if (info->stereo_type) { |
9175 | 0 | st3d->stereo_type = info->stereo_type; |
9176 | 0 | } else { |
9177 | 0 | gf_isom_box_del_parent(&ent->child_boxes, (GF_Box *) st3d); |
9178 | 0 | } |
9179 | 0 | } else if (info->stereo_type) { |
9180 | 0 | st3d = (GF_Stereo3DBox *) gf_isom_box_new_parent(&ent->child_boxes, GF_ISOM_BOX_TYPE_ST3D); |
9181 | 0 | if (!st3d) return GF_OUT_OF_MEM; |
9182 | 0 | st3d->stereo_type = info->stereo_type; |
9183 | 0 | } |
9184 | | |
9185 | | |
9186 | 0 | GF_Box *sv3d = gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_SV3D); |
9187 | 0 | if (sv3d && !info->projection_type) { |
9188 | 0 | gf_isom_box_del_parent(&ent->child_boxes, sv3d); |
9189 | 0 | return GF_OK; |
9190 | 0 | } |
9191 | | |
9192 | 0 | if (!sv3d && !info->projection_type) { |
9193 | 0 | return GF_OK; |
9194 | 0 | } |
9195 | 0 | if (!sv3d) { |
9196 | 0 | sv3d = gf_isom_box_new_parent(&ent->child_boxes, GF_ISOM_BOX_TYPE_SV3D); |
9197 | 0 | if (!sv3d) return GF_OUT_OF_MEM; |
9198 | 0 | } |
9199 | | |
9200 | | //svhd mandatory |
9201 | 0 | GF_SphericalVideoInfoBox *svhd = (GF_SphericalVideoInfoBox *) gf_isom_box_find_child(sv3d->child_boxes, GF_ISOM_BOX_TYPE_SVHD); |
9202 | 0 | if (svhd) { |
9203 | 0 | if (svhd->string) gf_free(svhd->string); |
9204 | 0 | } else { |
9205 | 0 | svhd = (GF_SphericalVideoInfoBox *) gf_isom_box_new_parent(&sv3d->child_boxes, GF_ISOM_BOX_TYPE_SVHD); |
9206 | 0 | if (!svhd) return GF_OUT_OF_MEM; |
9207 | 0 | } |
9208 | 0 | svhd->string = gf_strdup(info->meta_data ? info->meta_data : ""); |
9209 | | |
9210 | | //proj mandatory |
9211 | 0 | GF_Box *proj = gf_isom_box_find_child(sv3d->child_boxes, GF_ISOM_BOX_TYPE_PROJ); |
9212 | 0 | if (!proj) { |
9213 | 0 | proj = gf_isom_box_new_parent(&sv3d->child_boxes, GF_ISOM_BOX_TYPE_PROJ); |
9214 | 0 | if (!proj) return GF_OUT_OF_MEM; |
9215 | 0 | } |
9216 | | |
9217 | 0 | GF_ProjectionHeaderBox *projh = (GF_ProjectionHeaderBox *) gf_isom_box_find_child(proj->child_boxes, GF_ISOM_BOX_TYPE_PRHD); |
9218 | | //prj header mandatory |
9219 | 0 | if (!projh) { |
9220 | 0 | projh = (GF_ProjectionHeaderBox *) gf_isom_box_new_parent(&proj->child_boxes, GF_ISOM_BOX_TYPE_PRHD); |
9221 | 0 | if (!projh) return GF_OUT_OF_MEM; |
9222 | 0 | } |
9223 | 0 | projh->yaw = info->yaw; |
9224 | 0 | projh->pitch = info->pitch; |
9225 | 0 | projh->roll = info->roll; |
9226 | |
|
9227 | 0 | proj_type = (info->projection_type==GF_PROJ360_CUBE_MAP) ? GF_ISOM_BOX_TYPE_CBMP : GF_ISOM_BOX_TYPE_EQUI; |
9228 | |
|
9229 | 0 | GF_ProjectionTypeBox *projt = (GF_ProjectionTypeBox *) gf_isom_box_find_child(proj->child_boxes, proj_type); |
9230 | 0 | if (!projt) { |
9231 | 0 | projt = (GF_ProjectionTypeBox *) gf_isom_box_new_parent(&proj->child_boxes, proj_type); |
9232 | 0 | if (!projt) return GF_OUT_OF_MEM; |
9233 | 0 | } |
9234 | 0 | if (info->projection_type==GF_PROJ360_CUBE_MAP) { |
9235 | 0 | projt->layout = info->layout; |
9236 | 0 | projt->padding = info->padding; |
9237 | 0 | } else { |
9238 | 0 | projt->bounds_top = info->top; |
9239 | 0 | projt->bounds_bottom = info->bottom; |
9240 | 0 | projt->bounds_left = info->left; |
9241 | 0 | projt->bounds_right = info->right; |
9242 | 0 | } |
9243 | | |
9244 | | //remove other ones |
9245 | 0 | GF_Box *b = gf_isom_box_new_parent(&proj->child_boxes, GF_ISOM_BOX_TYPE_MSHP); |
9246 | 0 | if (b) gf_isom_box_del_parent(&proj->child_boxes, b); |
9247 | 0 | if (info->projection_type==GF_PROJ360_CUBE_MAP) { |
9248 | 0 | b = gf_isom_box_new_parent(&proj->child_boxes, GF_ISOM_BOX_TYPE_EQUI); |
9249 | 0 | if (b) gf_isom_box_del_parent(&proj->child_boxes, b); |
9250 | 0 | } else { |
9251 | 0 | b = gf_isom_box_new_parent(&proj->child_boxes, GF_ISOM_BOX_TYPE_EQUI); |
9252 | 0 | if (b) gf_isom_box_del_parent(&proj->child_boxes, b); |
9253 | |
|
9254 | 0 | } |
9255 | 0 | return GF_OK; |
9256 | 0 | } |
9257 | | |
9258 | | #endif //!defined(GPAC_DISABLE_ISOM_WRITE) |
9259 | | |
9260 | | #ifndef GPAC_DISABLE_ISOM |
9261 | | |
9262 | | GF_Err gf_isom_add_sample_aux_info_internal(GF_TrackBox *trak, void *_traf, u32 sampleNumber, u32 aux_type, u32 aux_info, u8 *data, u32 size) |
9263 | 0 | { |
9264 | 0 | u32 i, count; |
9265 | 0 | GF_List **child_box_cont, **child_box_sai, **child_box_saiz, **child_box_saio; |
9266 | 0 | GF_UnknownBox *sai_cont = NULL; |
9267 | |
|
9268 | 0 | if (!trak && !_traf) return GF_BAD_PARAM; |
9269 | | |
9270 | 0 | if (trak) { |
9271 | 0 | child_box_cont = &trak->child_boxes; |
9272 | 0 | child_box_sai = &trak->Media->information->sampleTable->child_boxes; |
9273 | 0 | child_box_saiz = &trak->Media->information->sampleTable->sai_sizes; |
9274 | 0 | child_box_saio = &trak->Media->information->sampleTable->sai_offsets; |
9275 | 0 | } else { |
9276 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
9277 | 0 | GF_TrackFragmentBox *traf = (GF_TrackFragmentBox *)_traf; |
9278 | |
|
9279 | 0 | child_box_cont = &traf->child_boxes; |
9280 | 0 | child_box_sai = &traf->child_boxes; |
9281 | 0 | child_box_saiz = &traf->sai_sizes; |
9282 | 0 | child_box_saio = &traf->sai_offsets; |
9283 | 0 | #endif |
9284 | 0 | } |
9285 | |
|
9286 | 0 | count = gf_list_count(*child_box_cont); |
9287 | 0 | for (i=0; i<count; i++) { |
9288 | 0 | GF_UnknownBox *unkn = gf_list_get(*child_box_cont, i); |
9289 | 0 | if (unkn->type != GF_ISOM_BOX_TYPE_UNKNOWN) continue; |
9290 | 0 | if (unkn->original_4cc != GF_ISOM_BOX_TYPE_GDAT) continue; |
9291 | 0 | if (unkn->sai_type != aux_type) continue; |
9292 | 0 | if (unkn->sai_aux_info != aux_info) continue; |
9293 | 0 | sai_cont = unkn; |
9294 | 0 | break; |
9295 | 0 | } |
9296 | 0 | if (!sai_cont) { |
9297 | 0 | sai_cont = (GF_UnknownBox *) gf_isom_box_new_parent(child_box_cont, GF_ISOM_BOX_TYPE_UNKNOWN); |
9298 | 0 | if (!sai_cont) return GF_OUT_OF_MEM; |
9299 | 0 | sai_cont->original_4cc = GF_ISOM_BOX_TYPE_GDAT; |
9300 | 0 | sai_cont->sai_type = aux_type; |
9301 | 0 | sai_cont->sai_aux_info = aux_info; |
9302 | 0 | } |
9303 | 0 | sai_cont->data = gf_realloc(sai_cont->data, (size+sai_cont->dataSize)); |
9304 | 0 | if (!sai_cont->data) return GF_OUT_OF_MEM; |
9305 | 0 | memcpy(sai_cont->data+sai_cont->dataSize, data, size); |
9306 | 0 | sai_cont->dataSize += size; |
9307 | |
|
9308 | 0 | GF_SampleAuxiliaryInfoSizeBox *saiz=NULL; |
9309 | 0 | GF_SampleAuxiliaryInfoOffsetBox *saio=NULL; |
9310 | 0 | count = gf_list_count(*child_box_saiz); |
9311 | 0 | for (i=0; i<count; i++) { |
9312 | 0 | saiz = gf_list_get(*child_box_saiz, i); |
9313 | 0 | if ((saiz->aux_info_type==aux_type) && (saiz->aux_info_type_parameter==aux_info)) break; |
9314 | 0 | saiz = NULL; |
9315 | 0 | } |
9316 | 0 | if (!saiz) { |
9317 | 0 | saiz = (GF_SampleAuxiliaryInfoSizeBox *) gf_isom_box_new_parent(child_box_sai, GF_ISOM_BOX_TYPE_SAIZ); |
9318 | 0 | if (!saiz) return GF_OUT_OF_MEM; |
9319 | 0 | if (! *child_box_saiz) *child_box_saiz = gf_list_new(); |
9320 | 0 | gf_list_add(*child_box_saiz, saiz); |
9321 | |
|
9322 | 0 | saiz->aux_info_type = aux_type; |
9323 | 0 | saiz->aux_info_type_parameter = aux_info; |
9324 | 0 | } |
9325 | | |
9326 | 0 | if (saiz->sample_count >= sampleNumber) |
9327 | 0 | return GF_BAD_PARAM; |
9328 | | |
9329 | 0 | if ( (!saiz->sample_count && (sampleNumber==1)) |
9330 | 0 | || ((saiz->default_sample_info_size==size) && size) |
9331 | 0 | ) { |
9332 | 0 | saiz->sample_count ++; |
9333 | 0 | saiz->default_sample_info_size = size; |
9334 | 0 | } else { |
9335 | 0 | if (sampleNumber > saiz->sample_alloc) { |
9336 | 0 | saiz->sample_alloc = sampleNumber+10; |
9337 | 0 | saiz->sample_info_size = (u8*)gf_realloc(saiz->sample_info_size, sizeof(u8)*(saiz->sample_alloc)); |
9338 | 0 | } |
9339 | |
|
9340 | 0 | if (saiz->default_sample_info_size) { |
9341 | 0 | for (i=0; i<saiz->sample_count; i++) |
9342 | 0 | saiz->sample_info_size[i] = saiz->default_sample_info_size; |
9343 | 0 | saiz->default_sample_info_size = 0; |
9344 | 0 | } |
9345 | 0 | for (i=saiz->sample_count; i<sampleNumber-1; i++) |
9346 | 0 | saiz->sample_info_size[i] = 0; |
9347 | |
|
9348 | 0 | saiz->sample_info_size[sampleNumber-1] = size; |
9349 | 0 | saiz->sample_count = sampleNumber; |
9350 | 0 | } |
9351 | | |
9352 | |
|
9353 | 0 | count = gf_list_count(*child_box_saio); |
9354 | 0 | for (i=0; i<count; i++) { |
9355 | 0 | saio = gf_list_get(*child_box_saio, i); |
9356 | 0 | if ((saio->aux_info_type==aux_type) && (saio->aux_info_type_parameter==aux_info)) break; |
9357 | 0 | saio = NULL; |
9358 | 0 | } |
9359 | 0 | if (!saio) { |
9360 | 0 | saio = (GF_SampleAuxiliaryInfoOffsetBox *) gf_isom_box_new_parent(child_box_sai, GF_ISOM_BOX_TYPE_SAIO); |
9361 | 0 | if (!saio) return GF_OUT_OF_MEM; |
9362 | 0 | if (! *child_box_saio) *child_box_saio = gf_list_new(); |
9363 | 0 | gf_list_add(*child_box_saio, saio); |
9364 | 0 | saio->aux_info_type = aux_type; |
9365 | 0 | saio->aux_info_type_parameter = aux_info; |
9366 | 0 | } |
9367 | 0 | if (!saio->sai_data) saio->sai_data = sai_cont; |
9368 | 0 | saio->version = 1; |
9369 | 0 | saio->entry_count = 1; |
9370 | |
|
9371 | 0 | return GF_OK; |
9372 | 0 | } |
9373 | | #endif // GPAC_DISABLE_ISOM |
9374 | | |
9375 | | |
9376 | | #if !defined(GPAC_DISABLE_ISOM_WRITE) |
9377 | | |
9378 | | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
9379 | | GF_Err gf_isom_fragment_set_sample_aux_info(GF_ISOFile *movie, u32 trackID, u32 sample_number_in_frag, u32 aux_type, u32 aux_info, u8 *data, u32 size) |
9380 | 0 | { |
9381 | 0 | GF_TrackFragmentBox *traf; |
9382 | 0 | if (!movie || !movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM; |
9383 | | |
9384 | 0 | traf = gf_isom_get_traf(movie, trackID); |
9385 | 0 | if (!traf) return GF_BAD_PARAM; |
9386 | 0 | return gf_isom_add_sample_aux_info_internal(NULL, traf, sample_number_in_frag, aux_type, aux_info, data, size); |
9387 | 0 | } |
9388 | | #endif |
9389 | | |
9390 | | GF_Err gf_isom_add_sample_aux_info(GF_ISOFile *file, u32 track, u32 sampleNumber, u32 aux_type, u32 aux_info, u8 *data, u32 size) |
9391 | 0 | { |
9392 | 0 | GF_Err e; |
9393 | 0 | GF_TrackBox *trak; |
9394 | |
|
9395 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
9396 | 0 | if (e) return e; |
9397 | | |
9398 | 0 | trak = gf_isom_get_track_box(file, track); |
9399 | 0 | if (!trak) return GF_BAD_PARAM; |
9400 | | |
9401 | 0 | return gf_isom_add_sample_aux_info_internal(trak, NULL, sampleNumber, aux_type, aux_info, data, size); |
9402 | 0 | } |
9403 | | |
9404 | | |
9405 | | GF_Err gf_isom_set_meta_qt(GF_ISOFile *file) |
9406 | 0 | { |
9407 | 0 | u32 i, count; |
9408 | 0 | if (!file) return GF_BAD_PARAM; |
9409 | 0 | GF_Err e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
9410 | 0 | if (e) return e; |
9411 | 0 | if (file->moov->meta) |
9412 | 0 | file->moov->meta->write_qt = 1; |
9413 | |
|
9414 | 0 | count = gf_list_count(file->moov->trackList); |
9415 | 0 | for (i=0; i<count; i++) { |
9416 | 0 | GF_TrackBox *trak = gf_list_get(file->moov->trackList, i); |
9417 | 0 | if (trak->meta) |
9418 | 0 | trak->meta->write_qt = 1; |
9419 | 0 | } |
9420 | 0 | return GF_OK; |
9421 | 0 | } |
9422 | | |
9423 | | |
9424 | | GF_EXPORT |
9425 | | GF_Err gf_isom_set_mpegh_compatible_profiles(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescIndex, const u32 *profiles, u32 nb_compat_profiles) |
9426 | 0 | { |
9427 | 0 | u32 i, type; |
9428 | 0 | GF_SampleEntryBox *ent; |
9429 | 0 | GF_MHACompatibleProfilesBox *mhap; |
9430 | 0 | GF_TrackBox *trak; |
9431 | |
|
9432 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
9433 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
9434 | 0 | ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescIndex-1); |
9435 | 0 | if (!ent) return GF_BAD_PARAM; |
9436 | 0 | type = ent->type; |
9437 | 0 | if (type==GF_ISOM_BOX_TYPE_GNRA) |
9438 | 0 | type = ((GF_GenericAudioSampleEntryBox *)ent)->EntryType; |
9439 | |
|
9440 | 0 | switch (type) { |
9441 | 0 | case GF_ISOM_BOX_TYPE_MHA1: |
9442 | 0 | case GF_ISOM_BOX_TYPE_MHA2: |
9443 | 0 | case GF_ISOM_BOX_TYPE_MHM1: |
9444 | 0 | case GF_ISOM_BOX_TYPE_MHM2: |
9445 | 0 | break; |
9446 | 0 | default: |
9447 | 0 | return GF_BAD_PARAM; |
9448 | 0 | } |
9449 | 0 | mhap = (GF_MHACompatibleProfilesBox *) gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_MHAP); |
9450 | 0 | if (!mhap) { |
9451 | 0 | if (! profiles || !nb_compat_profiles) return GF_OK; |
9452 | 0 | mhap = (GF_MHACompatibleProfilesBox *) gf_isom_box_new_parent(&ent->child_boxes, GF_ISOM_BOX_TYPE_MHAP); |
9453 | 0 | } else if (! profiles || !nb_compat_profiles) { |
9454 | 0 | gf_isom_box_del_parent(&ent->child_boxes, (GF_Box*)mhap); |
9455 | 0 | return GF_OK; |
9456 | 0 | } |
9457 | 0 | if (mhap->compat_profiles) gf_free(mhap->compat_profiles); |
9458 | 0 | mhap->compat_profiles = gf_malloc(sizeof(u8) * nb_compat_profiles); |
9459 | 0 | if (!mhap->compat_profiles) return GF_OUT_OF_MEM; |
9460 | 0 | for (i=0; i<nb_compat_profiles; i++) { |
9461 | 0 | mhap->compat_profiles[i] = (u8) profiles[i]; |
9462 | 0 | } |
9463 | 0 | mhap->num_profiles = nb_compat_profiles; |
9464 | 0 | return GF_OK; |
9465 | 0 | } |
9466 | | |
9467 | | GF_Err gf_isom_set_sample_description_restricted(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescIndex, u32 scheme_type) |
9468 | 0 | { |
9469 | 0 | u32 type; |
9470 | 0 | GF_SampleEntryBox *ent; |
9471 | 0 | GF_TrackBox *trak; |
9472 | |
|
9473 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
9474 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
9475 | 0 | ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescIndex-1); |
9476 | 0 | if (!ent) return GF_BAD_PARAM; |
9477 | 0 | type = ent->type; |
9478 | |
|
9479 | 0 | u32 original_format = type; |
9480 | 0 | u32 gnr_type=0; |
9481 | 0 | if (original_format==GF_ISOM_BOX_TYPE_GNRA) { |
9482 | 0 | gnr_type = original_format; |
9483 | 0 | type = ((GF_GenericAudioSampleEntryBox*)ent)->EntryType; |
9484 | 0 | } else if (original_format==GF_ISOM_BOX_TYPE_GNRV) { |
9485 | 0 | gnr_type = original_format; |
9486 | 0 | type = ((GF_GenericVisualSampleEntryBox*)ent)->EntryType; |
9487 | 0 | } else if (original_format==GF_ISOM_BOX_TYPE_GNRM) { |
9488 | 0 | gnr_type = original_format; |
9489 | 0 | type = ((GF_GenericSampleEntryBox*)ent)->EntryType; |
9490 | 0 | } |
9491 | |
|
9492 | 0 | switch (type) { |
9493 | 0 | case GF_ISOM_BOX_TYPE_RESV: |
9494 | 0 | case GF_ISOM_BOX_TYPE_RESA: |
9495 | 0 | case GF_ISOM_BOX_TYPE_RESM: |
9496 | 0 | case GF_ISOM_BOX_TYPE_REST: |
9497 | 0 | case GF_ISOM_BOX_TYPE_RESU: |
9498 | 0 | case GF_ISOM_BOX_TYPE_RESS: |
9499 | 0 | case GF_ISOM_BOX_TYPE_RESF: |
9500 | 0 | case GF_ISOM_BOX_TYPE_RESP: |
9501 | 0 | case GF_ISOM_BOX_TYPE_RES3: |
9502 | 0 | return GF_OK; |
9503 | 0 | case GF_ISOM_BOX_TYPE_STXT: |
9504 | 0 | type = GF_ISOM_BOX_TYPE_REST; |
9505 | 0 | break; |
9506 | 0 | case GF_ISOM_BOX_TYPE_STPP: |
9507 | 0 | type = GF_ISOM_BOX_TYPE_RESU; |
9508 | 0 | break; |
9509 | 0 | case GF_ISOM_BOX_TYPE_METX: |
9510 | 0 | case GF_ISOM_BOX_TYPE_METT: |
9511 | 0 | case GF_ISOM_BOX_TYPE_URIM: |
9512 | 0 | case GF_ISOM_BOX_TYPE_MEBX: |
9513 | 0 | type = GF_ISOM_BOX_TYPE_RESM; |
9514 | 0 | break; |
9515 | 0 | default: |
9516 | 0 | type=0; |
9517 | 0 | switch (trak->Media->handler->handlerType) { |
9518 | 0 | case GF_ISOM_MEDIA_VISUAL: |
9519 | 0 | case GF_ISOM_MEDIA_AUXV: |
9520 | 0 | case GF_ISOM_MEDIA_PICT: |
9521 | 0 | type = GF_ISOM_BOX_TYPE_RESV; |
9522 | 0 | break; |
9523 | 0 | case GF_ISOM_MEDIA_AUDIO: |
9524 | 0 | type = GF_ISOM_BOX_TYPE_RESA; |
9525 | 0 | break; |
9526 | 0 | case GF_ISOM_MEDIA_OD: |
9527 | 0 | case GF_ISOM_MEDIA_OCR: |
9528 | 0 | case GF_ISOM_MEDIA_SCENE: |
9529 | 0 | case GF_ISOM_MEDIA_MPEG7: |
9530 | 0 | case GF_ISOM_MEDIA_OCI: |
9531 | 0 | case GF_ISOM_MEDIA_IPMP: |
9532 | 0 | case GF_ISOM_MEDIA_MPEGJ: |
9533 | 0 | type = GF_ISOM_BOX_TYPE_RESS; |
9534 | 0 | break; |
9535 | 0 | } |
9536 | 0 | break; |
9537 | 0 | } |
9538 | 0 | if (!type) return GF_NOT_SUPPORTED; |
9539 | | |
9540 | 0 | GF_ProtectionSchemeInfoBox *rinf; |
9541 | 0 | rinf = (GF_ProtectionSchemeInfoBox *) gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_RINF); |
9542 | 0 | if (rinf) gf_isom_box_del_parent(&ent->child_boxes, (GF_Box *)rinf); |
9543 | |
|
9544 | 0 | rinf = (GF_ProtectionSchemeInfoBox *)gf_isom_box_new_parent(&ent->child_boxes, GF_ISOM_BOX_TYPE_RINF); |
9545 | 0 | if (!rinf) return GF_OUT_OF_MEM; |
9546 | | |
9547 | | |
9548 | 0 | rinf->original_format = (GF_OriginalFormatBox *)gf_isom_box_new_parent(&rinf->child_boxes, GF_ISOM_BOX_TYPE_FRMA); |
9549 | 0 | if (!rinf->original_format) return GF_OUT_OF_MEM; |
9550 | 0 | if (gnr_type) { |
9551 | 0 | rinf->original_format->data_format = gnr_type; |
9552 | 0 | rinf->original_format->gnr_type = original_format; |
9553 | 0 | } else { |
9554 | 0 | rinf->original_format->data_format = original_format; |
9555 | 0 | } |
9556 | | //common to isma, cenc and oma |
9557 | 0 | rinf->scheme_type = (GF_SchemeTypeBox *)gf_isom_box_new_parent(&rinf->child_boxes, GF_ISOM_BOX_TYPE_SCHM); |
9558 | 0 | if (!rinf->scheme_type) return GF_OUT_OF_MEM; |
9559 | 0 | rinf->scheme_type->scheme_type = scheme_type; |
9560 | |
|
9561 | 0 | ent->type = type; |
9562 | 0 | return GF_OK; |
9563 | 0 | } |
9564 | | |
9565 | | GF_Err isom_sample_refs_push(GF_SampleReferences *sref, s32 refID, u32 nb_refs, s32 *refs) |
9566 | 0 | { |
9567 | 0 | GF_SampleRefEntry *ent; |
9568 | 0 | GF_SAFEALLOC(ent, GF_SampleRefEntry); |
9569 | 0 | if (!ent) return GF_OUT_OF_MEM; |
9570 | 0 | refID += sref->id_shift; |
9571 | 0 | if (refID<0) { |
9572 | 0 | u32 new_shift = -refID; |
9573 | 0 | sref->id_shift += -refID; |
9574 | 0 | refID = 0; |
9575 | 0 | u32 i, j, count = gf_list_count(sref->entries); |
9576 | 0 | for (i=0; i<count; i++) { |
9577 | 0 | GF_SampleRefEntry *a = gf_list_get(sref->entries, i); |
9578 | 0 | a->sampleID += new_shift; |
9579 | 0 | for (j=0; j<a->nb_refs; j++) |
9580 | 0 | a->sample_refs[j] += new_shift; |
9581 | 0 | } |
9582 | 0 | } |
9583 | |
|
9584 | 0 | ent->sampleID = refID; |
9585 | 0 | if (nb_refs) { |
9586 | 0 | u32 j; |
9587 | 0 | ent->nb_refs = nb_refs; |
9588 | 0 | ent->sample_refs = gf_malloc(sizeof(u32)*nb_refs); |
9589 | 0 | memcpy(ent->sample_refs, refs, sizeof(s32)*nb_refs); |
9590 | 0 | if (sref->id_shift) { |
9591 | 0 | for (j=0; j<ent->nb_refs; j++) |
9592 | 0 | ent->sample_refs[j] += sref->id_shift; |
9593 | 0 | } |
9594 | 0 | } |
9595 | 0 | sref->cdrf_cache_size = 0; |
9596 | 0 | return gf_list_add(sref->entries, ent); |
9597 | 0 | } |
9598 | | |
9599 | | GF_Err gf_isom_set_sample_references(GF_ISOFile *file, u32 track, u32 sampleNumber, s32 refID, u32 nb_refs, s32 *refs) |
9600 | 0 | { |
9601 | 0 | GF_Err e; |
9602 | 0 | GF_TrackBox *trak; |
9603 | 0 | GF_SampleTableBox *stbl; |
9604 | |
|
9605 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
9606 | 0 | if (e) return e; |
9607 | | |
9608 | 0 | trak = gf_isom_get_track_box(file, track); |
9609 | 0 | if (!trak) return GF_BAD_PARAM; |
9610 | 0 | stbl = trak->Media->information->sampleTable; |
9611 | 0 | if (sampleNumber != stbl->SampleSize->sampleCount) |
9612 | 0 | return GF_BAD_PARAM; |
9613 | | |
9614 | 0 | if (!stbl->SampleRefs) { |
9615 | 0 | stbl->SampleRefs = (GF_SampleReferences *)gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_CDRF); |
9616 | 0 | if (!stbl->SampleRefs) return GF_OUT_OF_MEM; |
9617 | 0 | } |
9618 | 0 | return isom_sample_refs_push(stbl->SampleRefs, refID, nb_refs, refs); |
9619 | 0 | } |
9620 | | |
9621 | | GF_EXPORT |
9622 | | GF_Err gf_isom_enable_auto_track_reorder(GF_ISOFile *file, Bool enabled) |
9623 | 0 | { |
9624 | 0 | if (!file) return GF_BAD_PARAM; |
9625 | 0 | file->auto_reorder_tracks = enabled; |
9626 | 0 | return GF_OK; |
9627 | 0 | } |
9628 | | |
9629 | | #endif /*!defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)*/ |