/src/gpac/src/isomedia/isom_write.c
Line | Count | Source |
1 | | /* |
2 | | * GPAC - Multimedia Framework C SDK |
3 | | * |
4 | | * Authors: Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2000-2025 |
6 | | * All rights reserved |
7 | | * |
8 | | * This file is part of GPAC / ISO Media File Format sub-project |
9 | | * |
10 | | * GPAC is free software; you can redistribute it and/or modify |
11 | | * it under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2, or (at your option) |
13 | | * any later version. |
14 | | * |
15 | | * GPAC is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public |
21 | | * License along with this library; see the file COPYING. If not, write to |
22 | | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
23 | | * |
24 | | */ |
25 | | |
26 | | #include <gpac/internal/isomedia_dev.h> |
27 | | #include <gpac/constants.h> |
28 | | #include <gpac/iso639.h> |
29 | | |
30 | | |
31 | | #if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE) |
32 | | |
33 | | GF_EXPORT |
34 | | GF_Err gf_isom_can_access_movie(GF_ISOFile *movie, GF_ISOOpenMode Mode) |
35 | 0 | { |
36 | 0 | if (!movie) return GF_BAD_PARAM; |
37 | 0 | if (movie->openMode < Mode) return GF_ISOM_INVALID_MODE; |
38 | | |
39 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
40 | 0 | if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_ISOM_INVALID_MODE; |
41 | 0 | #endif |
42 | 0 | return GF_OK; |
43 | 0 | } |
44 | | |
45 | | static GF_Err unpack_track(GF_TrackBox *trak) |
46 | 0 | { |
47 | 0 | GF_Err e = GF_OK; |
48 | 0 | if (!trak->is_unpacked) { |
49 | 0 | e = stbl_UnpackOffsets(trak->Media->information->sampleTable); |
50 | 0 | if (e) return e; |
51 | 0 | e = stbl_unpackCTS(trak->Media->information->sampleTable); |
52 | 0 | trak->is_unpacked = GF_TRUE; |
53 | 0 | } |
54 | 0 | return e; |
55 | 0 | } |
56 | | |
57 | | |
58 | | GF_Err FlushCaptureMode(GF_ISOFile *movie) |
59 | 0 | { |
60 | 0 | GF_Err e; |
61 | 0 | if (movie->openMode != GF_ISOM_OPEN_WRITE) { |
62 | 0 | if (!movie->editFileMap) return GF_ISOM_INVALID_MODE; |
63 | 0 | return GF_OK; |
64 | 0 | } |
65 | | /*make sure nothing was added*/ |
66 | 0 | if (gf_bs_get_position(movie->editFileMap->bs)) return GF_OK; |
67 | | |
68 | 0 | if (movie->fileName && !strcmp(movie->fileName, "_gpac_isobmff_redirect")) { |
69 | 0 | if (!movie->on_block_out) { |
70 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block callback, cannot write\n")); |
71 | 0 | return GF_BAD_PARAM; |
72 | 0 | } |
73 | | |
74 | 0 | gf_bs_del(movie->editFileMap->bs); |
75 | 0 | movie->editFileMap->bs = gf_bs_new_cbk(isom_on_block_out, movie, movie->on_block_out_block_size); |
76 | 0 | } |
77 | | |
78 | | /*add all first boxes*/ |
79 | 0 | if (movie->brand) { |
80 | 0 | e = gf_isom_box_size((GF_Box *)movie->brand); |
81 | 0 | if (e) return e; |
82 | 0 | e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs); |
83 | 0 | if (e) return e; |
84 | 0 | } |
85 | 0 | if (movie->pdin) { |
86 | 0 | e = gf_isom_box_size((GF_Box *)movie->pdin); |
87 | 0 | if (e) return e; |
88 | 0 | e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs); |
89 | 0 | if (e) return e; |
90 | 0 | } |
91 | 0 | movie->mdat->bsOffset = gf_bs_get_position(movie->editFileMap->bs); |
92 | | |
93 | | /*we have a trick here: the data will be stored on the fly, so the first |
94 | | thing in the file is the MDAT. As we don't know if we have a large file (>4 GB) or not |
95 | | do as if we had one and write 16 bytes: 4 (type) + 4 (size) + 8 (largeSize)...*/ |
96 | 0 | gf_bs_write_long_int(movie->editFileMap->bs, 0, 64); |
97 | 0 | gf_bs_write_long_int(movie->editFileMap->bs, 0, 64); |
98 | 0 | return GF_OK; |
99 | 0 | } |
100 | | |
101 | | static GF_Err CheckNoData(GF_ISOFile *movie) |
102 | 0 | { |
103 | 0 | if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_OK; |
104 | 0 | if (gf_bs_get_position(movie->editFileMap->bs)) return GF_BAD_PARAM; |
105 | 0 | return GF_OK; |
106 | 0 | } |
107 | | |
108 | | /************************************************************** |
109 | | File Writing / Editing |
110 | | **************************************************************/ |
111 | | //quick function to add an IOD/OD to the file if not present (iods is optional) |
112 | | GF_Err AddMovieIOD(GF_MovieBox *moov, u8 isIOD) |
113 | 0 | { |
114 | 0 | GF_Descriptor *od; |
115 | 0 | GF_ObjectDescriptorBox *iods; |
116 | | |
117 | | //do we have an IOD ?? If not, create one. |
118 | 0 | if (moov->iods) return GF_OK; |
119 | | |
120 | 0 | if (isIOD) { |
121 | 0 | od = gf_odf_desc_new(GF_ODF_ISOM_IOD_TAG); |
122 | 0 | } else { |
123 | 0 | od = gf_odf_desc_new(GF_ODF_ISOM_OD_TAG); |
124 | 0 | } |
125 | 0 | if (!od) return GF_OUT_OF_MEM; |
126 | 0 | ((GF_IsomObjectDescriptor *)od)->objectDescriptorID = 1; |
127 | |
|
128 | 0 | iods = (GF_ObjectDescriptorBox *) gf_isom_box_new_parent(&moov->child_boxes, GF_ISOM_BOX_TYPE_IODS); |
129 | 0 | if (!iods) return GF_OUT_OF_MEM; |
130 | 0 | iods->descriptor = od; |
131 | 0 | return moov_on_child_box((GF_Box*)moov, (GF_Box *)iods, GF_FALSE); |
132 | 0 | } |
133 | | |
134 | | //add a track to the root OD |
135 | | GF_EXPORT |
136 | | GF_Err gf_isom_add_track_to_root_od(GF_ISOFile *movie, u32 trackNumber) |
137 | 0 | { |
138 | 0 | GF_Err e; |
139 | 0 | GF_ES_ID_Inc *inc; |
140 | |
|
141 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
142 | 0 | if (e) return e; |
143 | 0 | e = gf_isom_insert_moov(movie); |
144 | 0 | if (e) return e; |
145 | | |
146 | 0 | if (!movie->moov->iods) AddMovieIOD(movie->moov, 0); |
147 | |
|
148 | 0 | if (gf_isom_is_track_in_root_od(movie, trackNumber) == 1) return GF_OK; |
149 | | |
150 | 0 | inc = (GF_ES_ID_Inc *) gf_odf_desc_new(GF_ODF_ESD_INC_TAG); |
151 | 0 | inc->trackID = gf_isom_get_track_id(movie, trackNumber); |
152 | 0 | if (!inc->trackID) { |
153 | 0 | gf_odf_desc_del((GF_Descriptor *)inc); |
154 | 0 | return movie->LastError; |
155 | 0 | } |
156 | 0 | if ( (movie->LastError = gf_isom_add_desc_to_root_od(movie, (GF_Descriptor *)inc) ) ) { |
157 | 0 | return movie->LastError; |
158 | 0 | } |
159 | 0 | gf_odf_desc_del((GF_Descriptor *)inc); |
160 | 0 | return GF_OK; |
161 | 0 | } |
162 | | |
163 | | //remove the root OD |
164 | | GF_EXPORT |
165 | | GF_Err gf_isom_remove_root_od(GF_ISOFile *movie) |
166 | 0 | { |
167 | 0 | GF_Err e; |
168 | |
|
169 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
170 | 0 | if (e) return e; |
171 | 0 | if (!movie->moov || !movie->moov->iods) return GF_OK; |
172 | 0 | gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box *)movie->moov->iods); |
173 | 0 | movie->moov->iods = NULL; |
174 | 0 | return GF_OK; |
175 | 0 | } |
176 | | |
177 | | //remove a track to the root OD |
178 | | GF_EXPORT |
179 | | GF_Err gf_isom_remove_track_from_root_od(GF_ISOFile *movie, u32 trackNumber) |
180 | 0 | { |
181 | 0 | GF_List *esds; |
182 | 0 | GF_ES_ID_Inc *inc; |
183 | 0 | u32 i; |
184 | 0 | GF_Err e; |
185 | |
|
186 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
187 | 0 | if (e) return e; |
188 | 0 | if (!movie->moov) return GF_OK; |
189 | | |
190 | 0 | if (!gf_isom_is_track_in_root_od(movie, trackNumber)) return GF_OK; |
191 | | |
192 | 0 | if (!movie->moov->iods) { |
193 | 0 | e = AddMovieIOD(movie->moov, 0); |
194 | 0 | if (e) return e; |
195 | 0 | } |
196 | 0 | switch (movie->moov->iods->descriptor->tag) { |
197 | 0 | case GF_ODF_ISOM_IOD_TAG: |
198 | 0 | esds = ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors; |
199 | 0 | break; |
200 | 0 | case GF_ODF_ISOM_OD_TAG: |
201 | 0 | esds = ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors; |
202 | 0 | break; |
203 | 0 | default: |
204 | 0 | return GF_ISOM_INVALID_FILE; |
205 | 0 | } |
206 | | |
207 | | //get the desc |
208 | 0 | i=0; |
209 | 0 | while ((inc = (GF_ES_ID_Inc*)gf_list_enum(esds, &i))) { |
210 | 0 | if (inc->trackID == (u32) gf_isom_get_track_id(movie, trackNumber)) { |
211 | 0 | gf_odf_desc_del((GF_Descriptor *)inc); |
212 | 0 | gf_list_rem(esds, i-1); |
213 | 0 | break; |
214 | 0 | } |
215 | 0 | } |
216 | | //we don't remove the iod for P&Ls and other potential info |
217 | 0 | return GF_OK; |
218 | 0 | } |
219 | | |
220 | | GF_EXPORT |
221 | | GF_Err gf_isom_set_creation_time(GF_ISOFile *movie, u64 ctime, u64 mtime) |
222 | 0 | { |
223 | 0 | if (!movie || !movie->moov) return GF_BAD_PARAM; |
224 | 0 | movie->moov->mvhd->creationTime = ctime; |
225 | 0 | movie->moov->mvhd->modificationTime = mtime; |
226 | 0 | return GF_OK; |
227 | 0 | } |
228 | | |
229 | | GF_EXPORT |
230 | | GF_Err gf_isom_set_track_creation_time(GF_ISOFile *movie,u32 trackNumber, u64 ctime, u64 mtime) |
231 | 0 | { |
232 | 0 | GF_TrackBox *trak; |
233 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
234 | 0 | if (!trak) return GF_BAD_PARAM; |
235 | | |
236 | 0 | trak->Header->creationTime = ctime; |
237 | 0 | trak->Header->modificationTime = mtime; |
238 | 0 | return GF_OK; |
239 | 0 | } |
240 | | |
241 | | GF_EXPORT |
242 | | GF_Err gf_isom_set_media_creation_time(GF_ISOFile *movie,u32 trackNumber, u64 ctime, u64 mtime) |
243 | 0 | { |
244 | 0 | GF_TrackBox *trak; |
245 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
246 | 0 | if (!trak) return GF_BAD_PARAM; |
247 | 0 | if (!trak->Media || !trak->Media->mediaHeader) return GF_ISOM_INVALID_FILE; |
248 | | |
249 | 0 | trak->Media->mediaHeader->creationTime = ctime; |
250 | 0 | trak->Media->mediaHeader->modificationTime = mtime; |
251 | 0 | return GF_OK; |
252 | 0 | } |
253 | | |
254 | | //sets the enable flag of a track |
255 | | GF_EXPORT |
256 | | GF_Err gf_isom_set_track_enabled(GF_ISOFile *movie, u32 trackNumber, Bool enableTrack) |
257 | 0 | { |
258 | 0 | GF_Err e; |
259 | 0 | GF_TrackBox *trak; |
260 | |
|
261 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
262 | 0 | if (e) return e; |
263 | | |
264 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
265 | 0 | if (!trak) return GF_BAD_PARAM; |
266 | | |
267 | 0 | if (enableTrack) { |
268 | 0 | trak->Header->flags |= 1; |
269 | 0 | } else { |
270 | 0 | trak->Header->flags &= ~1; |
271 | 0 | } |
272 | 0 | return GF_OK; |
273 | 0 | } |
274 | | |
275 | | //sets the enable flag of a track |
276 | | GF_EXPORT |
277 | | GF_Err gf_isom_set_track_flags(GF_ISOFile *movie, u32 trackNumber, u32 flags, GF_ISOMTrackFlagOp op) |
278 | 0 | { |
279 | 0 | GF_Err e; |
280 | 0 | GF_TrackBox *trak; |
281 | |
|
282 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
283 | 0 | if (e) return e; |
284 | | |
285 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
286 | 0 | if (!trak) return GF_BAD_PARAM; |
287 | 0 | if (op==GF_ISOM_TKFLAGS_ADD) |
288 | 0 | trak->Header->flags |= flags; |
289 | 0 | else if (op==GF_ISOM_TKFLAGS_REM) |
290 | 0 | trak->Header->flags &= ~flags; |
291 | 0 | else |
292 | 0 | trak->Header->flags = flags; |
293 | 0 | return GF_OK; |
294 | 0 | } |
295 | | |
296 | | GF_EXPORT |
297 | | GF_Err gf_isom_set_media_language(GF_ISOFile *movie, u32 trackNumber, char *code) |
298 | 0 | { |
299 | 0 | GF_Err e; |
300 | 0 | GF_TrackBox *trak; |
301 | |
|
302 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
303 | 0 | if (!trak || !code) return GF_BAD_PARAM; |
304 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
305 | 0 | if (e) return e; |
306 | | |
307 | 0 | if (trak->extl) { |
308 | 0 | GF_ExtendedLanguageBox *elng = (GF_ExtendedLanguageBox *) gf_isom_box_find_child(trak->child_boxes, GF_ISOM_BOX_TYPE_ELNG); |
309 | 0 | if (!elng) { |
310 | 0 | elng = (GF_ExtendedLanguageBox *)gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_ELNG); |
311 | 0 | if (!elng) return GF_OUT_OF_MEM; |
312 | 0 | } |
313 | 0 | if (elng->extended_language) gf_free(elng->extended_language); |
314 | 0 | elng->extended_language = gf_strdup(code); |
315 | |
|
316 | 0 | if (!movie->keep_utc) |
317 | 0 | trak->Header->modificationTime = gf_isom_get_mp4time(); |
318 | 0 | return GF_OK; |
319 | 0 | } |
320 | | |
321 | | // Old language-storage processing |
322 | | // if the new code is on 3 chars, we use it |
323 | | // otherwise, we find the associated 3 chars code and use it |
324 | 0 | if (strlen(code) == 3) { |
325 | 0 | memcpy(trak->Media->mediaHeader->packedLanguage, code, sizeof(char)*3); |
326 | 0 | } else { |
327 | 0 | s32 lang_idx; |
328 | 0 | const char *code_3cc; |
329 | 0 | lang_idx = gf_lang_find(code); |
330 | 0 | if (lang_idx == -1) { |
331 | 0 | if (code[0]!=0) { |
332 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("The given code is not a valid one: %s, using 'und' as 3-letter code\n", code)); |
333 | 0 | } |
334 | 0 | code_3cc = "und"; |
335 | 0 | } else { |
336 | 0 | code_3cc = gf_lang_get_3cc(lang_idx); |
337 | 0 | } |
338 | 0 | memcpy(trak->Media->mediaHeader->packedLanguage, code_3cc, sizeof(char)*3); |
339 | 0 | } |
340 | | |
341 | | // New language-storage processing |
342 | | // change the code in the extended language box (if any) |
343 | | // otherwise add an extended language box only if the given code is not 3 chars |
344 | 0 | { |
345 | 0 | u32 i, count; |
346 | 0 | GF_ExtendedLanguageBox *elng; |
347 | 0 | elng = NULL; |
348 | 0 | count = gf_list_count(trak->Media->child_boxes); |
349 | 0 | for (i = 0; i < count; i++) { |
350 | 0 | GF_Box *box = (GF_Box *)gf_list_get(trak->Media->child_boxes, i); |
351 | 0 | if (box->type == GF_ISOM_BOX_TYPE_ELNG) { |
352 | 0 | elng = (GF_ExtendedLanguageBox *)box; |
353 | 0 | break; |
354 | 0 | } |
355 | 0 | } |
356 | 0 | if (!elng && (strlen(code) > 3)) { |
357 | 0 | elng = (GF_ExtendedLanguageBox *)gf_isom_box_new_parent(&trak->Media->child_boxes, GF_ISOM_BOX_TYPE_ELNG); |
358 | 0 | if (!elng) return GF_OUT_OF_MEM; |
359 | 0 | } |
360 | 0 | if (elng) { |
361 | 0 | if (elng->extended_language) { |
362 | 0 | gf_free(elng->extended_language); |
363 | 0 | } |
364 | 0 | elng->extended_language = gf_strdup(code); |
365 | 0 | } |
366 | 0 | } |
367 | 0 | if (!movie->keep_utc) |
368 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
369 | 0 | return GF_OK; |
370 | 0 | } |
371 | | |
372 | | static GF_Err gf_isom_set_root_iod(GF_ISOFile *movie) |
373 | 0 | { |
374 | 0 | GF_IsomInitialObjectDescriptor *iod; |
375 | 0 | GF_IsomObjectDescriptor *od; |
376 | 0 | GF_Err e; |
377 | |
|
378 | 0 | e = gf_isom_insert_moov(movie); |
379 | 0 | if (e) return e; |
380 | 0 | if (!movie->moov->iods) { |
381 | 0 | AddMovieIOD(movie->moov, 1); |
382 | 0 | return GF_OK; |
383 | 0 | } |
384 | | //if OD, switch to IOD |
385 | 0 | if (movie->moov->iods->descriptor->tag == GF_ODF_ISOM_IOD_TAG) return GF_OK; |
386 | 0 | od = (GF_IsomObjectDescriptor *) movie->moov->iods->descriptor; |
387 | 0 | iod = (GF_IsomInitialObjectDescriptor*)gf_malloc(sizeof(GF_IsomInitialObjectDescriptor)); |
388 | 0 | if (!iod) return GF_OUT_OF_MEM; |
389 | | |
390 | 0 | memset(iod, 0, sizeof(GF_IsomInitialObjectDescriptor)); |
391 | |
|
392 | 0 | iod->ES_ID_IncDescriptors = od->ES_ID_IncDescriptors; |
393 | 0 | od->ES_ID_IncDescriptors = NULL; |
394 | | //not used in root OD |
395 | 0 | iod->ES_ID_RefDescriptors = NULL; |
396 | 0 | iod->extensionDescriptors = od->extensionDescriptors; |
397 | 0 | od->extensionDescriptors = NULL; |
398 | 0 | iod->IPMP_Descriptors = od->IPMP_Descriptors; |
399 | 0 | od->IPMP_Descriptors = NULL; |
400 | 0 | iod->objectDescriptorID = od->objectDescriptorID; |
401 | 0 | iod->OCIDescriptors = od->OCIDescriptors; |
402 | 0 | od->OCIDescriptors = NULL; |
403 | 0 | iod->tag = GF_ODF_ISOM_IOD_TAG; |
404 | 0 | iod->URLString = od->URLString; |
405 | 0 | od->URLString = NULL; |
406 | |
|
407 | 0 | gf_odf_desc_del((GF_Descriptor *) od); |
408 | 0 | movie->moov->iods->descriptor = (GF_Descriptor *)iod; |
409 | 0 | return GF_OK; |
410 | 0 | } |
411 | | |
412 | | GF_EXPORT |
413 | | GF_Err gf_isom_add_desc_to_root_od(GF_ISOFile *movie, const GF_Descriptor *theDesc) |
414 | 0 | { |
415 | 0 | GF_Err e; |
416 | 0 | GF_Descriptor *desc, *dupDesc; |
417 | |
|
418 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
419 | 0 | if (e) return e; |
420 | 0 | e = gf_isom_insert_moov(movie); |
421 | 0 | if (e) return e; |
422 | | |
423 | 0 | if (!movie->moov->iods) { |
424 | 0 | e = AddMovieIOD(movie->moov, 0); |
425 | 0 | if (e) return e; |
426 | 0 | } |
427 | 0 | if (theDesc->tag==GF_ODF_IPMP_TL_TAG) gf_isom_set_root_iod(movie); |
428 | |
|
429 | 0 | desc = movie->moov->iods->descriptor; |
430 | | //the type of desc is handled at the OD/IOD level, we'll be notified |
431 | | //if the desc is not allowed |
432 | 0 | switch (desc->tag) { |
433 | 0 | case GF_ODF_ISOM_IOD_TAG: |
434 | 0 | case GF_ODF_ISOM_OD_TAG: |
435 | | //duplicate the desc |
436 | 0 | e = gf_odf_desc_copy((GF_Descriptor *)theDesc, &dupDesc); |
437 | 0 | if (e) return e; |
438 | | //add it (MUST BE (I)OD level desc) |
439 | 0 | movie->LastError = gf_odf_desc_add_desc(desc, dupDesc); |
440 | 0 | if (movie->LastError) gf_odf_desc_del((GF_Descriptor *)dupDesc); |
441 | 0 | break; |
442 | 0 | default: |
443 | 0 | movie->LastError = GF_ISOM_INVALID_FILE; |
444 | 0 | break; |
445 | 0 | } |
446 | 0 | return movie->LastError; |
447 | 0 | } |
448 | | |
449 | | |
450 | | GF_EXPORT |
451 | | GF_Err gf_isom_set_timescale(GF_ISOFile *movie, u32 timeScale) |
452 | 0 | { |
453 | 0 | GF_TrackBox *trak; |
454 | 0 | u32 i; |
455 | 0 | GF_Err e; |
456 | 0 | if (!timeScale) return GF_BAD_PARAM; |
457 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
458 | 0 | if (e) return e; |
459 | 0 | e = gf_isom_insert_moov(movie); |
460 | 0 | if (e) return e; |
461 | | |
462 | 0 | if (movie->moov->mvhd->timeScale == timeScale) return GF_OK; |
463 | | |
464 | | /*rewrite all durations and edit lists*/ |
465 | 0 | movie->moov->mvhd->duration *= timeScale; |
466 | 0 | movie->moov->mvhd->duration /= movie->moov->mvhd->timeScale; |
467 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
468 | 0 | if (movie->moov->mvex && movie->moov->mvex->mehd) { |
469 | 0 | movie->moov->mvex->mehd->fragment_duration *= timeScale; |
470 | 0 | movie->moov->mvex->mehd->fragment_duration /= movie->moov->mvhd->timeScale; |
471 | 0 | } |
472 | 0 | #endif |
473 | |
|
474 | 0 | i=0; |
475 | 0 | while ((trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) { |
476 | 0 | trak->Header->duration *= timeScale; |
477 | 0 | trak->Header->duration /= movie->moov->mvhd->timeScale; |
478 | |
|
479 | 0 | if (trak->editBox && trak->editBox->editList) { |
480 | 0 | u32 j, count = gf_list_count(trak->editBox->editList->entryList); |
481 | 0 | for (j=0; j<count; j++) { |
482 | 0 | GF_EdtsEntry *ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, j); |
483 | 0 | ent->segmentDuration *= timeScale; |
484 | 0 | ent->segmentDuration /= movie->moov->mvhd->timeScale; |
485 | 0 | } |
486 | 0 | } |
487 | 0 | } |
488 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
489 | 0 | if (movie->moov->mvex && movie->moov->mvex->mehd) { |
490 | 0 | movie->moov->mvex->mehd->fragment_duration *= timeScale; |
491 | 0 | movie->moov->mvex->mehd->fragment_duration /= movie->moov->mvhd->timeScale; |
492 | 0 | } |
493 | 0 | #endif |
494 | 0 | movie->moov->mvhd->timeScale = timeScale; |
495 | 0 | movie->interleavingTime = timeScale; |
496 | 0 | return GF_OK; |
497 | 0 | } |
498 | | |
499 | | |
500 | | GF_EXPORT |
501 | | GF_Err gf_isom_set_pl_indication(GF_ISOFile *movie, GF_ISOProfileLevelType PL_Code, u8 ProfileLevel) |
502 | 0 | { |
503 | 0 | GF_IsomInitialObjectDescriptor *iod; |
504 | 0 | GF_Err e; |
505 | |
|
506 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
507 | 0 | if (e) return e; |
508 | | |
509 | 0 | e = gf_isom_set_root_iod(movie); |
510 | 0 | if (e) return e; |
511 | | |
512 | 0 | iod = (GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor; |
513 | |
|
514 | 0 | switch (PL_Code) { |
515 | 0 | case GF_ISOM_PL_AUDIO: |
516 | 0 | iod->audio_profileAndLevel = ProfileLevel; |
517 | 0 | break; |
518 | 0 | case GF_ISOM_PL_GRAPHICS: |
519 | 0 | iod->graphics_profileAndLevel = ProfileLevel; |
520 | 0 | break; |
521 | 0 | case GF_ISOM_PL_OD: |
522 | 0 | iod->OD_profileAndLevel = ProfileLevel; |
523 | 0 | break; |
524 | 0 | case GF_ISOM_PL_SCENE: |
525 | 0 | iod->scene_profileAndLevel = ProfileLevel; |
526 | 0 | break; |
527 | 0 | case GF_ISOM_PL_VISUAL: |
528 | 0 | iod->visual_profileAndLevel = ProfileLevel; |
529 | 0 | break; |
530 | 0 | case GF_ISOM_PL_INLINE: |
531 | 0 | iod->inlineProfileFlag = ProfileLevel ? 1 : 0; |
532 | 0 | break; |
533 | 0 | default: |
534 | 0 | break; |
535 | 0 | } |
536 | 0 | return GF_OK; |
537 | 0 | } |
538 | | |
539 | | GF_EXPORT |
540 | | GF_Err gf_isom_set_root_od_id(GF_ISOFile *movie, u32 OD_ID) |
541 | 0 | { |
542 | 0 | GF_Err e; |
543 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
544 | 0 | if (e) return e; |
545 | | |
546 | 0 | e = gf_isom_insert_moov(movie); |
547 | 0 | if (e) return e; |
548 | 0 | if (!movie->moov->iods) { |
549 | 0 | e = AddMovieIOD(movie->moov, 0); |
550 | 0 | if (e) return e; |
551 | 0 | } |
552 | | |
553 | 0 | switch (movie->moov->iods->descriptor->tag) { |
554 | 0 | case GF_ODF_ISOM_OD_TAG: |
555 | 0 | ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID; |
556 | 0 | break; |
557 | 0 | case GF_ODF_ISOM_IOD_TAG: |
558 | 0 | ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID; |
559 | 0 | break; |
560 | 0 | default: |
561 | 0 | return GF_ISOM_INVALID_FILE; |
562 | 0 | } |
563 | 0 | return GF_OK; |
564 | 0 | } |
565 | | |
566 | | GF_EXPORT |
567 | | GF_Err gf_isom_set_root_od_url(GF_ISOFile *movie, const char *url_string) |
568 | 0 | { |
569 | 0 | GF_Err e; |
570 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
571 | 0 | if (e) return e; |
572 | 0 | e = gf_isom_insert_moov(movie); |
573 | 0 | if (e) return e; |
574 | | |
575 | 0 | if (!movie->moov->iods) { |
576 | 0 | e = AddMovieIOD(movie->moov, 0); |
577 | 0 | if (e) return e; |
578 | 0 | } |
579 | | |
580 | 0 | switch (movie->moov->iods->descriptor->tag) { |
581 | 0 | case GF_ODF_ISOM_OD_TAG: |
582 | 0 | if (((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString) gf_free(((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString); |
583 | 0 | ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? gf_strdup(url_string) : NULL; |
584 | 0 | break; |
585 | 0 | case GF_ODF_ISOM_IOD_TAG: |
586 | 0 | if (((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString) gf_free(((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString); |
587 | 0 | ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? gf_strdup(url_string) : NULL; |
588 | 0 | break; |
589 | 0 | default: |
590 | 0 | return GF_ISOM_INVALID_FILE; |
591 | 0 | } |
592 | 0 | return GF_OK; |
593 | 0 | } |
594 | | |
595 | | GF_EXPORT |
596 | | GF_ISOTrackID gf_isom_get_last_created_track_id(GF_ISOFile *movie) |
597 | 0 | { |
598 | 0 | return movie ? movie->last_created_track_id : 0; |
599 | 0 | } |
600 | | |
601 | | |
602 | | GF_EXPORT |
603 | | GF_Err gf_isom_load_extra_boxes(GF_ISOFile *movie, u8 *moov_boxes, u32 moov_boxes_size, Bool udta_only) |
604 | 0 | { |
605 | 0 | GF_BitStream *bs; |
606 | |
|
607 | 0 | GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
608 | 0 | if (e) return e; |
609 | 0 | e = gf_isom_insert_moov(movie); |
610 | 0 | if (e) return e; |
611 | | |
612 | 0 | bs = gf_bs_new(moov_boxes, moov_boxes_size, GF_BITSTREAM_READ); |
613 | | |
614 | | //we may have terminators in some QT files (4 bytes set to 0 ...) |
615 | 0 | while (gf_bs_available(bs) >= 8) { |
616 | 0 | GF_Box *a_box; |
617 | 0 | e = gf_isom_box_parse_ex((GF_Box**)&a_box, bs, GF_ISOM_BOX_TYPE_MOOV, GF_FALSE, 0); |
618 | 0 | if (e || !a_box) goto exit; |
619 | | |
620 | 0 | if (a_box->type == GF_ISOM_BOX_TYPE_UDTA) { |
621 | 0 | if (movie->moov->udta) gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box*)movie->moov->udta); |
622 | 0 | movie->moov->udta = (GF_UserDataBox*) a_box; |
623 | |
|
624 | 0 | if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new(); |
625 | 0 | gf_list_add(movie->moov->child_boxes, a_box); |
626 | |
|
627 | 0 | } else if (!udta_only && (a_box->type!=GF_ISOM_BOX_TYPE_PSSH) ) { |
628 | 0 | if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new(); |
629 | 0 | gf_list_add(movie->moov->child_boxes, a_box); |
630 | 0 | } else { |
631 | 0 | gf_isom_box_del(a_box); |
632 | 0 | } |
633 | 0 | } |
634 | 0 | exit: |
635 | 0 | gf_bs_del(bs); |
636 | 0 | return e; |
637 | 0 | } |
638 | | |
639 | | GF_EXPORT |
640 | | u32 gf_isom_new_track_from_template(GF_ISOFile *movie, GF_ISOTrackID trakID, u32 MediaType, u32 TimeScale, u8 *tk_box, u32 tk_box_size, Bool udta_only) |
641 | 0 | { |
642 | 0 | GF_Err e; |
643 | 0 | u64 now; |
644 | 0 | u8 isHint; |
645 | 0 | GF_TrackBox *trak; |
646 | 0 | GF_TrackHeaderBox *tkhd; |
647 | 0 | GF_MediaBox *mdia; |
648 | 0 | GF_UserDataBox *udta = NULL; |
649 | |
|
650 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
651 | 0 | if (e) { |
652 | 0 | gf_isom_set_last_error(movie, e); |
653 | 0 | return 0; |
654 | 0 | } |
655 | 0 | e = gf_isom_insert_moov(movie); |
656 | 0 | if (e) return e; |
657 | | |
658 | | |
659 | 0 | isHint = 0; |
660 | | //we're creating a hint track... it's the same, but mode HAS TO BE EDIT |
661 | 0 | if (MediaType == GF_ISOM_MEDIA_HINT) { |
662 | | // if (movie->openMode != GF_ISOM_OPEN_EDIT) return 0; |
663 | 0 | isHint = 1; |
664 | 0 | } |
665 | |
|
666 | 0 | mdia = NULL; |
667 | 0 | tkhd = NULL; |
668 | 0 | trak = NULL; |
669 | 0 | if (trakID) { |
670 | | //check if we are in ES_ID boundaries |
671 | 0 | if (!isHint && (trakID > 0xFFFF)) { |
672 | 0 | gf_isom_set_last_error(movie, GF_BAD_PARAM); |
673 | 0 | return 0; |
674 | 0 | } |
675 | | //here we should look for available IDs ... |
676 | 0 | if (!RequestTrack(movie->moov, trakID)) return 0; |
677 | 0 | } else { |
678 | 0 | trakID = movie->moov->mvhd->nextTrackID; |
679 | 0 | if (!trakID) trakID = 1; |
680 | | /*ESIDs are on 16 bits*/ |
681 | 0 | if (! isHint && (trakID > 0xFFFF)) trakID = 1; |
682 | |
|
683 | 0 | while (1) { |
684 | 0 | if (RequestTrack(movie->moov, trakID)) break; |
685 | 0 | trakID += 1; |
686 | 0 | if (trakID == 0xFFFFFFFF) break; |
687 | 0 | } |
688 | 0 | if (trakID == 0xFFFFFFFF) { |
689 | 0 | gf_isom_set_last_error(movie, GF_BAD_PARAM); |
690 | 0 | return 0; |
691 | 0 | } |
692 | 0 | if (! isHint && (trakID > 0xFFFF)) { |
693 | 0 | gf_isom_set_last_error(movie, GF_BAD_PARAM); |
694 | 0 | return 0; |
695 | 0 | } |
696 | 0 | } |
697 | | |
698 | 0 | if (tk_box) { |
699 | 0 | GF_BitStream *bs = gf_bs_new(tk_box, tk_box_size, GF_BITSTREAM_READ); |
700 | 0 | gf_bs_set_cookie(bs, GF_ISOM_BS_COOKIE_NO_LOGS|GF_ISOM_BS_COOKIE_CLONE_TRACK); |
701 | |
|
702 | 0 | e = gf_isom_box_parse_ex((GF_Box**)&trak, bs, GF_ISOM_BOX_TYPE_MOOV, GF_FALSE, 0); |
703 | 0 | gf_bs_del(bs); |
704 | 0 | if (e) trak = NULL; |
705 | 0 | else if (udta_only) { |
706 | 0 | udta = trak->udta; |
707 | 0 | trak->udta = NULL; |
708 | 0 | gf_list_del_item(trak->child_boxes, udta); |
709 | 0 | gf_isom_box_del((GF_Box*)trak); |
710 | 0 | trak = NULL; |
711 | 0 | } else { |
712 | 0 | Bool tpl_ok = GF_TRUE; |
713 | 0 | if (!trak->Header || !trak->Media || !trak->Media->handler || !trak->Media->mediaHeader || !trak->Media->information) tpl_ok = GF_FALSE; |
714 | | |
715 | 0 | else { |
716 | 0 | if (!MediaType) MediaType = trak->Media->handler->handlerType; |
717 | 0 | e = NewMedia(&trak->Media, MediaType, TimeScale); |
718 | 0 | if (e) tpl_ok = GF_FALSE; |
719 | 0 | } |
720 | 0 | if (!tpl_ok) { |
721 | 0 | udta = trak->udta; |
722 | 0 | trak->udta = NULL; |
723 | 0 | gf_isom_box_del((GF_Box*)trak); |
724 | 0 | } |
725 | 0 | } |
726 | 0 | } |
727 | 0 | now = gf_isom_get_mp4time(); |
728 | 0 | if (!trak) { |
729 | | //OK, now create a track... |
730 | 0 | trak = (GF_TrackBox *) gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_TRAK); |
731 | 0 | if (!trak) { |
732 | 0 | gf_isom_set_last_error(movie, GF_OUT_OF_MEM); |
733 | 0 | return 0; |
734 | 0 | } |
735 | 0 | tkhd = (GF_TrackHeaderBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TKHD); |
736 | 0 | if (!tkhd) { |
737 | 0 | gf_isom_set_last_error(movie, GF_OUT_OF_MEM); |
738 | 0 | return 0; |
739 | 0 | } |
740 | | |
741 | | //OK, set up the media trak |
742 | 0 | e = NewMedia(&mdia, MediaType, TimeScale); |
743 | 0 | if (e) { |
744 | 0 | gf_isom_box_del((GF_Box *)mdia); |
745 | 0 | return 0; |
746 | 0 | } |
747 | 0 | gf_assert(trak->child_boxes); |
748 | 0 | gf_list_add(trak->child_boxes, mdia); |
749 | | |
750 | | //OK, add this media to our track |
751 | 0 | mdia->mediaTrack = trak; |
752 | |
|
753 | 0 | e = trak_on_child_box((GF_Box*)trak, (GF_Box *) tkhd, GF_FALSE); |
754 | 0 | if (e) goto err_exit; |
755 | 0 | e = trak_on_child_box((GF_Box*)trak, (GF_Box *) mdia, GF_FALSE); |
756 | 0 | if (e) goto err_exit; |
757 | 0 | tkhd->trackID = trakID; |
758 | |
|
759 | 0 | if (gf_sys_is_test_mode() ) { |
760 | 0 | tkhd->creationTime = 0; |
761 | 0 | mdia->mediaHeader->creationTime = 0; |
762 | 0 | } else { |
763 | 0 | tkhd->creationTime = now; |
764 | 0 | mdia->mediaHeader->creationTime = now; |
765 | 0 | } |
766 | |
|
767 | 0 | } else { |
768 | 0 | tkhd = trak->Header; |
769 | 0 | tkhd->trackID = trakID; |
770 | 0 | mdia = trak->Media; |
771 | 0 | mdia->mediaTrack = trak; |
772 | 0 | mdia->mediaHeader->timeScale = TimeScale; |
773 | 0 | if (mdia->handler->handlerType != MediaType) { |
774 | 0 | mdia->handler->handlerType = MediaType; |
775 | 0 | tkhd->width = 0; |
776 | 0 | tkhd->height = 0; |
777 | 0 | tkhd->volume = 0; |
778 | 0 | } else { |
779 | 0 | MediaType = 0; |
780 | 0 | } |
781 | 0 | trak->Header->duration = 0; |
782 | 0 | mdia->mediaHeader->duration = 0; |
783 | |
|
784 | 0 | if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new(); |
785 | 0 | gf_list_add(movie->moov->child_boxes, trak); |
786 | 0 | } |
787 | 0 | if (MediaType) { |
788 | | //some default properties for Audio, Visual or private tracks |
789 | 0 | switch (MediaType) { |
790 | 0 | case GF_ISOM_MEDIA_VISUAL: |
791 | 0 | case GF_ISOM_MEDIA_AUXV: |
792 | 0 | case GF_ISOM_MEDIA_PICT: |
793 | 0 | case GF_ISOM_MEDIA_SCENE: |
794 | 0 | case GF_ISOM_MEDIA_TEXT: |
795 | 0 | case GF_ISOM_MEDIA_SUBT: |
796 | | /*320-240 pix in 16.16*/ |
797 | 0 | tkhd->width = 0x01400000; |
798 | 0 | tkhd->height = 0x00F00000; |
799 | 0 | break; |
800 | 0 | case GF_ISOM_MEDIA_AUDIO: |
801 | 0 | tkhd->volume = 0x0100; |
802 | 0 | break; |
803 | 0 | } |
804 | 0 | } |
805 | 0 | movie->last_created_track_id = tkhd->trackID; |
806 | |
|
807 | 0 | if (!movie->keep_utc && !gf_sys_is_test_mode() ) { |
808 | 0 | tkhd->modificationTime = now; |
809 | 0 | mdia->mediaHeader->modificationTime = now; |
810 | 0 | } |
811 | | |
812 | | //OK, add our trak |
813 | 0 | e = moov_on_child_box((GF_Box*)movie->moov, (GF_Box *)trak, GF_FALSE); |
814 | 0 | if (e) goto err_exit; |
815 | | //set the next track ID available |
816 | 0 | if (trakID >= movie->moov->mvhd->nextTrackID) |
817 | 0 | movie->moov->mvhd->nextTrackID = trakID+1; |
818 | |
|
819 | 0 | if (udta) { |
820 | 0 | trak->udta = udta; |
821 | 0 | gf_list_add(trak->child_boxes, udta); |
822 | 0 | } |
823 | | |
824 | | //and return our track number |
825 | 0 | return gf_isom_get_track_by_id(movie, trakID); |
826 | | |
827 | 0 | err_exit: |
828 | | //tkhd is registered with track and will be destroyed there |
829 | 0 | if (trak) gf_isom_box_del((GF_Box *)trak); |
830 | 0 | if (mdia) gf_isom_box_del((GF_Box *)mdia); |
831 | 0 | return 0; |
832 | 0 | } |
833 | | |
834 | | GF_EXPORT |
835 | | GF_Err gf_isom_set_track_stsd_templates(GF_ISOFile *movie, u32 trackNumber, u8 *stsd_data, u32 stsd_data_size) |
836 | 0 | { |
837 | 0 | GF_TrackBox *trak; |
838 | 0 | GF_Err e; |
839 | 0 | GF_SampleDescriptionBox *stsd=NULL; |
840 | 0 | GF_List *tmp; |
841 | |
|
842 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
843 | 0 | if (e) return e; |
844 | | |
845 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
846 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
847 | 0 | if (gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes)) |
848 | 0 | return GF_BAD_PARAM; |
849 | | |
850 | 0 | GF_BitStream *bs = gf_bs_new(stsd_data, stsd_data_size, GF_BITSTREAM_READ); |
851 | 0 | e = gf_isom_box_parse_ex((GF_Box **) &stsd, bs, GF_ISOM_BOX_TYPE_STBL, GF_FALSE, 0); |
852 | 0 | gf_bs_del(bs); |
853 | 0 | if (!e && (stsd->type==GF_ISOM_BOX_TYPE_STSD)) { |
854 | 0 | tmp = trak->Media->information->sampleTable->SampleDescription->child_boxes; |
855 | 0 | trak->Media->information->sampleTable->SampleDescription->child_boxes = stsd->child_boxes; |
856 | 0 | stsd->child_boxes = tmp; |
857 | 0 | } |
858 | 0 | if (stsd) gf_isom_box_del((GF_Box*)stsd); |
859 | 0 | return e; |
860 | 0 | } |
861 | | |
862 | | GF_EXPORT |
863 | | u32 gf_isom_new_track(GF_ISOFile *movie, GF_ISOTrackID trakID, u32 MediaType, u32 TimeScale) |
864 | 0 | { |
865 | 0 | return gf_isom_new_track_from_template(movie, trakID, MediaType, TimeScale, NULL, 0, GF_FALSE); |
866 | 0 | } |
867 | | |
868 | | GF_EXPORT |
869 | | u32 gf_isom_new_external_track(GF_ISOFile *movie, GF_ISOTrackID trakID, GF_ISOTrackID refTrakID, u32 MediaType, u32 TimeScale, const char *uri) |
870 | 0 | { |
871 | 0 | GF_TrackBox *trak; |
872 | 0 | if (!uri) { |
873 | 0 | gf_isom_set_last_error(movie, GF_BAD_PARAM); |
874 | 0 | return 0; |
875 | 0 | } |
876 | 0 | u32 track_num = gf_isom_new_track_from_template(movie, trakID, MediaType, TimeScale, NULL, 0, GF_FALSE); |
877 | 0 | if (!track_num) return GF_FALSE; |
878 | 0 | trak = gf_isom_get_track_box(movie, track_num); |
879 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
880 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box*)trak->Media); |
881 | 0 | trak->Media = NULL; |
882 | |
|
883 | 0 | trak->type = GF_ISOM_BOX_TYPE_EXTK; |
884 | 0 | trak->extl = (GF_ExternalTrackLocationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_EXTL); |
885 | 0 | gf_list_add(trak->child_boxes, trak->extl); |
886 | 0 | trak->extl->referenced_track_ID = refTrakID; |
887 | 0 | trak->extl->referenced_handler_type = MediaType; |
888 | 0 | trak->extl->media_timescale = TimeScale; |
889 | 0 | trak->extl->location = gf_strdup(uri); |
890 | 0 | trak->Header->flags = GF_ISOM_TK_IN_MOVIE | GF_ISOM_TK_ENABLED; |
891 | 0 | trak->Header->duration = 0xFFFFFFFF; |
892 | 0 | return track_num; |
893 | 0 | } |
894 | | |
895 | | GF_EXPORT |
896 | | GF_Err gf_isom_force_track_duration(GF_ISOFile *movie, u32 trackNumber, u64 dur) |
897 | 0 | { |
898 | 0 | GF_TrackBox *trak; |
899 | 0 | GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
900 | 0 | if (e) return e; |
901 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
902 | 0 | if (!trak || !trak->Header) return GF_BAD_PARAM; |
903 | 0 | trak->Header->duration = dur; |
904 | 0 | return GF_OK; |
905 | 0 | } |
906 | | |
907 | | |
908 | | GF_EXPORT |
909 | | GF_Err gf_isom_remove_stream_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex) |
910 | 0 | { |
911 | 0 | GF_TrackBox *trak; |
912 | 0 | GF_Err e; |
913 | 0 | GF_SampleEntryBox *entry; |
914 | |
|
915 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
916 | 0 | if (e) return e; |
917 | | |
918 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
919 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
920 | | |
921 | 0 | if (!movie->keep_utc) |
922 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
923 | |
|
924 | 0 | entry = (GF_SampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1); |
925 | 0 | if (!entry) return GF_BAD_PARAM; |
926 | 0 | gf_list_rem(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1); |
927 | 0 | gf_isom_box_del((GF_Box *)entry); |
928 | 0 | return GF_OK; |
929 | 0 | } |
930 | | |
931 | | //Create a new StreamDescription in the file. The URL and URN are used to describe external media |
932 | | GF_EXPORT |
933 | | GF_Err gf_isom_new_mpeg4_description(GF_ISOFile *movie, |
934 | | u32 trackNumber, |
935 | | const GF_ESD *esd, |
936 | | const char *URLname, |
937 | | const char *URNname, |
938 | | u32 *outDescriptionIndex) |
939 | 0 | { |
940 | 0 | GF_TrackBox *trak; |
941 | 0 | GF_Err e; |
942 | 0 | u32 dataRefIndex; |
943 | 0 | GF_ESD *new_esd; |
944 | |
|
945 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
946 | 0 | if (e) return e; |
947 | | |
948 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
949 | 0 | if (!trak || !trak->Media || |
950 | 0 | !esd || !esd->decoderConfig || |
951 | 0 | !esd->slConfig) return GF_BAD_PARAM; |
952 | | |
953 | | //get or create the data ref |
954 | 0 | e = Media_FindDataRef(trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex); |
955 | 0 | if (e) return e; |
956 | 0 | if (!dataRefIndex) { |
957 | 0 | e = Media_CreateDataRef(movie, trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex); |
958 | 0 | if (e) return e; |
959 | 0 | } |
960 | | //duplicate our desc |
961 | 0 | e = gf_odf_desc_copy((GF_Descriptor *)esd, (GF_Descriptor **)&new_esd); |
962 | 0 | if (e) return e; |
963 | 0 | if (!movie->keep_utc) |
964 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
965 | 0 | e = Track_SetStreamDescriptor(trak, 0, dataRefIndex, new_esd, outDescriptionIndex); |
966 | 0 | if (e) { |
967 | 0 | gf_odf_desc_del((GF_Descriptor *)new_esd); |
968 | 0 | return e; |
969 | 0 | } |
970 | 0 | return e; |
971 | 0 | } |
972 | | |
973 | | GF_Err gf_isom_flush_chunk(GF_TrackBox *trak, Bool is_final) |
974 | 0 | { |
975 | 0 | GF_Err e; |
976 | 0 | u64 data_offset; |
977 | 0 | u32 sample_number; |
978 | 0 | u8 *chunk_data; |
979 | 0 | u32 chunk_size, chunk_alloc; |
980 | 0 | if (!trak->chunk_cache) return GF_OK; |
981 | | |
982 | 0 | gf_bs_get_content_no_truncate(trak->chunk_cache, &chunk_data, &chunk_size, &chunk_alloc); |
983 | |
|
984 | 0 | data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler); |
985 | |
|
986 | 0 | e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, chunk_data, chunk_size); |
987 | 0 | if (e) return e; |
988 | | |
989 | 0 | sample_number = 1 + trak->Media->information->sampleTable->SampleSize->sampleCount; |
990 | 0 | sample_number -= trak->nb_samples_in_cache; |
991 | |
|
992 | 0 | e = stbl_AddChunkOffset(trak->Media, sample_number, trak->chunk_stsd_idx, data_offset, trak->nb_samples_in_cache); |
993 | |
|
994 | 0 | if (is_final) { |
995 | 0 | gf_free(chunk_data); |
996 | 0 | gf_bs_del(trak->chunk_cache); |
997 | 0 | trak->chunk_cache = NULL; |
998 | 0 | } else { |
999 | 0 | gf_bs_reassign_buffer(trak->chunk_cache, chunk_data, chunk_alloc); |
1000 | 0 | } |
1001 | 0 | return e; |
1002 | 0 | } |
1003 | | |
1004 | | static GF_Err trak_add_sample(GF_ISOFile *movie, GF_TrackBox *trak, const GF_ISOSample *sample, u32 descIndex, u64 data_offset, u32 syncShadowSampleNum) |
1005 | 0 | { |
1006 | 0 | Bool skip_data = GF_FALSE; |
1007 | 0 | GF_Err e; |
1008 | | |
1009 | | //faststart mode with interleaving time, cache data until we have a full chunk |
1010 | 0 | if ((movie->storageMode==GF_ISOM_STORE_FASTSTART) && movie->interleavingTime) { |
1011 | 0 | Bool flush_chunk = GF_FALSE; |
1012 | 0 | u64 stime = sample->DTS; |
1013 | 0 | stime *= movie->moov->mvhd->timeScale; |
1014 | 0 | stime /= trak->Media->mediaHeader->timeScale; |
1015 | |
|
1016 | 0 | if (stime - trak->first_dts_chunk > movie->interleavingTime) |
1017 | 0 | flush_chunk = GF_TRUE; |
1018 | |
|
1019 | 0 | if (movie->next_flush_chunk_time < stime) |
1020 | 0 | flush_chunk = GF_TRUE; |
1021 | |
|
1022 | 0 | if (trak->chunk_stsd_idx != descIndex) |
1023 | 0 | flush_chunk = GF_TRUE; |
1024 | |
|
1025 | 0 | if (trak->Media->information->sampleTable->MaxChunkSize && trak->Media->information->sampleTable->MaxChunkSize < trak->chunk_cache_size + sample->dataLength) |
1026 | 0 | flush_chunk = GF_TRUE; |
1027 | |
|
1028 | 0 | if (flush_chunk) { |
1029 | 0 | movie->next_flush_chunk_time = stime + movie->interleavingTime; |
1030 | 0 | if (trak->chunk_cache) { |
1031 | 0 | e = gf_isom_flush_chunk(trak, GF_FALSE); |
1032 | 0 | if (e) return e; |
1033 | 0 | } |
1034 | 0 | trak->nb_samples_in_cache = 0; |
1035 | 0 | trak->chunk_cache_size = 0; |
1036 | 0 | trak->first_dts_chunk = stime; |
1037 | 0 | } |
1038 | 0 | if (!trak->chunk_cache) |
1039 | 0 | trak->chunk_cache = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
1040 | 0 | gf_bs_write_data(trak->chunk_cache, sample->data, sample->dataLength); |
1041 | 0 | trak->nb_samples_in_cache += sample->nb_pack ? sample->nb_pack : 1; |
1042 | 0 | trak->chunk_cache_size += sample->dataLength; |
1043 | 0 | trak->chunk_stsd_idx = descIndex; |
1044 | |
|
1045 | 0 | skip_data = GF_TRUE; |
1046 | 0 | } |
1047 | | |
1048 | 0 | e = Media_AddSample(trak->Media, data_offset, sample, descIndex, syncShadowSampleNum); |
1049 | 0 | if (e) return e; |
1050 | | |
1051 | 0 | if (!skip_data && sample->dataLength) { |
1052 | 0 | e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, sample->data, sample->dataLength); |
1053 | 0 | if (e) return e; |
1054 | 0 | } |
1055 | | |
1056 | 0 | return GF_OK; |
1057 | 0 | } |
1058 | | |
1059 | | //Add samples to a track. Use streamDescriptionIndex to specify the desired stream (if several) |
1060 | | GF_EXPORT |
1061 | | GF_Err gf_isom_add_sample(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, const GF_ISOSample *sample) |
1062 | 0 | { |
1063 | 0 | GF_Err e; |
1064 | 0 | GF_TrackBox *trak; |
1065 | 0 | GF_SampleEntryBox *entry; |
1066 | 0 | u32 dataRefIndex; |
1067 | 0 | u64 data_offset; |
1068 | 0 | u32 descIndex; |
1069 | 0 | GF_DataEntryURLBox *Dentry; |
1070 | |
|
1071 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1072 | 0 | if (e) return e; |
1073 | | |
1074 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1075 | 0 | if (!trak) return GF_BAD_PARAM; |
1076 | | |
1077 | 0 | e = FlushCaptureMode(movie); |
1078 | 0 | if (e) return e; |
1079 | | |
1080 | 0 | e = unpack_track(trak); |
1081 | 0 | if (e) return e; |
1082 | | |
1083 | | //OK, add the sample |
1084 | | //1- Get the streamDescriptionIndex and dataRefIndex |
1085 | | //not specified, get the latest used... |
1086 | 0 | descIndex = StreamDescriptionIndex; |
1087 | 0 | if (!StreamDescriptionIndex) { |
1088 | 0 | descIndex = trak->Media->information->sampleTable->currentEntryIndex; |
1089 | 0 | } |
1090 | 0 | e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); |
1091 | 0 | if (e) return e; |
1092 | 0 | if (!entry || !dataRefIndex) return GF_BAD_PARAM; |
1093 | | //set the current to this one |
1094 | 0 | trak->Media->information->sampleTable->currentEntryIndex = descIndex; |
1095 | | |
1096 | | |
1097 | | //get this dataRef and return false if not self contained |
1098 | 0 | Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1); |
1099 | 0 | if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM; |
1100 | | |
1101 | | //Open our data map. We are adding stuff, so use EDIT |
1102 | 0 | e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1); |
1103 | 0 | if (e) return e; |
1104 | | |
1105 | | //Get the offset... |
1106 | 0 | data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler); |
1107 | | |
1108 | | /*rewrite OD frame*/ |
1109 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { |
1110 | 0 | GF_ISOSample *od_sample = NULL; |
1111 | |
|
1112 | 0 | e = Media_ParseODFrame(trak->Media, sample, &od_sample); |
1113 | 0 | if (e) return e; |
1114 | | |
1115 | 0 | e = trak_add_sample(movie, trak, od_sample, descIndex, data_offset, 0); |
1116 | |
|
1117 | 0 | if (od_sample) |
1118 | 0 | gf_isom_sample_del(&od_sample); |
1119 | 0 | } else { |
1120 | 0 | e = trak_add_sample(movie, trak, sample, descIndex, data_offset, 0); |
1121 | 0 | } |
1122 | 0 | if (e) return e; |
1123 | | |
1124 | 0 | if (!movie->keep_utc) |
1125 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1126 | | |
1127 | | //update media duration |
1128 | 0 | if ((s64)sample->DTS + sample->CTS_Offset>=0) { |
1129 | 0 | GF_TimeToSampleBox *stts = trak->Media->information->sampleTable->TimeToSample; |
1130 | 0 | u64 dur = sample->DTS + sample->CTS_Offset; |
1131 | 0 | dur += stts->entries[stts->nb_entries-1].sampleDelta; |
1132 | |
|
1133 | 0 | if (dur > trak->Media->mediaHeader->duration) { |
1134 | 0 | trak->Media->mediaHeader->duration = dur; |
1135 | 0 | } |
1136 | 0 | } |
1137 | | //do not update track duration yet, this is done on close |
1138 | 0 | return GF_OK; |
1139 | 0 | } |
1140 | | |
1141 | | GF_EXPORT |
1142 | | GF_Err gf_isom_add_sample_shadow(GF_ISOFile *movie, u32 trackNumber, GF_ISOSample *sample) |
1143 | 0 | { |
1144 | 0 | GF_Err e; |
1145 | 0 | GF_TrackBox *trak; |
1146 | 0 | GF_ISOSample *prev; |
1147 | 0 | GF_SampleEntryBox *entry; |
1148 | 0 | u32 dataRefIndex; |
1149 | 0 | u64 data_offset; |
1150 | 0 | u32 descIndex; |
1151 | 0 | u32 sampleNum, prevSampleNum; |
1152 | 0 | GF_DataEntryURLBox *Dentry; |
1153 | 0 | Bool offset_times = GF_FALSE; |
1154 | |
|
1155 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1156 | 0 | if (e) return e; |
1157 | | |
1158 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1159 | 0 | if (!trak || !sample) return GF_BAD_PARAM; |
1160 | | |
1161 | 0 | e = FlushCaptureMode(movie); |
1162 | 0 | if (e) return e; |
1163 | | |
1164 | 0 | e = unpack_track(trak); |
1165 | 0 | if (e) return e; |
1166 | | |
1167 | 0 | e = stbl_findEntryForTime(trak->Media->information->sampleTable, sample->DTS, 0, &sampleNum, &prevSampleNum); |
1168 | 0 | if (e) return e; |
1169 | | /*we need the EXACT match*/ |
1170 | 0 | if (!sampleNum) return GF_BAD_PARAM; |
1171 | | |
1172 | 0 | prev = gf_isom_get_sample_info(movie, trackNumber, sampleNum, &descIndex, NULL); |
1173 | 0 | if (!prev) return gf_isom_last_error(movie); |
1174 | | /*for conformance*/ |
1175 | 0 | if (sample->DTS==prev->DTS) offset_times = GF_TRUE; |
1176 | 0 | gf_isom_sample_del(&prev); |
1177 | |
|
1178 | 0 | e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); |
1179 | 0 | if (e) return e; |
1180 | 0 | if (!entry || !dataRefIndex) return GF_BAD_PARAM; |
1181 | 0 | trak->Media->information->sampleTable->currentEntryIndex = descIndex; |
1182 | | |
1183 | | //get this dataRef and return false if not self contained |
1184 | 0 | Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1); |
1185 | 0 | if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM; |
1186 | | |
1187 | | //Open our data map. We are adding stuff, so use EDIT |
1188 | 0 | e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1); |
1189 | 0 | if (e) return e; |
1190 | | |
1191 | 0 | data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler); |
1192 | 0 | if (offset_times) sample->DTS += 1; |
1193 | | |
1194 | | /*REWRITE ANY OD STUFF*/ |
1195 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { |
1196 | 0 | GF_ISOSample *od_sample = NULL; |
1197 | 0 | e = Media_ParseODFrame(trak->Media, sample, &od_sample); |
1198 | 0 | if (e) return e; |
1199 | | |
1200 | 0 | e = trak_add_sample(movie, trak, od_sample, descIndex, data_offset, sampleNum); |
1201 | 0 | if (od_sample) |
1202 | 0 | gf_isom_sample_del(&od_sample); |
1203 | 0 | } else { |
1204 | 0 | e = trak_add_sample(movie, trak, sample, descIndex, data_offset, sampleNum); |
1205 | 0 | } |
1206 | 0 | if (e) return e; |
1207 | 0 | if (offset_times) sample->DTS -= 1; |
1208 | | |
1209 | | //OK, update duration |
1210 | 0 | e = Media_SetDuration(trak); |
1211 | 0 | if (e) return e; |
1212 | 0 | if (!movie->keep_utc) |
1213 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1214 | 0 | return SetTrackDuration(trak); |
1215 | 0 | } |
1216 | | |
1217 | | GF_EXPORT |
1218 | | GF_Err gf_isom_append_sample_data(GF_ISOFile *movie, u32 trackNumber, u8 *data, u32 data_size) |
1219 | 0 | { |
1220 | 0 | GF_Err e; |
1221 | 0 | GF_TrackBox *trak; |
1222 | 0 | GF_SampleEntryBox *entry; |
1223 | 0 | u32 dataRefIndex; |
1224 | 0 | u32 descIndex; |
1225 | 0 | GF_DataEntryURLBox *Dentry; |
1226 | |
|
1227 | 0 | if (!data_size) return GF_OK; |
1228 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1229 | 0 | if (e) return e; |
1230 | | |
1231 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1232 | 0 | if (!trak) return GF_BAD_PARAM; |
1233 | | |
1234 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) return GF_BAD_PARAM; |
1235 | | |
1236 | | //OK, add the sample |
1237 | 0 | descIndex = trak->Media->information->sampleTable->currentEntryIndex; |
1238 | |
|
1239 | 0 | e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); |
1240 | 0 | if (e) return e; |
1241 | 0 | if (!entry || !dataRefIndex) return GF_BAD_PARAM; |
1242 | | |
1243 | | //get this dataRef and return false if not self contained |
1244 | 0 | Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1); |
1245 | 0 | if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM; |
1246 | | |
1247 | | //Open our data map. We are adding stuff, so use EDIT |
1248 | 0 | e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1); |
1249 | 0 | if (e) return e; |
1250 | | |
1251 | | //add the media data |
1252 | 0 | if (trak->chunk_cache) { |
1253 | 0 | gf_bs_write_data(trak->chunk_cache, data, data_size); |
1254 | 0 | trak->chunk_cache_size += data_size; |
1255 | 0 | } else { |
1256 | 0 | e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, data, data_size); |
1257 | 0 | if (e) return e; |
1258 | 0 | } |
1259 | | //update data size |
1260 | 0 | return stbl_SampleSizeAppend(trak->Media->information->sampleTable->SampleSize, data_size); |
1261 | 0 | } |
1262 | | |
1263 | | |
1264 | | //Add sample reference to a track. The SampleOffset is the offset of the data in the referenced file |
1265 | | //you must have created a StreamDescription with URL or URN specifying your referenced file |
1266 | | //the data offset specifies the beginning of the chunk |
1267 | | //Use streamDescriptionIndex to specify the desired stream (if several) |
1268 | | GF_EXPORT |
1269 | | GF_Err gf_isom_add_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_ISOSample *sample, u64 dataOffset) |
1270 | 0 | { |
1271 | 0 | GF_TrackBox *trak; |
1272 | 0 | GF_SampleEntryBox *entry; |
1273 | 0 | u32 dataRefIndex; |
1274 | 0 | u32 descIndex; |
1275 | 0 | GF_DataEntryURLBox *Dentry; |
1276 | 0 | GF_Err e; |
1277 | |
|
1278 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1279 | 0 | if (e) return e; |
1280 | | |
1281 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1282 | 0 | if (!trak) return GF_BAD_PARAM; |
1283 | | |
1284 | 0 | e = unpack_track(trak); |
1285 | 0 | if (e) return e; |
1286 | | |
1287 | | //OD is not allowed as a data ref |
1288 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { |
1289 | 0 | return GF_BAD_PARAM; |
1290 | 0 | } |
1291 | | //OK, add the sample |
1292 | | //1- Get the streamDescriptionIndex and dataRefIndex |
1293 | | //not specified, get the latest used... |
1294 | 0 | descIndex = StreamDescriptionIndex; |
1295 | 0 | if (!StreamDescriptionIndex) { |
1296 | 0 | descIndex = trak->Media->information->sampleTable->currentEntryIndex; |
1297 | 0 | } |
1298 | 0 | e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); |
1299 | 0 | if (e) return e; |
1300 | 0 | if (!entry || !dataRefIndex) return GF_BAD_PARAM; |
1301 | | //set the current to this one |
1302 | 0 | trak->Media->information->sampleTable->currentEntryIndex = descIndex; |
1303 | | |
1304 | | |
1305 | | //get this dataRef and return false if self contained |
1306 | 0 | Dentry =(GF_DataEntryURLBox*) gf_list_get(trak->Media->information->dataInformation->dref->child_boxes, dataRefIndex - 1); |
1307 | 0 | if (Dentry->flags == 1) return GF_BAD_PARAM; |
1308 | | |
1309 | | //add the meta data |
1310 | 0 | e = Media_AddSample(trak->Media, dataOffset, sample, descIndex, 0); |
1311 | 0 | if (e) return e; |
1312 | | |
1313 | 0 | if (!movie->keep_utc) |
1314 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1315 | | //OK, update duration |
1316 | 0 | e = Media_SetDuration(trak); |
1317 | 0 | if (e) return e; |
1318 | 0 | return SetTrackDuration(trak); |
1319 | |
|
1320 | 0 | } |
1321 | | |
1322 | | //set the duration of the last media sample. If not set, the duration of the last sample is the |
1323 | | //duration of the previous one if any, or 1000 (default value). |
1324 | | static GF_Err gf_isom_set_last_sample_duration_internal(GF_ISOFile *movie, u32 trackNumber, u64 dur_num, u32 dur_den, u32 mode) |
1325 | 0 | { |
1326 | 0 | GF_TrackBox *trak; |
1327 | 0 | GF_SttsEntry *ent; |
1328 | 0 | GF_TimeToSampleBox *stts; |
1329 | 0 | u64 mdur; |
1330 | 0 | u32 duration; |
1331 | 0 | GF_Err e; |
1332 | 0 | Bool is_patch = GF_FALSE; |
1333 | |
|
1334 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1335 | 0 | if (e) return e; |
1336 | | |
1337 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1338 | 0 | if (!trak) return GF_BAD_PARAM; |
1339 | | |
1340 | 0 | if (mode==0) { |
1341 | 0 | duration = (u32) dur_num; |
1342 | 0 | } else if (mode==1) { |
1343 | 0 | duration = (u32) dur_num; |
1344 | 0 | if (dur_den) { |
1345 | 0 | duration *= trak->Media->mediaHeader->timeScale; |
1346 | 0 | duration /= dur_den; |
1347 | 0 | } |
1348 | 0 | } else { |
1349 | 0 | is_patch = GF_TRUE; |
1350 | 0 | } |
1351 | 0 | mdur = trak->Media->mediaHeader->duration; |
1352 | 0 | stts = trak->Media->information->sampleTable->TimeToSample; |
1353 | 0 | if (!stts->nb_entries) return GF_BAD_PARAM; |
1354 | | |
1355 | 0 | if (is_patch) { |
1356 | 0 | u32 i, avg_dur, nb_samp=0; |
1357 | 0 | u64 cum_dur=0; |
1358 | 0 | for (i=0; i<stts->nb_entries; i++) { |
1359 | 0 | ent = (GF_SttsEntry*) &stts->entries[i]; |
1360 | 0 | cum_dur += ent->sampleCount*ent->sampleDelta; |
1361 | 0 | nb_samp += ent->sampleCount; |
1362 | 0 | } |
1363 | 0 | if (cum_dur <= dur_num || !nb_samp) return GF_OK; |
1364 | 0 | avg_dur = (u32) (dur_num / nb_samp); |
1365 | |
|
1366 | 0 | stts->entries[0].sampleDelta = avg_dur; |
1367 | 0 | stts->entries[0].sampleCount = nb_samp; |
1368 | 0 | stts->nb_entries = 1; |
1369 | 0 | stts->w_LastDTS = dur_num - avg_dur; |
1370 | 0 | return GF_OK; |
1371 | 0 | } |
1372 | | //get the last entry |
1373 | 0 | ent = (GF_SttsEntry*) &stts->entries[stts->nb_entries-1]; |
1374 | 0 | if ((mode==1) && !duration && !dur_den) { |
1375 | | //same as previous, nothing to adjust |
1376 | 0 | if (ent->sampleCount>1) return GF_OK; |
1377 | 0 | if (stts->nb_entries==1) return GF_OK; |
1378 | 0 | duration = stts->entries[stts->nb_entries-2].sampleDelta; |
1379 | 0 | } |
1380 | | |
1381 | 0 | mdur -= ent->sampleDelta; |
1382 | 0 | mdur += duration; |
1383 | | |
1384 | | //we only have one sample |
1385 | 0 | if (ent->sampleCount == 1) { |
1386 | 0 | ent->sampleDelta = (u32) duration; |
1387 | 0 | if (mode && (stts->nb_entries>1) && (stts->entries[stts->nb_entries-2].sampleDelta==duration)) { |
1388 | 0 | stts->entries[stts->nb_entries-2].sampleCount++; |
1389 | 0 | stts->nb_entries--; |
1390 | | //and update the write cache |
1391 | 0 | stts->w_currentSampleNum = trak->Media->information->sampleTable->SampleSize->sampleCount; |
1392 | 0 | } |
1393 | 0 | } else { |
1394 | 0 | if (ent->sampleDelta == duration) return GF_OK; |
1395 | 0 | ent->sampleCount -= 1; |
1396 | |
|
1397 | 0 | if (stts->nb_entries==stts->alloc_size) { |
1398 | 0 | stts->alloc_size++; |
1399 | 0 | stts->entries = (GF_SttsEntry*)gf_realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size); |
1400 | 0 | if (!stts->entries) return GF_OUT_OF_MEM; |
1401 | 0 | } |
1402 | 0 | stts->entries[stts->nb_entries].sampleCount = 1; |
1403 | 0 | stts->entries[stts->nb_entries].sampleDelta = (u32) duration; |
1404 | 0 | stts->nb_entries++; |
1405 | | //and update the write cache |
1406 | 0 | stts->w_currentSampleNum = trak->Media->information->sampleTable->SampleSize->sampleCount; |
1407 | 0 | } |
1408 | 0 | if (!movie->keep_utc) |
1409 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1410 | | |
1411 | | //update media duration if duration was set |
1412 | 0 | if (trak->Media->mediaHeader->duration) |
1413 | 0 | trak->Media->mediaHeader->duration = mdur; |
1414 | | //do not update track duration yet, this is done on close |
1415 | 0 | return GF_OK; |
1416 | 0 | } |
1417 | | |
1418 | | GF_EXPORT |
1419 | | GF_Err gf_isom_set_last_sample_duration(GF_ISOFile *movie, u32 trackNumber, u32 duration) |
1420 | 0 | { |
1421 | 0 | return gf_isom_set_last_sample_duration_internal(movie, trackNumber, duration, 0, 0); |
1422 | 0 | } |
1423 | | |
1424 | | GF_EXPORT |
1425 | | GF_Err gf_isom_patch_last_sample_duration(GF_ISOFile *movie, u32 trackNumber, u64 next_dts) |
1426 | 0 | { |
1427 | 0 | return gf_isom_set_last_sample_duration_internal(movie, trackNumber, next_dts, 0, 2); |
1428 | 0 | } |
1429 | | |
1430 | | GF_EXPORT |
1431 | | GF_Err gf_isom_set_last_sample_duration_ex(GF_ISOFile *movie, u32 trackNumber, u32 dur_num, u32 dur_den) |
1432 | 0 | { |
1433 | 0 | return gf_isom_set_last_sample_duration_internal(movie, trackNumber, dur_num, dur_den, 1); |
1434 | 0 | } |
1435 | | |
1436 | | //update a sample data in the media. Note that the sample MUST exists |
1437 | | GF_EXPORT |
1438 | | GF_Err gf_isom_update_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, Bool data_only) |
1439 | 0 | { |
1440 | 0 | GF_Err e; |
1441 | 0 | GF_TrackBox *trak; |
1442 | |
|
1443 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_EDIT); |
1444 | 0 | if (e) return e; |
1445 | | |
1446 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1447 | 0 | if (!trak) return GF_BAD_PARAM; |
1448 | | |
1449 | 0 | e = unpack_track(trak); |
1450 | 0 | if (e) return e; |
1451 | | |
1452 | | //block for hint tracks |
1453 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM; |
1454 | | |
1455 | | //REWRITE ANY OD STUFF |
1456 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { |
1457 | 0 | GF_ISOSample *od_sample = NULL; |
1458 | 0 | e = Media_ParseODFrame(trak->Media, sample, &od_sample); |
1459 | 0 | if (!e) e = Media_UpdateSample(trak->Media, sampleNumber, od_sample, data_only); |
1460 | 0 | if (od_sample) gf_isom_sample_del(&od_sample); |
1461 | 0 | gf_isom_disable_inplace_rewrite(movie); |
1462 | 0 | } else { |
1463 | 0 | e = Media_UpdateSample(trak->Media, sampleNumber, sample, data_only); |
1464 | 0 | if (data_only || sample->data) |
1465 | 0 | gf_isom_disable_inplace_rewrite(movie); |
1466 | 0 | } |
1467 | 0 | if (e) return e; |
1468 | 0 | if (!movie->keep_utc) |
1469 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1470 | |
|
1471 | 0 | return GF_OK; |
1472 | 0 | } |
1473 | | |
1474 | | //update a sample data in the media. Note that the sample MUST exists, |
1475 | | //that sample->data MUST be NULL and sample->dataLength must be NON NULL; |
1476 | | GF_EXPORT |
1477 | | GF_Err gf_isom_update_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset) |
1478 | 0 | { |
1479 | 0 | GF_Err e; |
1480 | 0 | GF_TrackBox *trak; |
1481 | |
|
1482 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_EDIT); |
1483 | 0 | if (e) return e; |
1484 | | |
1485 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1486 | 0 | if (!trak) return GF_BAD_PARAM; |
1487 | | |
1488 | | //block for hint tracks |
1489 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM; |
1490 | | |
1491 | 0 | if (!sampleNumber || !sample) return GF_BAD_PARAM; |
1492 | | |
1493 | 0 | e = unpack_track(trak); |
1494 | 0 | if (e) return e; |
1495 | | |
1496 | | //OD is not allowed as a data ref |
1497 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { |
1498 | 0 | return GF_BAD_PARAM; |
1499 | 0 | } |
1500 | | //OK, update it |
1501 | 0 | e = Media_UpdateSampleReference(trak->Media, sampleNumber, sample, data_offset); |
1502 | 0 | if (e) return e; |
1503 | | |
1504 | 0 | if (!movie->keep_utc) |
1505 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1506 | 0 | return GF_OK; |
1507 | 0 | } |
1508 | | |
1509 | | //for gf_isom_remove_sample and gf_isom_remove_track: check all items sharing their data with a sample being removed |
1510 | | //and remove sharing flag |
1511 | | // sample_number can be 0 for complete track removal |
1512 | | static void gf_isom_meta_track_remove(GF_ISOFile *movie, GF_TrackBox *trak, u32 sample_number) |
1513 | 0 | { |
1514 | 0 | u32 i, count; |
1515 | 0 | if (!movie || !movie->meta || !movie->meta->use_item_sample_sharing) |
1516 | 0 | return; |
1517 | | |
1518 | 0 | count = gf_list_count(movie->meta->item_locations->location_entries); |
1519 | 0 | for (i=0; i<count; i++) { |
1520 | 0 | u32 j; |
1521 | 0 | GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(movie->meta->item_locations->location_entries, i); |
1522 | | /*get item info*/ |
1523 | 0 | GF_ItemInfoEntryBox *iinf = NULL; |
1524 | 0 | j=0; |
1525 | 0 | while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(movie->meta->item_infos->item_infos, &j))) { |
1526 | 0 | if (iinf->item_ID==iloc->item_ID) break; |
1527 | 0 | } |
1528 | 0 | if (!iinf || !iinf->tk_id) continue; |
1529 | 0 | if (iinf->tk_id != trak->Header->trackID) continue; |
1530 | | |
1531 | 0 | if (sample_number && (iinf->sample_num != sample_number)) continue; |
1532 | 0 | iinf->tk_id = 0; |
1533 | 0 | iinf->sample_num = 0; |
1534 | 0 | } |
1535 | 0 | } |
1536 | | |
1537 | | |
1538 | | |
1539 | | //Remove a given sample |
1540 | | GF_EXPORT |
1541 | | GF_Err gf_isom_remove_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber) |
1542 | 0 | { |
1543 | 0 | GF_Err e; |
1544 | 0 | GF_TrackBox *trak; |
1545 | |
|
1546 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_EDIT); |
1547 | 0 | if (e) return e; |
1548 | | |
1549 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1550 | 0 | if (!trak || !sampleNumber || (sampleNumber > trak->Media->information->sampleTable->SampleSize->sampleCount) ) |
1551 | 0 | return GF_BAD_PARAM; |
1552 | | |
1553 | | //block for hint tracks |
1554 | 0 | if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM; |
1555 | | |
1556 | 0 | e = unpack_track(trak); |
1557 | 0 | if (e) return e; |
1558 | | //do NOT change the order DTS, CTS, size chunk |
1559 | | |
1560 | | //remove DTS |
1561 | 0 | e = stbl_RemoveDTS(trak->Media->information->sampleTable, sampleNumber, 1, trak->Media->mediaHeader->timeScale); |
1562 | 0 | if (e) return e; |
1563 | | //remove CTS if any |
1564 | 0 | if (trak->Media->information->sampleTable->CompositionOffset) { |
1565 | 0 | e = stbl_RemoveCTS(trak->Media->information->sampleTable, sampleNumber, 1); |
1566 | 0 | if (e) return e; |
1567 | 0 | } |
1568 | | //remove size |
1569 | 0 | e = stbl_RemoveSize(trak->Media->information->sampleTable, sampleNumber, 1); |
1570 | 0 | if (e) return e; |
1571 | | //remove sampleToChunk and chunk |
1572 | 0 | e = stbl_RemoveChunk(trak->Media->information->sampleTable, sampleNumber, 1); |
1573 | 0 | if (e) return e; |
1574 | | //remove sync |
1575 | 0 | if (trak->Media->information->sampleTable->SyncSample) { |
1576 | 0 | e = stbl_RemoveRAP(trak->Media->information->sampleTable, sampleNumber); |
1577 | 0 | if (e) return e; |
1578 | 0 | } |
1579 | | //remove sample dep |
1580 | 0 | if (trak->Media->information->sampleTable->SampleDep) { |
1581 | 0 | e = stbl_RemoveRedundant(trak->Media->information->sampleTable, sampleNumber, 1); |
1582 | 0 | if (e) return e; |
1583 | 0 | } |
1584 | | //remove shadow |
1585 | 0 | e = stbl_RemoveShadow(trak->Media->information->sampleTable, sampleNumber); |
1586 | 0 | if (e) return e; |
1587 | | |
1588 | | //remove padding |
1589 | 0 | e = stbl_RemovePaddingBits(trak->Media->information->sampleTable, sampleNumber); |
1590 | 0 | if (e) return e; |
1591 | | |
1592 | 0 | e = stbl_RemoveSubSample(trak->Media->information->sampleTable, sampleNumber); |
1593 | 0 | if (e) return e; |
1594 | | |
1595 | 0 | e = stbl_RemoveSampleGroup(trak->Media->information->sampleTable, sampleNumber); |
1596 | 0 | if (e) return e; |
1597 | | |
1598 | 0 | gf_isom_disable_inplace_rewrite(movie); |
1599 | |
|
1600 | 0 | gf_isom_meta_track_remove(movie, trak, sampleNumber); |
1601 | |
|
1602 | 0 | return SetTrackDuration(trak); |
1603 | 0 | } |
1604 | | |
1605 | | |
1606 | | GF_EXPORT |
1607 | | GF_Err gf_isom_set_final_name(GF_ISOFile *movie, char *filename) |
1608 | 0 | { |
1609 | 0 | GF_Err e; |
1610 | 0 | if (!movie ) return GF_BAD_PARAM; |
1611 | | |
1612 | | //if mode is not OPEN_EDIT file was created under the right name |
1613 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_EDIT); |
1614 | 0 | if (e) return e; |
1615 | | |
1616 | 0 | if (filename) { |
1617 | | //we don't allow file overwriting |
1618 | 0 | if ( (movie->openMode == GF_ISOM_OPEN_EDIT) |
1619 | 0 | && movie->fileName && !strcmp(filename, movie->fileName)) |
1620 | 0 | return GF_BAD_PARAM; |
1621 | 0 | if (movie->finalName) gf_free(movie->finalName); |
1622 | 0 | movie->finalName = gf_strdup(filename); |
1623 | 0 | if (!movie->finalName) return GF_OUT_OF_MEM; |
1624 | 0 | gf_isom_disable_inplace_rewrite(movie); |
1625 | 0 | } |
1626 | 0 | return GF_OK; |
1627 | 0 | } |
1628 | | |
1629 | | //Add a system descriptor to the ESD of a stream(EDIT or WRITE mode only) |
1630 | | GF_EXPORT |
1631 | | GF_Err gf_isom_add_desc_to_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, const GF_Descriptor *theDesc) |
1632 | 0 | { |
1633 | 0 | GF_IPIPtr *ipiD; |
1634 | 0 | GF_Err e; |
1635 | 0 | u16 tmpRef; |
1636 | 0 | GF_TrackBox *trak; |
1637 | 0 | GF_Descriptor *desc; |
1638 | 0 | GF_ESD *esd; |
1639 | 0 | GF_TrackReferenceBox *tref; |
1640 | 0 | GF_TrackReferenceTypeBox *dpnd; |
1641 | 0 | GF_MPEGVisualSampleEntryBox *entry; |
1642 | 0 | u32 msubtype; |
1643 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1644 | 0 | if (e) return e; |
1645 | | |
1646 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1647 | 0 | if (!trak) return GF_BAD_PARAM; |
1648 | | |
1649 | | /*GETS NATIVE DESCRIPTOR ONLY*/ |
1650 | 0 | e = Media_GetESD(trak->Media, StreamDescriptionIndex, &esd, GF_TRUE); |
1651 | 0 | if (e) return e; |
1652 | | |
1653 | 0 | entry = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex-1); |
1654 | 0 | if (!entry) return GF_BAD_PARAM; |
1655 | 0 | msubtype = entry->type; |
1656 | 0 | if ((msubtype==GF_ISOM_BOX_TYPE_ENCV) || (msubtype==GF_ISOM_BOX_TYPE_ENCA)) |
1657 | 0 | gf_isom_get_original_format_type(movie, trackNumber, StreamDescriptionIndex, &msubtype); |
1658 | | |
1659 | | //duplicate the desc |
1660 | 0 | e = gf_odf_desc_copy((GF_Descriptor *)theDesc, &desc); |
1661 | 0 | if (e) return e; |
1662 | | |
1663 | | //and add it to the ESD EXCEPT IPI PTR (we need to translate from ES_ID to TrackID!!! |
1664 | 0 | if (!movie->keep_utc) |
1665 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1666 | |
|
1667 | 0 | switch (desc->tag) { |
1668 | 0 | case GF_ODF_IPI_PTR_TAG: |
1669 | 0 | goto insertIPI; |
1670 | 0 | default: |
1671 | 0 | break; |
1672 | 0 | } |
1673 | | |
1674 | 0 | if ((msubtype==GF_ISOM_BOX_TYPE_MP4S) || (msubtype==GF_ISOM_BOX_TYPE_MP4V) || (msubtype==GF_ISOM_BOX_TYPE_MP4A)) { |
1675 | 0 | return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc); |
1676 | 0 | } |
1677 | | |
1678 | 0 | if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_VISUAL) { |
1679 | 0 | gf_odf_desc_del(desc); |
1680 | 0 | return GF_NOT_SUPPORTED; |
1681 | 0 | } |
1682 | 0 | GF_MPEG4ExtensionDescriptorsBox *mdesc = (GF_MPEG4ExtensionDescriptorsBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_M4DS); |
1683 | 0 | if (!mdesc) { |
1684 | 0 | mdesc = (GF_MPEG4ExtensionDescriptorsBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_M4DS); |
1685 | 0 | } |
1686 | 0 | return gf_list_add(mdesc->descriptors, desc); |
1687 | | |
1688 | 0 | insertIPI: |
1689 | 0 | if (esd->ipiPtr) { |
1690 | 0 | gf_odf_desc_del((GF_Descriptor *) esd->ipiPtr); |
1691 | 0 | esd->ipiPtr = NULL; |
1692 | 0 | } |
1693 | |
|
1694 | 0 | ipiD = (GF_IPIPtr *) desc; |
1695 | | //find a tref |
1696 | 0 | if (!trak->References) { |
1697 | 0 | tref = (GF_TrackReferenceBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TREF); |
1698 | 0 | if (!tref) return GF_OUT_OF_MEM; |
1699 | 0 | e = trak_on_child_box((GF_Box*)trak, (GF_Box *)tref, GF_FALSE); |
1700 | 0 | if (e) return e; |
1701 | 0 | } |
1702 | 0 | tref = trak->References; |
1703 | |
|
1704 | 0 | e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd); |
1705 | 0 | if (e) return e; |
1706 | 0 | if (!dpnd) { |
1707 | 0 | tmpRef = 0; |
1708 | 0 | dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT); |
1709 | 0 | if (!dpnd) return GF_OUT_OF_MEM; |
1710 | 0 | dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR; |
1711 | 0 | e = reftype_AddRefTrack(dpnd, ipiD->IPI_ES_Id, &tmpRef); |
1712 | 0 | if (e) return e; |
1713 | | //and replace the tag and value... |
1714 | 0 | ipiD->IPI_ES_Id = tmpRef; |
1715 | 0 | ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG; |
1716 | 0 | } else { |
1717 | | //Watch out! ONLY ONE IPI dependency is allowed per stream |
1718 | 0 | dpnd->trackIDCount = 1; |
1719 | 0 | dpnd->trackIDs[0] = ipiD->IPI_ES_Id; |
1720 | | //and replace the tag and value... |
1721 | 0 | ipiD->IPI_ES_Id = 1; |
1722 | 0 | ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG; |
1723 | 0 | } |
1724 | | //and add the desc to the esd... |
1725 | 0 | return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc); |
1726 | 0 | } |
1727 | | |
1728 | | |
1729 | | //use carefully. Very useful when you made a lot of changes (IPMP, IPI, OCI, ...) |
1730 | | //THIS WILL REPLACE THE WHOLE DESCRIPTOR ... |
1731 | | GF_EXPORT |
1732 | | GF_Err gf_isom_change_mpeg4_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, const GF_ESD *newESD) |
1733 | 0 | { |
1734 | 0 | GF_Err e; |
1735 | 0 | GF_ESD *esd; |
1736 | 0 | GF_TrackBox *trak; |
1737 | 0 | GF_SampleEntryBox *entry; |
1738 | 0 | GF_SampleDescriptionBox *stsd; |
1739 | |
|
1740 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1741 | 0 | if (e) return e; |
1742 | | |
1743 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1744 | 0 | if (!trak) return GF_BAD_PARAM; |
1745 | | |
1746 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
1747 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
1748 | | |
1749 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
1750 | 0 | return movie->LastError = GF_BAD_PARAM; |
1751 | 0 | } |
1752 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
1753 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
1754 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
1755 | | |
1756 | 0 | if (!movie->keep_utc) |
1757 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1758 | | //duplicate our desc |
1759 | 0 | e = gf_odf_desc_copy((GF_Descriptor *)newESD, (GF_Descriptor **)&esd); |
1760 | 0 | if (e) return e; |
1761 | 0 | e = Track_SetStreamDescriptor(trak, StreamDescriptionIndex, entry->dataReferenceIndex, esd, NULL); |
1762 | 0 | if (e != GF_OK) { |
1763 | 0 | gf_odf_desc_del((GF_Descriptor *) esd); |
1764 | 0 | } |
1765 | 0 | return e; |
1766 | 0 | } |
1767 | | |
1768 | | GF_EXPORT |
1769 | | GF_Err gf_isom_set_visual_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 Width, u32 Height) |
1770 | 0 | { |
1771 | 0 | GF_Err e; |
1772 | 0 | GF_TrackBox *trak; |
1773 | 0 | GF_SampleEntryBox *entry; |
1774 | 0 | GF_SampleDescriptionBox *stsd; |
1775 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1776 | 0 | if (e) return e; |
1777 | | |
1778 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1779 | 0 | if (!trak) return GF_BAD_PARAM; |
1780 | | |
1781 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
1782 | 0 | if (!stsd) { |
1783 | 0 | return movie->LastError = GF_ISOM_INVALID_FILE; |
1784 | 0 | } |
1785 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
1786 | 0 | return movie->LastError = GF_BAD_PARAM; |
1787 | 0 | } |
1788 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
1789 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
1790 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
1791 | 0 | if (!movie->keep_utc) |
1792 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1793 | | |
1794 | | //valid for MPEG visual, JPG and 3GPP H263 |
1795 | 0 | if (entry->internal_type == GF_ISOM_SAMPLE_ENTRY_VIDEO) { |
1796 | 0 | ((GF_VisualSampleEntryBox*)entry)->Width = Width; |
1797 | 0 | ((GF_VisualSampleEntryBox*)entry)->Height = Height; |
1798 | 0 | trak->Header->width = Width<<16; |
1799 | 0 | trak->Header->height = Height<<16; |
1800 | 0 | return GF_OK; |
1801 | 0 | } else if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_SCENE) { |
1802 | 0 | trak->Header->width = Width<<16; |
1803 | 0 | trak->Header->height = Height<<16; |
1804 | 0 | return GF_OK; |
1805 | 0 | } else { |
1806 | 0 | return GF_BAD_PARAM; |
1807 | 0 | } |
1808 | 0 | } |
1809 | | |
1810 | | GF_EXPORT |
1811 | | GF_Err gf_isom_set_visual_bit_depth(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u16 bitDepth) |
1812 | 0 | { |
1813 | 0 | GF_Err e; |
1814 | 0 | GF_TrackBox *trak; |
1815 | 0 | GF_MPEGVisualSampleEntryBox *entry; |
1816 | 0 | GF_SampleDescriptionBox *stsd; |
1817 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1818 | 0 | if (e) return e; |
1819 | | |
1820 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1821 | 0 | if (!trak) return GF_BAD_PARAM; |
1822 | | |
1823 | 0 | switch (trak->Media->handler->handlerType) { |
1824 | 0 | case GF_ISOM_MEDIA_VISUAL: |
1825 | 0 | case GF_ISOM_MEDIA_PICT: |
1826 | 0 | case GF_ISOM_MEDIA_AUXV: |
1827 | 0 | break; |
1828 | 0 | default: |
1829 | 0 | return GF_OK; |
1830 | 0 | } |
1831 | | |
1832 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
1833 | 0 | if (!stsd) { |
1834 | 0 | return movie->LastError = GF_ISOM_INVALID_FILE; |
1835 | 0 | } |
1836 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
1837 | 0 | return movie->LastError = GF_BAD_PARAM; |
1838 | 0 | } |
1839 | 0 | entry = (GF_MPEGVisualSampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
1840 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
1841 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
1842 | 0 | entry->bit_depth = bitDepth; |
1843 | 0 | return GF_OK; |
1844 | 0 | } |
1845 | | |
1846 | | GF_EXPORT |
1847 | | GF_Err gf_isom_set_pixel_aspect_ratio(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, s32 hSpacing, s32 vSpacing, Bool force_par) |
1848 | 0 | { |
1849 | 0 | GF_Err e; |
1850 | 0 | GF_TrackBox *trak; |
1851 | 0 | GF_SampleEntryBox *entry; |
1852 | 0 | GF_SampleDescriptionBox *stsd; |
1853 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1854 | 0 | if (e) return e; |
1855 | | |
1856 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1857 | 0 | if (!trak) return GF_BAD_PARAM; |
1858 | | |
1859 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
1860 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
1861 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
1862 | 0 | return movie->LastError = GF_BAD_PARAM; |
1863 | 0 | } |
1864 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
1865 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
1866 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
1867 | 0 | if (!movie->keep_utc) |
1868 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1869 | |
|
1870 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
1871 | | |
1872 | 0 | if (hSpacing<0) hSpacing = 1; |
1873 | 0 | if (vSpacing<0) vSpacing = 1; |
1874 | |
|
1875 | 0 | GF_PixelAspectRatioBox *pasp = (GF_PixelAspectRatioBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_PASP); |
1876 | 0 | if (!hSpacing || !vSpacing || ((hSpacing == vSpacing) && !force_par)) { |
1877 | 0 | if (pasp) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *)pasp); |
1878 | 0 | return GF_OK; |
1879 | 0 | } |
1880 | 0 | if (!pasp) { |
1881 | 0 | pasp = (GF_PixelAspectRatioBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_PASP); |
1882 | 0 | if (!pasp) return GF_OUT_OF_MEM; |
1883 | 0 | } |
1884 | 0 | pasp->hSpacing = (u32) hSpacing; |
1885 | 0 | pasp->vSpacing = (u32) vSpacing; |
1886 | 0 | return GF_OK; |
1887 | 0 | } |
1888 | | |
1889 | | GF_EXPORT |
1890 | | GF_Err gf_isom_set_visual_color_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 colour_type, u16 colour_primaries, u16 transfer_characteristics, u16 matrix_coefficients, Bool full_range_flag, u8 *icc_data, u32 icc_size) |
1891 | 0 | { |
1892 | 0 | GF_Err e; |
1893 | 0 | GF_TrackBox *trak; |
1894 | 0 | GF_SampleEntryBox *entry; |
1895 | 0 | GF_SampleDescriptionBox *stsd; |
1896 | 0 | GF_ColourInformationBox *clr=NULL; |
1897 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1898 | 0 | if (e) return e; |
1899 | | |
1900 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1901 | 0 | if (!trak) return GF_BAD_PARAM; |
1902 | | |
1903 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
1904 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
1905 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
1906 | 0 | return movie->LastError = GF_BAD_PARAM; |
1907 | 0 | } |
1908 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
1909 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
1910 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
1911 | 0 | if (!movie->keep_utc) |
1912 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1913 | |
|
1914 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_OK; |
1915 | | |
1916 | 0 | clr = (GF_ColourInformationBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_COLR); |
1917 | 0 | if (!colour_type) { |
1918 | 0 | if (clr) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *)clr); |
1919 | 0 | return GF_OK; |
1920 | 0 | } |
1921 | 0 | if (clr) { |
1922 | | //create another color box |
1923 | 0 | if (clr->opaque && !icc_data) clr = NULL; |
1924 | 0 | else if (!clr->opaque && icc_data) clr = NULL; |
1925 | 0 | } |
1926 | |
|
1927 | 0 | if (!clr) { |
1928 | 0 | clr = (GF_ColourInformationBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_COLR); |
1929 | 0 | if (!clr) return GF_OUT_OF_MEM; |
1930 | 0 | } |
1931 | 0 | clr->colour_type = colour_type; |
1932 | 0 | clr->colour_primaries = colour_primaries; |
1933 | 0 | clr->transfer_characteristics = transfer_characteristics; |
1934 | 0 | clr->matrix_coefficients = matrix_coefficients; |
1935 | 0 | clr->full_range_flag = full_range_flag; |
1936 | 0 | if (clr->opaque) gf_free(clr->opaque); |
1937 | 0 | clr->opaque = NULL; |
1938 | 0 | clr->opaque_size = 0; |
1939 | 0 | if ((colour_type==GF_ISOM_SUBTYPE_RICC) || (colour_type==GF_ISOM_SUBTYPE_PROF)) { |
1940 | 0 | clr->opaque_size = icc_data ? icc_size : 0; |
1941 | 0 | if (clr->opaque_size) { |
1942 | 0 | clr->opaque = gf_malloc(sizeof(char)*clr->opaque_size); |
1943 | 0 | if (!clr->opaque) return GF_OUT_OF_MEM; |
1944 | 0 | memcpy(clr->opaque, icc_data, sizeof(char)*clr->opaque_size); |
1945 | 0 | } |
1946 | 0 | } |
1947 | 0 | return GF_OK; |
1948 | 0 | } |
1949 | | |
1950 | | GF_EXPORT |
1951 | | GF_Err gf_isom_set_dolby_vision_profile(GF_ISOFile* movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_DOVIDecoderConfigurationRecord *dvcc) |
1952 | 0 | { |
1953 | 0 | GF_Err e; |
1954 | 0 | GF_TrackBox* trak; |
1955 | 0 | GF_Box *dv_cfge = NULL; |
1956 | 0 | GF_MPEGVisualSampleEntryBox* entry; |
1957 | 0 | Bool switch_type = GF_FALSE; |
1958 | 0 | Bool is_avc = GF_FALSE; |
1959 | 0 | GF_SampleDescriptionBox* stsd; |
1960 | 0 | GF_DOVIConfigurationBox* dovi = NULL; |
1961 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
1962 | 0 | if (e) return e; |
1963 | | |
1964 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
1965 | 0 | if (!trak) return GF_BAD_PARAM; |
1966 | | |
1967 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
1968 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
1969 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
1970 | 0 | return movie->LastError = GF_BAD_PARAM; |
1971 | 0 | } |
1972 | 0 | entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
1973 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
1974 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
1975 | 0 | if (!movie->keep_utc) |
1976 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
1977 | |
|
1978 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_OK; |
1979 | | |
1980 | 0 | if (dvcc) { |
1981 | 0 | switch_type = dvcc->force_dv; |
1982 | 0 | switch (dvcc->dv_profile) { |
1983 | 0 | case 1: |
1984 | 0 | case 3: |
1985 | 0 | case 5: |
1986 | 0 | switch_type = 1; |
1987 | 0 | break; |
1988 | 0 | } |
1989 | 0 | } |
1990 | | |
1991 | 0 | switch (entry->type) { |
1992 | 0 | case GF_ISOM_BOX_TYPE_HEV1: |
1993 | 0 | case GF_ISOM_BOX_TYPE_HEV2: |
1994 | 0 | case GF_ISOM_BOX_TYPE_DVHE: |
1995 | 0 | entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVHE : GF_ISOM_BOX_TYPE_HEV1; |
1996 | 0 | break; |
1997 | 0 | case GF_ISOM_BOX_TYPE_HVC1: |
1998 | 0 | case GF_ISOM_BOX_TYPE_HVC2: |
1999 | 0 | case GF_ISOM_BOX_TYPE_DVH1: |
2000 | 0 | entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVH1 : GF_ISOM_BOX_TYPE_HVC1; |
2001 | 0 | break; |
2002 | 0 | case GF_ISOM_BOX_TYPE_AVC1: |
2003 | 0 | case GF_ISOM_BOX_TYPE_DVA1: |
2004 | 0 | is_avc = GF_TRUE; |
2005 | 0 | entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVA1 : GF_ISOM_BOX_TYPE_AVC1; |
2006 | 0 | break; |
2007 | 0 | case GF_ISOM_BOX_TYPE_AVC3: |
2008 | 0 | case GF_ISOM_BOX_TYPE_DVAV: |
2009 | 0 | is_avc = GF_TRUE; |
2010 | 0 | entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVAV : GF_ISOM_BOX_TYPE_AVC3; |
2011 | 0 | break; |
2012 | 0 | case GF_ISOM_BOX_TYPE_AV01: |
2013 | 0 | case GF_ISOM_BOX_TYPE_DAV1: |
2014 | 0 | entry->type = switch_type ? GF_ISOM_BOX_TYPE_DAV1 : GF_ISOM_BOX_TYPE_AV01; |
2015 | 0 | break; |
2016 | 0 | default: |
2017 | 0 | return GF_NOT_SUPPORTED; |
2018 | 0 | } |
2019 | | |
2020 | 0 | u32 dve_type = is_avc ? GF_ISOM_BOX_TYPE_AVCE : GF_ISOM_BOX_TYPE_HVCE; |
2021 | 0 | dv_cfge = gf_isom_box_find_child(entry->child_boxes, dve_type); |
2022 | |
|
2023 | 0 | dovi = entry->dovi_config; |
2024 | 0 | if (!dvcc) { |
2025 | 0 | if (dovi) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)dovi); |
2026 | 0 | entry->dovi_config = NULL; |
2027 | |
|
2028 | 0 | if (dv_cfge) gf_isom_box_del_parent(&entry->child_boxes, dv_cfge); |
2029 | | //reverse entry type |
2030 | 0 | switch (entry->type) { |
2031 | 0 | case GF_ISOM_BOX_TYPE_DVHE: |
2032 | 0 | entry->type = GF_ISOM_BOX_TYPE_HEV1; |
2033 | 0 | break; |
2034 | 0 | case GF_ISOM_BOX_TYPE_DVH1: |
2035 | 0 | entry->type = GF_ISOM_BOX_TYPE_HVC1; |
2036 | 0 | break; |
2037 | 0 | case GF_ISOM_BOX_TYPE_DVA1: |
2038 | 0 | entry->type = GF_ISOM_BOX_TYPE_AVC1; |
2039 | 0 | break; |
2040 | 0 | case GF_ISOM_BOX_TYPE_DVAV: |
2041 | 0 | entry->type = GF_ISOM_BOX_TYPE_AVC3; |
2042 | 0 | break; |
2043 | 0 | case GF_ISOM_BOX_TYPE_DAV1: |
2044 | 0 | entry->type = GF_ISOM_BOX_TYPE_AV01; |
2045 | 0 | break; |
2046 | 0 | default: |
2047 | 0 | break; |
2048 | 0 | } |
2049 | 0 | return GF_OK; |
2050 | 0 | } |
2051 | 0 | if (!dovi) { |
2052 | 0 | dovi = (GF_DOVIConfigurationBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_DVCC); |
2053 | 0 | if (!dovi) return GF_OUT_OF_MEM; |
2054 | 0 | entry->dovi_config = dovi; |
2055 | 0 | } |
2056 | 0 | if (dvcc->dv_profile < 8) { |
2057 | 0 | dovi->type = GF_ISOM_BOX_TYPE_DVCC; |
2058 | 0 | } else { |
2059 | 0 | dovi->type = GF_ISOM_BOX_TYPE_DVVC; |
2060 | 0 | } |
2061 | 0 | dovi->DOVIConfig = *dvcc; |
2062 | |
|
2063 | 0 | if (dv_cfge) gf_isom_box_del_parent(&entry->child_boxes, dv_cfge); |
2064 | 0 | dv_cfge = NULL; |
2065 | | |
2066 | | //inject avcE / hvcE if enhancement layer and RPU present in single-track case |
2067 | | //not clear from the spec what is supposed to be in these, we just clone avcC/hvcC |
2068 | 0 | if (dvcc->bl_present_flag && dvcc->el_present_flag && dvcc->rpu_present_flag) { |
2069 | 0 | GF_Box *src = is_avc ? (GF_Box *)entry->avc_config : (GF_Box *)entry->hevc_config; |
2070 | 0 | if (!src) return GF_BAD_PARAM; |
2071 | 0 | e = gf_isom_clone_box(src, &dv_cfge); |
2072 | 0 | if (e) return e; |
2073 | 0 | dv_cfge->type = dve_type; |
2074 | 0 | gf_list_add(entry->child_boxes, dv_cfge); |
2075 | 0 | } |
2076 | 0 | return GF_OK; |
2077 | 0 | } |
2078 | | |
2079 | | GF_EXPORT |
2080 | | GF_Err gf_isom_set_high_dynamic_range_info(GF_ISOFile* movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_MasteringDisplayColourVolumeInfo* mdcv, GF_ContentLightLevelInfo* clli) |
2081 | 0 | { |
2082 | 0 | GF_Err e; |
2083 | 0 | GF_TrackBox* trak; |
2084 | 0 | GF_SampleEntryBox* entry; |
2085 | 0 | GF_SampleDescriptionBox* stsd; |
2086 | |
|
2087 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2088 | 0 | if (e) return e; |
2089 | | |
2090 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2091 | 0 | if (!trak) return GF_BAD_PARAM; |
2092 | | |
2093 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2094 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
2095 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2096 | 0 | return movie->LastError = GF_BAD_PARAM; |
2097 | 0 | } |
2098 | 0 | entry = (GF_SampleEntryBox*)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
2099 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2100 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2101 | 0 | if (!movie->keep_utc) |
2102 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2103 | |
|
2104 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
2105 | | |
2106 | 0 | GF_MasteringDisplayColourVolumeBox *mdcvb = (GF_MasteringDisplayColourVolumeBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_MDCV); |
2107 | 0 | if (mdcv) { |
2108 | 0 | if (!mdcvb) { |
2109 | 0 | mdcvb = (GF_MasteringDisplayColourVolumeBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_MDCV); |
2110 | 0 | if (!mdcvb) return GF_OUT_OF_MEM; |
2111 | 0 | } |
2112 | 0 | mdcvb->mdcv = *mdcv; |
2113 | 0 | } else { |
2114 | 0 | if (mdcvb) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *) mdcvb); |
2115 | 0 | } |
2116 | | |
2117 | | /*clli*/ |
2118 | 0 | GF_ContentLightLevelBox *cllib = (GF_ContentLightLevelBox *)gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_CLLI); |
2119 | 0 | if (clli) { |
2120 | 0 | if (!cllib) { |
2121 | 0 | cllib = (GF_ContentLightLevelBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_CLLI); |
2122 | 0 | if (!cllib) return GF_OUT_OF_MEM; |
2123 | 0 | } |
2124 | 0 | cllib->clli = *clli; |
2125 | 0 | } else { |
2126 | 0 | if (cllib) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box *) cllib); |
2127 | 0 | } |
2128 | 0 | return GF_OK; |
2129 | 0 | } |
2130 | | |
2131 | | GF_EXPORT |
2132 | | GF_Err gf_isom_set_clean_aperture(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 cleanApertureWidthN, u32 cleanApertureWidthD, u32 cleanApertureHeightN, u32 cleanApertureHeightD, s32 horizOffN, u32 horizOffD, s32 vertOffN, u32 vertOffD) |
2133 | 0 | { |
2134 | 0 | GF_Err e; |
2135 | 0 | GF_TrackBox *trak; |
2136 | 0 | GF_SampleEntryBox *entry; |
2137 | 0 | GF_SampleDescriptionBox *stsd; |
2138 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2139 | 0 | if (e) return e; |
2140 | | |
2141 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2142 | 0 | if (!trak) return GF_BAD_PARAM; |
2143 | | |
2144 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2145 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
2146 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2147 | 0 | return movie->LastError = GF_BAD_PARAM; |
2148 | 0 | } |
2149 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
2150 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2151 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2152 | 0 | if (!movie->keep_utc) |
2153 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2154 | |
|
2155 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
2156 | | |
2157 | 0 | GF_CleanApertureBox *clap = (GF_CleanApertureBox *)gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_CLAP); |
2158 | 0 | if (!cleanApertureHeightD || !cleanApertureWidthD || !horizOffD || !vertOffD) { |
2159 | 0 | if (clap) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)clap); |
2160 | 0 | return GF_OK; |
2161 | 0 | } |
2162 | 0 | if (!clap) { |
2163 | 0 | clap = (GF_CleanApertureBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_CLAP); |
2164 | 0 | if (!clap) return GF_OUT_OF_MEM; |
2165 | 0 | } |
2166 | | |
2167 | 0 | clap->cleanApertureWidthN = cleanApertureWidthN; |
2168 | 0 | clap->cleanApertureWidthD = cleanApertureWidthD; |
2169 | 0 | clap->cleanApertureHeightN = cleanApertureHeightN; |
2170 | 0 | clap->cleanApertureHeightD = cleanApertureHeightD; |
2171 | 0 | clap->horizOffN = horizOffN; |
2172 | 0 | clap->horizOffD = horizOffD; |
2173 | 0 | clap->vertOffN = vertOffN; |
2174 | 0 | clap->vertOffD = vertOffD; |
2175 | 0 | return GF_OK; |
2176 | 0 | } |
2177 | | |
2178 | | #include <gpac/maths.h> |
2179 | | GF_Err gf_isom_update_aperture_info(GF_ISOFile *movie, u32 trackNumber, Bool remove) |
2180 | 0 | { |
2181 | 0 | GF_Err e; |
2182 | 0 | GF_Box *box, *enof, *prof, *clef; |
2183 | 0 | GF_TrackBox *trak; |
2184 | 0 | GF_VisualSampleEntryBox *ventry; |
2185 | 0 | GF_PixelAspectRatioBox *pasp; |
2186 | 0 | GF_CleanApertureBox *clap; |
2187 | 0 | u32 j, hspacing, vspacing, clap_width_num, clap_width_den, clap_height_num, clap_height_den, high, low; |
2188 | 0 | Double width, height; |
2189 | |
|
2190 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2191 | 0 | if (e) return e; |
2192 | | |
2193 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2194 | 0 | if (!trak) return GF_BAD_PARAM; |
2195 | | |
2196 | 0 | if (remove) { |
2197 | 0 | if (trak->Aperture) { |
2198 | 0 | gf_isom_box_del(trak->Aperture); |
2199 | 0 | trak->Aperture = NULL; |
2200 | 0 | } |
2201 | 0 | return GF_OK; |
2202 | 0 | } |
2203 | 0 | enof = prof = clef = NULL; |
2204 | 0 | if (!trak->Aperture) { |
2205 | 0 | trak->Aperture = gf_isom_box_new_parent(&trak->child_boxes, GF_QT_BOX_TYPE_TAPT); |
2206 | 0 | if (!trak->Aperture) return GF_OUT_OF_MEM; |
2207 | 0 | } |
2208 | 0 | if (!trak->Aperture->child_boxes) { |
2209 | 0 | trak->Aperture->child_boxes = gf_list_new(); |
2210 | 0 | if (!trak->Aperture->child_boxes) |
2211 | 0 | return GF_OUT_OF_MEM; |
2212 | 0 | } |
2213 | | |
2214 | 0 | j=0; |
2215 | 0 | while ( (box = gf_list_enum(trak->Aperture->child_boxes, &j))) { |
2216 | 0 | switch (box->type) { |
2217 | 0 | case GF_QT_BOX_TYPE_CLEF: clef = box; break; |
2218 | 0 | case GF_QT_BOX_TYPE_PROF: prof = box; break; |
2219 | 0 | case GF_QT_BOX_TYPE_ENOF: enof = box; break; |
2220 | 0 | } |
2221 | 0 | } |
2222 | 0 | if (!clef) { |
2223 | 0 | clef = gf_isom_box_new(GF_QT_BOX_TYPE_CLEF); |
2224 | 0 | if (!clef) return GF_OUT_OF_MEM; |
2225 | 0 | gf_list_add(trak->Aperture->child_boxes, clef); |
2226 | 0 | } |
2227 | 0 | if (!enof) { |
2228 | 0 | enof = gf_isom_box_new(GF_QT_BOX_TYPE_ENOF); |
2229 | 0 | if (!enof) return GF_OUT_OF_MEM; |
2230 | 0 | gf_list_add(trak->Aperture->child_boxes, enof); |
2231 | 0 | } |
2232 | 0 | if (!prof) { |
2233 | 0 | prof = gf_isom_box_new(GF_QT_BOX_TYPE_PROF); |
2234 | 0 | if (!prof) return GF_OUT_OF_MEM; |
2235 | 0 | gf_list_add(trak->Aperture->child_boxes, prof); |
2236 | 0 | } |
2237 | | |
2238 | 0 | ventry = (GF_VisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, 0); |
2239 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2240 | 0 | if (ventry == NULL) return GF_BAD_PARAM; |
2241 | 0 | if (!movie->keep_utc) |
2242 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2243 | |
|
2244 | 0 | if (ventry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
2245 | | |
2246 | 0 | pasp = (GF_PixelAspectRatioBox *) gf_isom_box_find_child(ventry->child_boxes, GF_ISOM_BOX_TYPE_PASP); |
2247 | 0 | hspacing = vspacing = 0; |
2248 | 0 | if (pasp) { |
2249 | 0 | hspacing = pasp->hSpacing; |
2250 | 0 | vspacing = pasp->vSpacing; |
2251 | 0 | } |
2252 | 0 | clap_width_num = ventry->Width; |
2253 | 0 | clap_width_den = 1; |
2254 | 0 | clap_height_num = ventry->Height; |
2255 | 0 | clap_height_den = 1; |
2256 | 0 | clap = (GF_CleanApertureBox *) gf_isom_box_find_child(ventry->child_boxes, GF_ISOM_BOX_TYPE_CLAP); |
2257 | 0 | if (clap) { |
2258 | 0 | clap_width_num = clap->cleanApertureWidthN; |
2259 | 0 | clap_width_den = clap->cleanApertureWidthD; |
2260 | 0 | clap_height_num = clap->cleanApertureHeightN; |
2261 | 0 | clap_height_den = clap->cleanApertureHeightD; |
2262 | 0 | } |
2263 | | //enof: encoded pixels in 16.16 |
2264 | 0 | ((GF_ApertureBox *)enof)->width = (ventry->Width)<<16; |
2265 | 0 | ((GF_ApertureBox *)enof)->height = (ventry->Height)<<16; |
2266 | | |
2267 | | //prof: encoded pixels + pasp in 16.16 |
2268 | 0 | width = (Float) (ventry->Width * hspacing); |
2269 | 0 | width /= vspacing; |
2270 | 0 | high = (u32) floor((Float)width); |
2271 | 0 | low = (u32) ( 0xFFFF * (width - (Double)high) ); |
2272 | 0 | ((GF_ApertureBox *)prof)->width = (high)<<16 | low; |
2273 | 0 | ((GF_ApertureBox *)prof)->height = (ventry->Height)<<16; |
2274 | | |
2275 | | //clef: encoded pixels + pasp + clap in 16.16 |
2276 | 0 | width = (Double) (clap_width_num * hspacing); |
2277 | 0 | width /= clap_width_den * vspacing; |
2278 | 0 | height = (Float) clap_height_num; |
2279 | 0 | height /= clap_height_den; |
2280 | |
|
2281 | 0 | high = (u32) floor((Float)width); |
2282 | 0 | low = (u32) (0xFFFF * (width - (Double)high)); |
2283 | 0 | ((GF_ApertureBox *)clef)->width = (high)<<16 | low; |
2284 | 0 | high = (u32) floor((Float)height); |
2285 | 0 | low = (u32) (0xFFFF * (height - (Double)high)); |
2286 | 0 | ((GF_ApertureBox *)clef)->height = (high)<<16 | low; |
2287 | | |
2288 | |
|
2289 | 0 | return GF_OK; |
2290 | 0 | } |
2291 | | |
2292 | | GF_EXPORT |
2293 | | GF_Err gf_isom_set_image_sequence_coding_constraints(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, Bool remove, Bool all_ref_pics_intra, Bool intra_pred_used, u32 max_ref_per_pic) |
2294 | 0 | { |
2295 | 0 | GF_Err e; |
2296 | 0 | GF_TrackBox *trak; |
2297 | 0 | GF_SampleEntryBox *entry; |
2298 | 0 | GF_SampleDescriptionBox *stsd; |
2299 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2300 | 0 | if (e) return e; |
2301 | | |
2302 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2303 | 0 | if (!trak) return GF_BAD_PARAM; |
2304 | | |
2305 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2306 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
2307 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2308 | 0 | return movie->LastError = GF_BAD_PARAM; |
2309 | 0 | } |
2310 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
2311 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2312 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2313 | 0 | if (!movie->keep_utc) |
2314 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2315 | |
|
2316 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
2317 | | |
2318 | 0 | GF_CodingConstraintsBox*ccst = (GF_CodingConstraintsBox*) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_CCST); |
2319 | 0 | if (remove) { |
2320 | 0 | if (ccst) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)ccst); |
2321 | 0 | return GF_OK; |
2322 | 0 | } |
2323 | 0 | if (!ccst) { |
2324 | 0 | ccst = (GF_CodingConstraintsBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_CCST); |
2325 | 0 | if (!ccst) return GF_OUT_OF_MEM; |
2326 | 0 | } |
2327 | 0 | ccst->all_ref_pics_intra = all_ref_pics_intra; |
2328 | 0 | ccst->intra_pred_used = intra_pred_used; |
2329 | 0 | ccst->max_ref_per_pic = max_ref_per_pic; |
2330 | 0 | return GF_OK; |
2331 | 0 | } |
2332 | | |
2333 | | GF_EXPORT |
2334 | | GF_Err gf_isom_set_image_sequence_alpha(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, Bool remove) |
2335 | 0 | { |
2336 | 0 | GF_Err e; |
2337 | 0 | GF_TrackBox *trak; |
2338 | 0 | GF_SampleEntryBox *entry; |
2339 | 0 | GF_SampleDescriptionBox *stsd; |
2340 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2341 | 0 | if (e) return e; |
2342 | | |
2343 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2344 | 0 | if (!trak) return GF_BAD_PARAM; |
2345 | | |
2346 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2347 | 0 | if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; |
2348 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2349 | 0 | return movie->LastError = GF_BAD_PARAM; |
2350 | 0 | } |
2351 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
2352 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2353 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2354 | 0 | if (!movie->keep_utc) |
2355 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2356 | |
|
2357 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
2358 | | |
2359 | 0 | GF_AuxiliaryTypeInfoBox *auxi = (GF_AuxiliaryTypeInfoBox *)gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_AUXI); |
2360 | 0 | if (remove) { |
2361 | 0 | if (auxi) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)auxi); |
2362 | 0 | return GF_OK; |
2363 | 0 | } |
2364 | 0 | if (!auxi) { |
2365 | 0 | auxi = (GF_AuxiliaryTypeInfoBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_AUXI); |
2366 | 0 | if (!auxi) return GF_OUT_OF_MEM; |
2367 | 0 | } |
2368 | 0 | auxi->aux_track_type = gf_strdup("urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"); |
2369 | 0 | return GF_OK; |
2370 | 0 | } |
2371 | | |
2372 | | GF_EXPORT |
2373 | | GF_Err gf_isom_set_audio_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 sampleRate, u32 nbChannels, u8 bitsPerSample, GF_AudioSampleEntryImportMode asemode) |
2374 | 0 | { |
2375 | 0 | GF_Err e; |
2376 | 0 | u32 i, old_qtff_mode=GF_ISOM_AUDIO_QTFF_NONE; |
2377 | 0 | GF_TrackBox *trak; |
2378 | 0 | GF_SampleEntryBox *entry; |
2379 | 0 | GF_AudioSampleEntryBox*aud_entry; |
2380 | 0 | GF_SampleDescriptionBox *stsd; |
2381 | 0 | GF_Box *wave_box = NULL; |
2382 | 0 | GF_Box *gf_isom_audio_sample_get_audio_codec_cfg_box(GF_AudioSampleEntryBox *ptr); |
2383 | 0 | GF_Box *codec_ext = NULL; |
2384 | | #if 0 |
2385 | | GF_ChannelLayoutInfoBox *chan=NULL; |
2386 | | #endif |
2387 | 0 | GF_OriginalFormatBox *frma=NULL; |
2388 | 0 | GF_ChromaInfoBox *enda=NULL; |
2389 | 0 | GF_ESDBox *esds=NULL; |
2390 | 0 | GF_Box *terminator=NULL; |
2391 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2392 | 0 | if (e) return e; |
2393 | | |
2394 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2395 | 0 | if (!trak) return GF_BAD_PARAM; |
2396 | | |
2397 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2398 | 0 | if (!stsd) { |
2399 | 0 | return movie->LastError = GF_ISOM_INVALID_FILE; |
2400 | 0 | } |
2401 | 0 | if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2402 | 0 | return movie->LastError = GF_BAD_PARAM; |
2403 | 0 | } |
2404 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
2405 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2406 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2407 | 0 | if (!movie->keep_utc) |
2408 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2409 | |
|
2410 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_AUDIO) return GF_BAD_PARAM; |
2411 | 0 | aud_entry = (GF_AudioSampleEntryBox*) entry; |
2412 | |
|
2413 | 0 | if (entry->type==GF_ISOM_BOX_TYPE_MLPA) { |
2414 | 0 | aud_entry->samplerate_hi = sampleRate>>16; |
2415 | 0 | aud_entry->samplerate_lo = sampleRate & 0x0000FFFF; |
2416 | 0 | } else { |
2417 | 0 | aud_entry->samplerate_hi = sampleRate; |
2418 | 0 | aud_entry->samplerate_lo = 0; |
2419 | 0 | } |
2420 | 0 | aud_entry->bitspersample = bitsPerSample; |
2421 | |
|
2422 | 0 | switch (asemode) { |
2423 | 0 | case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v0_2: |
2424 | 0 | stsd->version = 0; |
2425 | 0 | aud_entry->version = 0; |
2426 | 0 | aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE; |
2427 | 0 | aud_entry->channel_count = 2; |
2428 | 0 | break; |
2429 | 0 | case GF_IMPORT_AUDIO_SAMPLE_ENTRY_NOT_SET: |
2430 | 0 | case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v0_BS: |
2431 | 0 | stsd->version = 0; |
2432 | 0 | aud_entry->version = 0; |
2433 | 0 | aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE; |
2434 | 0 | aud_entry->channel_count = nbChannels; |
2435 | 0 | break; |
2436 | 0 | case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_MPEG: |
2437 | 0 | stsd->version = 1; |
2438 | 0 | aud_entry->version = 1; |
2439 | 0 | aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_NONE; |
2440 | 0 | aud_entry->channel_count = nbChannels; |
2441 | 0 | break; |
2442 | 0 | case GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF: |
2443 | 0 | stsd->version = 0; |
2444 | | //don't change if already v2 |
2445 | 0 | if ((aud_entry->version==2) && aud_entry->qtff_mode) { |
2446 | 0 | break; |
2447 | 0 | } |
2448 | 0 | aud_entry->version = 1; |
2449 | 0 | aud_entry->channel_count = nbChannels; |
2450 | 0 | old_qtff_mode = aud_entry->qtff_mode; |
2451 | 0 | if (aud_entry->qtff_mode != GF_ISOM_AUDIO_QTFF_ON_EXT_VALID) |
2452 | 0 | aud_entry->qtff_mode = GF_ISOM_AUDIO_QTFF_ON_NOEXT; |
2453 | 0 | break; |
2454 | 0 | } |
2455 | | |
2456 | 0 | aud_entry->compression_id = 0; |
2457 | 0 | GF_SamplingRateBox *srat = NULL; |
2458 | | //check for wave+children and chan for QTFF or remove them for isobmff |
2459 | 0 | for (i=0; i<gf_list_count(aud_entry->child_boxes); i++) { |
2460 | 0 | GF_Box *b = gf_list_get(aud_entry->child_boxes, i); |
2461 | 0 | if (b->type == GF_ISOM_BOX_TYPE_SRAT) srat = (GF_SamplingRateBox *)b; |
2462 | |
|
2463 | 0 | if ((b->type != GF_QT_BOX_TYPE_WAVE) && (b->type != GF_QT_BOX_TYPE_CHAN) ) continue; |
2464 | 0 | if (asemode==GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF) { |
2465 | 0 | if (b->type == GF_QT_BOX_TYPE_WAVE) wave_box = b; |
2466 | | #if 0 |
2467 | | else chan = (GF_ChannelLayoutInfoBox *)b; |
2468 | | #endif |
2469 | |
|
2470 | 0 | } else { |
2471 | 0 | gf_isom_box_del_parent(&aud_entry->child_boxes, b); |
2472 | 0 | i--; |
2473 | 0 | } |
2474 | 0 | } |
2475 | |
|
2476 | 0 | if (sampleRate>0xFFFF) { |
2477 | 0 | if (!srat) { |
2478 | 0 | srat = (GF_SamplingRateBox *) gf_isom_box_new_parent(&aud_entry->child_boxes, GF_ISOM_BOX_TYPE_SRAT); |
2479 | 0 | } |
2480 | 0 | if (srat) srat->sampling_rate = sampleRate; |
2481 | 0 | } |
2482 | | |
2483 | | //TODO: insert channelLayout for ISOBMFF |
2484 | 0 | if (asemode!=GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF) return GF_OK; |
2485 | | |
2486 | 0 | if (aud_entry->type==GF_ISOM_BOX_TYPE_MP4A) |
2487 | 0 | aud_entry->compression_id = -2; |
2488 | |
|
2489 | 0 | if (!aud_entry->child_boxes) aud_entry->child_boxes = gf_list_new(); |
2490 | |
|
2491 | | #if 0 |
2492 | | if (!chan) { |
2493 | | chan = (GF_ChannelLayoutInfoBox *) gf_isom_box_new_parent(&aud_entry->child_boxes, GF_QT_BOX_TYPE_CHAN); |
2494 | | } |
2495 | | //TODO, proper channel mapping |
2496 | | chan->layout_tag = (nbChannels==2) ? 6750210 : 6553601; |
2497 | | #endif |
2498 | |
|
2499 | 0 | codec_ext = gf_isom_audio_sample_get_audio_codec_cfg_box((GF_AudioSampleEntryBox *)aud_entry); |
2500 | 0 | if (!codec_ext) return GF_OK; |
2501 | | |
2502 | 0 | if (!wave_box) { |
2503 | 0 | wave_box = gf_isom_box_new_parent(&aud_entry->child_boxes, GF_QT_BOX_TYPE_WAVE); |
2504 | 0 | } |
2505 | |
|
2506 | 0 | for (i=0; i<gf_list_count(wave_box->child_boxes); i++) { |
2507 | 0 | GF_Box *b = gf_list_get(wave_box->child_boxes, i); |
2508 | 0 | switch (b->type) { |
2509 | 0 | case GF_QT_BOX_TYPE_ENDA: |
2510 | 0 | enda = (GF_ChromaInfoBox *)b; |
2511 | 0 | break; |
2512 | 0 | case GF_QT_BOX_TYPE_FRMA: |
2513 | 0 | frma = (GF_OriginalFormatBox *)b; |
2514 | 0 | break; |
2515 | 0 | case GF_ISOM_BOX_TYPE_ESDS: |
2516 | 0 | esds = (GF_ESDBox *)b; |
2517 | 0 | break; |
2518 | 0 | case GF_ISOM_BOX_TYPE_UNKNOWN: |
2519 | 0 | if ( ((GF_UnknownBox*)b)->original_4cc == 0) |
2520 | 0 | terminator = b; |
2521 | 0 | break; |
2522 | 0 | case 0: |
2523 | 0 | terminator = b; |
2524 | 0 | break; |
2525 | 0 | } |
2526 | 0 | } |
2527 | 0 | if (!wave_box->child_boxes) wave_box->child_boxes = gf_list_new(); |
2528 | | |
2529 | | //do not use new_parent, we do this manually to ensure the order |
2530 | 0 | aud_entry->qtff_mode = old_qtff_mode ? old_qtff_mode : GF_ISOM_AUDIO_QTFF_ON_NOEXT; |
2531 | 0 | if (!frma) { |
2532 | 0 | frma = (GF_OriginalFormatBox *)gf_isom_box_new(GF_QT_BOX_TYPE_FRMA); |
2533 | 0 | } else { |
2534 | 0 | gf_list_del_item(wave_box->child_boxes, frma); |
2535 | 0 | } |
2536 | 0 | gf_list_add(wave_box->child_boxes, frma); |
2537 | |
|
2538 | 0 | if (esds) gf_list_del_item(wave_box->child_boxes, esds); |
2539 | 0 | if (!esds && (aud_entry->type==GF_ISOM_BOX_TYPE_MP4A) && ((GF_MPEGAudioSampleEntryBox*)aud_entry)->esd) { |
2540 | 0 | gf_list_del_item(entry->child_boxes, (GF_Box *) ((GF_MPEGAudioSampleEntryBox*)aud_entry)->esd); |
2541 | 0 | gf_list_add(wave_box->child_boxes, (GF_Box *) ((GF_MPEGAudioSampleEntryBox*)aud_entry)->esd); |
2542 | 0 | } |
2543 | |
|
2544 | 0 | if (!enda) { |
2545 | 0 | enda = (GF_ChromaInfoBox *)gf_isom_box_new(GF_QT_BOX_TYPE_ENDA); |
2546 | 0 | } else { |
2547 | 0 | gf_list_del_item(wave_box->child_boxes, enda); |
2548 | 0 | } |
2549 | 0 | enda->chroma=1; |
2550 | 0 | gf_list_add(wave_box->child_boxes, enda); |
2551 | |
|
2552 | 0 | if (!terminator) { |
2553 | 0 | terminator = gf_isom_box_new(0); |
2554 | 0 | } else { |
2555 | 0 | gf_list_del_item(wave_box->child_boxes, terminator); |
2556 | 0 | } |
2557 | 0 | gf_list_add(wave_box->child_boxes, terminator); |
2558 | |
|
2559 | 0 | if (aud_entry->type==GF_ISOM_BOX_TYPE_GNRA) { |
2560 | 0 | frma->data_format = ((GF_GenericAudioSampleEntryBox*) aud_entry)->EntryType; |
2561 | 0 | } else { |
2562 | 0 | frma->data_format = aud_entry->type; |
2563 | 0 | } |
2564 | |
|
2565 | 0 | return GF_OK; |
2566 | 0 | } |
2567 | | |
2568 | | GF_EXPORT |
2569 | | GF_Err gf_isom_set_audio_layout(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, GF_AudioChannelLayout *layout) |
2570 | 0 | { |
2571 | 0 | GF_Err e; |
2572 | 0 | GF_TrackBox *trak; |
2573 | 0 | GF_SampleEntryBox *entry; |
2574 | 0 | GF_AudioSampleEntryBox*aud_entry; |
2575 | 0 | GF_SampleDescriptionBox *stsd; |
2576 | 0 | GF_ChannelLayoutBox *chnl; |
2577 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2578 | 0 | if (e) return e; |
2579 | | |
2580 | 0 | if (!layout) return GF_BAD_PARAM; |
2581 | 0 | if ((layout->stream_structure & 1) && (layout->definedLayout==0) && (layout->channels_count>=64)) |
2582 | 0 | return GF_BAD_PARAM; |
2583 | | |
2584 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2585 | 0 | if (!trak) return GF_BAD_PARAM; |
2586 | | |
2587 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
2588 | 0 | if (!stsd) { |
2589 | 0 | return movie->LastError = GF_ISOM_INVALID_FILE; |
2590 | 0 | } |
2591 | 0 | if (!sampleDescriptionIndex || sampleDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
2592 | 0 | return movie->LastError = GF_BAD_PARAM; |
2593 | 0 | } |
2594 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, sampleDescriptionIndex - 1); |
2595 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
2596 | 0 | if (entry == NULL) return GF_BAD_PARAM; |
2597 | 0 | if (!movie->keep_utc) |
2598 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
2599 | |
|
2600 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_AUDIO) return GF_BAD_PARAM; |
2601 | 0 | aud_entry = (GF_AudioSampleEntryBox*) entry; |
2602 | 0 | if (aud_entry->qtff_mode) { |
2603 | 0 | u32 sr = aud_entry->samplerate_hi; |
2604 | 0 | if (aud_entry->type==GF_ISOM_BOX_TYPE_MLPA) { |
2605 | 0 | sr <<= 16; |
2606 | 0 | sr |= aud_entry->samplerate_lo; |
2607 | 0 | } |
2608 | 0 | e = gf_isom_set_audio_info(movie, trackNumber, sampleDescriptionIndex, sr, aud_entry->channel_count, (u8) aud_entry->bitspersample, GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_MPEG); |
2609 | 0 | if (e) return e; |
2610 | 0 | } |
2611 | 0 | chnl = (GF_ChannelLayoutBox *) gf_isom_box_find_child(aud_entry->child_boxes, GF_ISOM_BOX_TYPE_CHNL); |
2612 | 0 | if (!chnl) { |
2613 | 0 | chnl = (GF_ChannelLayoutBox *)gf_isom_box_new_parent(&aud_entry->child_boxes, GF_ISOM_BOX_TYPE_CHNL); |
2614 | 0 | if (!chnl) return GF_OUT_OF_MEM; |
2615 | 0 | } |
2616 | 0 | aud_entry->channel_count = layout->channels_count; |
2617 | 0 | memcpy(&chnl->layout, layout, sizeof(GF_AudioChannelLayout)); |
2618 | 0 | return GF_OK; |
2619 | 0 | } |
2620 | | |
2621 | | //set the storage mode of a file (FLAT, STREAMABLE, INTERLEAVED) |
2622 | | GF_EXPORT |
2623 | | GF_Err gf_isom_set_storage_mode(GF_ISOFile *movie, GF_ISOStorageMode storageMode) |
2624 | 0 | { |
2625 | 0 | GF_Err e; |
2626 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2627 | 0 | if (e) return e; |
2628 | | |
2629 | 0 | switch (storageMode) { |
2630 | 0 | case GF_ISOM_STORE_FLAT: |
2631 | 0 | case GF_ISOM_STORE_STREAMABLE: |
2632 | 0 | case GF_ISOM_STORE_INTERLEAVED: |
2633 | 0 | case GF_ISOM_STORE_DRIFT_INTERLEAVED: |
2634 | 0 | case GF_ISOM_STORE_TIGHT: |
2635 | 0 | case GF_ISOM_STORE_FASTSTART: |
2636 | 0 | movie->storageMode = storageMode; |
2637 | | //specifying a storage mode disables inplace rewrite |
2638 | 0 | gf_isom_disable_inplace_rewrite(movie); |
2639 | 0 | return GF_OK; |
2640 | 0 | default: |
2641 | 0 | return GF_BAD_PARAM; |
2642 | 0 | } |
2643 | 0 | } |
2644 | | |
2645 | | |
2646 | | GF_EXPORT |
2647 | | GF_Err gf_isom_enable_compression(GF_ISOFile *file, GF_ISOCompressMode compress_mode, u32 compress_flags) |
2648 | 0 | { |
2649 | 0 | if (!file) return GF_BAD_PARAM; |
2650 | 0 | file->compress_mode = compress_mode; |
2651 | 0 | file->compress_flags = compress_flags; |
2652 | 0 | return GF_OK; |
2653 | 0 | } |
2654 | | |
2655 | | GF_EXPORT |
2656 | | GF_Err gf_isom_force_64bit_chunk_offset(GF_ISOFile *file, Bool set_on) |
2657 | 0 | { |
2658 | 0 | if (!file) return GF_BAD_PARAM; |
2659 | 0 | file->force_co64 = set_on; |
2660 | 0 | return GF_OK; |
2661 | 0 | } |
2662 | | |
2663 | | |
2664 | | //update or insert a new edit segment in the track time line. Edits are used to modify |
2665 | | //the media normal timing. EditTime and EditDuration are expressed in Movie TimeScale |
2666 | | //If a segment with EditTime already exists, IT IS ERASED |
2667 | | static GF_Err gf_isom_set_edit_internal(GF_ISOFile *movie, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, u32 media_rate, GF_ISOEditType EditMode) |
2668 | 0 | { |
2669 | 0 | GF_TrackBox *trak; |
2670 | 0 | GF_EditBox *edts; |
2671 | 0 | GF_EditListBox *elst; |
2672 | 0 | GF_EdtsEntry *ent, *newEnt; |
2673 | 0 | u32 i; |
2674 | 0 | GF_Err e; |
2675 | 0 | u64 startTime; |
2676 | |
|
2677 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2678 | 0 | if (e) return e; |
2679 | | |
2680 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2681 | 0 | if (!trak) return GF_BAD_PARAM; |
2682 | | |
2683 | 0 | edts = trak->editBox; |
2684 | 0 | if (! edts) { |
2685 | 0 | edts = (GF_EditBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_EDTS); |
2686 | 0 | if (!edts) return GF_OUT_OF_MEM; |
2687 | 0 | trak_on_child_box((GF_Box*)trak, (GF_Box *)edts, GF_FALSE); |
2688 | 0 | } |
2689 | 0 | elst = edts->editList; |
2690 | 0 | if (!elst) { |
2691 | 0 | elst = (GF_EditListBox *) gf_isom_box_new_parent(&edts->child_boxes, GF_ISOM_BOX_TYPE_ELST); |
2692 | 0 | if (!elst) return GF_OUT_OF_MEM; |
2693 | 0 | edts_on_child_box((GF_Box*)edts, (GF_Box *)elst, GF_FALSE); |
2694 | 0 | } |
2695 | | |
2696 | 0 | if (trak->extl) { |
2697 | 0 | if (!trak->extl->media_timescale) trak->extl->media_timescale = movie->moov->mvhd->timeScale; |
2698 | 0 | trak->extl->flags |= GF_ISOM_EXTK_EDTS_SKIP; |
2699 | 0 | } |
2700 | 0 | startTime = 0; |
2701 | 0 | ent = NULL; |
2702 | | //get the prev entry to this startTime if any |
2703 | 0 | i=0; |
2704 | 0 | while ((ent = (GF_EdtsEntry *)gf_list_enum(elst->entryList, &i))) { |
2705 | 0 | if ( (startTime <= EditTime) && (startTime + ent->segmentDuration > EditTime) ) |
2706 | 0 | goto found; |
2707 | 0 | startTime += ent->segmentDuration; |
2708 | 0 | } |
2709 | | |
2710 | | //not found, add a new entry, insert empty one if gap |
2711 | 0 | if (!ent) { |
2712 | 0 | Bool empty_inserted = GF_FALSE; |
2713 | 0 | if (startTime != EditTime) { |
2714 | 0 | newEnt = CreateEditEntry(EditTime - startTime, 0, 0, GF_ISOM_EDIT_EMPTY); |
2715 | 0 | if (!newEnt) return GF_OUT_OF_MEM; |
2716 | 0 | empty_inserted = GF_TRUE; |
2717 | 0 | gf_list_add(elst->entryList, newEnt); |
2718 | 0 | } |
2719 | 0 | newEnt = CreateEditEntry(EditDuration, MediaTime, media_rate, EditMode); |
2720 | 0 | if (!newEnt) return GF_OUT_OF_MEM; |
2721 | 0 | gf_list_add(elst->entryList, newEnt); |
2722 | 0 | e = SetTrackDuration(trak); |
2723 | 0 | if (e) return e; |
2724 | 0 | return empty_inserted ? GF_EOS : GF_OK; |
2725 | 0 | } |
2726 | | |
2727 | 0 | startTime -= ent->segmentDuration; |
2728 | |
|
2729 | 0 | found: |
2730 | | |
2731 | | //if same time, we erase the current one... |
2732 | 0 | if (startTime == EditTime) { |
2733 | 0 | ent->segmentDuration = EditDuration; |
2734 | 0 | switch (EditMode) { |
2735 | 0 | case GF_ISOM_EDIT_EMPTY: |
2736 | 0 | ent->mediaRate = 0x10000; |
2737 | 0 | ent->mediaTime = -1; |
2738 | 0 | break; |
2739 | 0 | case GF_ISOM_EDIT_DWELL: |
2740 | 0 | ent->mediaRate = 0; |
2741 | 0 | ent->mediaTime = MediaTime; |
2742 | 0 | break; |
2743 | 0 | default: |
2744 | 0 | ent->mediaRate = media_rate; |
2745 | 0 | ent->mediaTime = MediaTime; |
2746 | 0 | break; |
2747 | 0 | } |
2748 | 0 | return SetTrackDuration(trak); |
2749 | 0 | } |
2750 | | |
2751 | | //adjust so that the prev ent leads to EntryTime |
2752 | | //Note: we don't change the next one as it is unknown to us in |
2753 | | //a lot of case (the author's changes) |
2754 | 0 | ent->segmentDuration = EditTime - startTime; |
2755 | 0 | newEnt = CreateEditEntry(EditDuration, MediaTime, media_rate, EditMode); |
2756 | 0 | if (!newEnt) return GF_OUT_OF_MEM; |
2757 | | //is it the last entry ??? |
2758 | 0 | if (i >= gf_list_count(elst->entryList) - 1) { |
2759 | | //add the new entry at the end |
2760 | 0 | gf_list_add(elst->entryList, newEnt); |
2761 | 0 | return SetTrackDuration(trak); |
2762 | 0 | } else { |
2763 | | //insert after the current entry (which is i) |
2764 | 0 | gf_list_insert(elst->entryList, newEnt, i+1); |
2765 | 0 | return SetTrackDuration(trak); |
2766 | 0 | } |
2767 | 0 | } |
2768 | | |
2769 | | GF_EXPORT |
2770 | | GF_Err gf_isom_set_edit(GF_ISOFile *movie, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, GF_ISOEditType EditMode) |
2771 | 0 | { |
2772 | 0 | return gf_isom_set_edit_internal(movie, trackNumber, EditTime, EditDuration, MediaTime, 0x10000, EditMode); |
2773 | 0 | } |
2774 | | |
2775 | | GF_EXPORT |
2776 | | GF_Err gf_isom_set_edit_with_rate(GF_ISOFile *movie, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, u32 media_rate) |
2777 | 0 | { |
2778 | 0 | return gf_isom_set_edit_internal(movie, trackNumber, EditTime, EditDuration, MediaTime, media_rate, GF_ISOM_EDIT_NORMAL); |
2779 | |
|
2780 | 0 | } |
2781 | | |
2782 | | //remove the edit segments for the whole track |
2783 | | GF_EXPORT |
2784 | | GF_Err gf_isom_remove_edits(GF_ISOFile *movie, u32 trackNumber) |
2785 | 0 | { |
2786 | 0 | GF_Err e; |
2787 | 0 | GF_TrackBox *trak; |
2788 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2789 | 0 | if (!trak) return GF_BAD_PARAM; |
2790 | | |
2791 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2792 | 0 | if (e) return e; |
2793 | | |
2794 | 0 | if (trak->extl) trak->extl->flags &= ~GF_ISOM_EXTK_EDTS_SKIP; |
2795 | 0 | if (!trak->editBox || !trak->editBox->editList) return GF_OK; |
2796 | | |
2797 | 0 | while (gf_list_count(trak->editBox->editList->entryList)) { |
2798 | 0 | GF_EdtsEntry *ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, 0); |
2799 | 0 | gf_free(ent); |
2800 | 0 | e = gf_list_rem(trak->editBox->editList->entryList, 0); |
2801 | 0 | if (e) return e; |
2802 | 0 | } |
2803 | | //then delete the GF_EditBox... |
2804 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->editBox); |
2805 | 0 | trak->editBox = NULL; |
2806 | 0 | return SetTrackDuration(trak); |
2807 | 0 | } |
2808 | | |
2809 | | |
2810 | | //remove the edit segments for the whole track |
2811 | | GF_EXPORT |
2812 | | GF_Err gf_isom_remove_edit(GF_ISOFile *movie, u32 trackNumber, u32 seg_index) |
2813 | 0 | { |
2814 | 0 | GF_Err e; |
2815 | 0 | GF_TrackBox *trak; |
2816 | 0 | GF_EdtsEntry *ent, *next_ent; |
2817 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2818 | 0 | if (!trak || !seg_index) return GF_BAD_PARAM; |
2819 | | |
2820 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2821 | 0 | if (e) return e; |
2822 | | |
2823 | 0 | if (!trak->editBox || !trak->editBox->editList) return GF_OK; |
2824 | 0 | if (gf_list_count(trak->editBox->editList->entryList)<=1) return gf_isom_remove_edits(movie, trackNumber); |
2825 | | |
2826 | 0 | ent = (GF_EdtsEntry*) gf_list_get(trak->editBox->editList->entryList, seg_index-1); |
2827 | 0 | gf_list_rem(trak->editBox->editList->entryList, seg_index-1); |
2828 | 0 | next_ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, seg_index-1); |
2829 | 0 | if (next_ent) next_ent->segmentDuration += ent->segmentDuration; |
2830 | 0 | gf_free(ent); |
2831 | 0 | return SetTrackDuration(trak); |
2832 | 0 | } |
2833 | | |
2834 | | |
2835 | | GF_EXPORT |
2836 | | GF_Err gf_isom_append_edit(GF_ISOFile *movie, u32 trackNumber, u64 EditDuration, u64 MediaTime, GF_ISOEditType EditMode) |
2837 | 0 | { |
2838 | 0 | GF_Err e; |
2839 | 0 | GF_TrackBox *trak; |
2840 | 0 | GF_EdtsEntry *ent; |
2841 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2842 | 0 | if (!trak) return GF_BAD_PARAM; |
2843 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2844 | 0 | if (e) return e; |
2845 | | |
2846 | 0 | if (!trak->editBox) { |
2847 | 0 | GF_EditBox *edts = (GF_EditBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_EDTS); |
2848 | 0 | if (!edts) return GF_OUT_OF_MEM; |
2849 | 0 | trak_on_child_box((GF_Box*)trak, (GF_Box *)edts, GF_FALSE); |
2850 | 0 | gf_assert(trak->editBox); |
2851 | 0 | } |
2852 | 0 | if (!trak->editBox->editList) { |
2853 | 0 | GF_EditListBox *elst = (GF_EditListBox *) gf_isom_box_new_parent(&trak->editBox->child_boxes, GF_ISOM_BOX_TYPE_ELST); |
2854 | 0 | if (!elst) return GF_OUT_OF_MEM; |
2855 | 0 | edts_on_child_box((GF_Box*)trak->editBox, (GF_Box *)elst, GF_FALSE); |
2856 | 0 | gf_assert(trak->editBox->editList); |
2857 | 0 | } |
2858 | 0 | ent = (GF_EdtsEntry *)gf_malloc(sizeof(GF_EdtsEntry)); |
2859 | 0 | if (!ent) return GF_OUT_OF_MEM; |
2860 | | |
2861 | 0 | ent->segmentDuration = EditDuration; |
2862 | 0 | switch (EditMode) { |
2863 | 0 | case GF_ISOM_EDIT_EMPTY: |
2864 | 0 | ent->mediaRate = 0x10000; |
2865 | 0 | ent->mediaTime = -1; |
2866 | 0 | break; |
2867 | 0 | case GF_ISOM_EDIT_DWELL: |
2868 | 0 | ent->mediaRate = 0; |
2869 | 0 | ent->mediaTime = MediaTime; |
2870 | 0 | break; |
2871 | 0 | default: |
2872 | 0 | ent->mediaRate = 0x10000; |
2873 | 0 | ent->mediaTime = MediaTime; |
2874 | 0 | break; |
2875 | 0 | } |
2876 | 0 | gf_list_add(trak->editBox->editList->entryList, ent); |
2877 | 0 | return SetTrackDuration(trak); |
2878 | 0 | } |
2879 | | |
2880 | | GF_EXPORT |
2881 | | GF_Err gf_isom_modify_edit(GF_ISOFile *movie, u32 trackNumber, u32 seg_index, u64 EditDuration, u64 MediaTime, GF_ISOEditType EditMode) |
2882 | 0 | { |
2883 | 0 | GF_Err e; |
2884 | 0 | GF_TrackBox *trak; |
2885 | 0 | GF_EdtsEntry *ent; |
2886 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
2887 | 0 | if (!trak || !seg_index) return GF_BAD_PARAM; |
2888 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2889 | 0 | if (e) return e; |
2890 | | |
2891 | 0 | if (!trak->editBox || !trak->editBox->editList) return GF_OK; |
2892 | 0 | if (gf_list_count(trak->editBox->editList->entryList)<seg_index) return GF_BAD_PARAM; |
2893 | 0 | ent = (GF_EdtsEntry*) gf_list_get(trak->editBox->editList->entryList, seg_index-1); |
2894 | |
|
2895 | 0 | ent->segmentDuration = EditDuration; |
2896 | 0 | switch (EditMode) { |
2897 | 0 | case GF_ISOM_EDIT_EMPTY: |
2898 | 0 | ent->mediaRate = 0x10000; |
2899 | 0 | ent->mediaTime = -1; |
2900 | 0 | break; |
2901 | 0 | case GF_ISOM_EDIT_DWELL: |
2902 | 0 | ent->mediaRate = 0; |
2903 | 0 | ent->mediaTime = MediaTime; |
2904 | 0 | break; |
2905 | 0 | default: |
2906 | 0 | ent->mediaRate = 0x10000; |
2907 | 0 | ent->mediaTime = MediaTime; |
2908 | 0 | break; |
2909 | 0 | } |
2910 | 0 | return SetTrackDuration(trak); |
2911 | 0 | } |
2912 | | |
2913 | | static void update_next_track_id(GF_ISOFile *movie) |
2914 | 0 | { |
2915 | 0 | GF_TrackBox *trak; |
2916 | | /*update next track ID*/ |
2917 | 0 | movie->moov->mvhd->nextTrackID = 0; |
2918 | 0 | u32 i=0; |
2919 | 0 | while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { |
2920 | 0 | if (trak->Header->trackID>movie->moov->mvhd->nextTrackID) |
2921 | 0 | movie->moov->mvhd->nextTrackID = trak->Header->trackID; |
2922 | 0 | } |
2923 | | //shall be larger than the largest track_ID in use |
2924 | 0 | movie->moov->mvhd->nextTrackID++; |
2925 | 0 | } |
2926 | | |
2927 | | //removes the desired track |
2928 | | GF_EXPORT |
2929 | | GF_Err gf_isom_remove_track(GF_ISOFile *movie, u32 trackNumber) |
2930 | 0 | { |
2931 | 0 | GF_Err e; |
2932 | 0 | GF_TrackBox *the_trak, *trak; |
2933 | 0 | GF_TrackReferenceTypeBox *tref; |
2934 | 0 | u32 i, j, k, descIndex; |
2935 | 0 | GF_ISOTrackID *newRefs; |
2936 | 0 | u8 found; |
2937 | 0 | GF_ISOSample *samp; |
2938 | 0 | the_trak = gf_isom_get_track_box(movie, trackNumber); |
2939 | 0 | if (!the_trak) return GF_BAD_PARAM; |
2940 | | |
2941 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
2942 | 0 | if (e) return e; |
2943 | | |
2944 | 0 | if (movie->moov->iods && movie->moov->iods->descriptor) { |
2945 | 0 | GF_Descriptor *desc; |
2946 | 0 | GF_ES_ID_Inc *inc; |
2947 | 0 | GF_List *ESDs; |
2948 | 0 | desc = movie->moov->iods->descriptor; |
2949 | 0 | if (desc->tag == GF_ODF_ISOM_IOD_TAG) { |
2950 | 0 | ESDs = ((GF_IsomInitialObjectDescriptor *)desc)->ES_ID_IncDescriptors; |
2951 | 0 | } else if (desc->tag == GF_ODF_ISOM_OD_TAG) { |
2952 | 0 | ESDs = ((GF_IsomObjectDescriptor *)desc)->ES_ID_IncDescriptors; |
2953 | 0 | } else { |
2954 | 0 | return GF_ISOM_INVALID_FILE; |
2955 | 0 | } |
2956 | | |
2957 | | //remove the track ref from the root OD if any |
2958 | 0 | i=0; |
2959 | 0 | while ((inc = (GF_ES_ID_Inc *)gf_list_enum(ESDs, &i))) { |
2960 | 0 | if (inc->trackID == the_trak->Header->trackID) { |
2961 | 0 | gf_odf_desc_del((GF_Descriptor *)inc); |
2962 | 0 | i--; |
2963 | 0 | gf_list_rem(ESDs, i); |
2964 | 0 | } |
2965 | 0 | } |
2966 | 0 | } |
2967 | | |
2968 | | //remove the track from the movie |
2969 | 0 | gf_list_del_item(movie->moov->trackList, the_trak); |
2970 | | |
2971 | | //rewrite any OD tracks |
2972 | 0 | i=0; |
2973 | 0 | while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { |
2974 | 0 | if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_OD) continue; |
2975 | | |
2976 | | //this is an OD track... |
2977 | 0 | j = gf_isom_get_sample_count(movie, i); |
2978 | 0 | for (k=0; k < j; k++) { |
2979 | | //getting the sample will remove the references to the deleted track in the output OD frame |
2980 | 0 | samp = gf_isom_get_sample(movie, i, k+1, &descIndex); |
2981 | 0 | if (!samp) break; |
2982 | | //so let's update with the new OD frame ! If the sample is empty, remove it |
2983 | 0 | if (!samp->dataLength) { |
2984 | 0 | e = gf_isom_remove_sample(movie, i, k+1); |
2985 | 0 | if (e) return e; |
2986 | 0 | } else { |
2987 | 0 | e = gf_isom_update_sample(movie, i, k+1, samp, GF_TRUE); |
2988 | 0 | if (e) return e; |
2989 | 0 | } |
2990 | | //and don't forget to delete the sample |
2991 | 0 | gf_isom_sample_del(&samp); |
2992 | 0 | } |
2993 | 0 | } |
2994 | | |
2995 | | //remove the track ref from any "tref" box in all tracks, except the one to delete |
2996 | | //note that we don't touch scal references, as we don't want to rewrite AVC/HEVC samples ... |
2997 | 0 | i=0; |
2998 | 0 | while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { |
2999 | 0 | if (! trak->References || ! gf_list_count(trak->References->child_boxes)) continue; |
3000 | | |
3001 | 0 | j=0; |
3002 | 0 | while ((tref = (GF_TrackReferenceTypeBox *)gf_list_enum(trak->References->child_boxes, &j))) { |
3003 | 0 | if (tref->reference_type==GF_ISOM_REF_SCAL) |
3004 | 0 | continue; |
3005 | | |
3006 | 0 | found = 0; |
3007 | 0 | for (k=0; k<tref->trackIDCount; k++) { |
3008 | 0 | if (tref->trackIDs[k] == the_trak->Header->trackID) found++; |
3009 | 0 | } |
3010 | 0 | if (!found) continue; |
3011 | | //no more refs, remove this ref_type |
3012 | 0 | if (found == tref->trackIDCount) { |
3013 | 0 | gf_isom_box_del_parent(&trak->References->child_boxes, (GF_Box *)tref); |
3014 | 0 | j--; |
3015 | 0 | } else { |
3016 | 0 | newRefs = (GF_ISOTrackID*)gf_malloc(sizeof(GF_ISOTrackID) * (tref->trackIDCount - found)); |
3017 | 0 | if (!newRefs) return GF_OUT_OF_MEM; |
3018 | 0 | found = 0; |
3019 | 0 | for (k = 0; k < tref->trackIDCount; k++) { |
3020 | 0 | if (tref->trackIDs[k] != the_trak->Header->trackID) { |
3021 | 0 | newRefs[k-found] = tref->trackIDs[k]; |
3022 | 0 | } else { |
3023 | 0 | found++; |
3024 | 0 | } |
3025 | 0 | } |
3026 | 0 | gf_free(tref->trackIDs); |
3027 | 0 | tref->trackIDs = newRefs; |
3028 | 0 | tref->trackIDCount -= found; |
3029 | 0 | } |
3030 | 0 | } |
3031 | | //a little opt: remove the ref box if empty... |
3032 | 0 | if (! gf_list_count(trak->References->child_boxes)) { |
3033 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->References); |
3034 | 0 | trak->References = NULL; |
3035 | 0 | } |
3036 | 0 | } |
3037 | | |
3038 | 0 | gf_isom_disable_inplace_rewrite(movie); |
3039 | |
|
3040 | 0 | gf_isom_meta_track_remove(movie, the_trak, 0); |
3041 | | |
3042 | | //delete the track |
3043 | 0 | gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box *)the_trak); |
3044 | | |
3045 | | /*update next track ID*/ |
3046 | 0 | update_next_track_id(movie); |
3047 | |
|
3048 | 0 | if (!gf_list_count(movie->moov->trackList)) { |
3049 | 0 | gf_list_del_item(movie->TopBoxes, movie->moov); |
3050 | 0 | gf_isom_box_del((GF_Box *)movie->moov); |
3051 | 0 | movie->moov = NULL; |
3052 | 0 | } |
3053 | 0 | return GF_OK; |
3054 | 0 | } |
3055 | | |
3056 | | |
3057 | | GF_EXPORT |
3058 | | GF_Err gf_isom_set_copyright(GF_ISOFile *movie, const char *threeCharCode, char *notice) |
3059 | 0 | { |
3060 | 0 | GF_Err e; |
3061 | 0 | GF_CopyrightBox *ptr; |
3062 | 0 | GF_UserDataMap *map; |
3063 | 0 | u32 count, i; |
3064 | |
|
3065 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3066 | 0 | if (e) return e; |
3067 | | |
3068 | 0 | if (!notice || !threeCharCode) return GF_BAD_PARAM; |
3069 | | |
3070 | 0 | e = gf_isom_insert_moov(movie); |
3071 | 0 | if (e) return e; |
3072 | | |
3073 | 0 | if (!movie->moov->udta) { |
3074 | 0 | e = moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3075 | 0 | if (e) return e; |
3076 | 0 | } |
3077 | 0 | map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL); |
3078 | |
|
3079 | 0 | if (map) { |
3080 | | //try to find one in our language... |
3081 | 0 | count = gf_list_count(map->boxes); |
3082 | 0 | for (i=0; i<count; i++) { |
3083 | 0 | ptr = (GF_CopyrightBox*)gf_list_get(map->boxes, i); |
3084 | 0 | if (!strcmp(threeCharCode, (const char *) ptr->packedLanguageCode)) { |
3085 | 0 | gf_free(ptr->notice); |
3086 | 0 | ptr->notice = (char*)gf_malloc(sizeof(char) * (strlen(notice) + 1)); |
3087 | 0 | if (!ptr->notice) return GF_OUT_OF_MEM; |
3088 | 0 | strcpy(ptr->notice, notice); |
3089 | 0 | return GF_OK; |
3090 | 0 | } |
3091 | 0 | } |
3092 | 0 | } |
3093 | | //nope, create one |
3094 | 0 | ptr = (GF_CopyrightBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CPRT); |
3095 | 0 | if (!ptr) return GF_OUT_OF_MEM; |
3096 | | |
3097 | 0 | memcpy(ptr->packedLanguageCode, threeCharCode, 4); |
3098 | 0 | ptr->notice = (char*)gf_malloc(sizeof(char) * (strlen(notice)+1)); |
3099 | 0 | if (!ptr->notice) return GF_OUT_OF_MEM; |
3100 | 0 | strcpy(ptr->notice, notice); |
3101 | 0 | return udta_on_child_box_ex((GF_Box *)movie->moov->udta, (GF_Box *) ptr, GF_FALSE, GF_FALSE); |
3102 | 0 | } |
3103 | | |
3104 | | GF_EXPORT |
3105 | | GF_Err gf_isom_add_track_kind(GF_ISOFile *movie, u32 trackNumber, const char *schemeURI, const char *value) |
3106 | 0 | { |
3107 | 0 | GF_Err e; |
3108 | 0 | GF_KindBox *ptr; |
3109 | 0 | GF_UserDataBox *udta; |
3110 | 0 | GF_UserDataMap *map; |
3111 | 0 | u32 i, count; |
3112 | |
|
3113 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3114 | 0 | if (e) return e; |
3115 | | |
3116 | 0 | e = gf_isom_insert_moov(movie); |
3117 | 0 | if (e) return e; |
3118 | | |
3119 | 0 | if (trackNumber) { |
3120 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
3121 | 0 | if (!trak) return GF_BAD_PARAM; |
3122 | 0 | if (!trak->udta) { |
3123 | 0 | e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3124 | 0 | if (e) return e; |
3125 | 0 | } |
3126 | 0 | udta = trak->udta; |
3127 | 0 | } else { |
3128 | 0 | return GF_BAD_PARAM; |
3129 | 0 | } |
3130 | | |
3131 | | //we may get null on schemeURI if not set in the source |
3132 | 0 | if (!schemeURI) schemeURI = ""; |
3133 | |
|
3134 | 0 | map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_KIND, NULL); |
3135 | 0 | if (map) { |
3136 | 0 | count = gf_list_count(map->boxes); |
3137 | 0 | for (i=0; i<count; i++) { |
3138 | 0 | GF_Box *b = (GF_Box *)gf_list_get(map->boxes, i); |
3139 | 0 | if (b->type == GF_ISOM_BOX_TYPE_KIND) { |
3140 | 0 | GF_KindBox *kb = (GF_KindBox *)b; |
3141 | 0 | if (!strcmp(kb->schemeURI, schemeURI) && |
3142 | 0 | ((value && kb->value && !strcmp(value, kb->value)) || (!value && !kb->value))) { |
3143 | | // Already there |
3144 | 0 | return GF_OK; |
3145 | 0 | } |
3146 | 0 | if (!strcmp(kb->schemeURI, schemeURI)) { |
3147 | 0 | if (kb->value) gf_free(kb->value); |
3148 | 0 | kb->value = value ? gf_strdup(value) : NULL; |
3149 | 0 | return GF_OK; |
3150 | 0 | } |
3151 | 0 | } |
3152 | 0 | } |
3153 | 0 | } |
3154 | | |
3155 | 0 | ptr = (GF_KindBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_KIND); |
3156 | 0 | if (e) return e; |
3157 | | |
3158 | 0 | ptr->schemeURI = gf_strdup(schemeURI); |
3159 | 0 | if (value) ptr->value = gf_strdup(value); |
3160 | 0 | return udta_on_child_box_ex((GF_Box *)udta, (GF_Box *) ptr, GF_FALSE, GF_FALSE); |
3161 | 0 | } |
3162 | | |
3163 | | GF_EXPORT |
3164 | | GF_Err gf_isom_remove_track_kind(GF_ISOFile *movie, u32 trackNumber, const char *schemeURI, const char *value) |
3165 | 0 | { |
3166 | 0 | GF_Err e; |
3167 | 0 | GF_UserDataBox *udta; |
3168 | 0 | GF_UserDataMap *map; |
3169 | 0 | u32 i; |
3170 | |
|
3171 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3172 | 0 | if (e) return e; |
3173 | 0 | e = gf_isom_insert_moov(movie); |
3174 | 0 | if (e) return e; |
3175 | | |
3176 | 0 | if (trackNumber) { |
3177 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
3178 | 0 | if (!trak) return GF_BAD_PARAM; |
3179 | 0 | if (!trak->udta) { |
3180 | 0 | e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3181 | 0 | if (e) return e; |
3182 | 0 | } |
3183 | 0 | udta = trak->udta; |
3184 | 0 | } else { |
3185 | 0 | return GF_OK; |
3186 | 0 | } |
3187 | 0 | map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_KIND, NULL); |
3188 | 0 | if (map) { |
3189 | 0 | for (i=0; i<gf_list_count(map->boxes); i++) { |
3190 | 0 | GF_Box *b = (GF_Box *)gf_list_get(map->boxes, i); |
3191 | 0 | if (b->type == GF_ISOM_BOX_TYPE_KIND) { |
3192 | 0 | GF_KindBox *kb = (GF_KindBox *)b; |
3193 | 0 | if (!schemeURI || |
3194 | 0 | (!strcmp(kb->schemeURI, schemeURI) && |
3195 | 0 | ((value && kb->value && !strcmp(value, kb->value)) || (!value && !kb->value)))) { |
3196 | 0 | gf_isom_box_del_parent(&map->boxes, b); |
3197 | 0 | i--; |
3198 | 0 | } |
3199 | 0 | } |
3200 | 0 | } |
3201 | 0 | } |
3202 | 0 | return GF_OK; |
3203 | 0 | } |
3204 | | |
3205 | | GF_EXPORT |
3206 | | GF_Err gf_isom_add_chapter(GF_ISOFile *movie, u32 trackNumber, u64 timestamp, char *name) |
3207 | 0 | { |
3208 | 0 | GF_Err e; |
3209 | 0 | GF_ChapterListBox *ptr; |
3210 | 0 | u32 i, count; |
3211 | 0 | GF_ChapterEntry *ce; |
3212 | 0 | GF_UserDataBox *udta; |
3213 | 0 | GF_UserDataMap *map; |
3214 | |
|
3215 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3216 | 0 | if (e) return e; |
3217 | | |
3218 | 0 | e = gf_isom_insert_moov(movie); |
3219 | 0 | if (e) return e; |
3220 | | |
3221 | 0 | if (trackNumber) { |
3222 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
3223 | 0 | if (!trak) return GF_BAD_PARAM; |
3224 | 0 | if (!trak->udta) { |
3225 | 0 | e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3226 | 0 | if (e) return e; |
3227 | 0 | } |
3228 | 0 | udta = trak->udta; |
3229 | 0 | } else { |
3230 | 0 | if (!movie->moov->udta) { |
3231 | 0 | e = moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3232 | 0 | if (e) return e; |
3233 | 0 | } |
3234 | 0 | udta = movie->moov->udta; |
3235 | 0 | } |
3236 | | |
3237 | 0 | ptr = NULL; |
3238 | 0 | map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL); |
3239 | 0 | if (!map) { |
3240 | 0 | ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL); |
3241 | 0 | e = udta_on_child_box_ex((GF_Box *)udta, (GF_Box *) ptr, GF_FALSE, GF_FALSE); |
3242 | 0 | if (e) return e; |
3243 | 0 | map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL); |
3244 | 0 | } else { |
3245 | 0 | ptr = (GF_ChapterListBox*)gf_list_get(map->boxes, 0); |
3246 | 0 | } |
3247 | 0 | if (!map) return GF_OUT_OF_MEM; |
3248 | | |
3249 | | /*this may happen if original MP4 is not properly formatted*/ |
3250 | 0 | if (!ptr) { |
3251 | 0 | ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL); |
3252 | 0 | if (!ptr) return GF_OUT_OF_MEM; |
3253 | 0 | gf_list_add(map->boxes, ptr); |
3254 | 0 | } |
3255 | | |
3256 | 0 | GF_SAFEALLOC(ce, GF_ChapterEntry); |
3257 | 0 | if (!ce) return GF_OUT_OF_MEM; |
3258 | | |
3259 | 0 | ce->start_time = timestamp * 10000L; |
3260 | 0 | ce->name = name ? gf_strdup(name) : NULL; |
3261 | | |
3262 | | /*insert in order*/ |
3263 | 0 | count = gf_list_count(ptr->list); |
3264 | 0 | for (i=0; i<count; i++) { |
3265 | 0 | GF_ChapterEntry *ace = (GF_ChapterEntry *)gf_list_get(ptr->list, i); |
3266 | 0 | if (ace->start_time == ce->start_time) { |
3267 | 0 | if (ace->name) gf_free(ace->name); |
3268 | 0 | ace->name = ce->name; |
3269 | 0 | gf_free(ce); |
3270 | 0 | return GF_OK; |
3271 | 0 | } |
3272 | 0 | if (ace->start_time >= ce->start_time) |
3273 | 0 | return gf_list_insert(ptr->list, ce, i); |
3274 | 0 | } |
3275 | 0 | return gf_list_add(ptr->list, ce); |
3276 | 0 | } |
3277 | | |
3278 | | GF_EXPORT |
3279 | | GF_Err gf_isom_remove_chapter(GF_ISOFile *movie, u32 trackNumber, u32 index) |
3280 | 0 | { |
3281 | 0 | GF_Err e; |
3282 | 0 | GF_ChapterListBox *ptr; |
3283 | 0 | GF_ChapterEntry *ce; |
3284 | 0 | GF_UserDataBox *udta; |
3285 | 0 | GF_UserDataMap *map; |
3286 | |
|
3287 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3288 | 0 | if (e) return e; |
3289 | 0 | e = gf_isom_insert_moov(movie); |
3290 | 0 | if (e) return e; |
3291 | | |
3292 | 0 | if (trackNumber) { |
3293 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
3294 | 0 | if (!trak) return GF_BAD_PARAM; |
3295 | 0 | if (!trak->udta) return GF_OK; |
3296 | 0 | udta = trak->udta; |
3297 | 0 | } else { |
3298 | 0 | if (!movie->moov->udta) return GF_OK; |
3299 | 0 | udta = movie->moov->udta; |
3300 | 0 | } |
3301 | | |
3302 | 0 | map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL); |
3303 | 0 | if (!map) return GF_OK; |
3304 | 0 | ptr = (GF_ChapterListBox*)gf_list_get(map->boxes, 0); |
3305 | 0 | if (!ptr) return GF_OK; |
3306 | | |
3307 | 0 | if (index) { |
3308 | 0 | ce = (GF_ChapterEntry *)gf_list_get(ptr->list, index-1); |
3309 | 0 | if (!ce) return GF_BAD_PARAM; |
3310 | 0 | if (ce->name) gf_free(ce->name); |
3311 | 0 | gf_free(ce); |
3312 | 0 | gf_list_rem(ptr->list, index-1); |
3313 | 0 | } else { |
3314 | 0 | while (gf_list_count(ptr->list)) { |
3315 | 0 | ce = (GF_ChapterEntry *)gf_list_get(ptr->list, 0); |
3316 | 0 | if (ce->name) gf_free(ce->name); |
3317 | 0 | gf_free(ce); |
3318 | 0 | gf_list_rem(ptr->list, 0); |
3319 | 0 | } |
3320 | 0 | } |
3321 | 0 | if (!gf_list_count(ptr->list)) { |
3322 | 0 | gf_list_del_item(udta->recordList, map); |
3323 | 0 | gf_isom_box_array_del(map->boxes); |
3324 | 0 | gf_free(map); |
3325 | 0 | } |
3326 | 0 | return GF_OK; |
3327 | 0 | } |
3328 | | |
3329 | | #if 0 //unused |
3330 | | GF_Err gf_isom_remove_copyright(GF_ISOFile *movie, u32 index) |
3331 | | { |
3332 | | GF_Err e; |
3333 | | GF_CopyrightBox *ptr; |
3334 | | GF_UserDataMap *map; |
3335 | | u32 count; |
3336 | | |
3337 | | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3338 | | if (e) return e; |
3339 | | e = gf_isom_insert_moov(movie); |
3340 | | if (e) return e; |
3341 | | |
3342 | | if (!index) return GF_BAD_PARAM; |
3343 | | if (!movie->moov->udta) return GF_OK; |
3344 | | |
3345 | | map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL); |
3346 | | if (!map) return GF_OK; |
3347 | | |
3348 | | count = gf_list_count(map->boxes); |
3349 | | if (index>count) return GF_BAD_PARAM; |
3350 | | |
3351 | | ptr = (GF_CopyrightBox*)gf_list_get(map->boxes, index-1); |
3352 | | if (ptr) { |
3353 | | gf_list_rem(map->boxes, index-1); |
3354 | | if (ptr->notice) gf_free(ptr->notice); |
3355 | | gf_free(ptr); |
3356 | | } |
3357 | | /*last copyright, remove*/ |
3358 | | if (!gf_list_count(map->boxes)) { |
3359 | | gf_list_del_item(movie->moov->udta->recordList, map); |
3360 | | gf_list_del(map->boxes); |
3361 | | gf_free(map); |
3362 | | } |
3363 | | return GF_OK; |
3364 | | } |
3365 | | |
3366 | | GF_Err gf_isom_set_watermark(GF_ISOFile *movie, bin128 UUID, u8* data, u32 length) |
3367 | | { |
3368 | | GF_Err e; |
3369 | | GF_UnknownUUIDBox *ptr; |
3370 | | GF_UserDataMap *map; |
3371 | | |
3372 | | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3373 | | if (e) return e; |
3374 | | |
3375 | | e = gf_isom_insert_moov(movie); |
3376 | | if (e) return e; |
3377 | | if (!movie->moov->udta) { |
3378 | | e = moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA)); |
3379 | | if (e) return e; |
3380 | | } |
3381 | | |
3382 | | map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_UUID, (bin128 *) & UUID); |
3383 | | if (map) { |
3384 | | ptr = (GF_UnknownUUIDBox *)gf_list_get(map->boxes, 0); |
3385 | | if (ptr) { |
3386 | | gf_free(ptr->data); |
3387 | | ptr->data = (char*)gf_malloc(length); |
3388 | | if (!ptr->data) return GF_OUT_OF_MEM; |
3389 | | memcpy(ptr->data, data, length); |
3390 | | ptr->dataSize = length; |
3391 | | return GF_OK; |
3392 | | } |
3393 | | } |
3394 | | //nope, create one |
3395 | | ptr = (GF_UnknownUUIDBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID); |
3396 | | if (!ptr) return GF_OUT_OF_MEM; |
3397 | | |
3398 | | memcpy(ptr->uuid, UUID, 16); |
3399 | | ptr->data = (char*)gf_malloc(length); |
3400 | | if (!ptr->data) return GF_OUT_OF_MEM; |
3401 | | memcpy(ptr->data, data, length); |
3402 | | ptr->dataSize = length; |
3403 | | return udta_on_child_box_ex((GF_Box *)movie->moov->udta, (GF_Box *) ptr, GF_FALSE, GF_FALSE); |
3404 | | } |
3405 | | #endif |
3406 | | |
3407 | | //set the interleaving time of media data (INTERLEAVED mode only) |
3408 | | //InterleaveTime is in MovieTimeScale |
3409 | | GF_EXPORT |
3410 | | GF_Err gf_isom_set_interleave_time(GF_ISOFile *movie, u32 InterleaveTime) |
3411 | 0 | { |
3412 | 0 | GF_Err e; |
3413 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3414 | 0 | if (e) return e; |
3415 | | |
3416 | 0 | if (!InterleaveTime || !movie->moov) return GF_OK; |
3417 | 0 | movie->interleavingTime = InterleaveTime; |
3418 | 0 | return GF_OK; |
3419 | 0 | } |
3420 | | |
3421 | | |
3422 | | |
3423 | | //use a compact track version for sample size. This is not usually recommended |
3424 | | //except for speech codecs where the track has a lot of small samples |
3425 | | //compaction is done automatically while writing based on the track's sample sizes |
3426 | | GF_EXPORT |
3427 | | GF_Err gf_isom_use_compact_size(GF_ISOFile *movie, u32 trackNumber, Bool CompactionOn) |
3428 | 0 | { |
3429 | 0 | GF_TrackBox *trak; |
3430 | 0 | u32 i, size; |
3431 | 0 | GF_SampleSizeBox *stsz; |
3432 | 0 | GF_Err e; |
3433 | |
|
3434 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3435 | 0 | if (e) return e; |
3436 | | |
3437 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
3438 | 0 | if (!trak) return GF_BAD_PARAM; |
3439 | | |
3440 | 0 | if (!trak->Media || !trak->Media->information |
3441 | 0 | || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleSize) |
3442 | 0 | return GF_ISOM_INVALID_FILE; |
3443 | | |
3444 | 0 | stsz = trak->Media->information->sampleTable->SampleSize; |
3445 | | |
3446 | | //switch to regular table |
3447 | 0 | if (!CompactionOn) { |
3448 | 0 | if (stsz->type == GF_ISOM_BOX_TYPE_STSZ) return GF_OK; |
3449 | 0 | stsz->type = GF_ISOM_BOX_TYPE_STSZ; |
3450 | | //invalidate the sampleSize and recompute it |
3451 | 0 | stsz->sampleSize = 0; |
3452 | 0 | if (!stsz->sampleCount) return GF_OK; |
3453 | | //if the table is empty we can only assume the track is empty (no size indication) |
3454 | 0 | if (!stsz->sizes) return GF_OK; |
3455 | 0 | size = stsz->sizes[0]; |
3456 | | //check whether the sizes are all the same or not |
3457 | 0 | for (i=1; i<stsz->sampleCount; i++) { |
3458 | 0 | if (size != stsz->sizes[i]) { |
3459 | 0 | size = 0; |
3460 | 0 | break; |
3461 | 0 | } |
3462 | 0 | } |
3463 | 0 | if (size) { |
3464 | 0 | gf_free(stsz->sizes); |
3465 | 0 | stsz->sizes = NULL; |
3466 | 0 | stsz->sampleSize = size; |
3467 | 0 | } |
3468 | 0 | return GF_OK; |
3469 | 0 | } |
3470 | | |
3471 | | //switch to compact table |
3472 | 0 | if (stsz->type == GF_ISOM_BOX_TYPE_STZ2) return GF_OK; |
3473 | | //fill the table. Although it seems weird , this is needed in case of edition |
3474 | | //after the function is called. NOte however than we force regular table |
3475 | | //at write time if all samples are of same size |
3476 | 0 | if (stsz->sampleSize && stsz->sampleCount) { |
3477 | | //this is a weird table indeed ;) |
3478 | 0 | if (stsz->sizes) gf_free(stsz->sizes); |
3479 | 0 | stsz->sizes = (u32*) gf_malloc(sizeof(u32)*stsz->sampleCount); |
3480 | 0 | if (!stsz->sizes) return GF_OUT_OF_MEM; |
3481 | 0 | memset(stsz->sizes, stsz->sampleSize, sizeof(u32)); |
3482 | 0 | } |
3483 | | //set the SampleSize to 0 while the file is open |
3484 | 0 | stsz->sampleSize = 0; |
3485 | 0 | stsz->type = GF_ISOM_BOX_TYPE_STZ2; |
3486 | 0 | return GF_OK; |
3487 | 0 | } |
3488 | | |
3489 | | |
3490 | | GF_EXPORT |
3491 | | GF_Err gf_isom_disable_brand_rewrite(GF_ISOFile *movie, Bool do_disable) |
3492 | 0 | { |
3493 | 0 | if (!movie) return GF_BAD_PARAM; |
3494 | 0 | movie->disable_brand_rewrite = do_disable ? 1: 0; |
3495 | 0 | return GF_OK; |
3496 | 0 | } |
3497 | | |
3498 | | GF_EXPORT |
3499 | | GF_Err gf_isom_set_brand_info(GF_ISOFile *movie, u32 MajorBrand, u32 MinorVersion) |
3500 | 0 | { |
3501 | 0 | u32 i, *p; |
3502 | |
|
3503 | 0 | if (!movie) return GF_BAD_PARAM; |
3504 | 0 | if (movie->disable_brand_rewrite) return GF_OK; |
3505 | 0 | if (!MajorBrand) return GF_BAD_PARAM; |
3506 | | |
3507 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
3508 | 0 | if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) { |
3509 | 0 | GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3510 | 0 | if (e) return e; |
3511 | | |
3512 | 0 | e = CheckNoData(movie); |
3513 | 0 | if (e) return e; |
3514 | 0 | } |
3515 | 0 | #endif |
3516 | | |
3517 | 0 | if (!movie->brand) { |
3518 | 0 | movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP); |
3519 | 0 | if (!movie->brand) return GF_OUT_OF_MEM; |
3520 | 0 | gf_list_add(movie->TopBoxes, movie->brand); |
3521 | 0 | } |
3522 | | |
3523 | 0 | movie->brand->majorBrand = MajorBrand; |
3524 | 0 | movie->brand->minorVersion = MinorVersion; |
3525 | |
|
3526 | 0 | if (!movie->brand->altBrand) { |
3527 | 0 | movie->brand->altBrand = (u32*)gf_malloc(sizeof(u32)); |
3528 | 0 | if (!movie->brand->altBrand) return GF_OUT_OF_MEM; |
3529 | 0 | movie->brand->altBrand[0] = MajorBrand; |
3530 | 0 | movie->brand->altCount = 1; |
3531 | 0 | return GF_OK; |
3532 | 0 | } |
3533 | | |
3534 | | //if brand already present don't change anything |
3535 | 0 | for (i=0; i<movie->brand->altCount; i++) { |
3536 | 0 | if (movie->brand->altBrand[i] == MajorBrand) return GF_OK; |
3537 | 0 | } |
3538 | 0 | p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount + 1)); |
3539 | 0 | if (!p) return GF_OUT_OF_MEM; |
3540 | 0 | memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount); |
3541 | 0 | p[movie->brand->altCount] = MajorBrand; |
3542 | 0 | movie->brand->altCount += 1; |
3543 | 0 | gf_free(movie->brand->altBrand); |
3544 | 0 | movie->brand->altBrand = p; |
3545 | 0 | return GF_OK; |
3546 | 0 | } |
3547 | | |
3548 | | |
3549 | | GF_EXPORT |
3550 | | GF_Err gf_isom_modify_alternate_brand(GF_ISOFile *movie, u32 Brand, Bool AddIt) |
3551 | 0 | { |
3552 | 0 | u32 i, k, *p; |
3553 | |
|
3554 | 0 | if (!movie) return GF_BAD_PARAM; |
3555 | 0 | if (movie->disable_brand_rewrite) return GF_OK; |
3556 | 0 | if (!Brand) return GF_BAD_PARAM; |
3557 | | |
3558 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
3559 | 0 | if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) { |
3560 | 0 | GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3561 | 0 | if (e) return e; |
3562 | | |
3563 | 0 | e = CheckNoData(movie); |
3564 | 0 | if (e) return e; |
3565 | 0 | } |
3566 | 0 | #endif |
3567 | | |
3568 | 0 | if (!movie->brand && AddIt) { |
3569 | 0 | movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP); |
3570 | 0 | if (!movie->brand) return GF_OUT_OF_MEM; |
3571 | 0 | gf_list_add(movie->TopBoxes, movie->brand); |
3572 | 0 | } |
3573 | 0 | if (!AddIt && !movie->brand) return GF_OK; |
3574 | | |
3575 | | //do not mofify major one |
3576 | 0 | if (!AddIt && movie->brand->majorBrand == Brand) return GF_OK; |
3577 | | |
3578 | 0 | if (!AddIt && movie->brand->altCount == 1) { |
3579 | | //fixes it in case |
3580 | 0 | movie->brand->altBrand[0] = movie->brand->majorBrand; |
3581 | 0 | return GF_OK; |
3582 | 0 | } |
3583 | | //check for the brand |
3584 | 0 | for (i=0; i<movie->brand->altCount; i++) { |
3585 | 0 | if (movie->brand->altBrand[i] == Brand) goto found; |
3586 | 0 | } |
3587 | | //Not found |
3588 | 0 | if (!AddIt) return GF_OK; |
3589 | | //add it |
3590 | 0 | p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount + 1)); |
3591 | 0 | if (!p) return GF_OUT_OF_MEM; |
3592 | 0 | if (movie->brand->altBrand) { |
3593 | 0 | memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount); |
3594 | 0 | gf_free(movie->brand->altBrand); |
3595 | 0 | } |
3596 | 0 | p[movie->brand->altCount] = Brand; |
3597 | 0 | movie->brand->altCount += 1; |
3598 | 0 | movie->brand->altBrand = p; |
3599 | 0 | return GF_OK; |
3600 | | |
3601 | 0 | found: |
3602 | | |
3603 | | //found |
3604 | 0 | if (AddIt) return GF_OK; |
3605 | 0 | gf_assert(movie->brand->altCount>1); |
3606 | | |
3607 | | //remove it |
3608 | 0 | p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount - 1)); |
3609 | 0 | if (!p) return GF_OUT_OF_MEM; |
3610 | 0 | k = 0; |
3611 | 0 | for (i=0; i<movie->brand->altCount; i++) { |
3612 | 0 | if (movie->brand->altBrand[i] == Brand) continue; |
3613 | 0 | else { |
3614 | 0 | p[k] = movie->brand->altBrand[i]; |
3615 | 0 | k++; |
3616 | 0 | } |
3617 | 0 | } |
3618 | 0 | movie->brand->altCount -= 1; |
3619 | 0 | gf_free(movie->brand->altBrand); |
3620 | 0 | movie->brand->altBrand = p; |
3621 | 0 | return GF_OK; |
3622 | 0 | } |
3623 | | |
3624 | | GF_EXPORT |
3625 | | GF_Err gf_isom_reset_alt_brands_ex(GF_ISOFile *movie, Bool leave_empty) |
3626 | 0 | { |
3627 | 0 | u32 *p; |
3628 | |
|
3629 | 0 | if (!movie) return GF_BAD_PARAM; |
3630 | 0 | if (movie->disable_brand_rewrite) return GF_OK; |
3631 | | |
3632 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
3633 | 0 | if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) { |
3634 | 0 | GF_Err e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3635 | 0 | if (e) return e; |
3636 | | |
3637 | 0 | e = CheckNoData(movie); |
3638 | 0 | if (e) return e; |
3639 | 0 | } |
3640 | 0 | #endif |
3641 | | |
3642 | 0 | if (!movie->brand) { |
3643 | 0 | movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP); |
3644 | 0 | if (!movie->brand) return GF_OUT_OF_MEM; |
3645 | 0 | gf_list_add(movie->TopBoxes, movie->brand); |
3646 | 0 | } |
3647 | 0 | gf_free(movie->brand->altBrand); |
3648 | 0 | if (leave_empty) { |
3649 | 0 | movie->brand->altCount = 0; |
3650 | 0 | movie->brand->altBrand = NULL; |
3651 | 0 | } else { |
3652 | 0 | p = (u32*)gf_malloc(sizeof(u32)); |
3653 | 0 | if (!p) return GF_OUT_OF_MEM; |
3654 | 0 | p[0] = movie->brand->majorBrand; |
3655 | 0 | movie->brand->altCount = 1; |
3656 | 0 | movie->brand->altBrand = p; |
3657 | 0 | } |
3658 | 0 | return GF_OK; |
3659 | 0 | } |
3660 | | GF_EXPORT |
3661 | | GF_Err gf_isom_reset_alt_brands(GF_ISOFile *movie) |
3662 | 0 | { |
3663 | 0 | return gf_isom_reset_alt_brands_ex(movie, GF_FALSE); |
3664 | 0 | } |
3665 | | |
3666 | | #if 0 //unused |
3667 | | GF_Err gf_isom_set_sample_padding_bits(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u8 NbBits) |
3668 | | { |
3669 | | GF_TrackBox *trak; |
3670 | | GF_Err e; |
3671 | | |
3672 | | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3673 | | if (e) return e; |
3674 | | |
3675 | | trak = gf_isom_get_track_box(movie, trackNumber); |
3676 | | if (!trak || NbBits > 7) return GF_BAD_PARAM; |
3677 | | |
3678 | | //set Padding info |
3679 | | return stbl_SetPaddingBits(trak->Media->information->sampleTable, sampleNumber, NbBits); |
3680 | | } |
3681 | | #endif |
3682 | | |
3683 | | GF_EXPORT |
3684 | | GF_Err gf_isom_remove_user_data_item(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, u32 UserDataIndex) |
3685 | 0 | { |
3686 | 0 | GF_UserDataMap *map; |
3687 | 0 | GF_Box *a; |
3688 | 0 | u32 i; |
3689 | 0 | bin128 t; |
3690 | 0 | GF_Err e; |
3691 | 0 | GF_TrackBox *trak; |
3692 | 0 | GF_UserDataBox *udta; |
3693 | |
|
3694 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3695 | 0 | if (e) return e; |
3696 | | |
3697 | 0 | if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; |
3698 | 0 | memset(t, 1, 16); |
3699 | |
|
3700 | 0 | if (trackNumber) { |
3701 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
3702 | 0 | if (!trak) return GF_BAD_PARAM; |
3703 | 0 | udta = trak->udta; |
3704 | 0 | } else { |
3705 | 0 | if (!movie->moov) return GF_BAD_PARAM; |
3706 | 0 | udta = movie->moov->udta; |
3707 | 0 | } |
3708 | 0 | if (!udta) return GF_BAD_PARAM; |
3709 | 0 | if (!UserDataIndex) return GF_BAD_PARAM; |
3710 | | |
3711 | 0 | i=0; |
3712 | 0 | while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) { |
3713 | 0 | if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found; |
3714 | 0 | else if (map->boxType == UserDataType) goto found; |
3715 | 0 | } |
3716 | | //not found |
3717 | 0 | return GF_OK; |
3718 | | |
3719 | 0 | found: |
3720 | |
|
3721 | 0 | if (UserDataIndex > gf_list_count(map->boxes) ) return GF_BAD_PARAM; |
3722 | | //delete the box |
3723 | 0 | a = (GF_Box*)gf_list_get(map->boxes, UserDataIndex-1); |
3724 | 0 | gf_isom_box_del_parent(&map->boxes, a); |
3725 | | |
3726 | | //remove the map if empty |
3727 | 0 | if (!gf_list_count(map->boxes)) { |
3728 | 0 | gf_list_rem(udta->recordList, i-1); |
3729 | 0 | gf_isom_box_array_del(map->boxes); |
3730 | 0 | gf_free(map); |
3731 | 0 | } |
3732 | | //but we keep the UDTA no matter what |
3733 | 0 | return GF_OK; |
3734 | 0 | } |
3735 | | |
3736 | | GF_EXPORT |
3737 | | GF_Err gf_isom_remove_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID) |
3738 | 0 | { |
3739 | 0 | GF_UserDataMap *map; |
3740 | 0 | u32 i; |
3741 | 0 | GF_Err e; |
3742 | 0 | bin128 t; |
3743 | 0 | GF_TrackBox *trak; |
3744 | 0 | GF_UserDataBox *udta; |
3745 | |
|
3746 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3747 | 0 | if (e) return e; |
3748 | | |
3749 | 0 | if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; |
3750 | 0 | memset(t, 1, 16); |
3751 | |
|
3752 | 0 | if (trackNumber) { |
3753 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
3754 | 0 | if (!trak) return GF_EOS; |
3755 | 0 | udta = trak->udta; |
3756 | 0 | } else { |
3757 | 0 | if (!movie->moov) return GF_BAD_PARAM; |
3758 | 0 | udta = movie->moov->udta; |
3759 | 0 | } |
3760 | | //do not return any error if no udta |
3761 | 0 | if (!udta) return GF_EOS; |
3762 | | |
3763 | 0 | i=0; |
3764 | 0 | while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) { |
3765 | 0 | if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found; |
3766 | 0 | else if (map->boxType == UserDataType) goto found; |
3767 | 0 | } |
3768 | | //not found |
3769 | 0 | return GF_OK; |
3770 | | |
3771 | 0 | found: |
3772 | |
|
3773 | 0 | gf_list_rem(udta->recordList, i-1); |
3774 | 0 | gf_isom_box_array_del(map->boxes); |
3775 | 0 | gf_free(map); |
3776 | | |
3777 | | //but we keep the UDTA no matter what |
3778 | 0 | return GF_OK; |
3779 | 0 | } |
3780 | | |
3781 | | GF_EXPORT |
3782 | | GF_Err gf_isom_add_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, u8 *data, u32 DataLength) |
3783 | 0 | { |
3784 | 0 | GF_Err e; |
3785 | 0 | GF_TrackBox *trak; |
3786 | 0 | GF_UserDataBox *udta; |
3787 | |
|
3788 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3789 | 0 | if (e) return e; |
3790 | | |
3791 | 0 | if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; |
3792 | |
|
3793 | 0 | if (trackNumber) { |
3794 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
3795 | 0 | if (!trak) return GF_BAD_PARAM; |
3796 | 0 | if (!trak->udta) trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3797 | 0 | udta = trak->udta; |
3798 | 0 | } else { |
3799 | 0 | if (!movie->moov) return GF_BAD_PARAM; |
3800 | 0 | if (!movie->moov->udta) moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3801 | 0 | udta = movie->moov->udta; |
3802 | 0 | } |
3803 | 0 | if (!udta) return GF_OUT_OF_MEM; |
3804 | | |
3805 | | //create a default box |
3806 | 0 | if (UserDataType) { |
3807 | 0 | GF_UnknownBox *a = (GF_UnknownBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UNKNOWN); |
3808 | 0 | if (!a) return GF_OUT_OF_MEM; |
3809 | 0 | a->original_4cc = UserDataType; |
3810 | 0 | if (DataLength) { |
3811 | 0 | a->data = (char*)gf_malloc(sizeof(char)*DataLength); |
3812 | 0 | if (!a->data) return GF_OUT_OF_MEM; |
3813 | 0 | memcpy(a->data, data, DataLength); |
3814 | 0 | a->dataSize = DataLength; |
3815 | 0 | } |
3816 | 0 | return udta_on_child_box_ex((GF_Box *)udta, (GF_Box *) a, GF_FALSE, GF_TRUE); |
3817 | 0 | } else if (UUID) { |
3818 | 0 | GF_UnknownUUIDBox *a = (GF_UnknownUUIDBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID); |
3819 | 0 | if (!a) return GF_OUT_OF_MEM; |
3820 | 0 | memcpy(a->uuid, UUID, 16); |
3821 | 0 | if (DataLength) { |
3822 | 0 | a->data = (char*)gf_malloc(sizeof(char)*DataLength); |
3823 | 0 | if (!a->data) return GF_OUT_OF_MEM; |
3824 | 0 | memcpy(a->data, data, DataLength); |
3825 | 0 | a->dataSize = DataLength; |
3826 | 0 | } |
3827 | 0 | return udta_on_child_box_ex((GF_Box *)udta, (GF_Box *) a, GF_FALSE, GF_TRUE); |
3828 | 0 | } else { |
3829 | 0 | return GF_BAD_PARAM; |
3830 | 0 | } |
3831 | 0 | return GF_OK; |
3832 | 0 | } |
3833 | | |
3834 | | GF_EXPORT |
3835 | | GF_Err gf_isom_add_user_data_boxes(GF_ISOFile *movie, u32 trackNumber, u8 *data, u32 DataLength) |
3836 | 0 | { |
3837 | 0 | GF_Err e; |
3838 | 0 | GF_TrackBox *trak; |
3839 | 0 | GF_UserDataBox *udta; |
3840 | 0 | GF_BitStream *bs; |
3841 | |
|
3842 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
3843 | 0 | if (e) return e; |
3844 | | |
3845 | 0 | if (trackNumber) { |
3846 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
3847 | 0 | if (!trak) return GF_BAD_PARAM; |
3848 | 0 | if (!trak->udta) trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3849 | 0 | udta = trak->udta; |
3850 | 0 | } else { |
3851 | 0 | if (!movie->moov) return GF_BAD_PARAM; |
3852 | 0 | if (!movie->moov->udta) moov_on_child_box((GF_Box*)movie->moov, gf_isom_box_new_parent(&movie->moov->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
3853 | 0 | udta = movie->moov->udta; |
3854 | 0 | } |
3855 | 0 | if (!udta) return GF_OUT_OF_MEM; |
3856 | | |
3857 | 0 | bs = gf_bs_new(data, DataLength, GF_BITSTREAM_READ); |
3858 | 0 | while (gf_bs_available(bs)) { |
3859 | 0 | GF_Box *a; |
3860 | 0 | e = gf_isom_box_parse(&a, bs); |
3861 | 0 | if (e) break; |
3862 | 0 | e = udta_on_child_box_ex((GF_Box *)udta, a, GF_FALSE, GF_FALSE); |
3863 | 0 | if (e) break; |
3864 | 0 | } |
3865 | 0 | gf_bs_del(bs); |
3866 | 0 | return e; |
3867 | 0 | } |
3868 | | |
3869 | | GF_EXPORT |
3870 | | GF_Err gf_isom_clone_pl_indications(GF_ISOFile *orig, GF_ISOFile *dest) |
3871 | 0 | { |
3872 | 0 | GF_IsomInitialObjectDescriptor *iod_d; |
3873 | 0 | if (!orig || !dest) return GF_BAD_PARAM; |
3874 | 0 | if (!orig->moov->iods || !orig->moov->iods->descriptor) return GF_OK; |
3875 | 0 | if (orig->moov->iods->descriptor->tag != GF_ODF_ISOM_IOD_TAG) return GF_OK; |
3876 | | |
3877 | 0 | AddMovieIOD(dest->moov, 1); |
3878 | 0 | gf_odf_desc_del((GF_Descriptor *)dest->moov->iods->descriptor); |
3879 | 0 | gf_odf_desc_copy((GF_Descriptor *)orig->moov->iods->descriptor, (GF_Descriptor **)&dest->moov->iods->descriptor); |
3880 | 0 | iod_d = (GF_IsomInitialObjectDescriptor *) dest->moov->iods->descriptor; |
3881 | 0 | while (gf_list_count(iod_d->ES_ID_IncDescriptors)) { |
3882 | 0 | GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_IncDescriptors, 0); |
3883 | 0 | gf_list_rem(iod_d->ES_ID_IncDescriptors, 0); |
3884 | 0 | gf_odf_desc_del(d); |
3885 | 0 | } |
3886 | 0 | while (gf_list_count(iod_d->ES_ID_RefDescriptors)) { |
3887 | 0 | GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_RefDescriptors, 0); |
3888 | 0 | gf_list_rem(iod_d->ES_ID_RefDescriptors, 0); |
3889 | 0 | gf_odf_desc_del(d); |
3890 | 0 | } |
3891 | 0 | return GF_OK; |
3892 | 0 | } |
3893 | | |
3894 | | GF_EXPORT |
3895 | | GF_Err gf_isom_clone_box(GF_Box *src, GF_Box **dst) |
3896 | 0 | { |
3897 | 0 | GF_Err e; |
3898 | 0 | u8 *data; |
3899 | 0 | u32 data_size; |
3900 | 0 | GF_BitStream *bs; |
3901 | |
|
3902 | 0 | if (*dst) { |
3903 | 0 | gf_isom_box_del(*dst); |
3904 | 0 | *dst=NULL; |
3905 | 0 | } |
3906 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
3907 | 0 | if (!bs) return GF_OUT_OF_MEM; |
3908 | 0 | e = gf_isom_box_size( (GF_Box *) src); |
3909 | 0 | if (!e) e = gf_isom_box_write((GF_Box *) src, bs); |
3910 | 0 | gf_bs_get_content(bs, &data, &data_size); |
3911 | 0 | gf_bs_del(bs); |
3912 | 0 | if (e) { |
3913 | 0 | if (data) gf_free(data); |
3914 | 0 | return e; |
3915 | 0 | } |
3916 | 0 | bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); |
3917 | 0 | if (!bs) { |
3918 | 0 | if (data) gf_free(data); |
3919 | 0 | return GF_OUT_OF_MEM; |
3920 | 0 | } |
3921 | 0 | e = gf_isom_box_parse(dst, bs); |
3922 | 0 | gf_bs_del(bs); |
3923 | 0 | gf_free(data); |
3924 | 0 | return e; |
3925 | 0 | } |
3926 | | |
3927 | | #if 0 //unused |
3928 | | /*clones the entire movie file to destination. Tracks can be cloned if clone_tracks is set, in which case hint tracks can be |
3929 | | kept if keep_hint_tracks is set |
3930 | | if keep_pssh, all pssh boxes will be kept |
3931 | | fragment information (mvex) is not kept*/ |
3932 | | GF_Err gf_isom_clone_movie(GF_ISOFile *orig_file, GF_ISOFile *dest_file, Bool clone_tracks, Bool keep_hint_tracks, Bool keep_pssh) |
3933 | | { |
3934 | | GF_Err e; |
3935 | | u32 i; |
3936 | | GF_Box *box; |
3937 | | |
3938 | | e = gf_isom_can_access_movie(dest_file, GF_ISOM_OPEN_WRITE); |
3939 | | if (e) return e; |
3940 | | |
3941 | | if (orig_file->brand) { |
3942 | | gf_list_del_item(dest_file->TopBoxes, dest_file->brand); |
3943 | | gf_isom_box_del((GF_Box *)dest_file->brand); |
3944 | | dest_file->brand = NULL; |
3945 | | gf_isom_clone_box((GF_Box *)orig_file->brand, (GF_Box **)&dest_file->brand); |
3946 | | if (dest_file->brand) gf_list_add(dest_file->TopBoxes, dest_file->brand); |
3947 | | } |
3948 | | |
3949 | | if (orig_file->meta) { |
3950 | | gf_list_del_item(dest_file->TopBoxes, dest_file->meta); |
3951 | | gf_isom_box_del((GF_Box *)dest_file->meta); |
3952 | | dest_file->meta = NULL; |
3953 | | /*fixme - check imports*/ |
3954 | | gf_isom_clone_box((GF_Box *)orig_file->meta, (GF_Box **)&dest_file->meta); |
3955 | | if (dest_file->meta) gf_list_add(dest_file->TopBoxes, dest_file->meta); |
3956 | | } |
3957 | | if (orig_file->moov) { |
3958 | | u32 i, dstTrack; |
3959 | | GF_Box *iods; |
3960 | | GF_List *tracks = gf_list_new(); |
3961 | | GF_List *old_tracks = orig_file->moov->trackList; |
3962 | | orig_file->moov->trackList = tracks; |
3963 | | iods = (GF_Box*)orig_file->moov->iods; |
3964 | | orig_file->moov->iods = NULL; |
3965 | | e = gf_isom_clone_box((GF_Box *)orig_file->moov, (GF_Box **)&dest_file->moov); |
3966 | | if (e) { |
3967 | | gf_list_del(tracks); |
3968 | | orig_file->moov->trackList = old_tracks; |
3969 | | return e; |
3970 | | } |
3971 | | orig_file->moov->trackList = old_tracks; |
3972 | | gf_list_del(tracks); |
3973 | | orig_file->moov->iods = (GF_ObjectDescriptorBox*)iods; |
3974 | | gf_list_add(dest_file->TopBoxes, dest_file->moov); |
3975 | | |
3976 | | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
3977 | | if (dest_file->moov->mvex) { |
3978 | | gf_isom_box_del_parent(&dest_file->moov->child_boxes, (GF_Box *)dest_file->moov->mvex); |
3979 | | dest_file->moov->mvex = NULL; |
3980 | | } |
3981 | | #endif |
3982 | | |
3983 | | if (clone_tracks) { |
3984 | | for (i=0; i<gf_list_count(orig_file->moov->trackList); i++) { |
3985 | | GF_TrackBox *trak = (GF_TrackBox*)gf_list_get( orig_file->moov->trackList, i); |
3986 | | if (!trak) continue; |
3987 | | if (keep_hint_tracks || (trak->Media->handler->handlerType != GF_ISOM_MEDIA_HINT)) { |
3988 | | e = gf_isom_clone_track(orig_file, i+1, dest_file, 0, &dstTrack); |
3989 | | if (e) return e; |
3990 | | } |
3991 | | } |
3992 | | if (iods) |
3993 | | gf_isom_clone_box((GF_Box *)orig_file->moov->iods, (GF_Box **)dest_file->moov->iods); |
3994 | | } else { |
3995 | | dest_file->moov->mvhd->nextTrackID = 1; |
3996 | | gf_isom_clone_pl_indications(orig_file, dest_file); |
3997 | | } |
3998 | | dest_file->moov->mov = dest_file; |
3999 | | } |
4000 | | |
4001 | | if (!keep_pssh) { |
4002 | | i=0; |
4003 | | while ((box = (GF_Box*)gf_list_get(dest_file->moov->child_boxes, i++))) { |
4004 | | if (box->type == GF_ISOM_BOX_TYPE_PSSH) { |
4005 | | i--; |
4006 | | gf_isom_box_del_parent(&dest_file->moov->child_boxes, box); |
4007 | | } |
4008 | | } |
4009 | | } |
4010 | | |
4011 | | //duplicate other boxes |
4012 | | i=0; |
4013 | | while ((box = (GF_Box*)gf_list_get(orig_file->TopBoxes, i++))) { |
4014 | | switch(box->type) { |
4015 | | case GF_ISOM_BOX_TYPE_MOOV: |
4016 | | case GF_ISOM_BOX_TYPE_META: |
4017 | | case GF_ISOM_BOX_TYPE_MDAT: |
4018 | | case GF_ISOM_BOX_TYPE_FTYP: |
4019 | | case GF_ISOM_BOX_TYPE_PDIN: |
4020 | | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
4021 | | case GF_ISOM_BOX_TYPE_STYP: |
4022 | | case GF_ISOM_BOX_TYPE_SIDX: |
4023 | | case GF_ISOM_BOX_TYPE_SSIX: |
4024 | | case GF_ISOM_BOX_TYPE_MOOF: |
4025 | | #endif |
4026 | | case GF_ISOM_BOX_TYPE_JP: |
4027 | | break; |
4028 | | |
4029 | | case GF_ISOM_BOX_TYPE_PSSH: |
4030 | | if (!keep_pssh) |
4031 | | break; |
4032 | | |
4033 | | default: |
4034 | | { |
4035 | | GF_Box *box2 = NULL; |
4036 | | gf_isom_clone_box(box, &box2); |
4037 | | gf_list_add(dest_file->TopBoxes, box2); |
4038 | | } |
4039 | | break; |
4040 | | } |
4041 | | } |
4042 | | |
4043 | | return GF_OK; |
4044 | | } |
4045 | | #endif |
4046 | | |
4047 | | |
4048 | | GF_EXPORT |
4049 | | GF_Err gf_isom_get_raw_user_data(GF_ISOFile *file, u8 **output, u32 *output_size) |
4050 | 0 | { |
4051 | 0 | GF_BitStream *bs; |
4052 | 0 | GF_Err e; |
4053 | 0 | GF_Box *b; |
4054 | 0 | u32 i; |
4055 | |
|
4056 | 0 | *output = NULL; |
4057 | 0 | *output_size = 0; |
4058 | 0 | if (!file || !file->moov || (!file->moov->udta && !file->moov->child_boxes)) return GF_OK; |
4059 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4060 | |
|
4061 | 0 | if (file->moov->udta) { |
4062 | 0 | e = gf_isom_box_size( (GF_Box *) file->moov->udta); |
4063 | 0 | if (e) goto exit; |
4064 | 0 | e = gf_isom_box_write((GF_Box *) file->moov->udta, bs); |
4065 | 0 | if (e) goto exit; |
4066 | 0 | } |
4067 | 0 | e = GF_OK; |
4068 | 0 | i=0; |
4069 | 0 | while ((b = gf_list_enum(file->moov->child_boxes, &i))) { |
4070 | 0 | switch (b->type) { |
4071 | 0 | case GF_ISOM_BOX_TYPE_TRAK: |
4072 | 0 | case GF_ISOM_BOX_TYPE_MVHD: |
4073 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
4074 | 0 | case GF_ISOM_BOX_TYPE_MVEX: |
4075 | 0 | #endif |
4076 | 0 | case GF_ISOM_BOX_TYPE_IODS: |
4077 | 0 | case GF_ISOM_BOX_TYPE_META: |
4078 | 0 | continue; |
4079 | 0 | } |
4080 | 0 | e = gf_isom_box_size( (GF_Box *) b); |
4081 | 0 | if (e) goto exit; |
4082 | 0 | e = gf_isom_box_write((GF_Box *) b, bs); |
4083 | 0 | if (e) goto exit; |
4084 | 0 | } |
4085 | | |
4086 | 0 | gf_bs_get_content(bs, output, output_size); |
4087 | |
|
4088 | 0 | exit: |
4089 | 0 | gf_bs_del(bs); |
4090 | 0 | return e; |
4091 | 0 | } |
4092 | | |
4093 | | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
4094 | | static GF_Err gf_isom_get_trex_props(GF_ISOFile *file, GF_TrackBox *trak, GF_TrackExtendsBox **trex, GF_TrackExtensionPropertiesBox **trexprop) |
4095 | 0 | { |
4096 | 0 | u32 i; |
4097 | 0 | if (!file->moov->mvex) return GF_NOT_FOUND; |
4098 | 0 | *trex = NULL; |
4099 | 0 | for (i=0; i<gf_list_count(file->moov->mvex->TrackExList); i++) { |
4100 | 0 | *trex = gf_list_get(file->moov->mvex->TrackExList, i); |
4101 | 0 | if ((*trex)->trackID == trak->Header->trackID) break; |
4102 | 0 | *trex = NULL; |
4103 | 0 | } |
4104 | 0 | if (! *trex) return GF_NOT_FOUND; |
4105 | | |
4106 | 0 | for (i=0; i<gf_list_count(file->moov->mvex->TrackExPropList); i++) { |
4107 | 0 | *trexprop = gf_list_get(file->moov->mvex->TrackExPropList, i); |
4108 | 0 | if ((*trexprop)->trackID== trak->Header->trackID) break; |
4109 | 0 | *trexprop = NULL; |
4110 | 0 | } |
4111 | 0 | return GF_OK; |
4112 | 0 | } |
4113 | | #endif |
4114 | | |
4115 | | GF_EXPORT |
4116 | | GF_Err gf_isom_get_track_template(GF_ISOFile *file, u32 track, u8 **output, u32 *output_size) |
4117 | 0 | { |
4118 | 0 | GF_TrackBox *trak; |
4119 | 0 | GF_BitStream *bs; |
4120 | 0 | GF_DataReferenceBox *dref; |
4121 | 0 | GF_SampleTableBox *stbl, *stbl_temp; |
4122 | 0 | GF_SampleEncryptionBox *senc; |
4123 | 0 | GF_List *gpac_internals = NULL; |
4124 | 0 | u32 i, count; |
4125 | |
|
4126 | 0 | *output = NULL; |
4127 | 0 | *output_size = 0; |
4128 | | /*get orig sample desc and clone it*/ |
4129 | 0 | trak = gf_isom_get_track_box(file, track); |
4130 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
4131 | | |
4132 | | //don't serialize dref |
4133 | 0 | dref = NULL; |
4134 | 0 | if (trak->Media->information->dataInformation) { |
4135 | 0 | dref = trak->Media->information->dataInformation->dref; |
4136 | 0 | trak->Media->information->dataInformation->dref = NULL; |
4137 | 0 | gf_list_del_item(trak->Media->information->dataInformation->child_boxes, dref); |
4138 | 0 | } |
4139 | | |
4140 | | //don't serialize stbl but create a temp one |
4141 | 0 | stbl_temp = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL); |
4142 | 0 | if (!stbl_temp->child_boxes) stbl_temp->child_boxes = gf_list_new(); |
4143 | 0 | stbl = trak->Media->information->sampleTable; |
4144 | 0 | gf_list_del_item(trak->Media->information->child_boxes, stbl); |
4145 | |
|
4146 | 0 | trak->Media->information->sampleTable = stbl_temp; |
4147 | 0 | gf_list_add(trak->Media->information->child_boxes, stbl_temp); |
4148 | | |
4149 | | /*do not clone sampleDescription table but create an empty one*/ |
4150 | 0 | stbl_temp->SampleDescription = (GF_SampleDescriptionBox *) gf_isom_box_new_parent(&stbl_temp->child_boxes, GF_ISOM_BOX_TYPE_STSD); |
4151 | | |
4152 | | /*clone sampleGroups description tables if any*/ |
4153 | 0 | stbl_temp->sampleGroupsDescription = stbl->sampleGroupsDescription; |
4154 | 0 | count = gf_list_count(stbl->sampleGroupsDescription); |
4155 | 0 | for (i=0;i<count; i++) { |
4156 | 0 | GF_SampleGroupDescriptionBox *b = gf_list_get(stbl->sampleGroupsDescription, i); |
4157 | | //don't add our internal sample groups |
4158 | 0 | if (b->grouping_type==GF_4CC('E','M','S','G')) |
4159 | 0 | continue; |
4160 | 0 | if (b->grouping_type==GF_4CC('P','S','S','H')) |
4161 | 0 | continue; |
4162 | 0 | gf_list_add(stbl_temp->child_boxes, b); |
4163 | 0 | } |
4164 | | /*clone CompositionToDecode table, we may remove it later*/ |
4165 | 0 | stbl_temp->CompositionToDecode = stbl->CompositionToDecode; |
4166 | 0 | gf_list_add(stbl_temp->child_boxes, stbl->CompositionToDecode); |
4167 | | |
4168 | |
|
4169 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
4170 | | //get CSLG from trex if not in stbl |
4171 | 0 | GF_CompositionToDecodeBox *trex_cslg=NULL; |
4172 | 0 | if (!stbl_temp->CompositionToDecode) { |
4173 | 0 | GF_TrackExtendsBox *trex=NULL; |
4174 | 0 | GF_TrackExtensionPropertiesBox *trexprop=NULL; |
4175 | 0 | gf_isom_get_trex_props(file, trak, &trex, &trexprop); |
4176 | 0 | if (trexprop) { |
4177 | 0 | trex_cslg = (GF_CompositionToDecodeBox *) gf_isom_box_find_child(trexprop->child_boxes, GF_ISOM_BOX_TYPE_CSLG); |
4178 | 0 | if (trex_cslg) { |
4179 | 0 | stbl_temp->CompositionToDecode = trex_cslg; |
4180 | 0 | gf_list_add(stbl_temp->child_boxes, trex_cslg); |
4181 | 0 | } |
4182 | 0 | } |
4183 | 0 | } |
4184 | 0 | #endif |
4185 | |
|
4186 | 0 | count = gf_list_count(trak->child_boxes); |
4187 | 0 | for (i=0;i<count; i++) { |
4188 | 0 | GF_UnknownBox *b = gf_list_get(trak->child_boxes, i); |
4189 | 0 | if (b->type != GF_ISOM_BOX_TYPE_UNKNOWN) continue; |
4190 | 0 | if (b->original_4cc==GF_ISOM_BOX_TYPE_GDAT) { |
4191 | 0 | if (!gpac_internals) gpac_internals = gf_list_new(); |
4192 | 0 | gf_list_add(gpac_internals, b); |
4193 | 0 | gf_list_rem(trak->child_boxes, i); |
4194 | 0 | i--; |
4195 | 0 | count--; |
4196 | 0 | } |
4197 | 0 | } |
4198 | | |
4199 | | //don't serialize senc |
4200 | 0 | senc = trak->sample_encryption; |
4201 | 0 | if (senc) { |
4202 | 0 | gf_assert(trak->child_boxes); |
4203 | 0 | gf_list_del_item(trak->child_boxes, senc); |
4204 | 0 | trak->sample_encryption = NULL; |
4205 | 0 | } |
4206 | |
|
4207 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4208 | |
|
4209 | 0 | gf_isom_box_size( (GF_Box *) trak); |
4210 | 0 | gf_isom_box_write((GF_Box *) trak, bs); |
4211 | 0 | gf_bs_get_content(bs, output, output_size); |
4212 | 0 | gf_bs_del(bs); |
4213 | | |
4214 | | //restore our pointers |
4215 | 0 | if (dref) { |
4216 | 0 | trak->Media->information->dataInformation->dref = dref; |
4217 | 0 | gf_list_add(trak->Media->information->dataInformation->child_boxes, dref); |
4218 | 0 | } |
4219 | 0 | trak->Media->information->sampleTable = stbl; |
4220 | 0 | gf_list_add(trak->Media->information->child_boxes, stbl); |
4221 | 0 | gf_list_del_item(trak->Media->information->child_boxes, stbl_temp); |
4222 | 0 | if (senc) { |
4223 | 0 | trak->sample_encryption = senc; |
4224 | 0 | gf_list_add(trak->child_boxes, senc); |
4225 | 0 | } |
4226 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
4227 | 0 | if (trex_cslg) { |
4228 | 0 | stbl_temp->CompositionToDecode = NULL; |
4229 | 0 | gf_list_del_item(stbl_temp->child_boxes, trex_cslg); |
4230 | 0 | } |
4231 | 0 | #endif |
4232 | |
|
4233 | 0 | stbl_temp->sampleGroupsDescription = NULL; |
4234 | 0 | count = gf_list_count(stbl->sampleGroupsDescription); |
4235 | 0 | for (i=0;i<count; i++) { |
4236 | 0 | GF_Box *b = gf_list_get(stbl->sampleGroupsDescription, i); |
4237 | 0 | gf_list_del_item(stbl_temp->child_boxes, b); |
4238 | 0 | } |
4239 | |
|
4240 | 0 | stbl_temp->CompositionToDecode = NULL; |
4241 | 0 | gf_list_del_item(stbl_temp->child_boxes, stbl->CompositionToDecode); |
4242 | 0 | gf_isom_box_del((GF_Box *)stbl_temp); |
4243 | |
|
4244 | 0 | if (gpac_internals) { |
4245 | 0 | gf_list_transfer(trak->child_boxes, gpac_internals); |
4246 | 0 | gf_list_del(gpac_internals); |
4247 | 0 | } |
4248 | 0 | return GF_OK; |
4249 | |
|
4250 | 0 | } |
4251 | | |
4252 | | GF_EXPORT |
4253 | | GF_Err gf_isom_get_trex_template(GF_ISOFile *file, u32 track, u8 **output, u32 *output_size) |
4254 | 0 | { |
4255 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
4256 | 0 | GF_TrackBox *trak; |
4257 | 0 | GF_BitStream *bs; |
4258 | 0 | GF_TrackExtendsBox *trex = NULL; |
4259 | 0 | GF_TrackExtensionPropertiesBox *trexprop = NULL; |
4260 | |
|
4261 | 0 | *output = NULL; |
4262 | 0 | *output_size = 0; |
4263 | | /*get orig sample desc and clone it*/ |
4264 | 0 | trak = gf_isom_get_track_box(file, track); |
4265 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
4266 | 0 | if (!file->moov->mvex) return GF_NOT_FOUND; |
4267 | 0 | GF_Err e = gf_isom_get_trex_props(file, trak, &trex, &trexprop); |
4268 | 0 | if (e) return e; |
4269 | | |
4270 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4271 | 0 | gf_isom_box_size( (GF_Box *) trex); |
4272 | 0 | gf_isom_box_write((GF_Box *) trex, bs); |
4273 | |
|
4274 | 0 | if (trexprop) { |
4275 | 0 | gf_isom_box_size( (GF_Box *) trexprop); |
4276 | 0 | gf_isom_box_write((GF_Box *) trexprop, bs); |
4277 | 0 | } |
4278 | 0 | gf_bs_get_content(bs, output, output_size); |
4279 | 0 | gf_bs_del(bs); |
4280 | |
|
4281 | | #else |
4282 | | *output = NULL; |
4283 | | *output_size = 0; |
4284 | | #endif |
4285 | 0 | return GF_OK; |
4286 | 0 | } |
4287 | | |
4288 | | GF_EXPORT |
4289 | | GF_Err gf_isom_get_stsd_template(GF_ISOFile *file, u32 track, u32 stsd_idx, u8 **output, u32 *output_size) |
4290 | 0 | { |
4291 | 0 | GF_TrackBox *trak; |
4292 | 0 | GF_BitStream *bs; |
4293 | 0 | GF_Box *ent; |
4294 | |
|
4295 | 0 | *output = NULL; |
4296 | 0 | *output_size = 0; |
4297 | | /*get orig sample desc and clone it*/ |
4298 | 0 | trak = gf_isom_get_track_box(file, track); |
4299 | 0 | if (!trak || !trak->Media || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription) return GF_BAD_PARAM; |
4300 | | |
4301 | 0 | if (stsd_idx) { |
4302 | 0 | ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, stsd_idx-1); |
4303 | 0 | if (!ent) return GF_BAD_PARAM; |
4304 | 0 | } else { |
4305 | 0 | ent = (GF_Box*) trak->Media->information->sampleTable->SampleDescription; |
4306 | 0 | } |
4307 | | |
4308 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4309 | |
|
4310 | 0 | gf_isom_box_size(ent); |
4311 | 0 | gf_isom_box_write(ent, bs); |
4312 | 0 | gf_bs_get_content(bs, output, output_size); |
4313 | 0 | gf_bs_del(bs); |
4314 | 0 | return GF_OK; |
4315 | |
|
4316 | 0 | } |
4317 | | |
4318 | | |
4319 | | GF_EXPORT |
4320 | | GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *dest_file, GF_ISOTrackCloneFlags flags, u32 *dest_track) |
4321 | 0 | { |
4322 | 0 | GF_TrackBox *trak, *new_tk; |
4323 | 0 | GF_BitStream *bs; |
4324 | 0 | u8 *data; |
4325 | 0 | const u8 *buffer; |
4326 | 0 | u32 data_size; |
4327 | 0 | u32 i, count; |
4328 | 0 | GF_Err e; |
4329 | 0 | GF_SampleTableBox *stbl=NULL, *stbl_temp=NULL; |
4330 | 0 | GF_SampleEncryptionBox *senc=NULL; |
4331 | | |
4332 | |
|
4333 | 0 | e = gf_isom_can_access_movie(dest_file, GF_ISOM_OPEN_WRITE); |
4334 | 0 | if (e) return e; |
4335 | 0 | e = gf_isom_insert_moov(dest_file); |
4336 | 0 | if (e) return e; |
4337 | | |
4338 | | /*get orig sample desc and clone it*/ |
4339 | 0 | trak = gf_isom_get_track_box(orig_file, orig_track); |
4340 | 0 | if (!trak) return GF_BAD_PARAM; |
4341 | 0 | if (!trak->Media && !trak->extl) return GF_BAD_PARAM; |
4342 | | |
4343 | | //for non-external tracks only |
4344 | 0 | if (trak->Media) { |
4345 | 0 | stbl = trak->Media->information->sampleTable; |
4346 | 0 | stbl_temp = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL); |
4347 | 0 | if (!stbl_temp->child_boxes) stbl_temp->child_boxes = gf_list_new(); |
4348 | |
|
4349 | 0 | trak->Media->information->sampleTable = stbl_temp; |
4350 | 0 | gf_list_add(trak->Media->information->child_boxes, stbl_temp); |
4351 | 0 | gf_list_del_item(trak->Media->information->child_boxes, stbl); |
4352 | |
|
4353 | 0 | if (!stbl_temp->child_boxes) stbl_temp->child_boxes = gf_list_new(); |
4354 | | |
4355 | | /*clone sampleDescription table*/ |
4356 | 0 | stbl_temp->SampleDescription = stbl->SampleDescription; |
4357 | 0 | gf_list_add(stbl_temp->child_boxes, stbl->SampleDescription); |
4358 | | /*also clone sampleGroups description tables if any*/ |
4359 | 0 | stbl_temp->sampleGroupsDescription = stbl->sampleGroupsDescription; |
4360 | 0 | count = gf_list_count(stbl->sampleGroupsDescription); |
4361 | 0 | for (i=0; i<count; i++) { |
4362 | 0 | GF_Box *b = gf_list_get(stbl->sampleGroupsDescription, i); |
4363 | 0 | gf_list_add(stbl_temp->child_boxes, b); |
4364 | 0 | } |
4365 | | /*clone CompositionToDecode table, we may remove it later*/ |
4366 | 0 | stbl_temp->CompositionToDecode = stbl->CompositionToDecode; |
4367 | 0 | gf_list_add(stbl_temp->child_boxes, stbl->CompositionToDecode); |
4368 | |
|
4369 | 0 | senc = trak->sample_encryption; |
4370 | 0 | if (senc) { |
4371 | 0 | gf_assert(trak->child_boxes); |
4372 | 0 | gf_list_del_item(trak->child_boxes, senc); |
4373 | 0 | trak->sample_encryption = NULL; |
4374 | 0 | } |
4375 | 0 | } |
4376 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4377 | |
|
4378 | 0 | gf_isom_box_size( (GF_Box *) trak); |
4379 | 0 | gf_isom_box_write((GF_Box *) trak, bs); |
4380 | 0 | gf_bs_get_content(bs, &data, &data_size); |
4381 | 0 | gf_bs_del(bs); |
4382 | 0 | bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); |
4383 | 0 | if (flags & GF_ISOM_CLONE_TRACK_NO_QT) |
4384 | 0 | gf_bs_set_cookie(bs, GF_ISOM_BS_COOKIE_QT_CONV | GF_ISOM_BS_COOKIE_CLONE_TRACK); |
4385 | 0 | else |
4386 | 0 | gf_bs_set_cookie(bs, GF_ISOM_BS_COOKIE_CLONE_TRACK); |
4387 | |
|
4388 | 0 | e = gf_isom_box_parse((GF_Box **) &new_tk, bs); |
4389 | 0 | gf_bs_del(bs); |
4390 | 0 | gf_free(data); |
4391 | |
|
4392 | 0 | if (trak->Media) { |
4393 | 0 | trak->Media->information->sampleTable = stbl; |
4394 | 0 | gf_list_del_item(trak->Media->information->child_boxes, stbl_temp); |
4395 | 0 | gf_list_add(trak->Media->information->child_boxes, stbl); |
4396 | |
|
4397 | 0 | if (senc) { |
4398 | 0 | trak->sample_encryption = senc; |
4399 | 0 | gf_list_add(trak->child_boxes, senc); |
4400 | 0 | } |
4401 | 0 | gf_list_del_item(stbl_temp->child_boxes, stbl_temp->SampleDescription); |
4402 | 0 | stbl_temp->SampleDescription = NULL; |
4403 | |
|
4404 | 0 | count = gf_list_count(stbl->sampleGroupsDescription); |
4405 | 0 | for (i=0; i<count; i++) { |
4406 | 0 | GF_Box *b = gf_list_get(stbl->sampleGroupsDescription, i); |
4407 | 0 | gf_list_del_item(stbl_temp->child_boxes, b); |
4408 | 0 | } |
4409 | 0 | stbl_temp->sampleGroupsDescription = NULL; |
4410 | |
|
4411 | 0 | gf_list_del_item(stbl_temp->child_boxes, stbl_temp->CompositionToDecode); |
4412 | 0 | stbl_temp->CompositionToDecode = NULL; |
4413 | 0 | gf_isom_box_del((GF_Box *)stbl_temp); |
4414 | 0 | } |
4415 | |
|
4416 | 0 | if (e) { |
4417 | 0 | if (new_tk) gf_isom_box_del((GF_Box *)new_tk); |
4418 | 0 | return e; |
4419 | 0 | } |
4420 | | |
4421 | 0 | gf_isom_disable_inplace_rewrite(dest_file); |
4422 | |
|
4423 | 0 | if (trak->Media) { |
4424 | | /*create default boxes*/ |
4425 | 0 | stbl = new_tk->Media->information->sampleTable; |
4426 | 0 | stbl->ChunkOffset = gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STCO); |
4427 | 0 | if (!stbl->ChunkOffset) return GF_OUT_OF_MEM; |
4428 | 0 | stbl->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSZ); |
4429 | 0 | if (!stbl->SampleSize) return GF_OUT_OF_MEM; |
4430 | 0 | stbl->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSC); |
4431 | 0 | if (!stbl->SampleToChunk) return GF_OUT_OF_MEM; |
4432 | 0 | stbl->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STTS); |
4433 | 0 | if (!stbl->TimeToSample) return GF_OUT_OF_MEM; |
4434 | 0 | } |
4435 | | |
4436 | 0 | if (flags & GF_ISOM_CLONE_TRACK_DROP_ID) |
4437 | 0 | new_tk->Header->trackID = 0; |
4438 | | |
4439 | | /*check trackID validity before adding track*/ |
4440 | 0 | if (!new_tk->Header->trackID || gf_isom_get_track_by_id(dest_file, new_tk->Header->trackID)) { |
4441 | 0 | u32 ID = 1; |
4442 | 0 | while (1) { |
4443 | 0 | if (RequestTrack(dest_file->moov, ID)) break; |
4444 | 0 | ID += 1; |
4445 | 0 | if (ID == 0xFFFFFFFF) break; |
4446 | 0 | } |
4447 | 0 | new_tk->Header->trackID = ID; |
4448 | 0 | } |
4449 | 0 | if (!dest_file->moov->child_boxes) dest_file->moov->child_boxes = gf_list_new(); |
4450 | 0 | gf_list_add(dest_file->moov->child_boxes, new_tk); |
4451 | 0 | moov_on_child_box((GF_Box*)dest_file->moov, (GF_Box *)new_tk, GF_FALSE); |
4452 | | |
4453 | | /*set originalID*/ |
4454 | 0 | new_tk->originalID = trak->Header->trackID; |
4455 | | /*set originalFile*/ |
4456 | 0 | buffer = gf_isom_get_filename(orig_file); |
4457 | 0 | new_tk->originalFile = gf_crc_32(buffer, (u32) strlen(buffer)); |
4458 | | |
4459 | | /*rewrite edit list segmentDuration to new movie timescale*/ |
4460 | 0 | if (dest_file->moov->mvhd->timeScale != orig_file->moov->mvhd->timeScale) { |
4461 | 0 | Double ts_scale = dest_file->moov->mvhd->timeScale; |
4462 | 0 | ts_scale /= orig_file->moov->mvhd->timeScale; |
4463 | 0 | new_tk->Header->duration = (u64) (new_tk->Header->duration * ts_scale); |
4464 | 0 | if (new_tk->editBox && new_tk->editBox->editList) { |
4465 | 0 | count = gf_list_count(new_tk->editBox->editList->entryList); |
4466 | 0 | for (i=0; i<count; i++) { |
4467 | 0 | GF_EdtsEntry *ent = (GF_EdtsEntry *)gf_list_get(new_tk->editBox->editList->entryList, i); |
4468 | 0 | ent->segmentDuration = (u64) (ent->segmentDuration * ts_scale); |
4469 | 0 | } |
4470 | 0 | } |
4471 | 0 | } |
4472 | |
|
4473 | 0 | if (trak->Media) { |
4474 | 0 | if (flags & GF_ISOM_CLONE_RESET_DURATION) |
4475 | 0 | new_tk->Media->mediaHeader->duration = 0; |
4476 | |
|
4477 | 0 | if (!new_tk->Media->information->dataInformation->dref) return GF_BAD_PARAM; |
4478 | | |
4479 | | /*reset data ref*/ |
4480 | 0 | if (! (flags & GF_ISOM_CLONE_TRACK_KEEP_DREF) ) { |
4481 | 0 | GF_SampleEntryBox *entry; |
4482 | 0 | Bool use_alis = GF_FALSE; |
4483 | 0 | if (! (flags & GF_ISOM_CLONE_TRACK_NO_QT)) { |
4484 | 0 | GF_Box *b = gf_list_get(new_tk->Media->information->dataInformation->dref->child_boxes, 0); |
4485 | 0 | if (b && (b->type==GF_QT_BOX_TYPE_ALIS)) |
4486 | 0 | use_alis = GF_TRUE; |
4487 | 0 | } |
4488 | 0 | gf_isom_box_array_del(new_tk->Media->information->dataInformation->dref->child_boxes); |
4489 | 0 | new_tk->Media->information->dataInformation->dref->child_boxes = gf_list_new(); |
4490 | | /*update data ref*/ |
4491 | 0 | entry = (GF_SampleEntryBox*)gf_list_get(new_tk->Media->information->sampleTable->SampleDescription->child_boxes, 0); |
4492 | 0 | if (entry) { |
4493 | 0 | u32 dref; |
4494 | 0 | Media_CreateDataRef(dest_file, new_tk->Media->information->dataInformation->dref, use_alis ? "alis" : NULL, NULL, &dref); |
4495 | 0 | entry->dataReferenceIndex = dref; |
4496 | 0 | } |
4497 | 0 | } else { |
4498 | 0 | for (i=0; i<gf_list_count(new_tk->Media->information->dataInformation->dref->child_boxes); i++) { |
4499 | 0 | GF_DataEntryBox *dref_entry = (GF_DataEntryBox *)gf_list_get(new_tk->Media->information->dataInformation->dref->child_boxes, i); |
4500 | 0 | if (dref_entry->flags & 1) { |
4501 | 0 | dref_entry->flags &= ~1; |
4502 | 0 | e = Media_SetDrefURL((GF_DataEntryURLBox *)dref_entry, orig_file->fileName, dest_file->finalName); |
4503 | 0 | if (e) return e; |
4504 | 0 | } |
4505 | 0 | } |
4506 | 0 | } |
4507 | 0 | } |
4508 | | |
4509 | | //purge all 'gpac' boxes at track level |
4510 | 0 | for (i=0; i<gf_list_count(new_tk->child_boxes); i++) { |
4511 | 0 | GF_UnknownBox *b = (GF_UnknownBox *)gf_list_get(new_tk->child_boxes, i); |
4512 | 0 | if (b->type != GF_ISOM_BOX_TYPE_UNKNOWN) continue; |
4513 | 0 | if (b->original_4cc==GF_ISOM_BOX_TYPE_GDAT) { |
4514 | 0 | gf_list_rem(new_tk->child_boxes, i); |
4515 | 0 | i--; |
4516 | 0 | gf_isom_box_del((GF_Box*)b); |
4517 | 0 | } |
4518 | 0 | } |
4519 | |
|
4520 | 0 | *dest_track = gf_list_count(dest_file->moov->trackList); |
4521 | |
|
4522 | 0 | if (dest_file->moov->mvhd->nextTrackID <= new_tk->Header->trackID) |
4523 | 0 | dest_file->moov->mvhd->nextTrackID = new_tk->Header->trackID+1; |
4524 | | |
4525 | | /*if it contains IAMF, add the iamf brand to ftyp*/ |
4526 | 0 | if (gf_isom_get_media_subtype(dest_file, new_tk->Header->trackID, 1) == GF_ISOM_SUBTYPE_IAMF) { |
4527 | 0 | gf_isom_modify_alternate_brand(dest_file, GF_ISOM_BRAND_IAMF, GF_TRUE); |
4528 | |
|
4529 | 0 | } |
4530 | |
|
4531 | 0 | return GF_OK; |
4532 | 0 | } |
4533 | | |
4534 | | #if 0 |
4535 | | /*clones all sampleDescription entries in new track, after an optional reset of existing entries*/ |
4536 | | GF_Err gf_isom_clone_sample_descriptions(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, Bool reset_existing) |
4537 | | { |
4538 | | u32 i; |
4539 | | GF_TrackBox *dst_trak, *src_trak; |
4540 | | GF_Err e = gf_isom_can_access_movie(the_file, GF_ISOM_OPEN_WRITE); |
4541 | | if (e) return e; |
4542 | | |
4543 | | dst_trak = gf_isom_get_track_box(the_file, trackNumber); |
4544 | | if (!dst_trak || !dst_trak->Media) return GF_BAD_PARAM; |
4545 | | src_trak = gf_isom_get_track_box(orig_file, orig_track); |
4546 | | if (!src_trak || !src_trak->Media) return GF_BAD_PARAM; |
4547 | | |
4548 | | if (reset_existing) { |
4549 | | gf_isom_box_array_del(dst_trak->Media->information->sampleTable->SampleDescription->child_boxes); |
4550 | | dst_trak->Media->information->sampleTable->SampleDescription->child_boxes = gf_list_new(); |
4551 | | } |
4552 | | |
4553 | | for (i=0; i<gf_list_count(src_trak->Media->information->sampleTable->SampleDescription->child_boxes); i++) { |
4554 | | u32 outDesc; |
4555 | | e = gf_isom_clone_sample_description(the_file, trackNumber, orig_file, orig_track, i+1, NULL, NULL, &outDesc); |
4556 | | if (e) break; |
4557 | | } |
4558 | | return e; |
4559 | | } |
4560 | | #endif |
4561 | | |
4562 | | |
4563 | | GF_EXPORT |
4564 | | GF_Err gf_isom_clone_sample_description(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, u32 orig_desc_index, const char *URLname, const char *URNname, u32 *outDescriptionIndex) |
4565 | 0 | { |
4566 | 0 | GF_TrackBox *trak; |
4567 | 0 | GF_BitStream *bs; |
4568 | 0 | u8 *data; |
4569 | 0 | u32 data_size; |
4570 | 0 | GF_Box *entry; |
4571 | 0 | GF_Err e; |
4572 | 0 | u32 dataRefIndex; |
4573 | 0 | u32 mtype; |
4574 | 0 | u32 internal_type; |
4575 | |
|
4576 | 0 | e = gf_isom_can_access_movie(the_file, GF_ISOM_OPEN_WRITE); |
4577 | 0 | if (e) return e; |
4578 | | |
4579 | | /*get orig sample desc and clone it*/ |
4580 | 0 | trak = gf_isom_get_track_box(orig_file, orig_track); |
4581 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
4582 | | |
4583 | 0 | entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, orig_desc_index-1); |
4584 | 0 | if (!entry) return GF_BAD_PARAM; |
4585 | 0 | internal_type = ((GF_SampleEntryBox *)entry)->internal_type; |
4586 | | |
4587 | | |
4588 | | // for generic entries, force the entrytype to unknown to avoid allocating potentially disallowed box types |
4589 | 0 | u32 generic_entry_type = 0; |
4590 | 0 | if (entry->type == GF_ISOM_BOX_TYPE_GNRV) { |
4591 | 0 | generic_entry_type = ((GF_GenericVisualSampleEntryBox *) entry)->EntryType; |
4592 | 0 | ((GF_GenericVisualSampleEntryBox *) entry)->EntryType = GF_ISOM_BOX_TYPE_UNKNOWN; |
4593 | 0 | } else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) { |
4594 | 0 | generic_entry_type = ((GF_GenericAudioSampleEntryBox *) entry)->EntryType; |
4595 | 0 | ((GF_GenericAudioSampleEntryBox *) entry)->EntryType = GF_ISOM_BOX_TYPE_UNKNOWN; |
4596 | 0 | } else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) { |
4597 | 0 | generic_entry_type = ((GF_GenericSampleEntryBox *) entry)->EntryType; |
4598 | 0 | ((GF_GenericSampleEntryBox *) entry)->EntryType = GF_ISOM_BOX_TYPE_UNKNOWN; |
4599 | 0 | } |
4600 | |
|
4601 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4602 | |
|
4603 | 0 | gf_isom_box_size(entry); |
4604 | 0 | gf_isom_box_write(entry, bs); |
4605 | 0 | gf_bs_get_content(bs, &data, &data_size); |
4606 | 0 | gf_bs_del(bs); |
4607 | 0 | bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); |
4608 | | |
4609 | | // restore the original entrytype before losing the entry pointer |
4610 | 0 | if (entry->type == GF_ISOM_BOX_TYPE_GNRV) { |
4611 | 0 | ((GF_GenericVisualSampleEntryBox *) entry)->EntryType = generic_entry_type; |
4612 | 0 | } else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) { |
4613 | 0 | ((GF_GenericAudioSampleEntryBox *) entry)->EntryType = generic_entry_type; |
4614 | 0 | } else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) { |
4615 | 0 | ((GF_GenericSampleEntryBox *) entry)->EntryType = generic_entry_type; |
4616 | 0 | } |
4617 | | |
4618 | |
|
4619 | 0 | e = gf_isom_box_parse(&entry, bs); |
4620 | 0 | gf_bs_del(bs); |
4621 | 0 | gf_free(data); |
4622 | 0 | if (e) return e; |
4623 | | |
4624 | 0 | if (entry->type==GF_ISOM_BOX_TYPE_UNKNOWN) { |
4625 | 0 | GF_UnknownBox *ubox = (GF_UnknownBox*)entry; |
4626 | 0 | if (internal_type == GF_ISOM_SAMPLE_ENTRY_VIDEO) { |
4627 | 0 | GF_GenericVisualSampleEntryBox *ve = (GF_GenericVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRV); |
4628 | 0 | ve->EntryType = generic_entry_type ? generic_entry_type : ubox->original_4cc; |
4629 | 0 | ve->data = ubox->data; |
4630 | 0 | ve->data_size = ubox->dataSize; |
4631 | 0 | entry = (GF_Box *) ve; |
4632 | 0 | } |
4633 | 0 | else if (internal_type == GF_ISOM_SAMPLE_ENTRY_AUDIO) { |
4634 | 0 | GF_GenericAudioSampleEntryBox *ae = (GF_GenericAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRA); |
4635 | 0 | ae->EntryType = generic_entry_type ? generic_entry_type : ubox->original_4cc; |
4636 | 0 | ae->data = ubox->data; |
4637 | 0 | ae->data_size = ubox->dataSize; |
4638 | 0 | entry = (GF_Box *) ae; |
4639 | 0 | } |
4640 | 0 | else { |
4641 | 0 | GF_GenericSampleEntryBox *ge = (GF_GenericSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRM); |
4642 | 0 | ge->EntryType = generic_entry_type ? generic_entry_type : ubox->original_4cc; |
4643 | 0 | ge->data = ubox->data; |
4644 | 0 | ge->data_size = ubox->dataSize; |
4645 | 0 | entry = (GF_Box *) ge; |
4646 | 0 | } |
4647 | 0 | ubox->data = NULL; |
4648 | 0 | ubox->dataSize = 0; |
4649 | 0 | gf_isom_box_del((GF_Box *)ubox); |
4650 | 0 | } |
4651 | | |
4652 | | /*get new track and insert clone*/ |
4653 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
4654 | 0 | if (!trak || !trak->Media) goto exit; |
4655 | | |
4656 | | /*get or create the data ref*/ |
4657 | 0 | e = Media_FindDataRef(trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex); |
4658 | 0 | if (e) goto exit; |
4659 | 0 | if (!dataRefIndex) { |
4660 | 0 | e = Media_CreateDataRef(the_file, trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex); |
4661 | 0 | if (e) goto exit; |
4662 | 0 | } |
4663 | 0 | if (!the_file->keep_utc) |
4664 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
4665 | | /*overwrite dref*/ |
4666 | 0 | ((GF_SampleEntryBox *)entry)->dataReferenceIndex = dataRefIndex; |
4667 | 0 | e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, entry); |
4668 | 0 | *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes); |
4669 | | |
4670 | | /*also clone track w/h info*/ |
4671 | 0 | mtype = gf_isom_get_media_type(the_file, trackNumber); |
4672 | 0 | if (gf_isom_is_video_handler_type(mtype) ) { |
4673 | 0 | gf_isom_set_visual_info(the_file, trackNumber, (*outDescriptionIndex), ((GF_VisualSampleEntryBox*)entry)->Width, ((GF_VisualSampleEntryBox*)entry)->Height); |
4674 | 0 | } |
4675 | 0 | return e; |
4676 | | |
4677 | 0 | exit: |
4678 | 0 | gf_isom_box_del(entry); |
4679 | 0 | return e; |
4680 | 0 | } |
4681 | | |
4682 | | GF_EXPORT |
4683 | | GF_Err gf_isom_new_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, const char *URLname, const char *URNname, GF_GenericSampleDescription *udesc, u32 *outDescriptionIndex) |
4684 | 0 | { |
4685 | 0 | GF_TrackBox *trak; |
4686 | 0 | GF_Err e; |
4687 | 0 | u8 **wrap_data; |
4688 | 0 | u32 *wrap_size; |
4689 | 0 | u32 dataRefIndex; |
4690 | |
|
4691 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
4692 | 0 | if (e) return e; |
4693 | | |
4694 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
4695 | 0 | if (!trak || !trak->Media || !udesc) return GF_BAD_PARAM; |
4696 | | |
4697 | | //get or create the data ref |
4698 | 0 | e = Media_FindDataRef(trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex); |
4699 | 0 | if (e) return e; |
4700 | 0 | if (!dataRefIndex) { |
4701 | 0 | e = Media_CreateDataRef(movie, trak->Media->information->dataInformation->dref, (char *)URLname, (char *)URNname, &dataRefIndex); |
4702 | 0 | if (e) return e; |
4703 | 0 | } |
4704 | 0 | if (!movie->keep_utc) |
4705 | 0 | trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); |
4706 | |
|
4707 | 0 | if (gf_isom_is_video_handler_type(trak->Media->handler->handlerType)) { |
4708 | 0 | GF_GenericVisualSampleEntryBox *entry; |
4709 | | //create a new entry |
4710 | 0 | entry = (GF_GenericVisualSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRV); |
4711 | 0 | if (!entry) return GF_OUT_OF_MEM; |
4712 | | |
4713 | 0 | if (!udesc->codec_tag) { |
4714 | 0 | entry->EntryType = GF_ISOM_BOX_TYPE_UUID; |
4715 | 0 | memcpy(entry->uuid, udesc->UUID, sizeof(bin128)); |
4716 | 0 | } else { |
4717 | 0 | entry->EntryType = udesc->codec_tag; |
4718 | 0 | } |
4719 | 0 | if (entry->EntryType == 0) { |
4720 | 0 | gf_isom_box_del((GF_Box *)entry); |
4721 | 0 | return GF_NOT_SUPPORTED; |
4722 | 0 | } |
4723 | | |
4724 | 0 | entry->dataReferenceIndex = dataRefIndex; |
4725 | 0 | entry->vendor = udesc->vendor_code; |
4726 | 0 | entry->version = udesc->version; |
4727 | 0 | entry->revision = udesc->revision; |
4728 | 0 | entry->temporal_quality = udesc->temporal_quality; |
4729 | 0 | entry->spatial_quality = udesc->spatial_quality; |
4730 | 0 | entry->Width = udesc->width; |
4731 | 0 | entry->Height = udesc->height; |
4732 | 0 | strncpy(entry->compressor_name, udesc->compressor_name, GF_ARRAY_LENGTH(entry->compressor_name)); |
4733 | 0 | entry->compressor_name[ GF_ARRAY_LENGTH(entry->compressor_name) - 1] = 0; |
4734 | 0 | entry->color_table_index = -1; |
4735 | 0 | entry->frames_per_sample = 1; |
4736 | 0 | entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000; |
4737 | 0 | entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000; |
4738 | 0 | entry->bit_depth = udesc->depth ? udesc->depth : 0x18; |
4739 | 0 | wrap_data = &entry->data; |
4740 | 0 | wrap_size = &entry->data_size; |
4741 | 0 | e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, entry); |
4742 | 0 | } |
4743 | 0 | else if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_AUDIO) { |
4744 | 0 | GF_GenericAudioSampleEntryBox *gena; |
4745 | | //create a new entry |
4746 | 0 | gena = (GF_GenericAudioSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRA); |
4747 | 0 | if (!gena) return GF_OUT_OF_MEM; |
4748 | | |
4749 | 0 | if (!udesc->codec_tag) { |
4750 | 0 | gena->EntryType = GF_ISOM_BOX_TYPE_UUID; |
4751 | 0 | memcpy(gena->uuid, udesc->UUID, sizeof(bin128)); |
4752 | 0 | } else { |
4753 | 0 | gena->EntryType = udesc->codec_tag; |
4754 | 0 | } |
4755 | 0 | if (gena->EntryType == 0) { |
4756 | 0 | gf_isom_box_del((GF_Box *)gena); |
4757 | 0 | return GF_NOT_SUPPORTED; |
4758 | 0 | } |
4759 | | |
4760 | 0 | gena->dataReferenceIndex = dataRefIndex; |
4761 | 0 | gena->vendor = udesc->vendor_code; |
4762 | 0 | gena->version = udesc->version; |
4763 | 0 | gena->revision = udesc->revision; |
4764 | 0 | gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16; |
4765 | 0 | gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2; |
4766 | 0 | gena->samplerate_hi = udesc->samplerate; |
4767 | 0 | gena->samplerate_lo = 0; |
4768 | 0 | gena->qtff_mode = udesc->is_qtff ? GF_ISOM_AUDIO_QTFF_ON_NOEXT : GF_ISOM_AUDIO_QTFF_NONE; |
4769 | 0 | if (gena->EntryType==GF_QT_SUBTYPE_LPCM) { |
4770 | 0 | gena->version = 2; |
4771 | 0 | gena->qtff_mode = GF_ISOM_AUDIO_QTFF_ON_EXT_VALID; |
4772 | 0 | GF_BitStream *bs = gf_bs_new(gena->extensions, 36, GF_BITSTREAM_WRITE); |
4773 | 0 | gf_bs_write_u32(bs, 72); |
4774 | 0 | gf_bs_write_double(bs, udesc->samplerate); |
4775 | 0 | gf_bs_write_u32(bs, udesc->nb_channels); |
4776 | 0 | gf_bs_write_u32(bs, 0x7F000000); |
4777 | 0 | gf_bs_write_u32(bs, gena->bitspersample); |
4778 | 0 | gf_bs_write_u32(bs, udesc->lpcm_flags); |
4779 | 0 | gf_bs_write_u32(bs, udesc->nb_channels*gena->bitspersample/8); //constBytesPerAudioPacket |
4780 | 0 | gf_bs_write_u32(bs, 1); //constLPCMFramesPerAudioPacket |
4781 | 0 | gf_bs_del(bs); |
4782 | 0 | gena->revision = 0; |
4783 | 0 | gena->vendor = 0; |
4784 | 0 | gena->channel_count = 3; |
4785 | 0 | gena->bitspersample = 16; |
4786 | 0 | gena->compression_id = 0xFFFE; |
4787 | 0 | gena->packet_size = 0; |
4788 | 0 | gena->samplerate_hi = 1; |
4789 | 0 | } else if (udesc->is_qtff) { |
4790 | 0 | GF_Box *b = gf_isom_box_new_parent(&gena->child_boxes, GF_QT_BOX_TYPE_WAVE); |
4791 | 0 | GF_ChromaInfoBox *enda = (GF_ChromaInfoBox*) gf_isom_box_new_parent(&b->child_boxes, GF_QT_BOX_TYPE_ENDA); |
4792 | 0 | ((GF_ChromaInfoBox *)enda)->chroma = (udesc->lpcm_flags & (1<<1)) ? 0 : 1; |
4793 | |
|
4794 | 0 | GF_UnknownBox *term = (GF_UnknownBox*) gf_isom_box_new_parent(&b->child_boxes, GF_ISOM_BOX_TYPE_UNKNOWN); |
4795 | 0 | if (term) term->original_4cc = 0; |
4796 | 0 | } |
4797 | |
|
4798 | 0 | wrap_data = &gena->data; |
4799 | 0 | wrap_size = &gena->data_size; |
4800 | 0 | e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, gena); |
4801 | 0 | } |
4802 | 0 | else { |
4803 | 0 | GF_GenericSampleEntryBox *genm; |
4804 | | //create a new entry |
4805 | 0 | genm = (GF_GenericSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRM); |
4806 | 0 | if (!genm) return GF_OUT_OF_MEM; |
4807 | | |
4808 | 0 | if (!udesc->codec_tag) { |
4809 | 0 | genm->EntryType = GF_ISOM_BOX_TYPE_UUID; |
4810 | 0 | memcpy(genm->uuid, udesc->UUID, sizeof(bin128)); |
4811 | 0 | } else { |
4812 | 0 | genm->EntryType = udesc->codec_tag; |
4813 | 0 | } |
4814 | 0 | if (genm->EntryType == 0) { |
4815 | 0 | gf_isom_box_del((GF_Box *)genm); |
4816 | 0 | return GF_NOT_SUPPORTED; |
4817 | 0 | } |
4818 | 0 | genm->dataReferenceIndex = dataRefIndex; |
4819 | 0 | wrap_data = &genm->data; |
4820 | 0 | wrap_size = &genm->data_size; |
4821 | 0 | e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, genm); |
4822 | 0 | } |
4823 | 0 | *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes); |
4824 | |
|
4825 | 0 | if (udesc->extension_buf && udesc->extension_buf_size) { |
4826 | 0 | GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
4827 | 0 | if (udesc->ext_box_wrap) { |
4828 | 0 | gf_bs_write_u32(bs, 8+udesc->extension_buf_size); |
4829 | 0 | gf_bs_write_u32(bs, udesc->ext_box_wrap); |
4830 | 0 | } |
4831 | 0 | gf_bs_write_data(bs, udesc->extension_buf, udesc->extension_buf_size); |
4832 | 0 | gf_bs_get_content(bs, wrap_data, wrap_size); |
4833 | 0 | gf_bs_del(bs); |
4834 | 0 | } |
4835 | 0 | return e; |
4836 | 0 | } |
4837 | | |
4838 | | //use carefully. Very useful when you made a lot of changes (IPMP, IPI, OCI, ...) |
4839 | | //THIS WILL REPLACE THE WHOLE DESCRIPTOR ... |
4840 | | #if 0 //unused |
4841 | | /*change the data field of an unknown sample description*/ |
4842 | | GF_Err gf_isom_change_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_GenericSampleDescription *udesc) |
4843 | | { |
4844 | | GF_TrackBox *trak; |
4845 | | GF_Err e; |
4846 | | GF_GenericVisualSampleEntryBox *entry; |
4847 | | |
4848 | | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
4849 | | if (e) return e; |
4850 | | |
4851 | | trak = gf_isom_get_track_box(movie, trackNumber); |
4852 | | if (!trak || !trak->Media || !StreamDescriptionIndex) return GF_BAD_PARAM; |
4853 | | |
4854 | | entry = (GF_GenericVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex-1); |
4855 | | if (!entry) return GF_BAD_PARAM; |
4856 | | if (entry->type == GF_ISOM_BOX_TYPE_GNRV) { |
4857 | | entry->vendor = udesc->vendor_code; |
4858 | | entry->version = udesc->version; |
4859 | | entry->revision = udesc->revision; |
4860 | | entry->temporal_quality = udesc->temporal_quality; |
4861 | | entry->spatial_quality = udesc->spatial_quality; |
4862 | | entry->Width = udesc->width; |
4863 | | entry->Height = udesc->height; |
4864 | | strcpy(entry->compressor_name, udesc->compressor_name); |
4865 | | entry->color_table_index = -1; |
4866 | | entry->frames_per_sample = 1; |
4867 | | entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000; |
4868 | | entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000; |
4869 | | entry->bit_depth = udesc->depth ? udesc->depth : 0x18; |
4870 | | if (entry->data) gf_free(entry->data); |
4871 | | entry->data = NULL; |
4872 | | entry->data_size = 0; |
4873 | | if (udesc->extension_buf && udesc->extension_buf_size) { |
4874 | | entry->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size); |
4875 | | if (!entry->data) { |
4876 | | gf_isom_box_del((GF_Box *) entry); |
4877 | | return GF_OUT_OF_MEM; |
4878 | | } |
4879 | | memcpy(entry->data, udesc->extension_buf, udesc->extension_buf_size); |
4880 | | entry->data_size = udesc->extension_buf_size; |
4881 | | } |
4882 | | return GF_OK; |
4883 | | } else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) { |
4884 | | GF_GenericAudioSampleEntryBox *gena = (GF_GenericAudioSampleEntryBox *)entry; |
4885 | | gena->vendor = udesc->vendor_code; |
4886 | | gena->version = udesc->version; |
4887 | | gena->revision = udesc->revision; |
4888 | | gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16; |
4889 | | gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2; |
4890 | | gena->samplerate_hi = udesc->samplerate; |
4891 | | gena->samplerate_lo = 0; |
4892 | | if (gena->data) gf_free(gena->data); |
4893 | | gena->data = NULL; |
4894 | | gena->data_size = 0; |
4895 | | |
4896 | | if (udesc->extension_buf && udesc->extension_buf_size) { |
4897 | | gena->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size); |
4898 | | if (!gena->data) { |
4899 | | gf_isom_box_del((GF_Box *) gena); |
4900 | | return GF_OUT_OF_MEM; |
4901 | | } |
4902 | | memcpy(gena->data, udesc->extension_buf, udesc->extension_buf_size); |
4903 | | gena->data_size = udesc->extension_buf_size; |
4904 | | } |
4905 | | return GF_OK; |
4906 | | } else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) { |
4907 | | GF_GenericSampleEntryBox *genm = (GF_GenericSampleEntryBox *)entry; |
4908 | | if (genm->data) gf_free(genm->data); |
4909 | | genm->data = NULL; |
4910 | | genm->data_size = 0; |
4911 | | |
4912 | | if (udesc->extension_buf && udesc->extension_buf_size) { |
4913 | | genm->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size); |
4914 | | if (!genm->data) { |
4915 | | gf_isom_box_del((GF_Box *) genm); |
4916 | | return GF_OUT_OF_MEM; |
4917 | | } |
4918 | | memcpy(genm->data, udesc->extension_buf, udesc->extension_buf_size); |
4919 | | genm->data_size = udesc->extension_buf_size; |
4920 | | } |
4921 | | return GF_OK; |
4922 | | } |
4923 | | return GF_BAD_PARAM; |
4924 | | } |
4925 | | #endif |
4926 | | |
4927 | | #if 0 |
4928 | | /*removes given stream description*/ |
4929 | | GF_Err gf_isom_remove_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 streamDescIndex) |
4930 | | { |
4931 | | GF_TrackBox *trak; |
4932 | | GF_Err e; |
4933 | | GF_Box *entry; |
4934 | | |
4935 | | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
4936 | | if (e) return e; |
4937 | | trak = gf_isom_get_track_box(movie, trackNumber); |
4938 | | if (!trak || !trak->Media || !streamDescIndex) return GF_BAD_PARAM; |
4939 | | entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, streamDescIndex-1); |
4940 | | if (!entry) return GF_BAD_PARAM; |
4941 | | gf_list_rem(trak->Media->information->sampleTable->SampleDescription->child_boxes, streamDescIndex-1); |
4942 | | gf_isom_box_del(entry); |
4943 | | return GF_OK; |
4944 | | } |
4945 | | #endif |
4946 | | |
4947 | | //sets a track reference |
4948 | | GF_EXPORT |
4949 | | GF_Err gf_isom_set_track_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, GF_ISOTrackID ReferencedTrackID) |
4950 | 0 | { |
4951 | 0 | GF_Err e; |
4952 | 0 | GF_TrackBox *trak; |
4953 | 0 | GF_TrackReferenceBox *tref; |
4954 | 0 | GF_TrackReferenceTypeBox *dpnd; |
4955 | |
|
4956 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
4957 | 0 | if (!trak) return GF_BAD_PARAM; |
4958 | | |
4959 | | //no tref, create one |
4960 | 0 | tref = trak->References; |
4961 | 0 | if (!tref) { |
4962 | 0 | tref = (GF_TrackReferenceBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TREF); |
4963 | 0 | if (!tref) return GF_OUT_OF_MEM; |
4964 | 0 | e = trak_on_child_box((GF_Box*)trak, (GF_Box *) tref, GF_FALSE); |
4965 | 0 | if (e) return e; |
4966 | 0 | } |
4967 | | //find a ref of the given type |
4968 | 0 | e = Track_FindRef(trak, referenceType, &dpnd); |
4969 | 0 | if (e) return e; |
4970 | | |
4971 | 0 | if (!dpnd) { |
4972 | 0 | dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT); |
4973 | 0 | if (!dpnd) return GF_OUT_OF_MEM; |
4974 | 0 | dpnd->reference_type = referenceType; |
4975 | 0 | } |
4976 | | //add the ref |
4977 | 0 | return reftype_AddRefTrack(dpnd, ReferencedTrackID, NULL); |
4978 | 0 | } |
4979 | | |
4980 | | GF_EXPORT |
4981 | | GF_Err gf_isom_purge_track_reference(GF_ISOFile *the_file, u32 trackNumber) |
4982 | 0 | { |
4983 | 0 | GF_TrackBox *trak; |
4984 | 0 | GF_TrackReferenceTypeBox *ref; |
4985 | 0 | u32 i=0; |
4986 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
4987 | 0 | if (!trak) return GF_BAD_PARAM; |
4988 | | |
4989 | | //no tref, nothing to remove |
4990 | 0 | if (!trak->References) return GF_OK; |
4991 | | |
4992 | 0 | while ((ref = gf_list_enum(trak->References->child_boxes, &i))) { |
4993 | 0 | u32 k; |
4994 | 0 | if (!ref->reference_type) continue; |
4995 | | |
4996 | 0 | for (k=0; k<ref->trackIDCount; k++) { |
4997 | 0 | u32 tk = gf_isom_get_track_by_id(the_file, ref->trackIDs[k]); |
4998 | 0 | if (!tk) { |
4999 | 0 | memmove(&ref->trackIDs[k], &ref->trackIDs[k+1], ref->trackIDCount-k-1); |
5000 | 0 | k--; |
5001 | 0 | ref->trackIDCount--; |
5002 | 0 | } |
5003 | 0 | } |
5004 | 0 | if (!ref->trackIDCount) { |
5005 | 0 | i--; |
5006 | 0 | gf_isom_box_del_parent(&trak->References->child_boxes, (GF_Box *) ref); |
5007 | 0 | } |
5008 | 0 | } |
5009 | 0 | if (!trak->References->child_boxes || !gf_list_count(trak->References->child_boxes)) { |
5010 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *) trak->References); |
5011 | 0 | trak->References = NULL; |
5012 | 0 | } |
5013 | 0 | return GF_OK; |
5014 | 0 | } |
5015 | | |
5016 | | //sets a track reference |
5017 | | GF_EXPORT |
5018 | | GF_Err gf_isom_remove_track_references(GF_ISOFile *the_file, u32 trackNumber) |
5019 | 0 | { |
5020 | 0 | GF_TrackBox *trak; |
5021 | |
|
5022 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
5023 | 0 | if (!trak) return GF_BAD_PARAM; |
5024 | | |
5025 | 0 | if (trak->References) { |
5026 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->References); |
5027 | 0 | trak->References = NULL; |
5028 | 0 | } |
5029 | 0 | return GF_OK; |
5030 | 0 | } |
5031 | | |
5032 | | GF_EXPORT |
5033 | | GF_Err gf_isom_remove_track_reference(GF_ISOFile *isom_file, u32 trackNumber, u32 ref_type) |
5034 | 0 | { |
5035 | 0 | GF_TrackBox *trak; |
5036 | 0 | u32 i=0; |
5037 | 0 | GF_TrackReferenceTypeBox *ref; |
5038 | 0 | trak = gf_isom_get_track_box(isom_file, trackNumber); |
5039 | 0 | if (!trak) return GF_BAD_PARAM; |
5040 | | |
5041 | 0 | if (!trak->References) return GF_OK; |
5042 | 0 | while ((ref = gf_list_enum(trak->References->child_boxes, &i))) { |
5043 | 0 | if (ref->reference_type == ref_type) { |
5044 | 0 | gf_isom_box_del_parent(&trak->References->child_boxes, (GF_Box *)ref); |
5045 | 0 | break; |
5046 | 0 | } |
5047 | 0 | } |
5048 | 0 | if (!gf_list_count(trak->References->child_boxes)) { |
5049 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->References); |
5050 | 0 | trak->References = NULL; |
5051 | 0 | } |
5052 | 0 | return GF_OK; |
5053 | |
|
5054 | 0 | } |
5055 | | |
5056 | | //changes track ID |
5057 | | GF_EXPORT |
5058 | | GF_Err gf_isom_set_track_id(GF_ISOFile *movie, u32 trackNumber, GF_ISOTrackID trackID) |
5059 | 0 | { |
5060 | 0 | GF_TrackReferenceTypeBox *ref; |
5061 | 0 | GF_TrackBox *trak, *a_trak; |
5062 | 0 | u32 i, j, k; |
5063 | |
|
5064 | 0 | if (!movie) return GF_BAD_PARAM; |
5065 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
5066 | 0 | if (trak && (trak->Header->trackID==trackID)) return GF_OK; |
5067 | 0 | a_trak = gf_isom_get_track_from_id(movie->moov, trackID); |
5068 | 0 | if (!trak || a_trak) return GF_BAD_PARAM; |
5069 | | |
5070 | | /*rewrite all dependencies*/ |
5071 | 0 | i=0; |
5072 | 0 | while ((a_trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) { |
5073 | 0 | if (!a_trak->References) continue; |
5074 | 0 | j=0; |
5075 | 0 | while ((ref = (GF_TrackReferenceTypeBox *)gf_list_enum(a_trak->References->child_boxes, &j))) { |
5076 | 0 | for (k=0; k<ref->trackIDCount; k++) { |
5077 | 0 | if (ref->trackIDs[k]==trak->Header->trackID) { |
5078 | 0 | ref->trackIDs[k] = trackID; |
5079 | 0 | break; |
5080 | 0 | } |
5081 | 0 | } |
5082 | 0 | } |
5083 | 0 | } |
5084 | | |
5085 | | /*and update IOD if any*/ |
5086 | 0 | if (movie->moov->iods && movie->moov->iods->descriptor) { |
5087 | 0 | GF_ES_ID_Inc *inc; |
5088 | 0 | GF_IsomObjectDescriptor *od = (GF_IsomObjectDescriptor *)movie->moov->iods->descriptor; |
5089 | |
|
5090 | 0 | i=0; |
5091 | 0 | while ((inc = (GF_ES_ID_Inc*)gf_list_enum(od->ES_ID_IncDescriptors, &i))) { |
5092 | 0 | if (inc->trackID==trak->Header->trackID) inc->trackID = trackID; |
5093 | 0 | } |
5094 | 0 | } |
5095 | 0 | trak->Header->trackID = trackID; |
5096 | 0 | update_next_track_id(movie); |
5097 | 0 | return GF_OK; |
5098 | 0 | } |
5099 | | |
5100 | | /*force to rewrite all dependencies when the trackID of referenced track changes*/ |
5101 | | GF_EXPORT |
5102 | | GF_Err gf_isom_rewrite_track_dependencies(GF_ISOFile *movie, u32 trackNumber) |
5103 | 0 | { |
5104 | 0 | GF_TrackReferenceTypeBox *ref; |
5105 | 0 | GF_TrackBox *trak, *a_trak; |
5106 | 0 | u32 i, k; |
5107 | |
|
5108 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
5109 | 0 | if (!trak) |
5110 | 0 | return GF_BAD_PARAM; |
5111 | 0 | if (!trak->References) |
5112 | 0 | return GF_OK; |
5113 | | |
5114 | 0 | i=0; |
5115 | 0 | while ((ref = (GF_TrackReferenceTypeBox *)gf_list_enum(trak->References->child_boxes, &i))) { |
5116 | 0 | for (k=0; k < ref->trackIDCount; k++) { |
5117 | 0 | a_trak = gf_isom_get_track_from_original_id(movie->moov, ref->trackIDs[k], trak->originalFile); |
5118 | 0 | if (a_trak) { |
5119 | 0 | ref->trackIDs[k] = a_trak->Header->trackID; |
5120 | 0 | } else { |
5121 | 0 | a_trak = gf_isom_get_track_from_id(movie->moov, ref->trackIDs[k]); |
5122 | | /*we should have a track with no original ID (not imported) - should we rewrite the dependency ?*/ |
5123 | 0 | if (! a_trak || a_trak->originalID) return GF_BAD_PARAM; |
5124 | 0 | } |
5125 | 0 | } |
5126 | 0 | } |
5127 | | |
5128 | 0 | return GF_OK; |
5129 | 0 | } |
5130 | | |
5131 | | #if 0 //unused |
5132 | | |
5133 | | /*! changes the sample description index of a sample |
5134 | | \param isom_file the destination ISO file |
5135 | | \param trackNumber the destination track |
5136 | | \param sampleNum the target sample number |
5137 | | \param fnewSampleDescIndex the new sample description index to assign to the sample |
5138 | | \return error if any |
5139 | | */ |
5140 | | GF_EXPORT |
5141 | | GF_Err gf_isom_change_sample_desc_index(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 newSampleDescIndex) |
5142 | | { |
5143 | | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5144 | | if (!trak || !sample_number || !newSampleDescIndex) return GF_BAD_PARAM; |
5145 | | if (!trak->is_unpacked) { |
5146 | | unpack_track(trak); |
5147 | | } |
5148 | | if (!trak->Media->information->sampleTable->SampleToChunk) return GF_BAD_PARAM; |
5149 | | if (trak->Media->information->sampleTable->SampleToChunk->nb_entries < sample_number) return GF_BAD_PARAM; |
5150 | | trak->Media->information->sampleTable->SampleToChunk->entries[sample_number-1].sampleDescriptionIndex = newSampleDescIndex; |
5151 | | return GF_OK; |
5152 | | } |
5153 | | |
5154 | | /*modify CTS offset of a given sample (used for B-frames) - MUST be called in unpack mode only*/ |
5155 | | GF_EXPORT |
5156 | | GF_Err gf_isom_modify_cts_offset(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 offset) |
5157 | | { |
5158 | | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5159 | | if (!trak) return GF_BAD_PARAM; |
5160 | | if (!trak->Media->information->sampleTable->CompositionOffset) return GF_BAD_PARAM; |
5161 | | if (!trak->Media->information->sampleTable->CompositionOffset->unpack_mode) return GF_BAD_PARAM; |
5162 | | /*we're in unpack mode: one entry per sample*/ |
5163 | | trak->Media->information->sampleTable->CompositionOffset->entries[sample_number - 1].decodingOffset = offset; |
5164 | | return GF_OK; |
5165 | | } |
5166 | | #endif |
5167 | | |
5168 | | GF_EXPORT |
5169 | | GF_Err gf_isom_shift_cts_offset(GF_ISOFile *the_file, u32 trackNumber, s32 offset_shift) |
5170 | 0 | { |
5171 | 0 | u32 i; |
5172 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5173 | 0 | if (!trak) return GF_BAD_PARAM; |
5174 | 0 | if (!trak->Media->information->sampleTable->CompositionOffset) return GF_BAD_PARAM; |
5175 | 0 | if (!trak->Media->information->sampleTable->CompositionOffset->unpack_mode) return GF_BAD_PARAM; |
5176 | | |
5177 | 0 | GF_CompositionOffsetBox *ctso = trak->Media->information->sampleTable->CompositionOffset; |
5178 | 0 | for (i=0; i<ctso->nb_entries; i++) { |
5179 | 0 | s64 new_ts = ctso->entries[i].decodingOffset; |
5180 | 0 | new_ts -= offset_shift; |
5181 | | /*we're in unpack mode: one entry per sample*/ |
5182 | 0 | ctso->entries[i].decodingOffset = (s32) new_ts; |
5183 | 0 | } |
5184 | 0 | if (trak->Media->mediaHeader->duration >= -offset_shift) { |
5185 | 0 | s64 new_dur = trak->Media->mediaHeader->duration; |
5186 | 0 | new_dur -= offset_shift; |
5187 | 0 | if (new_dur<0) new_dur = 0; |
5188 | 0 | trak->Media->mediaHeader->duration = (u32) new_dur; |
5189 | 0 | } |
5190 | 0 | return GF_OK; |
5191 | 0 | } |
5192 | | |
5193 | | #if 0 //unused |
5194 | | GF_Err gf_isom_remove_cts_info(GF_ISOFile *the_file, u32 trackNumber) |
5195 | | { |
5196 | | GF_SampleTableBox *stbl; |
5197 | | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5198 | | if (!trak) return GF_BAD_PARAM; |
5199 | | |
5200 | | stbl = trak->Media->information->sampleTable; |
5201 | | if (!stbl->CompositionOffset) return GF_OK; |
5202 | | |
5203 | | gf_isom_box_del_parent(&stbl->child_boxes, (GF_Box *)stbl->CompositionOffset); |
5204 | | stbl->CompositionOffset = NULL; |
5205 | | return GF_OK; |
5206 | | } |
5207 | | #endif |
5208 | | |
5209 | | GF_EXPORT |
5210 | | GF_Err gf_isom_set_cts_packing(GF_ISOFile *the_file, u32 trackNumber, Bool unpack) |
5211 | 0 | { |
5212 | 0 | GF_Err e; |
5213 | 0 | GF_Err stbl_repackCTS(GF_CompositionOffsetBox *ctts); |
5214 | 0 | GF_Err stbl_unpackCTS(GF_SampleTableBox *stbl); |
5215 | 0 | GF_SampleTableBox *stbl; |
5216 | |
|
5217 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5218 | 0 | if (!trak) return GF_BAD_PARAM; |
5219 | | |
5220 | 0 | stbl = trak->Media->information->sampleTable; |
5221 | 0 | if (unpack) { |
5222 | 0 | if (!stbl->CompositionOffset) { |
5223 | 0 | stbl->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_CTTS); |
5224 | 0 | if (!stbl->CompositionOffset) return GF_OUT_OF_MEM; |
5225 | 0 | } |
5226 | 0 | e = stbl_unpackCTS(stbl); |
5227 | 0 | } else { |
5228 | 0 | if (!stbl->CompositionOffset) return GF_OK; |
5229 | 0 | e = stbl_repackCTS(stbl->CompositionOffset); |
5230 | 0 | } |
5231 | 0 | if (e) return e; |
5232 | 0 | return SetTrackDuration(trak); |
5233 | 0 | } |
5234 | | |
5235 | | GF_EXPORT |
5236 | | GF_Err gf_isom_set_track_matrix(GF_ISOFile *the_file, u32 trackNumber, s32 matrix[9]) |
5237 | 0 | { |
5238 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5239 | 0 | if (!trak || !trak->Header) return GF_BAD_PARAM; |
5240 | 0 | memcpy(trak->Header->matrix, matrix, sizeof(trak->Header->matrix)); |
5241 | 0 | return GF_OK; |
5242 | 0 | } |
5243 | | |
5244 | | GF_EXPORT |
5245 | | GF_Err gf_isom_set_track_layout_info(GF_ISOFile *the_file, u32 trackNumber, u32 width, u32 height, s32 translation_x, s32 translation_y, s16 layer) |
5246 | 0 | { |
5247 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); |
5248 | 0 | if (!trak || !trak->Header) return GF_BAD_PARAM; |
5249 | 0 | trak->Header->width = width; |
5250 | 0 | trak->Header->height = height; |
5251 | 0 | trak->Header->matrix[6] = translation_x; |
5252 | 0 | trak->Header->matrix[7] = translation_y; |
5253 | 0 | trak->Header->layer = layer; |
5254 | 0 | return GF_OK; |
5255 | 0 | } |
5256 | | |
5257 | | GF_EXPORT |
5258 | | GF_Err gf_isom_set_media_timescale(GF_ISOFile *the_file, u32 trackNumber, u32 newTS, u32 new_tsinc, u32 force_rescale_type) |
5259 | 0 | { |
5260 | 0 | Double scale; |
5261 | 0 | u32 old_ts_inc=0; |
5262 | 0 | u32 old_timescale; |
5263 | 0 | GF_TrackBox *trak; |
5264 | 0 | GF_SampleTableBox *stbl; |
5265 | |
|
5266 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
5267 | 0 | if (!trak || !trak->Media || !trak->Media->mediaHeader) return GF_BAD_PARAM; |
5268 | 0 | if ((trak->Media->mediaHeader->timeScale==newTS) && !new_tsinc) |
5269 | 0 | return GF_OK; //nothing to do |
5270 | | |
5271 | 0 | if (!newTS) newTS = trak->Media->mediaHeader->timeScale; |
5272 | 0 | scale = newTS; |
5273 | 0 | scale /= trak->Media->mediaHeader->timeScale; |
5274 | 0 | old_timescale = trak->Media->mediaHeader->timeScale; |
5275 | 0 | trak->Media->mediaHeader->timeScale = newTS; |
5276 | |
|
5277 | 0 | stbl = trak->Media->information->sampleTable; |
5278 | 0 | if (new_tsinc) { |
5279 | 0 | u32 i; |
5280 | 0 | if (!stbl->TimeToSample || !stbl->TimeToSample->nb_entries) |
5281 | 0 | return GF_BAD_PARAM; |
5282 | | |
5283 | 0 | for (i=0; i<stbl->TimeToSample->nb_entries; i++) { |
5284 | 0 | if (!old_ts_inc) |
5285 | 0 | old_ts_inc = stbl->TimeToSample->entries[i].sampleDelta; |
5286 | 0 | else if (old_ts_inc<stbl->TimeToSample->entries[i].sampleDelta) |
5287 | 0 | old_ts_inc = stbl->TimeToSample->entries[i].sampleDelta; |
5288 | 0 | } |
5289 | |
|
5290 | 0 | if ((old_timescale==newTS) && (old_ts_inc==new_tsinc) && (force_rescale_type!=2) ) |
5291 | 0 | return GF_EOS; |
5292 | | |
5293 | 0 | if (!force_rescale_type) |
5294 | 0 | force_rescale_type = 1; |
5295 | 0 | else if (force_rescale_type==2) { |
5296 | 0 | gf_free(stbl->TimeToSample->entries); |
5297 | 0 | stbl->TimeToSample->alloc_size = 1; |
5298 | 0 | stbl->TimeToSample->nb_entries = 1; |
5299 | 0 | stbl->TimeToSample->entries = gf_malloc(sizeof(GF_SttsEntry)); |
5300 | 0 | stbl->TimeToSample->entries[0].sampleDelta = new_tsinc; |
5301 | 0 | stbl->TimeToSample->entries[0].sampleCount = stbl->SampleSize->sampleCount; |
5302 | 0 | } |
5303 | | |
5304 | |
|
5305 | 0 | for (i=0; i<stbl->TimeToSample->nb_entries; i++) { |
5306 | 0 | stbl->TimeToSample->entries[i].sampleDelta = new_tsinc; |
5307 | 0 | } |
5308 | |
|
5309 | 0 | if (stbl->CompositionOffset) { |
5310 | 0 | for (i=0; i<stbl->CompositionOffset->nb_entries; i++) { |
5311 | 0 | u32 old_offset = stbl->CompositionOffset->entries[i].decodingOffset; |
5312 | 0 | if (force_rescale_type==2) { |
5313 | 0 | u32 val = old_offset ; |
5314 | | //get number of TS delta |
5315 | 0 | old_offset /= old_ts_inc; |
5316 | 0 | if (old_offset * old_ts_inc < val) |
5317 | 0 | old_offset++; |
5318 | 0 | old_offset *= new_tsinc; |
5319 | 0 | } else { |
5320 | 0 | old_offset *= new_tsinc; |
5321 | 0 | old_offset /= old_ts_inc; |
5322 | 0 | } |
5323 | 0 | stbl->CompositionOffset->entries[i].decodingOffset = old_offset; |
5324 | 0 | } |
5325 | 0 | } |
5326 | |
|
5327 | 0 | #define RESCALE_TSVAL(_tsval) {\ |
5328 | 0 | s64 val = ((s64) _tsval) * new_tsinc;\ |
5329 | 0 | val /= old_ts_inc;\ |
5330 | 0 | _tsval = (s32) val;\ |
5331 | 0 | } |
5332 | |
|
5333 | 0 | if (stbl->CompositionToDecode) { |
5334 | 0 | RESCALE_TSVAL(stbl->CompositionToDecode->compositionEndTime) |
5335 | 0 | RESCALE_TSVAL(stbl->CompositionToDecode->compositionStartTime) |
5336 | 0 | RESCALE_TSVAL(stbl->CompositionToDecode->compositionToDTSShift) |
5337 | 0 | RESCALE_TSVAL(stbl->CompositionToDecode->greatestDecodeToDisplayDelta) |
5338 | 0 | RESCALE_TSVAL(stbl->CompositionToDecode->leastDecodeToDisplayDelta) |
5339 | 0 | } |
5340 | 0 | if (trak->editBox) { |
5341 | 0 | GF_EdtsEntry *ent; |
5342 | 0 | i=0; |
5343 | 0 | while ((ent = (GF_EdtsEntry*)gf_list_enum(trak->editBox->editList->entryList, &i))) { |
5344 | 0 | RESCALE_TSVAL(ent->mediaTime) |
5345 | 0 | } |
5346 | 0 | } |
5347 | 0 | #undef RESCALE_TSVAL |
5348 | | //force recompute of duration |
5349 | 0 | trak->Media->mediaHeader->duration=0; |
5350 | 0 | return SetTrackDuration(trak); |
5351 | 0 | } |
5352 | | |
5353 | | //rescale timings |
5354 | 0 | u32 i, k, idx, last_delta; |
5355 | 0 | u64 cur_dts; |
5356 | 0 | u64*DTSs = NULL; |
5357 | 0 | s64*CTSs = NULL; |
5358 | |
|
5359 | 0 | if (trak->editBox) { |
5360 | 0 | GF_EdtsEntry *ent; |
5361 | 0 | i=0; |
5362 | 0 | while ((ent = (GF_EdtsEntry*)gf_list_enum(trak->editBox->editList->entryList, &i))) { |
5363 | | //only update if media time is >=0 (neg means empty edit) |
5364 | 0 | if (ent->mediaTime>=0) |
5365 | 0 | ent->mediaTime = (u32) (scale*ent->mediaTime); |
5366 | 0 | } |
5367 | 0 | } |
5368 | 0 | if (! stbl || !stbl->TimeToSample || !stbl->TimeToSample->nb_entries) { |
5369 | 0 | return SetTrackDuration(trak); |
5370 | 0 | } |
5371 | | |
5372 | 0 | idx = 0; |
5373 | 0 | cur_dts = 0; |
5374 | | //unpack the DTSs |
5375 | 0 | DTSs = (u64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount) ); |
5376 | 0 | if (!DTSs) return GF_OUT_OF_MEM; |
5377 | | |
5378 | 0 | CTSs = NULL; |
5379 | 0 | if (stbl->CompositionOffset) { |
5380 | 0 | CTSs = (s64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount) ); |
5381 | 0 | if (!CTSs) return GF_OUT_OF_MEM; |
5382 | 0 | } |
5383 | | |
5384 | 0 | for (i=0; i<stbl->TimeToSample->nb_entries; i++) { |
5385 | 0 | for (k=0; k<stbl->TimeToSample->entries[i].sampleCount; k++) { |
5386 | 0 | cur_dts += stbl->TimeToSample->entries[i].sampleDelta; |
5387 | 0 | DTSs[idx] = (u64) (cur_dts * scale); |
5388 | |
|
5389 | 0 | if (stbl->CompositionOffset) { |
5390 | 0 | s32 cts_o; |
5391 | 0 | stbl_GetSampleCTS(stbl->CompositionOffset, idx+1, &cts_o); |
5392 | 0 | CTSs[idx] = (s64) ( ((s64) cur_dts + cts_o) * scale); |
5393 | 0 | } |
5394 | 0 | idx++; |
5395 | 0 | } |
5396 | 0 | } |
5397 | 0 | last_delta = (u32) (stbl->TimeToSample->entries[stbl->TimeToSample->nb_entries-1].sampleDelta * scale); |
5398 | | |
5399 | | //repack DTS |
5400 | 0 | if (stbl->SampleSize->sampleCount) { |
5401 | 0 | stbl->TimeToSample->entries = gf_realloc(stbl->TimeToSample->entries, sizeof(GF_SttsEntry)*stbl->SampleSize->sampleCount); |
5402 | 0 | memset(stbl->TimeToSample->entries, 0, sizeof(GF_SttsEntry)*stbl->SampleSize->sampleCount); |
5403 | 0 | stbl->TimeToSample->entries[0].sampleDelta = (u32) DTSs[0]; |
5404 | 0 | stbl->TimeToSample->entries[0].sampleCount = 1; |
5405 | 0 | idx=0; |
5406 | 0 | for (i=1; i< stbl->SampleSize->sampleCount - 1; i++) { |
5407 | 0 | if (DTSs[i+1] - DTSs[i] == stbl->TimeToSample->entries[idx].sampleDelta) { |
5408 | 0 | stbl->TimeToSample->entries[idx].sampleCount++; |
5409 | 0 | } else { |
5410 | 0 | idx++; |
5411 | 0 | stbl->TimeToSample->entries[idx].sampleDelta = (u32) ( DTSs[i+1] - DTSs[i] ); |
5412 | 0 | stbl->TimeToSample->entries[idx].sampleCount=1; |
5413 | 0 | } |
5414 | 0 | } |
5415 | 0 | if (stbl->SampleSize->sampleCount > 1) { |
5416 | | //add the sample delta for the last sample |
5417 | 0 | if (stbl->TimeToSample->entries[idx].sampleDelta == last_delta) { |
5418 | 0 | stbl->TimeToSample->entries[idx].sampleCount++; |
5419 | 0 | } else { |
5420 | 0 | idx++; |
5421 | 0 | stbl->TimeToSample->entries[idx].sampleDelta = last_delta; |
5422 | 0 | stbl->TimeToSample->entries[idx].sampleCount=1; |
5423 | 0 | } |
5424 | |
|
5425 | 0 | stbl->TimeToSample->nb_entries = idx+1; |
5426 | 0 | stbl->TimeToSample->entries = gf_realloc(stbl->TimeToSample->entries, sizeof(GF_SttsEntry)*stbl->TimeToSample->nb_entries); |
5427 | 0 | } |
5428 | 0 | } |
5429 | |
|
5430 | 0 | if (CTSs && stbl->SampleSize->sampleCount>0) { |
5431 | | //repack CTS |
5432 | 0 | stbl->CompositionOffset->entries = gf_realloc(stbl->CompositionOffset->entries, sizeof(GF_DttsEntry)*stbl->SampleSize->sampleCount); |
5433 | 0 | memset(stbl->CompositionOffset->entries, 0, sizeof(GF_DttsEntry)*stbl->SampleSize->sampleCount); |
5434 | 0 | stbl->CompositionOffset->entries[0].decodingOffset = (s32) (CTSs[0] - DTSs[0]); |
5435 | 0 | stbl->CompositionOffset->entries[0].sampleCount = 1; |
5436 | 0 | idx=0; |
5437 | 0 | for (i=1; i< stbl->SampleSize->sampleCount; i++) { |
5438 | 0 | s32 cts_o = (s32) (CTSs[i] - DTSs[i]); |
5439 | 0 | if (cts_o == stbl->CompositionOffset->entries[idx].decodingOffset) { |
5440 | 0 | stbl->CompositionOffset->entries[idx].sampleCount++; |
5441 | 0 | } else { |
5442 | 0 | idx++; |
5443 | 0 | stbl->CompositionOffset->entries[idx].decodingOffset = cts_o; |
5444 | 0 | stbl->CompositionOffset->entries[idx].sampleCount=1; |
5445 | 0 | } |
5446 | 0 | } |
5447 | 0 | stbl->CompositionOffset->nb_entries = idx+1; |
5448 | 0 | stbl->CompositionOffset->entries = gf_realloc(stbl->CompositionOffset->entries, sizeof(GF_DttsEntry)*stbl->CompositionOffset->nb_entries); |
5449 | |
|
5450 | 0 | gf_free(CTSs); |
5451 | 0 | } |
5452 | 0 | gf_free(DTSs); |
5453 | |
|
5454 | 0 | if (stbl->CompositionToDecode) { |
5455 | 0 | stbl->CompositionToDecode->compositionEndTime = (s32) (stbl->CompositionToDecode->compositionEndTime * scale); |
5456 | 0 | stbl->CompositionToDecode->compositionStartTime = (s32)(stbl->CompositionToDecode->compositionStartTime * scale); |
5457 | 0 | stbl->CompositionToDecode->compositionToDTSShift = (s32)(stbl->CompositionToDecode->compositionToDTSShift * scale); |
5458 | 0 | stbl->CompositionToDecode->greatestDecodeToDisplayDelta = (s32)(stbl->CompositionToDecode->greatestDecodeToDisplayDelta * scale); |
5459 | 0 | stbl->CompositionToDecode->leastDecodeToDisplayDelta = (s32)(stbl->CompositionToDecode->leastDecodeToDisplayDelta * scale); |
5460 | 0 | } |
5461 | |
|
5462 | 0 | return SetTrackDuration(trak); |
5463 | 0 | } |
5464 | | |
5465 | | GF_EXPORT |
5466 | | Bool gf_isom_box_equal(GF_Box *a, GF_Box *b) |
5467 | 0 | { |
5468 | 0 | Bool ret; |
5469 | 0 | u8 *data1, *data2; |
5470 | 0 | u32 data1_size, data2_size; |
5471 | 0 | GF_BitStream *bs; |
5472 | |
|
5473 | 0 | if (a == b) return GF_TRUE; |
5474 | 0 | if (!a || !b) return GF_FALSE; |
5475 | | //do NOT check size, they could be set/not set at this point |
5476 | | |
5477 | 0 | data1 = data2 = NULL; |
5478 | |
|
5479 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
5480 | 0 | gf_isom_box_size(a); |
5481 | 0 | gf_isom_box_write(a, bs); |
5482 | 0 | gf_bs_get_content(bs, &data1, &data1_size); |
5483 | 0 | gf_bs_del(bs); |
5484 | |
|
5485 | 0 | bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
5486 | 0 | gf_isom_box_size(b); |
5487 | 0 | gf_isom_box_write(b, bs); |
5488 | 0 | gf_bs_get_content(bs, &data2, &data2_size); |
5489 | 0 | gf_bs_del(bs); |
5490 | |
|
5491 | 0 | ret = GF_FALSE; |
5492 | 0 | if (data1_size == data2_size) { |
5493 | 0 | ret = (memcmp(data1, data2, sizeof(char)*data1_size) == 0) ? GF_TRUE : GF_FALSE; |
5494 | 0 | } |
5495 | 0 | gf_free(data1); |
5496 | 0 | gf_free(data2); |
5497 | 0 | return ret; |
5498 | 0 | } |
5499 | | |
5500 | | static u32 base_sample_entry_type(u32 type) |
5501 | 0 | { |
5502 | 0 | if (type==GF_ISOM_SUBTYPE_DVH1) return GF_ISOM_SUBTYPE_HVC1; |
5503 | 0 | if (type==GF_ISOM_SUBTYPE_DVHE) return GF_ISOM_SUBTYPE_HEV1; |
5504 | 0 | if (type==GF_ISOM_SUBTYPE_DVA1) return GF_ISOM_SUBTYPE_AVC_H264; |
5505 | 0 | if (type==GF_ISOM_SUBTYPE_DVAV) return GF_ISOM_SUBTYPE_AVC3_H264; |
5506 | 0 | if (type==GF_ISOM_SUBTYPE_DAV1) return GF_ISOM_SUBTYPE_AV01; |
5507 | 0 | return type; |
5508 | 0 | } |
5509 | | |
5510 | | GF_EXPORT |
5511 | | Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, u32 sdesc_index1, GF_ISOFile *f2, u32 tk2, u32 sdesc_index2) |
5512 | 0 | { |
5513 | 0 | u32 i, count; |
5514 | 0 | GF_TrackBox *trak1, *trak2; |
5515 | 0 | GF_ESD *esd1, *esd2; |
5516 | 0 | Bool need_memcmp, ret; |
5517 | 0 | GF_Box *a, *b; |
5518 | | |
5519 | | /*get orig sample desc and clone it*/ |
5520 | 0 | trak1 = gf_isom_get_track_box(f1, tk1); |
5521 | 0 | if (!trak1 || !trak1->Media) return GF_FALSE; |
5522 | 0 | trak2 = gf_isom_get_track_box(f2, tk2); |
5523 | 0 | if (!trak2 || !trak2->Media) return GF_FALSE; |
5524 | | |
5525 | 0 | if (trak1->Media->handler->handlerType != trak2->Media->handler->handlerType) return GF_FALSE; |
5526 | 0 | count = gf_list_count(trak1->Media->information->sampleTable->SampleDescription->child_boxes); |
5527 | 0 | if (count != gf_list_count(trak2->Media->information->sampleTable->SampleDescription->child_boxes)) { |
5528 | 0 | if (!sdesc_index1 && !sdesc_index2) return GF_FALSE; |
5529 | 0 | } |
5530 | | |
5531 | 0 | need_memcmp = GF_TRUE; |
5532 | 0 | for (i=0; i<count; i++) { |
5533 | 0 | u32 type1, type2; |
5534 | 0 | GF_SampleEntryBox *ent1 = (GF_SampleEntryBox *)gf_list_get(trak1->Media->information->sampleTable->SampleDescription->child_boxes, i); |
5535 | 0 | GF_SampleEntryBox *ent2 = (GF_SampleEntryBox *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->child_boxes, i); |
5536 | |
|
5537 | 0 | if (sdesc_index1) ent1 = (GF_SampleEntryBox *)gf_list_get(trak1->Media->information->sampleTable->SampleDescription->child_boxes, sdesc_index1 - 1); |
5538 | 0 | if (sdesc_index2) ent2 = (GF_SampleEntryBox *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->child_boxes, sdesc_index2 - 1); |
5539 | |
|
5540 | 0 | if (!ent1 || !ent2) return GF_FALSE; |
5541 | 0 | if (ent1->internal_type != ent2->internal_type) return GF_FALSE; |
5542 | 0 | type1 = base_sample_entry_type(ent1->type); |
5543 | 0 | type2 = base_sample_entry_type(ent2->type); |
5544 | 0 | if (type1 != type2) return GF_FALSE; |
5545 | | |
5546 | 0 | switch (ent1->type) { |
5547 | | /*for MPEG-4 streams, only compare decSpecInfo (bitrate may not be the same but that's not an issue)*/ |
5548 | 0 | case GF_ISOM_BOX_TYPE_MP4S: |
5549 | 0 | case GF_ISOM_BOX_TYPE_MP4A: |
5550 | 0 | case GF_ISOM_BOX_TYPE_MP4V: |
5551 | 0 | case GF_ISOM_BOX_TYPE_ENCA: |
5552 | 0 | case GF_ISOM_BOX_TYPE_ENCV: |
5553 | 0 | case GF_ISOM_BOX_TYPE_RESV: |
5554 | 0 | case GF_ISOM_BOX_TYPE_ENCS: |
5555 | 0 | Media_GetESD(trak1->Media, sdesc_index1 ? sdesc_index1 : i+1, &esd1, GF_TRUE); |
5556 | 0 | Media_GetESD(trak2->Media, sdesc_index2 ? sdesc_index2 : i+1, &esd2, GF_TRUE); |
5557 | 0 | if (!esd1 || !esd2 || !esd1->decoderConfig || !esd2->decoderConfig) continue; |
5558 | 0 | need_memcmp = GF_FALSE; |
5559 | 0 | if (esd1->decoderConfig->streamType != esd2->decoderConfig->streamType) return GF_FALSE; |
5560 | 0 | if (esd1->decoderConfig->objectTypeIndication != esd2->decoderConfig->objectTypeIndication) return GF_FALSE; |
5561 | 0 | if (!esd1->decoderConfig->decoderSpecificInfo && esd2->decoderConfig->decoderSpecificInfo) return GF_FALSE; |
5562 | 0 | if (esd1->decoderConfig->decoderSpecificInfo && !esd2->decoderConfig->decoderSpecificInfo) return GF_FALSE; |
5563 | 0 | if (!esd1->decoderConfig->decoderSpecificInfo || !esd2->decoderConfig->decoderSpecificInfo) continue; |
5564 | 0 | if (esd1->decoderConfig->decoderSpecificInfo->dataLength != esd2->decoderConfig->decoderSpecificInfo->dataLength) |
5565 | 0 | return GF_FALSE; |
5566 | 0 | if (memcmp(esd1->decoderConfig->decoderSpecificInfo->data, esd2->decoderConfig->decoderSpecificInfo->data, sizeof(char)*esd1->decoderConfig->decoderSpecificInfo->dataLength)!=0) return GF_FALSE; |
5567 | 0 | break; |
5568 | 0 | case GF_ISOM_BOX_TYPE_HVT1: |
5569 | 0 | return GF_TRUE; |
5570 | 0 | case GF_ISOM_BOX_TYPE_AVC1: |
5571 | 0 | case GF_ISOM_BOX_TYPE_AVC2: |
5572 | 0 | case GF_ISOM_BOX_TYPE_AVC3: |
5573 | 0 | case GF_ISOM_BOX_TYPE_AVC4: |
5574 | 0 | case GF_ISOM_BOX_TYPE_SVC1: |
5575 | 0 | case GF_ISOM_BOX_TYPE_MVC1: |
5576 | 0 | case GF_ISOM_BOX_TYPE_HVC1: |
5577 | 0 | case GF_ISOM_BOX_TYPE_HEV1: |
5578 | 0 | case GF_ISOM_BOX_TYPE_HVC2: |
5579 | 0 | case GF_ISOM_BOX_TYPE_HEV2: |
5580 | 0 | case GF_ISOM_BOX_TYPE_LHE1: |
5581 | 0 | case GF_ISOM_BOX_TYPE_LHV1: |
5582 | 0 | case GF_ISOM_BOX_TYPE_AV01: |
5583 | 0 | case GF_ISOM_BOX_TYPE_VVC1: |
5584 | 0 | case GF_ISOM_BOX_TYPE_VVI1: |
5585 | 0 | case GF_ISOM_BOX_TYPE_DVHE: |
5586 | 0 | case GF_ISOM_BOX_TYPE_DVH1: |
5587 | 0 | case GF_ISOM_BOX_TYPE_DVA1: |
5588 | 0 | case GF_ISOM_BOX_TYPE_DVAV: |
5589 | 0 | case GF_ISOM_BOX_TYPE_DAV1: |
5590 | 0 | { |
5591 | 0 | GF_MPEGVisualSampleEntryBox *avc1 = (GF_MPEGVisualSampleEntryBox *)ent1; |
5592 | 0 | GF_MPEGVisualSampleEntryBox *avc2 = (GF_MPEGVisualSampleEntryBox *)ent2; |
5593 | |
|
5594 | 0 | if (avc1->hevc_config) |
5595 | 0 | a = (GF_Box *) avc1->hevc_config; |
5596 | 0 | else if (avc1->lhvc_config) |
5597 | 0 | a = (GF_Box *) avc1->lhvc_config; |
5598 | 0 | else if (avc1->svc_config) |
5599 | 0 | a = (GF_Box *) avc1->svc_config; |
5600 | 0 | else if (avc1->mvc_config) |
5601 | 0 | a = (GF_Box *) avc1->mvc_config; |
5602 | 0 | else if (avc1->av1_config) |
5603 | 0 | a = (GF_Box *)avc1->av1_config; |
5604 | 0 | else if (avc1->vvc_config) |
5605 | 0 | a = (GF_Box *)avc1->vvc_config; |
5606 | 0 | else if (avc1->vp_config) |
5607 | 0 | a = (GF_Box *)avc1->vp_config; |
5608 | 0 | else if (avc1->cfg_3gpp) |
5609 | 0 | a = (GF_Box *)avc1->cfg_3gpp; |
5610 | 0 | else |
5611 | 0 | a = (GF_Box *) avc1->avc_config; |
5612 | |
|
5613 | 0 | if (avc2->hevc_config) |
5614 | 0 | b = (GF_Box *) avc2->hevc_config; |
5615 | 0 | else if (avc2->lhvc_config) |
5616 | 0 | b = (GF_Box *) avc2->lhvc_config; |
5617 | 0 | else if (avc2->svc_config) |
5618 | 0 | b = (GF_Box *) avc2->svc_config; |
5619 | 0 | else if (avc2->mvc_config) |
5620 | 0 | b = (GF_Box *) avc2->mvc_config; |
5621 | 0 | else if (avc2->av1_config) |
5622 | 0 | b = (GF_Box *)avc2->av1_config; |
5623 | 0 | else if (avc2->vvc_config) |
5624 | 0 | b = (GF_Box *)avc2->vvc_config; |
5625 | 0 | else if (avc2->vp_config) |
5626 | 0 | b = (GF_Box *)avc2->vp_config; |
5627 | 0 | else if (avc2->cfg_3gpp) |
5628 | 0 | b = (GF_Box *)avc2->cfg_3gpp; |
5629 | 0 | else |
5630 | 0 | b = (GF_Box *) avc2->avc_config; |
5631 | |
|
5632 | 0 | Bool res = gf_isom_box_equal(a,b); |
5633 | 0 | if (!res) return GF_FALSE; |
5634 | | |
5635 | | //check dovi config disabled for now |
5636 | | //res = gf_isom_box_equal((GF_Box*)avc1->dovi_config, (GF_Box*)avc2->dovi_config); |
5637 | | //if (!res) return GF_FALSE; |
5638 | 0 | return GF_TRUE; |
5639 | 0 | } |
5640 | 0 | break; |
5641 | 0 | case GF_ISOM_BOX_TYPE_LSR1: |
5642 | 0 | { |
5643 | 0 | GF_LASeRSampleEntryBox *lsr1 = (GF_LASeRSampleEntryBox *)ent1; |
5644 | 0 | GF_LASeRSampleEntryBox *lsr2 = (GF_LASeRSampleEntryBox *)ent2; |
5645 | 0 | if (lsr1->lsr_config && lsr2->lsr_config |
5646 | 0 | && lsr1->lsr_config->hdr && lsr2->lsr_config->hdr |
5647 | 0 | && (lsr1->lsr_config->hdr_size==lsr2->lsr_config->hdr_size) |
5648 | 0 | && !memcmp(lsr1->lsr_config->hdr, lsr2->lsr_config->hdr, lsr2->lsr_config->hdr_size) |
5649 | 0 | ) { |
5650 | 0 | return GF_TRUE; |
5651 | 0 | } |
5652 | 0 | return GF_FALSE; |
5653 | 0 | } |
5654 | 0 | break; |
5655 | 0 | #ifndef GPAC_DISABLE_VTT |
5656 | 0 | case GF_ISOM_BOX_TYPE_WVTT: |
5657 | 0 | { |
5658 | 0 | GF_WebVTTSampleEntryBox *wvtt1 = (GF_WebVTTSampleEntryBox *)ent1; |
5659 | 0 | GF_WebVTTSampleEntryBox *wvtt2 = (GF_WebVTTSampleEntryBox *)ent2; |
5660 | 0 | if (wvtt1->config && wvtt2->config && |
5661 | 0 | (wvtt1->config->string && wvtt2->config->string && !strcmp(wvtt1->config->string, wvtt2->config->string))) { |
5662 | 0 | return GF_TRUE; |
5663 | 0 | } |
5664 | 0 | return GF_FALSE; |
5665 | 0 | } |
5666 | 0 | break; |
5667 | 0 | #endif |
5668 | 0 | case GF_ISOM_BOX_TYPE_STPP: |
5669 | 0 | { |
5670 | 0 | GF_MetaDataSampleEntryBox *stpp1 = (GF_MetaDataSampleEntryBox *)ent1; |
5671 | 0 | GF_MetaDataSampleEntryBox *stpp2 = (GF_MetaDataSampleEntryBox *)ent2; |
5672 | 0 | if (stpp1->xml_namespace && stpp2->xml_namespace && !strcmp(stpp1->xml_namespace, stpp2->xml_namespace)) { |
5673 | 0 | return GF_TRUE; |
5674 | 0 | } |
5675 | 0 | return GF_FALSE; |
5676 | 0 | } |
5677 | 0 | break; |
5678 | 0 | case GF_ISOM_BOX_TYPE_SBTT: |
5679 | 0 | { |
5680 | 0 | return GF_FALSE; |
5681 | 0 | } |
5682 | 0 | break; |
5683 | 0 | case GF_ISOM_BOX_TYPE_STXT: |
5684 | 0 | { |
5685 | 0 | GF_MetaDataSampleEntryBox *stxt1 = (GF_MetaDataSampleEntryBox *)ent1; |
5686 | 0 | GF_MetaDataSampleEntryBox *stxt2 = (GF_MetaDataSampleEntryBox *)ent2; |
5687 | 0 | if (stxt1->mime_type && stxt2->mime_type && |
5688 | 0 | ( (!stxt1->config && !stxt2->config) || |
5689 | 0 | (stxt1->config && stxt2->config && stxt1->config->config && stxt2->config->config && |
5690 | 0 | !strcmp(stxt1->config->config, stxt2->config->config)))) { |
5691 | 0 | return GF_TRUE; |
5692 | 0 | } |
5693 | 0 | return GF_FALSE; |
5694 | 0 | } |
5695 | 0 | case GF_ISOM_BOX_TYPE_MP3: |
5696 | 0 | case GF_QT_SUBTYPE_RAW_AUD: |
5697 | 0 | case GF_QT_SUBTYPE_TWOS: |
5698 | 0 | case GF_QT_SUBTYPE_SOWT: |
5699 | 0 | case GF_QT_SUBTYPE_FL32: |
5700 | 0 | case GF_QT_SUBTYPE_FL64: |
5701 | 0 | case GF_QT_SUBTYPE_IN24: |
5702 | 0 | case GF_QT_SUBTYPE_IN32: |
5703 | 0 | case GF_QT_SUBTYPE_ULAW: |
5704 | 0 | case GF_QT_SUBTYPE_ALAW: |
5705 | 0 | case GF_QT_SUBTYPE_ADPCM: |
5706 | 0 | case GF_QT_SUBTYPE_IMA_ADPCM: |
5707 | 0 | case GF_QT_SUBTYPE_DVCA: |
5708 | 0 | case GF_QT_SUBTYPE_QDMC: |
5709 | 0 | case GF_QT_SUBTYPE_QDMC2: |
5710 | 0 | case GF_QT_SUBTYPE_QCELP: |
5711 | 0 | case GF_QT_SUBTYPE_kMP3: |
5712 | 0 | case GF_QT_SUBTYPE_APCH: |
5713 | 0 | case GF_QT_SUBTYPE_APCO: |
5714 | 0 | case GF_QT_SUBTYPE_APCN: |
5715 | 0 | case GF_QT_SUBTYPE_APCS: |
5716 | 0 | case GF_QT_SUBTYPE_AP4X: |
5717 | 0 | case GF_QT_SUBTYPE_AP4H: |
5718 | 0 | case GF_QT_SUBTYPE_RAW_VID: |
5719 | 0 | case GF_QT_SUBTYPE_YUYV: |
5720 | 0 | case GF_QT_SUBTYPE_UYVY: |
5721 | 0 | case GF_QT_SUBTYPE_YUV444: |
5722 | 0 | case GF_QT_SUBTYPE_YUVA444: |
5723 | 0 | case GF_QT_SUBTYPE_YUV422_10: |
5724 | 0 | case GF_QT_SUBTYPE_YUV444_10: |
5725 | 0 | case GF_QT_SUBTYPE_YUV422_16: |
5726 | 0 | case GF_QT_SUBTYPE_YUV420: |
5727 | 0 | case GF_QT_SUBTYPE_I420: |
5728 | 0 | case GF_QT_SUBTYPE_IYUV: |
5729 | 0 | case GF_QT_SUBTYPE_YV12: |
5730 | 0 | case GF_QT_SUBTYPE_YVYU: |
5731 | 0 | case GF_QT_SUBTYPE_RGBA: |
5732 | 0 | case GF_QT_SUBTYPE_ABGR: |
5733 | 0 | default: |
5734 | 0 | if (ent1->internal_type == GF_ISOM_SAMPLE_ENTRY_VIDEO) { |
5735 | 0 | GF_VisualSampleEntryBox *vent1 = (GF_VisualSampleEntryBox *) ent1; |
5736 | 0 | GF_VisualSampleEntryBox *vent2 = (GF_VisualSampleEntryBox *) ent2; |
5737 | 0 | if (vent1->Width != vent2->Width) return GF_FALSE; |
5738 | 0 | if (vent1->Height != vent2->Height) return GF_FALSE; |
5739 | 0 | } |
5740 | 0 | else if (ent1->internal_type == GF_ISOM_SAMPLE_ENTRY_AUDIO) { |
5741 | 0 | GF_AudioSampleEntryBox *aent1 = (GF_AudioSampleEntryBox *) ent1; |
5742 | 0 | GF_AudioSampleEntryBox *aent2 = (GF_AudioSampleEntryBox *) ent2; |
5743 | 0 | if (aent1->samplerate_hi != aent2->samplerate_hi) return GF_FALSE; |
5744 | 0 | if (aent1->samplerate_lo != aent2->samplerate_lo) return GF_FALSE; |
5745 | 0 | if (aent1->channel_count != aent2->channel_count) return GF_FALSE; |
5746 | 0 | } |
5747 | 0 | return GF_TRUE; |
5748 | 0 | } |
5749 | | |
5750 | 0 | if (sdesc_index1 && sdesc_index2) break; |
5751 | 0 | } |
5752 | 0 | if (!need_memcmp) return GF_TRUE; |
5753 | 0 | a = (GF_Box *)trak1->Media->information->sampleTable->SampleDescription; |
5754 | 0 | b = (GF_Box *)trak2->Media->information->sampleTable->SampleDescription; |
5755 | | //we ignore all bitrate boxes when comparing the box, disable their writing |
5756 | 0 | gf_isom_registry_disable(GF_ISOM_BOX_TYPE_BTRT, GF_TRUE); |
5757 | 0 | ret = gf_isom_box_equal(a,b); |
5758 | | //re-enable btrt writing |
5759 | 0 | gf_isom_registry_disable(GF_ISOM_BOX_TYPE_BTRT, GF_FALSE); |
5760 | |
|
5761 | 0 | return ret; |
5762 | 0 | } |
5763 | | |
5764 | | GF_EXPORT |
5765 | | u64 gf_isom_estimate_size(GF_ISOFile *movie) |
5766 | 0 | { |
5767 | 0 | GF_Err e; |
5768 | 0 | GF_Box *a; |
5769 | 0 | u32 i, count; |
5770 | 0 | u64 mdat_size; |
5771 | 0 | if (!movie || !movie->moov) return 0; |
5772 | | |
5773 | 0 | mdat_size = 0; |
5774 | 0 | count = gf_list_count(movie->moov->trackList); |
5775 | 0 | for (i=0; i<count; i++) { |
5776 | 0 | mdat_size += gf_isom_get_media_data_size(movie, i+1); |
5777 | 0 | } |
5778 | 0 | if (mdat_size) { |
5779 | 0 | mdat_size += 8; |
5780 | 0 | if (mdat_size > 0xFFFFFFFF) mdat_size += 8; |
5781 | 0 | } |
5782 | |
|
5783 | 0 | i=0; |
5784 | 0 | while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { |
5785 | 0 | e = gf_isom_box_size(a); |
5786 | 0 | if (e == GF_OK) |
5787 | 0 | mdat_size += a->size; |
5788 | 0 | } |
5789 | 0 | return mdat_size; |
5790 | 0 | } |
5791 | | |
5792 | | |
5793 | | //set shadowing on/off |
5794 | | #if 0 //unused |
5795 | | GF_Err gf_isom_remove_sync_shadows(GF_ISOFile *movie, u32 trackNumber) |
5796 | | { |
5797 | | GF_TrackBox *trak; |
5798 | | GF_SampleTableBox *stbl; |
5799 | | |
5800 | | if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; |
5801 | | trak = gf_isom_get_track_box(movie, trackNumber); |
5802 | | if (!trak) return GF_BAD_PARAM; |
5803 | | |
5804 | | stbl = trak->Media->information->sampleTable; |
5805 | | if (stbl->ShadowSync) { |
5806 | | gf_isom_box_del_parent(&stbl->child_boxes, (GF_Box *) stbl->ShadowSync); |
5807 | | stbl->ShadowSync = NULL; |
5808 | | } |
5809 | | return GF_OK; |
5810 | | } |
5811 | | |
5812 | | /*Use this function to do the shadowing if you use shadowing. |
5813 | | the sample to be shadowed MUST be a non-sync sample (ignored if not) |
5814 | | the sample shadowing must be a Sync sample (error if not)*/ |
5815 | | GF_Err gf_isom_set_sync_shadow(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u32 syncSample) |
5816 | | { |
5817 | | GF_TrackBox *trak; |
5818 | | GF_SampleTableBox *stbl; |
5819 | | GF_ISOSAPType isRAP; |
5820 | | GF_Err e; |
5821 | | |
5822 | | if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; |
5823 | | trak = gf_isom_get_track_box(movie, trackNumber); |
5824 | | if (!trak || !sampleNumber || !syncSample) return GF_BAD_PARAM; |
5825 | | |
5826 | | stbl = trak->Media->information->sampleTable; |
5827 | | if (!stbl->ShadowSync) { |
5828 | | stbl->ShadowSync = (GF_ShadowSyncBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSH); |
5829 | | if (!stbl->ShadowSync) return GF_OUT_OF_MEM; |
5830 | | } |
5831 | | |
5832 | | //if no sync, skip |
5833 | | if (!stbl->SyncSample) return GF_OK; |
5834 | | //else set the sync shadow. |
5835 | | //if the sample is sync, ignore |
5836 | | e = stbl_GetSampleRAP(stbl->SyncSample, sampleNumber, &isRAP, NULL, NULL); |
5837 | | if (e) return e; |
5838 | | if (isRAP) return GF_OK; |
5839 | | //if the shadowing sample is not sync, error |
5840 | | e = stbl_GetSampleRAP(stbl->SyncSample, syncSample, &isRAP, NULL, NULL); |
5841 | | if (e) return e; |
5842 | | if (!isRAP) return GF_BAD_PARAM; |
5843 | | |
5844 | | return stbl_SetSyncShadow(stbl->ShadowSync, sampleNumber, syncSample); |
5845 | | } |
5846 | | #endif |
5847 | | |
5848 | | //set the GroupID of a track (only used for interleaving) |
5849 | | GF_EXPORT |
5850 | | GF_Err gf_isom_set_track_interleaving_group(GF_ISOFile *movie, u32 trackNumber, u32 GroupID) |
5851 | 0 | { |
5852 | 0 | GF_TrackBox *trak; |
5853 | |
|
5854 | 0 | if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE; |
5855 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
5856 | 0 | if (!trak || !GroupID) return GF_BAD_PARAM; |
5857 | | |
5858 | 0 | trak->Media->information->sampleTable->groupID = GroupID; |
5859 | 0 | return GF_OK; |
5860 | 0 | } |
5861 | | |
5862 | | |
5863 | | //set the Priority of a track within a Group (only used for tight interleaving) |
5864 | | //Priority ranges from 1 to 9 |
5865 | | GF_EXPORT |
5866 | | GF_Err gf_isom_set_track_priority_in_group(GF_ISOFile *movie, u32 trackNumber, u32 Priority) |
5867 | 0 | { |
5868 | 0 | GF_TrackBox *trak; |
5869 | |
|
5870 | 0 | if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE; |
5871 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
5872 | 0 | if (!trak || !Priority) return GF_BAD_PARAM; |
5873 | | |
5874 | 0 | trak->Media->information->sampleTable->trackPriority = Priority > 255 ? 255 : Priority; |
5875 | 0 | return GF_OK; |
5876 | 0 | } |
5877 | | |
5878 | | //set the max SamplesPerChunk (for file optimization) |
5879 | | GF_EXPORT |
5880 | | GF_Err gf_isom_hint_max_chunk_size(GF_ISOFile *movie, u32 trackNumber, u32 maxChunkSize) |
5881 | 0 | { |
5882 | 0 | GF_TrackBox *trak; |
5883 | |
|
5884 | 0 | if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; |
5885 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
5886 | 0 | if (!trak || !maxChunkSize) return GF_BAD_PARAM; |
5887 | | |
5888 | 0 | trak->Media->information->sampleTable->MaxChunkSize = maxChunkSize; |
5889 | 0 | return GF_OK; |
5890 | 0 | } |
5891 | | |
5892 | | |
5893 | | //set the max SamplesPerChunk (for file optimization) |
5894 | | GF_EXPORT |
5895 | | GF_Err gf_isom_hint_max_chunk_duration(GF_ISOFile *movie, u32 trackNumber, u32 maxChunkDur) |
5896 | 0 | { |
5897 | 0 | GF_TrackBox *trak; |
5898 | |
|
5899 | 0 | if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; |
5900 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
5901 | 0 | if (!trak) return GF_BAD_PARAM; |
5902 | | |
5903 | 0 | trak->Media->information->sampleTable->MaxChunkDur = maxChunkDur; |
5904 | 0 | return GF_OK; |
5905 | 0 | } |
5906 | | |
5907 | | |
5908 | | GF_EXPORT |
5909 | | GF_Err gf_isom_set_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, const GF_SLConfig *slConfig) |
5910 | 0 | { |
5911 | 0 | GF_TrackBox *trak; |
5912 | 0 | GF_SampleEntryBox *entry; |
5913 | 0 | GF_Err e; |
5914 | 0 | GF_SLConfig **slc; |
5915 | 0 | GF_ESDBox *esds; |
5916 | |
|
5917 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
5918 | 0 | if (!trak) return GF_BAD_PARAM; |
5919 | | |
5920 | 0 | e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL); |
5921 | 0 | if (e) return e; |
5922 | | |
5923 | | //we must be sure we are not using a remote ESD |
5924 | 0 | switch (entry->type) { |
5925 | 0 | case GF_ISOM_BOX_TYPE_MP4S: |
5926 | 0 | esds = ((GF_MPEGSampleEntryBox *)entry)->esd; |
5927 | 0 | if (!esds || !esds->desc || !esds->desc->slConfig || (esds->desc->slConfig->predefined != SLPredef_MP4)) |
5928 | 0 | return GF_ISOM_INVALID_FILE; |
5929 | 0 | slc = & ((GF_MPEGSampleEntryBox *)entry)->slc; |
5930 | 0 | break; |
5931 | 0 | case GF_ISOM_BOX_TYPE_MP4A: |
5932 | 0 | esds = ((GF_MPEGAudioSampleEntryBox *)entry)->esd; |
5933 | 0 | if (!esds || !esds->desc || !esds->desc->slConfig || (esds->desc->slConfig->predefined != SLPredef_MP4)) |
5934 | 0 | return GF_ISOM_INVALID_FILE; |
5935 | 0 | slc = & ((GF_MPEGAudioSampleEntryBox *)entry)->slc; |
5936 | 0 | break; |
5937 | 0 | case GF_ISOM_BOX_TYPE_MP4V: |
5938 | 0 | esds = ((GF_MPEGVisualSampleEntryBox *)entry)->esd; |
5939 | 0 | if (!esds || !esds->desc || !esds->desc->slConfig || (esds->desc->slConfig->predefined != SLPredef_MP4)) |
5940 | 0 | return GF_ISOM_INVALID_FILE; |
5941 | 0 | slc = & ((GF_MPEGVisualSampleEntryBox *)entry)->slc; |
5942 | 0 | break; |
5943 | 0 | default: |
5944 | 0 | return GF_OK; |
5945 | 0 | } |
5946 | | |
5947 | 0 | if (*slc) { |
5948 | 0 | gf_odf_desc_del((GF_Descriptor *)*slc); |
5949 | 0 | *slc = NULL; |
5950 | 0 | } |
5951 | 0 | if (!slConfig) return GF_OK; |
5952 | | //finally duplicate the SL |
5953 | 0 | return gf_odf_desc_copy((GF_Descriptor *) slConfig, (GF_Descriptor **) slc); |
5954 | 0 | } |
5955 | | |
5956 | | #if 0 //unused |
5957 | | GF_Err gf_isom_get_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig **slConfig) |
5958 | | { |
5959 | | GF_TrackBox *trak; |
5960 | | GF_SampleEntryBox *entry; |
5961 | | GF_Err e; |
5962 | | GF_SLConfig *slc; |
5963 | | |
5964 | | trak = gf_isom_get_track_box(the_file, trackNumber); |
5965 | | if (!trak) return GF_BAD_PARAM; |
5966 | | |
5967 | | e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL); |
5968 | | if (e) return e; |
5969 | | |
5970 | | //we must be sure we are not using a remote ESD |
5971 | | slc = NULL; |
5972 | | *slConfig = NULL; |
5973 | | switch (entry->type) { |
5974 | | case GF_ISOM_BOX_TYPE_MP4S: |
5975 | | if (((GF_MPEGSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; |
5976 | | slc = ((GF_MPEGSampleEntryBox *)entry)->slc; |
5977 | | break; |
5978 | | case GF_ISOM_BOX_TYPE_MP4A: |
5979 | | if (((GF_MPEGAudioSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; |
5980 | | slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc; |
5981 | | break; |
5982 | | case GF_ISOM_BOX_TYPE_MP4V: |
5983 | | if (((GF_MPEGVisualSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; |
5984 | | slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc; |
5985 | | break; |
5986 | | default: |
5987 | | return GF_BAD_PARAM; |
5988 | | } |
5989 | | |
5990 | | if (!slc) return GF_OK; |
5991 | | //finally duplicate the SL |
5992 | | return gf_odf_desc_copy((GF_Descriptor *) slc, (GF_Descriptor **) slConfig); |
5993 | | } |
5994 | | |
5995 | | u32 gf_isom_get_track_group(GF_ISOFile *the_file, u32 trackNumber) |
5996 | | { |
5997 | | GF_TrackBox *trak; |
5998 | | trak = gf_isom_get_track_box(the_file, trackNumber); |
5999 | | if (!trak) return 0; |
6000 | | return trak->Media->information->sampleTable->groupID; |
6001 | | } |
6002 | | |
6003 | | u32 gf_isom_get_track_priority_in_group(GF_ISOFile *the_file, u32 trackNumber) |
6004 | | { |
6005 | | GF_TrackBox *trak; |
6006 | | trak = gf_isom_get_track_box(the_file, trackNumber); |
6007 | | if (!trak) return 0; |
6008 | | return trak->Media->information->sampleTable->trackPriority; |
6009 | | } |
6010 | | #endif |
6011 | | |
6012 | | |
6013 | | GF_EXPORT |
6014 | | GF_Err gf_isom_make_interleave_ex(GF_ISOFile *file, GF_Fraction *fTimeInSec) |
6015 | 0 | { |
6016 | 0 | GF_Err e; |
6017 | 0 | u64 itime; |
6018 | 0 | if (!file || !fTimeInSec->den || (fTimeInSec->num<=0)) return GF_BAD_PARAM; |
6019 | | |
6020 | 0 | itime = (u64) fTimeInSec->num; |
6021 | 0 | itime *= gf_isom_get_timescale(file); |
6022 | 0 | itime /= fTimeInSec->den; |
6023 | 0 | if (file->storageMode==GF_ISOM_STORE_FASTSTART) { |
6024 | 0 | return gf_isom_set_interleave_time(file, (u32) itime); |
6025 | 0 | } |
6026 | 0 | if (gf_isom_get_mode(file) < GF_ISOM_OPEN_EDIT) return GF_BAD_PARAM; |
6027 | 0 | e = gf_isom_set_storage_mode(file, GF_ISOM_STORE_DRIFT_INTERLEAVED); |
6028 | 0 | if (e) return e; |
6029 | 0 | return gf_isom_set_interleave_time(file, (u32) itime); |
6030 | 0 | } |
6031 | | |
6032 | | GF_EXPORT |
6033 | | GF_Err gf_isom_make_interleave(GF_ISOFile *file, Double TimeInSec) |
6034 | 0 | { |
6035 | 0 | GF_Fraction f; |
6036 | 0 | f.num = (s32) (TimeInSec * 1000); |
6037 | 0 | f.den = 1000; |
6038 | 0 | return gf_isom_make_interleave_ex(file, &f); |
6039 | |
|
6040 | 0 | } |
6041 | | GF_EXPORT |
6042 | | GF_Err gf_isom_set_handler_name(GF_ISOFile *the_file, u32 trackNumber, const char *nameUTF8) |
6043 | 0 | { |
6044 | 0 | GF_TrackBox *trak; |
6045 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
6046 | 0 | if (!trak) return GF_BAD_PARAM; |
6047 | 0 | if (trak->Media->handler->nameUTF8) gf_free(trak->Media->handler->nameUTF8); |
6048 | 0 | trak->Media->handler->nameUTF8 = NULL; |
6049 | |
|
6050 | 0 | if (!nameUTF8) return GF_OK; |
6051 | | |
6052 | 0 | if (!strnicmp(nameUTF8, "file://", 7)) { |
6053 | 0 | u8 BOM[4]; |
6054 | 0 | FILE *f = gf_fopen(nameUTF8+7, "rb"); |
6055 | 0 | u64 size; |
6056 | 0 | if (!f) return GF_URL_ERROR; |
6057 | 0 | size = gf_fsize(f); |
6058 | 0 | if (3!=gf_fread(BOM, 3, f)) { |
6059 | 0 | gf_fclose(f); |
6060 | 0 | return GF_CORRUPTED_DATA; |
6061 | 0 | } |
6062 | | /*skip BOM if any*/ |
6063 | 0 | if ((BOM[0]==0xEF) && (BOM[1]==0xBB) && (BOM[2]==0xBF)) size -= 3; |
6064 | 0 | else if ((BOM[0]==0xEF) || (BOM[0]==0xFF)) { |
6065 | 0 | gf_fclose(f); |
6066 | 0 | return GF_BAD_PARAM; |
6067 | 0 | } |
6068 | 0 | else gf_fseek(f, 0, SEEK_SET); |
6069 | 0 | trak->Media->handler->nameUTF8 = (char*)gf_malloc(sizeof(char)*(size_t)(size+1)); |
6070 | 0 | if (!trak->Media->handler->nameUTF8) { |
6071 | 0 | gf_fclose(f); |
6072 | 0 | return GF_OUT_OF_MEM; |
6073 | 0 | } |
6074 | 0 | size = gf_fread(trak->Media->handler->nameUTF8, (size_t)size, f); |
6075 | 0 | trak->Media->handler->nameUTF8[size] = 0; |
6076 | 0 | gf_fclose(f); |
6077 | 0 | } else { |
6078 | 0 | u32 i, j, len; |
6079 | 0 | char szOrig[1024], szLine[1024]; |
6080 | 0 | strcpy(szOrig, nameUTF8); |
6081 | 0 | j=0; |
6082 | 0 | len = (u32) strlen(szOrig); |
6083 | 0 | for (i=0; i<len; i++) { |
6084 | 0 | if (szOrig[i] & 0x80) { |
6085 | | /*non UTF8 (likely some win-CP)*/ |
6086 | 0 | if ( (szOrig[i+1] & 0xc0) != 0x80) { |
6087 | 0 | szLine[j] = 0xc0 | ( (szOrig[i] >> 6) & 0x3 ); |
6088 | 0 | j++; |
6089 | 0 | szOrig[i] &= 0xbf; |
6090 | 0 | } |
6091 | | /*UTF8 2 bytes char */ |
6092 | 0 | else if ( (szOrig[i] & 0xe0) == 0xc0) { |
6093 | 0 | szLine[j] = szOrig[i]; |
6094 | 0 | i++; |
6095 | 0 | j++; |
6096 | 0 | } |
6097 | | /*UTF8 3 bytes char */ |
6098 | 0 | else if ( (szOrig[i] & 0xf0) == 0xe0) { |
6099 | 0 | szLine[j] = szOrig[i]; |
6100 | 0 | i++; |
6101 | 0 | j++; |
6102 | 0 | szLine[j] = szOrig[i]; |
6103 | 0 | i++; |
6104 | 0 | j++; |
6105 | 0 | } |
6106 | | /*UTF8 4 bytes char */ |
6107 | 0 | else if ( (szOrig[i] & 0xf8) == 0xf0) { |
6108 | 0 | szLine[j] = szOrig[i]; |
6109 | 0 | i++; |
6110 | 0 | j++; |
6111 | 0 | szLine[j] = szOrig[i]; |
6112 | 0 | i++; |
6113 | 0 | j++; |
6114 | 0 | szLine[j] = szOrig[i]; |
6115 | 0 | i++; |
6116 | 0 | j++; |
6117 | 0 | } |
6118 | 0 | } |
6119 | 0 | szLine[j] = szOrig[i]; |
6120 | 0 | j++; |
6121 | 0 | } |
6122 | 0 | szLine[j] = 0; |
6123 | 0 | trak->Media->handler->nameUTF8 = gf_strdup(szLine); |
6124 | 0 | } |
6125 | 0 | return GF_OK; |
6126 | 0 | } |
6127 | | |
6128 | | #if 0 //unused |
6129 | | /*clones root OD from input to output file, without copying root OD track references*/ |
6130 | | GF_Err gf_isom_clone_root_od(GF_ISOFile *input, GF_ISOFile *output) |
6131 | | { |
6132 | | GF_List *esds; |
6133 | | GF_Err e; |
6134 | | u32 i; |
6135 | | GF_Descriptor *desc; |
6136 | | |
6137 | | e = gf_isom_remove_root_od(output); |
6138 | | if (e) return e; |
6139 | | if (!input->moov || !input->moov->iods || !input->moov->iods->descriptor) return GF_OK; |
6140 | | e = gf_isom_insert_moov(output); |
6141 | | if (e) return e; |
6142 | | e = AddMovieIOD(output->moov, 0); |
6143 | | if (e) return e; |
6144 | | if (output->moov->iods->descriptor) gf_odf_desc_del(output->moov->iods->descriptor); |
6145 | | output->moov->iods->descriptor = NULL; |
6146 | | gf_odf_desc_copy(input->moov->iods->descriptor, &output->moov->iods->descriptor); |
6147 | | |
6148 | | switch (output->moov->iods->descriptor->tag) { |
6149 | | case GF_ODF_ISOM_IOD_TAG: |
6150 | | esds = ((GF_IsomInitialObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors; |
6151 | | break; |
6152 | | case GF_ODF_ISOM_OD_TAG: |
6153 | | esds = ((GF_IsomObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors; |
6154 | | break; |
6155 | | default: |
6156 | | return GF_ISOM_INVALID_FILE; |
6157 | | } |
6158 | | |
6159 | | //get the desc |
6160 | | i=0; |
6161 | | while ((desc = (GF_Descriptor*)gf_list_enum(esds, &i))) { |
6162 | | gf_odf_desc_del(desc); |
6163 | | gf_list_rem(esds, i-1); |
6164 | | } |
6165 | | return GF_OK; |
6166 | | } |
6167 | | #endif |
6168 | | |
6169 | | GF_EXPORT |
6170 | | GF_Err gf_isom_set_media_type(GF_ISOFile *movie, u32 trackNumber, u32 new_type) |
6171 | 0 | { |
6172 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
6173 | 0 | if (!trak || !new_type) return GF_BAD_PARAM; |
6174 | 0 | trak->Media->handler->handlerType = new_type; |
6175 | 0 | return GF_OK; |
6176 | 0 | } |
6177 | | |
6178 | | GF_EXPORT |
6179 | | GF_Err gf_isom_set_media_subtype(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, u32 new_type) |
6180 | 0 | { |
6181 | 0 | GF_SampleEntryBox*entry; |
6182 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
6183 | 0 | if (!trak || !sampleDescriptionIndex || !new_type) return GF_BAD_PARAM; |
6184 | | |
6185 | 0 | entry = (GF_SampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex - 1); |
6186 | 0 | if (!entry) return GF_BAD_PARAM; |
6187 | 0 | if (entry->type==GF_ISOM_BOX_TYPE_GNRV) { |
6188 | 0 | ((GF_GenericVisualSampleEntryBox *)entry)->EntryType = new_type; |
6189 | 0 | } else if (entry->type==GF_ISOM_BOX_TYPE_GNRA) { |
6190 | 0 | ((GF_GenericAudioSampleEntryBox *)entry)->EntryType = new_type; |
6191 | 0 | } else if (entry->type==GF_ISOM_BOX_TYPE_GNRM) { |
6192 | 0 | ((GF_GenericSampleEntryBox *)entry)->EntryType = new_type; |
6193 | 0 | } else { |
6194 | 0 | entry->type = new_type; |
6195 | 0 | } |
6196 | 0 | return GF_OK; |
6197 | 0 | } |
6198 | | |
6199 | | |
6200 | | #if 0 //unused |
6201 | | GF_Err gf_isom_set_JPEG2000(GF_ISOFile *mov, Bool set_on) |
6202 | | { |
6203 | | if (!mov) return GF_BAD_PARAM; |
6204 | | mov->is_jp2 = set_on; |
6205 | | return GF_OK; |
6206 | | } |
6207 | | #endif |
6208 | | |
6209 | | GF_Err gf_isom_remove_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID) |
6210 | 0 | { |
6211 | 0 | u32 i, count; |
6212 | 0 | GF_List *list; |
6213 | |
|
6214 | 0 | if (trackNumber==(u32) -1) { |
6215 | 0 | if (!movie) return GF_BAD_PARAM; |
6216 | 0 | list = movie->TopBoxes; |
6217 | 0 | } else if (trackNumber) { |
6218 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
6219 | 0 | if (!trak) return GF_BAD_PARAM; |
6220 | 0 | list = trak->child_boxes; |
6221 | 0 | } else { |
6222 | 0 | if (!movie) return GF_BAD_PARAM; |
6223 | 0 | list = movie->moov->child_boxes; |
6224 | 0 | } |
6225 | | |
6226 | 0 | count = list ? gf_list_count(list) : 0; |
6227 | 0 | for (i=0; i<count; i++) { |
6228 | 0 | GF_UnknownUUIDBox *uuid = (GF_UnknownUUIDBox *)gf_list_get(list, i); |
6229 | 0 | if (uuid->type != GF_ISOM_BOX_TYPE_UUID) continue; |
6230 | 0 | if (memcmp(UUID, uuid->uuid, sizeof(bin128))) continue; |
6231 | 0 | gf_list_rem(list, i); |
6232 | 0 | i--; |
6233 | 0 | count--; |
6234 | 0 | gf_isom_box_del((GF_Box*)uuid); |
6235 | 0 | } |
6236 | 0 | return GF_OK; |
6237 | 0 | } |
6238 | | |
6239 | | GF_EXPORT |
6240 | | GF_Err gf_isom_add_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID, const u8 *data, u32 data_size) |
6241 | 0 | { |
6242 | 0 | GF_List *list; |
6243 | 0 | u32 btype; |
6244 | 0 | GF_Box *box; |
6245 | 0 | GF_UnknownUUIDBox *uuidb; |
6246 | |
|
6247 | 0 | if (data_size && !data) return GF_BAD_PARAM; |
6248 | 0 | if (trackNumber==(u32) -1) { |
6249 | 0 | if (!movie) return GF_BAD_PARAM; |
6250 | 0 | list = movie->TopBoxes; |
6251 | 0 | } else if (trackNumber) { |
6252 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
6253 | 0 | if (!trak) return GF_BAD_PARAM; |
6254 | 0 | if (!trak->child_boxes) trak->child_boxes = gf_list_new(); |
6255 | 0 | list = trak->child_boxes; |
6256 | 0 | } else { |
6257 | 0 | if (!movie) return GF_BAD_PARAM; |
6258 | 0 | if (!movie->moov->child_boxes) movie->moov->child_boxes = gf_list_new(); |
6259 | 0 | list = movie->moov->child_boxes; |
6260 | 0 | } |
6261 | 0 | btype = gf_isom_solve_uuid_box((char *) UUID); |
6262 | 0 | if (!btype) btype = GF_ISOM_BOX_TYPE_UUID; |
6263 | 0 | box = gf_isom_box_new(btype); |
6264 | 0 | if (!box) return GF_OUT_OF_MEM; |
6265 | 0 | uuidb = (GF_UnknownUUIDBox*)box; |
6266 | 0 | uuidb->internal_4cc = gf_isom_solve_uuid_box((char *) UUID); |
6267 | 0 | memcpy(uuidb->uuid, UUID, sizeof(bin128)); |
6268 | 0 | uuidb->dataSize = data_size; |
6269 | 0 | if (data_size) { |
6270 | 0 | uuidb->data = (char*)gf_malloc(sizeof(char)*data_size); |
6271 | 0 | if (!uuidb->data) return GF_OUT_OF_MEM; |
6272 | 0 | memcpy(uuidb->data, data, sizeof(char)*data_size); |
6273 | 0 | } |
6274 | 0 | gf_list_add(list, uuidb); |
6275 | 0 | return GF_OK; |
6276 | 0 | } |
6277 | | |
6278 | | |
6279 | | GF_EXPORT |
6280 | | GF_Err gf_isom_apple_set_tag_ex(GF_ISOFile *mov, GF_ISOiTunesTag tag, const u8 *data, u32 data_len, u64 int_val, u32 int_val2, const char *in_cust_name, const char *in_cust_mean, u32 locale) |
6281 | 0 | { |
6282 | 0 | GF_Err e; |
6283 | 0 | GF_ItemListBox *ilst; |
6284 | 0 | GF_MetaBox *meta; |
6285 | 0 | GF_ListItemBox *info; |
6286 | 0 | u32 btype=0, i, itype; |
6287 | 0 | s32 tag_idx; |
6288 | 0 | u32 n=0, d=0; |
6289 | 0 | u8 loc_data[10]; |
6290 | 0 | u32 int_flags = 0x15; |
6291 | 0 | GF_DataBox *dbox; |
6292 | 0 | char *cust_mean=NULL, *cust_name=NULL; |
6293 | |
|
6294 | 0 | if (in_cust_name || in_cust_mean) tag = GF_4CC('c','u','s','t'); |
6295 | |
|
6296 | 0 | e = gf_isom_can_access_movie(mov, GF_ISOM_OPEN_WRITE); |
6297 | 0 | if (e) return e; |
6298 | | |
6299 | 0 | tag_idx = gf_itags_find_by_itag(tag); |
6300 | 0 | if (tag_idx<0) { |
6301 | 0 | itype = GF_ITAG_STR; |
6302 | 0 | } else { |
6303 | 0 | itype = gf_itags_get_type(tag_idx); |
6304 | 0 | } |
6305 | 0 | meta = (GF_MetaBox *) gf_isom_create_meta_extensions(mov, GF_FALSE); |
6306 | 0 | if (!meta) return GF_BAD_PARAM; |
6307 | 0 | if (mov->brand->majorBrand == GF_4CC_CSTR("qt ")) |
6308 | 0 | meta->write_qt = 1; |
6309 | |
|
6310 | 0 | ilst = gf_isom_locate_box(meta->child_boxes, GF_ISOM_BOX_TYPE_ILST, NULL); |
6311 | 0 | if (!ilst) { |
6312 | 0 | ilst = (GF_ItemListBox *) gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_ILST); |
6313 | 0 | } |
6314 | |
|
6315 | 0 | if (tag==GF_ISOM_ITUNE_RESET) { |
6316 | 0 | gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) ilst); |
6317 | | //if last, delete udta - we may still have a handler box remaining |
6318 | 0 | if ((gf_list_count(meta->child_boxes) <= 1) && (gf_list_count(mov->moov->udta->recordList)==1)) { |
6319 | 0 | gf_isom_box_del_parent(&mov->moov->child_boxes, (GF_Box *) mov->moov->udta); |
6320 | 0 | mov->moov->udta = NULL; |
6321 | 0 | } |
6322 | 0 | return GF_OK; |
6323 | 0 | } |
6324 | | |
6325 | 0 | if (tag==GF_ISOM_ITUNE_GENRE) { |
6326 | 0 | if (!int_val && data) { |
6327 | 0 | int_val = gf_id3_get_genre_tag(data); |
6328 | 0 | if (int_val) { |
6329 | 0 | data = NULL; |
6330 | 0 | data_len = 0; |
6331 | 0 | itype = GF_ITAG_INT16; |
6332 | 0 | int_flags = 0; |
6333 | 0 | } |
6334 | 0 | } |
6335 | 0 | btype = data ? GF_ISOM_ITUNE_GENRE_USER : GF_ISOM_ITUNE_GENRE; |
6336 | 0 | } else if (tag==GF_4CC('c','u','s','t') ) { |
6337 | 0 | if (in_cust_name || in_cust_mean) { |
6338 | 0 | if (in_cust_mean && in_cust_mean[0]) |
6339 | 0 | cust_mean = gf_strdup(in_cust_mean); |
6340 | 0 | if (in_cust_name && in_cust_name[0]) |
6341 | 0 | cust_name = gf_strdup(in_cust_name); |
6342 | 0 | btype = GF_ISOM_BOX_TYPE_iTunesSpecificInfo; |
6343 | 0 | } else { |
6344 | 0 | char *sep = strchr(data, ','); |
6345 | 0 | if (sep) { |
6346 | 0 | sep[0] = 0; |
6347 | 0 | if (data[0]) |
6348 | 0 | cust_mean = gf_strdup(data); |
6349 | 0 | sep[0] = ','; |
6350 | 0 | sep++; |
6351 | |
|
6352 | 0 | char *sep2 = strchr(sep+1, ','); |
6353 | 0 | if (sep2) { |
6354 | 0 | sep2[0] = 0; |
6355 | 0 | if (sep[0]) |
6356 | 0 | cust_name = gf_strdup(sep); |
6357 | 0 | sep2[0] = ','; |
6358 | 0 | data_len -= (u32) (sep2-(char*)data) +1; |
6359 | 0 | data = sep2+1; |
6360 | 0 | } else { |
6361 | 0 | data_len -= (u32) (sep-(char*)data)+1; |
6362 | 0 | data = sep+1; |
6363 | 0 | } |
6364 | 0 | btype = GF_ISOM_BOX_TYPE_iTunesSpecificInfo; |
6365 | 0 | } |
6366 | 0 | } |
6367 | 0 | } else { |
6368 | 0 | btype = tag; |
6369 | 0 | } |
6370 | | /*remove tag*/ |
6371 | 0 | i = 0; |
6372 | 0 | while ((info = (GF_ListItemBox*)gf_list_enum(ilst->child_boxes, &i))) { |
6373 | 0 | if (info->type==GF_ISOM_BOX_TYPE_iTunesSpecificInfo) { |
6374 | |
|
6375 | 0 | if (info->name && cust_name && !strcmp(info->name->string, cust_name)) {} |
6376 | 0 | else if (!info->name && !cust_name) {} |
6377 | 0 | else continue; |
6378 | | |
6379 | 0 | if (info->mean && cust_mean && !strcmp(info->mean->string, cust_mean)) {} |
6380 | 0 | else if (!info->mean && !cust_mean) {} |
6381 | 0 | else continue; |
6382 | | |
6383 | 0 | gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *) info); |
6384 | 0 | info = NULL; |
6385 | 0 | break; |
6386 | 0 | } |
6387 | 0 | if (info->type==btype) { |
6388 | 0 | gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *) info); |
6389 | 0 | info = NULL; |
6390 | 0 | break; |
6391 | 0 | } |
6392 | 0 | if (info->type==GF_ISOM_BOX_TYPE_UNKNOWN) { |
6393 | 0 | GF_UnknownBox *u = (GF_UnknownBox *) info; |
6394 | 0 | if (u->original_4cc==btype) { |
6395 | 0 | gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *) info); |
6396 | 0 | info = NULL; |
6397 | 0 | break; |
6398 | 0 | } |
6399 | 0 | } |
6400 | 0 | } |
6401 | |
|
6402 | 0 | if (!data && data_len) { |
6403 | 0 | if (!gf_list_count(ilst->child_boxes) ) |
6404 | 0 | gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) ilst); |
6405 | 0 | if (cust_mean) gf_free(cust_mean); |
6406 | 0 | if (cust_name) gf_free(cust_name); |
6407 | 0 | return GF_OK; |
6408 | 0 | } |
6409 | | |
6410 | | //watch out for cprt, we don't want to create a regular cprt box |
6411 | 0 | if (btype==GF_ISOM_ITUNE_COPYRIGHT) { |
6412 | 0 | info = (GF_ListItemBox *)gf_isom_box_new(GF_ISOM_ITUNE_TOOL); |
6413 | 0 | info->type = GF_ISOM_ITUNE_COPYRIGHT; |
6414 | 0 | } else { |
6415 | 0 | info = (GF_ListItemBox *)gf_isom_box_new(btype); |
6416 | 0 | } |
6417 | 0 | if (info == NULL) { |
6418 | 0 | if (cust_mean) gf_free(cust_mean); |
6419 | 0 | if (cust_name) gf_free(cust_name); |
6420 | 0 | return GF_OUT_OF_MEM; |
6421 | 0 | } |
6422 | | |
6423 | 0 | dbox = (GF_DataBox *)gf_isom_box_new_parent(&info->child_boxes, GF_ISOM_BOX_TYPE_DATA); |
6424 | 0 | if (!dbox) { |
6425 | 0 | gf_isom_box_del((GF_Box *)info); |
6426 | 0 | if (cust_mean) gf_free(cust_mean); |
6427 | 0 | if (cust_name) gf_free(cust_name); |
6428 | 0 | return GF_OUT_OF_MEM; |
6429 | 0 | } |
6430 | 0 | dbox->locale = locale; |
6431 | |
|
6432 | 0 | if (info->type!=GF_ISOM_BOX_TYPE_UNKNOWN) { |
6433 | 0 | info->data = dbox; |
6434 | |
|
6435 | 0 | if (cust_name) { |
6436 | 0 | info->name = (GF_NameBox *)gf_isom_box_new_parent(&info->child_boxes, GF_QT_BOX_TYPE_NAME); |
6437 | 0 | info->name->string = cust_name; |
6438 | 0 | } |
6439 | 0 | if (cust_mean) { |
6440 | 0 | info->mean = (GF_NameBox *)gf_isom_box_new_parent(&info->child_boxes, GF_QT_BOX_TYPE_MEAN); |
6441 | 0 | info->mean->string = cust_mean; |
6442 | 0 | } |
6443 | 0 | } |
6444 | |
|
6445 | 0 | switch (itype) { |
6446 | 0 | case GF_ITAG_FRAC6: |
6447 | 0 | case GF_ITAG_FRAC8: |
6448 | 0 | if (data && data_len) { |
6449 | 0 | if (sscanf(data, "%u/%u", &n, &d) != 2) { |
6450 | 0 | n = d = 0; |
6451 | 0 | if (sscanf(data, "%u", &n) != 1) |
6452 | 0 | n = 0; |
6453 | 0 | } |
6454 | 0 | } else { |
6455 | 0 | n = (u32) int_val; |
6456 | 0 | d = int_val2; |
6457 | 0 | } |
6458 | 0 | if (n) { |
6459 | 0 | memset(loc_data, 0, sizeof(char) * 8); |
6460 | 0 | data_len = (itype == GF_ITAG_FRAC6) ? 6 : 8; |
6461 | 0 | loc_data[3] = n; |
6462 | 0 | loc_data[2] = n >> 8; |
6463 | 0 | loc_data[5] = d; |
6464 | 0 | loc_data[4] = d >> 8; |
6465 | 0 | data = loc_data; |
6466 | 0 | } else { |
6467 | 0 | data = NULL; |
6468 | 0 | } |
6469 | 0 | dbox->flags = 0x15; |
6470 | 0 | break; |
6471 | 0 | case GF_ITAG_BOOL: |
6472 | 0 | loc_data[0] = 0; |
6473 | 0 | if (data && data_len) { |
6474 | 0 | if ( !strcmp(data, "yes") || !strcmp(data, "1") || !strcmp(data, "true")) |
6475 | 0 | loc_data[0] = 1; |
6476 | 0 | } else { |
6477 | 0 | loc_data[0] = int_val ? 1 : 0; |
6478 | 0 | } |
6479 | 0 | data = loc_data; |
6480 | 0 | data_len = 1; |
6481 | 0 | dbox->flags = int_flags; |
6482 | 0 | break; |
6483 | 0 | case GF_ITAG_INT8: |
6484 | 0 | loc_data[0] = 0; |
6485 | 0 | if (data && data_len) int_val = atoi(data); |
6486 | 0 | loc_data[0] = (u8) int_val; |
6487 | 0 | data = loc_data; |
6488 | 0 | data_len = 1; |
6489 | 0 | dbox->flags = int_flags; |
6490 | 0 | break; |
6491 | 0 | case GF_ITAG_INT16: |
6492 | 0 | loc_data[0] = 0; |
6493 | 0 | if (data && data_len) int_val = atoi(data); |
6494 | 0 | loc_data[1] = (u8) int_val; |
6495 | 0 | loc_data[0] = (u8) (int_val>>8); |
6496 | 0 | data = loc_data; |
6497 | 0 | data_len = 2; |
6498 | 0 | dbox->flags = int_flags; |
6499 | 0 | break; |
6500 | 0 | case GF_ITAG_INT32: |
6501 | 0 | loc_data[0] = 0; |
6502 | 0 | if (data && data_len) int_val = atoi(data); |
6503 | 0 | loc_data[3] = (u8) int_val; |
6504 | 0 | loc_data[2] = (u8) (int_val>>8); |
6505 | 0 | loc_data[1] = (u8) (int_val>>16); |
6506 | 0 | loc_data[0] = (u8) (int_val>>24); |
6507 | 0 | data = loc_data; |
6508 | 0 | data_len = 4; |
6509 | 0 | dbox->flags = int_flags; |
6510 | 0 | break; |
6511 | 0 | case GF_ITAG_INT64: |
6512 | 0 | loc_data[0] = 0; |
6513 | 0 | if (data && data_len) sscanf(data, LLU, &int_val); |
6514 | 0 | loc_data[7] = (u8) int_val; |
6515 | 0 | loc_data[6] = (u8) (int_val>>8); |
6516 | 0 | loc_data[5] = (u8) (int_val>>16); |
6517 | 0 | loc_data[4] = (u8) (int_val>>24); |
6518 | 0 | loc_data[3] = (u8) (int_val>>32); |
6519 | 0 | loc_data[2] = (u8) (int_val>>40); |
6520 | 0 | loc_data[1] = (u8) (int_val>>48); |
6521 | 0 | loc_data[0] = (u8) (int_val>>56); |
6522 | 0 | data = loc_data; |
6523 | 0 | data_len = 8; |
6524 | 0 | dbox->flags = int_flags; |
6525 | 0 | break; |
6526 | 0 | default: |
6527 | 0 | dbox->flags = 1; |
6528 | 0 | break; |
6529 | 0 | } |
6530 | | |
6531 | 0 | if (!data) return GF_BAD_PARAM; |
6532 | | |
6533 | | |
6534 | 0 | if (tag==GF_ISOM_ITUNE_COVER_ART) { |
6535 | 0 | info->data->flags = 0; |
6536 | | /*check for PNG sig*/ |
6537 | 0 | if ((data_len>4) && (data[0] == 0x89) && (data[1] == 0x50) && (data[2] == 0x4E) && (data[3] == 0x47) ) { |
6538 | 0 | info->data->flags = 14; |
6539 | 0 | } |
6540 | | //JPG and JFIF - do not check second tag type |
6541 | 0 | else if ((data_len>4) && (data[0] == 0xFF) && (data[1] == 0xD8) && (data[2] == 0xFF) /*&& ((data[3] == 0xE0) || (data[3] == 0xDB))*/ ) { |
6542 | 0 | info->data->flags = 13; |
6543 | 0 | } |
6544 | | //GIF |
6545 | 0 | else if ((data_len>3) && (data[0] == 'G') && (data[1] == 'I') && (data[2] == 'F') ) { |
6546 | 0 | info->data->flags = 12; |
6547 | 0 | } |
6548 | 0 | } |
6549 | |
|
6550 | 0 | dbox->dataSize = data_len; |
6551 | 0 | dbox->data = (char*)gf_malloc(sizeof(char)*data_len); |
6552 | 0 | if (!dbox->data) return GF_OUT_OF_MEM; |
6553 | 0 | memcpy(dbox->data, data, sizeof(char)*data_len); |
6554 | |
|
6555 | 0 | if (!info && !gf_list_count(ilst->child_boxes) ) { |
6556 | 0 | gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) ilst); |
6557 | 0 | return GF_OK; |
6558 | 0 | } |
6559 | 0 | if (!ilst->child_boxes) ilst->child_boxes = gf_list_new(); |
6560 | |
|
6561 | 0 | return gf_list_add(ilst->child_boxes, info); |
6562 | 0 | } |
6563 | | |
6564 | | GF_EXPORT |
6565 | | GF_Err gf_isom_apple_set_tag(GF_ISOFile *mov, GF_ISOiTunesTag tag, const u8 *data, u32 data_len, u64 int_val, u32 int_val2) |
6566 | 0 | { |
6567 | 0 | return gf_isom_apple_set_tag_ex(mov, tag, data, data_len, int_val, int_val2, NULL, NULL, 0); |
6568 | 0 | } |
6569 | | |
6570 | | #include <gpac/utf.h> |
6571 | | |
6572 | | GF_EXPORT |
6573 | | GF_Err gf_isom_wma_set_tag(GF_ISOFile *mov, char *name, char *value) |
6574 | 0 | { |
6575 | 0 | GF_Err e; |
6576 | 0 | GF_XtraTag *tag=NULL; |
6577 | 0 | u32 count, i; |
6578 | 0 | GF_XtraBox *xtra; |
6579 | |
|
6580 | 0 | e = gf_isom_can_access_movie(mov, GF_ISOM_OPEN_WRITE); |
6581 | 0 | if (e) return e; |
6582 | | |
6583 | 0 | gf_isom_create_meta_extensions(mov, GF_FALSE); |
6584 | |
|
6585 | 0 | xtra = (GF_XtraBox *) gf_isom_create_meta_extensions(mov, GF_TRUE); |
6586 | 0 | if (!xtra) return GF_BAD_PARAM; |
6587 | | |
6588 | 0 | count = gf_list_count(xtra->tags); |
6589 | 0 | for (i=0; i<count; i++) { |
6590 | 0 | tag = gf_list_get(xtra->tags, i); |
6591 | 0 | if (name && tag->name && !strcmp(tag->name, name)) { |
6592 | |
|
6593 | 0 | } else { |
6594 | 0 | tag = NULL; |
6595 | 0 | continue; |
6596 | 0 | } |
6597 | | |
6598 | 0 | if (!value) { |
6599 | 0 | gf_list_rem(xtra->tags, i); |
6600 | 0 | gf_free(tag->name); |
6601 | 0 | if (tag->prop_value) gf_free(tag->prop_value); |
6602 | 0 | gf_free(tag); |
6603 | 0 | return GF_OK; |
6604 | 0 | } |
6605 | 0 | gf_free(tag->prop_value); |
6606 | 0 | tag->prop_value = NULL; |
6607 | 0 | break; |
6608 | 0 | } |
6609 | 0 | if (!tag) { |
6610 | 0 | if (!name) return GF_OK; |
6611 | | |
6612 | 0 | GF_SAFEALLOC(tag, GF_XtraTag); |
6613 | 0 | tag->name = gf_strdup(name); |
6614 | 0 | tag->prop_type = 0; |
6615 | 0 | tag->flags = 1; |
6616 | 0 | gf_list_add(xtra->tags, tag); |
6617 | 0 | } |
6618 | | |
6619 | 0 | u32 len = (u32) strlen(value); |
6620 | 0 | tag->prop_value = gf_malloc(sizeof(u16) * ((len/2)*2+2) ); |
6621 | 0 | memset(tag->prop_value, 0, sizeof(u16) * ((len/2)*2+2) ); |
6622 | 0 | if (len) { |
6623 | 0 | u32 _len = gf_utf8_mbstowcs((u16 *) tag->prop_value, len+1, (const char **) &value); |
6624 | 0 | if (_len == GF_UTF8_FAIL) _len = 0; |
6625 | 0 | tag->prop_value[2 * _len] = 0; |
6626 | 0 | tag->prop_value[2 * _len + 1] = 0; |
6627 | 0 | tag->prop_size = 2 * _len + 2; |
6628 | 0 | } else { |
6629 | 0 | tag->prop_size = 2; |
6630 | 0 | } |
6631 | 0 | return GF_OK; |
6632 | 0 | } |
6633 | | |
6634 | | GF_EXPORT |
6635 | | GF_Err gf_isom_set_qt_key(GF_ISOFile *movie, GF_QT_UDTAKey *key) |
6636 | 0 | { |
6637 | 0 | GF_Err e; |
6638 | 0 | GF_MetaBox *meta; |
6639 | 0 | GF_ItemListBox *ilst; |
6640 | 0 | GF_MetaKeysBox *keys; |
6641 | 0 | u32 i, nb_keys; |
6642 | |
|
6643 | 0 | if (!movie) return GF_BAD_PARAM; |
6644 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
6645 | 0 | if (e) { |
6646 | 0 | gf_isom_set_last_error(movie, e); |
6647 | 0 | return 0; |
6648 | 0 | } |
6649 | 0 | e = gf_isom_insert_moov(movie); |
6650 | 0 | if (e) return e; |
6651 | | |
6652 | 0 | meta = (GF_MetaBox *) gf_isom_create_meta_extensions(movie, 2); |
6653 | 0 | if (!meta) return GF_BAD_PARAM; |
6654 | | |
6655 | 0 | keys = (GF_MetaKeysBox *) gf_isom_locate_box(meta->child_boxes, GF_ISOM_BOX_TYPE_KEYS, NULL); |
6656 | 0 | if (!keys) { |
6657 | 0 | keys = (GF_MetaKeysBox *) gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_KEYS); |
6658 | 0 | meta->keys = keys; |
6659 | 0 | } |
6660 | 0 | ilst = (GF_ItemListBox *) gf_isom_locate_box(meta->child_boxes, GF_ISOM_BOX_TYPE_ILST, NULL); |
6661 | 0 | if (!ilst) { |
6662 | 0 | ilst = (GF_ItemListBox *) gf_isom_box_new_parent(&meta->child_boxes, GF_ISOM_BOX_TYPE_ILST); |
6663 | 0 | } |
6664 | 0 | if (!keys || !ilst) return GF_OUT_OF_MEM; |
6665 | | |
6666 | 0 | nb_keys = gf_list_count(keys->keys); |
6667 | 0 | if (!key) { |
6668 | 0 | gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) keys); |
6669 | 0 | for (i=0; i<gf_list_count(ilst->child_boxes); i++) { |
6670 | 0 | GF_ListItemBox *info = gf_list_get(ilst->child_boxes, i); |
6671 | 0 | if (info->type <= nb_keys) { |
6672 | 0 | gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *) info); |
6673 | 0 | i--; |
6674 | 0 | } |
6675 | 0 | } |
6676 | 0 | if (!gf_list_count(ilst->child_boxes)) { |
6677 | 0 | gf_isom_box_del_parent(&meta->child_boxes, (GF_Box *) ilst); |
6678 | | //if last, delete udta - we may still have a handler box remaining |
6679 | 0 | if ((gf_list_count(meta->child_boxes) <= 1) && (gf_list_count(movie->moov->udta->recordList)==1)) { |
6680 | 0 | gf_isom_box_del_parent(&movie->moov->child_boxes, (GF_Box *) movie->moov->udta); |
6681 | 0 | movie->moov->udta = NULL; |
6682 | 0 | } |
6683 | 0 | } |
6684 | 0 | return GF_OK; |
6685 | 0 | } |
6686 | | //locate key |
6687 | 0 | GF_MetaKey *o_key = NULL; |
6688 | 0 | u32 ksize = (u32) strlen(key->name); |
6689 | 0 | for (i=0; i<nb_keys; i++) { |
6690 | 0 | o_key = gf_list_get(keys->keys, i); |
6691 | 0 | if ((o_key->ns == key->ns) && (o_key->size==ksize) && !strcmp(o_key->data, key->name)) |
6692 | 0 | break; |
6693 | 0 | o_key = NULL; |
6694 | 0 | } |
6695 | 0 | if (!o_key) { |
6696 | 0 | if (key->type==GF_QT_KEY_REMOVE) return GF_OK; |
6697 | 0 | GF_SAFEALLOC(o_key, GF_MetaKey); |
6698 | 0 | o_key->ns = key->ns; |
6699 | 0 | o_key->data = gf_strdup(key->name); |
6700 | 0 | o_key->size = ksize; |
6701 | 0 | gf_list_add(keys->keys, o_key); |
6702 | 0 | } |
6703 | 0 | u32 key_idx = gf_list_find(keys->keys, o_key)+1; |
6704 | 0 | GF_UnknownBox *info=NULL; |
6705 | 0 | for (i=0; i<gf_list_count(ilst->child_boxes); i++) { |
6706 | 0 | info = gf_list_get(ilst->child_boxes, i); |
6707 | 0 | if (info->original_4cc == key_idx) break; |
6708 | 0 | info = NULL; |
6709 | 0 | } |
6710 | |
|
6711 | 0 | if (key->type==GF_QT_KEY_REMOVE) { |
6712 | 0 | gf_list_del_item(keys->keys, o_key); |
6713 | 0 | if (o_key->data) gf_free(o_key->data); |
6714 | 0 | gf_free(o_key); |
6715 | 0 | for (i=0; i<gf_list_count(ilst->child_boxes); i++) { |
6716 | 0 | info = gf_list_get(ilst->child_boxes, i); |
6717 | 0 | if (info->type!=GF_ISOM_BOX_TYPE_UNKNOWN) continue; |
6718 | 0 | if (info->original_4cc==key_idx) { |
6719 | 0 | gf_isom_box_del_parent(&ilst->child_boxes, (GF_Box *)info); |
6720 | 0 | i--; |
6721 | 0 | continue; |
6722 | 0 | } |
6723 | 0 | if (info->original_4cc>key_idx) { |
6724 | 0 | info->original_4cc--; |
6725 | 0 | } |
6726 | 0 | } |
6727 | 0 | return GF_OK; |
6728 | 0 | } |
6729 | 0 | if (!info) { |
6730 | 0 | info = (GF_UnknownBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_UNKNOWN); |
6731 | 0 | if (!info) return GF_OUT_OF_MEM; |
6732 | 0 | if (!ilst->child_boxes) ilst->child_boxes = gf_list_new(); |
6733 | 0 | gf_list_add(ilst->child_boxes, info); |
6734 | 0 | } |
6735 | | |
6736 | 0 | GF_DataBox *dbox = (GF_DataBox *) gf_isom_box_find_child(info->child_boxes, GF_ISOM_BOX_TYPE_DATA); |
6737 | 0 | if (!dbox) { |
6738 | 0 | dbox = (GF_DataBox *)gf_isom_box_new_parent(&info->child_boxes, GF_ISOM_BOX_TYPE_DATA); |
6739 | 0 | if (!dbox) { |
6740 | 0 | gf_isom_box_del((GF_Box *)info); |
6741 | 0 | return GF_OUT_OF_MEM; |
6742 | 0 | } |
6743 | 0 | } |
6744 | 0 | u32 nb_bits=0; |
6745 | 0 | info->original_4cc = key_idx; |
6746 | 0 | dbox->version = 0; |
6747 | 0 | dbox->flags = key->type; |
6748 | | //serialize |
6749 | 0 | GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); |
6750 | |
|
6751 | 0 | switch (key->type) { |
6752 | 0 | case GF_QT_KEY_UTF8: |
6753 | 0 | case GF_QT_KEY_UTF8_SORT: |
6754 | 0 | if (key->value.string) |
6755 | 0 | gf_bs_write_data(bs, key->value.string, (u32) strlen(key->value.string)); |
6756 | 0 | break; |
6757 | | |
6758 | 0 | case GF_QT_KEY_SIGNED_VSIZE: |
6759 | 0 | if (ABS(key->value.sint)<=0x7F) nb_bits=8; |
6760 | 0 | else if (ABS(key->value.sint)<=0x7FFF) nb_bits=16; |
6761 | 0 | else if (ABS(key->value.sint)<=0x7FFFFF) nb_bits=24; |
6762 | 0 | else nb_bits = 32; |
6763 | 0 | gf_bs_write_int(bs, (s32) key->value.sint, nb_bits); |
6764 | 0 | break; |
6765 | 0 | case GF_QT_KEY_UNSIGNED_VSIZE: |
6766 | 0 | if (key->value.uint<=0xFF) nb_bits=8; |
6767 | 0 | else if (key->value.uint<=0xFFFF) nb_bits=16; |
6768 | 0 | else if (key->value.uint<=0xFFFFFF) nb_bits=24; |
6769 | 0 | else nb_bits = 32; |
6770 | 0 | gf_bs_write_int(bs, (u32) key->value.uint, nb_bits); |
6771 | 0 | break; |
6772 | 0 | case GF_QT_KEY_FLOAT: |
6773 | 0 | gf_bs_write_float(bs, (Float) key->value.number); |
6774 | 0 | break; |
6775 | 0 | case GF_QT_KEY_DOUBLE: |
6776 | 0 | gf_bs_write_double(bs, key->value.number); |
6777 | 0 | break; |
6778 | 0 | case GF_QT_KEY_SIGNED_8: |
6779 | 0 | gf_bs_write_int(bs, (s32) key->value.sint, 8); |
6780 | 0 | break; |
6781 | 0 | case GF_QT_KEY_SIGNED_16: |
6782 | 0 | gf_bs_write_int(bs, (s32) key->value.sint, 16); |
6783 | 0 | break; |
6784 | 0 | case GF_QT_KEY_SIGNED_32: |
6785 | 0 | gf_bs_write_int(bs, (s32) key->value.sint, 32); |
6786 | 0 | break; |
6787 | 0 | case GF_QT_KEY_SIGNED_64: |
6788 | 0 | gf_bs_write_long_int(bs, key->value.sint, 64); |
6789 | 0 | break; |
6790 | 0 | case GF_QT_KEY_POINTF: |
6791 | 0 | case GF_QT_KEY_SIZEF: |
6792 | 0 | gf_bs_write_float(bs, key->value.pos_size.x); |
6793 | 0 | gf_bs_write_float(bs, key->value.pos_size.y); |
6794 | 0 | break; |
6795 | 0 | case GF_QT_KEY_RECTF: |
6796 | 0 | gf_bs_write_float(bs, key->value.rect.x); |
6797 | 0 | gf_bs_write_float(bs, key->value.rect.y); |
6798 | 0 | gf_bs_write_float(bs, key->value.rect.w); |
6799 | 0 | gf_bs_write_float(bs, key->value.rect.h); |
6800 | 0 | break; |
6801 | | |
6802 | 0 | case GF_QT_KEY_UNSIGNED_8: |
6803 | 0 | gf_bs_write_int(bs, (s32) key->value.uint, 8); |
6804 | 0 | break; |
6805 | 0 | case GF_QT_KEY_UNSIGNED_16: |
6806 | 0 | gf_bs_write_int(bs, (s32) key->value.uint, 16); |
6807 | 0 | break; |
6808 | 0 | case GF_QT_KEY_UNSIGNED_32: |
6809 | 0 | gf_bs_write_int(bs, (s32) key->value.uint, 32); |
6810 | 0 | break; |
6811 | 0 | case GF_QT_KEY_UNSIGNED_64: |
6812 | 0 | gf_bs_write_long_int(bs, key->value.uint, 64); |
6813 | 0 | break; |
6814 | 0 | case GF_QT_KEY_MATRIXF: |
6815 | 0 | for (i=0; i<9; i++) |
6816 | 0 | gf_bs_write_float(bs, (Float) key->value.matrix[i] ); |
6817 | 0 | break; |
6818 | | |
6819 | 0 | case GF_QT_KEY_OPAQUE: |
6820 | 0 | case GF_QT_KEY_UTF16_BE: |
6821 | 0 | case GF_QT_KEY_JIS: |
6822 | 0 | case GF_QT_KEY_UTF16_SORT: |
6823 | 0 | case GF_QT_KEY_JPEG: |
6824 | 0 | case GF_QT_KEY_PNG: |
6825 | 0 | case GF_QT_KEY_BMP: |
6826 | 0 | case GF_QT_KEY_METABOX: |
6827 | 0 | default: |
6828 | 0 | gf_bs_write_data(bs, key->value.data.data, key->value.data.data_len); |
6829 | 0 | break; |
6830 | 0 | } |
6831 | | //write extra 0 at end, not serialized |
6832 | 0 | gf_bs_write_u8(bs, 0); |
6833 | 0 | if (dbox->data) gf_free(dbox->data); |
6834 | |
|
6835 | 0 | gf_bs_get_content(bs, &dbox->data, &i); |
6836 | 0 | if (i) i--; |
6837 | 0 | dbox->dataSize = i; |
6838 | 0 | gf_bs_del(bs); |
6839 | 0 | return GF_OK; |
6840 | 0 | } |
6841 | | |
6842 | | |
6843 | | GF_EXPORT |
6844 | | GF_Err gf_isom_set_alternate_group_id(GF_ISOFile *movie, u32 trackNumber, u32 groupId) |
6845 | 0 | { |
6846 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
6847 | 0 | if (!trak) return GF_BAD_PARAM; |
6848 | 0 | trak->Header->alternate_group = groupId; |
6849 | 0 | return GF_OK; |
6850 | 0 | } |
6851 | | |
6852 | | |
6853 | | GF_EXPORT |
6854 | | GF_Err gf_isom_set_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, u32 trackRefGroup, Bool is_switch_group, u32 *switchGroupID, u32 *criteriaList, u32 criteriaListCount) |
6855 | 0 | { |
6856 | 0 | GF_TrackSelectionBox *tsel; |
6857 | 0 | GF_TrackBox *trak; |
6858 | 0 | GF_UserDataMap *map; |
6859 | 0 | GF_Err e; |
6860 | 0 | u32 alternateGroupID = 0; |
6861 | 0 | u32 next_switch_group_id = 0; |
6862 | |
|
6863 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
6864 | 0 | if (!trak || !switchGroupID) return GF_BAD_PARAM; |
6865 | | |
6866 | | |
6867 | 0 | if (trackRefGroup) { |
6868 | 0 | GF_TrackBox *trak_ref = gf_isom_get_track_box(movie, trackRefGroup); |
6869 | 0 | if (trak_ref != trak) { |
6870 | 0 | if (!trak_ref || !trak_ref->Header->alternate_group) { |
6871 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Track %d has not an alternate group - skipping\n", trak_ref ? trak_ref->Header->trackID : 0)); |
6872 | 0 | return GF_BAD_PARAM; |
6873 | 0 | } |
6874 | 0 | alternateGroupID = trak_ref->Header->alternate_group; |
6875 | 0 | } else { |
6876 | 0 | alternateGroupID = trak->Header->alternate_group; |
6877 | 0 | } |
6878 | 0 | } |
6879 | 0 | if (!alternateGroupID) { |
6880 | | /*there is a function for this ....*/ |
6881 | 0 | if (trak->Header->alternate_group) { |
6882 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Track %d has already an alternate group - skipping\n", trak->Header->trackID)); |
6883 | 0 | return GF_BAD_PARAM; |
6884 | 0 | } |
6885 | 0 | alternateGroupID = gf_isom_get_next_alternate_group_id(movie); |
6886 | 0 | } |
6887 | | |
6888 | 0 | if (is_switch_group) { |
6889 | 0 | u32 i=0; |
6890 | 0 | while (i< gf_isom_get_track_count(movie) ) { |
6891 | | //locate first available ID |
6892 | 0 | GF_TrackBox *a_trak = gf_isom_get_track_box(movie, i+1); |
6893 | |
|
6894 | 0 | if (a_trak->udta) { |
6895 | 0 | u32 j, count; |
6896 | 0 | map = udta_getEntry(a_trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); |
6897 | 0 | if (map) { |
6898 | 0 | count = gf_list_count(map->boxes); |
6899 | 0 | for (j=0; j<count; j++) { |
6900 | 0 | tsel = (GF_TrackSelectionBox*)gf_list_get(map->boxes, j); |
6901 | |
|
6902 | 0 | if (*switchGroupID) { |
6903 | 0 | if (tsel->switchGroup==next_switch_group_id) { |
6904 | 0 | if (a_trak->Header->alternate_group != alternateGroupID) return GF_BAD_PARAM; |
6905 | 0 | } |
6906 | 0 | } else { |
6907 | 0 | if (tsel->switchGroup && (tsel->switchGroup>=next_switch_group_id) ) |
6908 | 0 | next_switch_group_id = tsel->switchGroup; |
6909 | 0 | } |
6910 | 0 | } |
6911 | 0 | } |
6912 | |
|
6913 | 0 | } |
6914 | 0 | i++; |
6915 | 0 | } |
6916 | 0 | if (! *switchGroupID) *switchGroupID = next_switch_group_id+1; |
6917 | 0 | } |
6918 | | |
6919 | | |
6920 | 0 | trak->Header->alternate_group = alternateGroupID; |
6921 | |
|
6922 | 0 | tsel = NULL; |
6923 | 0 | if (*switchGroupID) { |
6924 | 0 | if (!trak->udta) { |
6925 | 0 | e = trak_on_child_box((GF_Box*)trak, gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_UDTA), GF_FALSE); |
6926 | 0 | if (e) return e; |
6927 | 0 | } |
6928 | | |
6929 | 0 | map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); |
6930 | | |
6931 | | /*locate tsel box with no switch group*/ |
6932 | 0 | if (map) { |
6933 | 0 | u32 j, count = gf_list_count(map->boxes); |
6934 | 0 | for (j=0; j<count; j++) { |
6935 | 0 | tsel = (GF_TrackSelectionBox*)gf_list_get(map->boxes, j); |
6936 | 0 | if (tsel->switchGroup == *switchGroupID) break; |
6937 | 0 | tsel = NULL; |
6938 | 0 | } |
6939 | 0 | } |
6940 | 0 | if (!tsel) { |
6941 | 0 | tsel = (GF_TrackSelectionBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TSEL); |
6942 | 0 | if (!tsel) return GF_OUT_OF_MEM; |
6943 | 0 | e = udta_on_child_box_ex((GF_Box *)trak->udta, (GF_Box *) tsel, GF_FALSE, GF_TRUE); |
6944 | 0 | if (e) return e; |
6945 | 0 | } |
6946 | | |
6947 | 0 | tsel->switchGroup = *switchGroupID; |
6948 | 0 | tsel->attributeListCount = criteriaListCount; |
6949 | 0 | if (tsel->attributeList) gf_free(tsel->attributeList); |
6950 | 0 | tsel->attributeList = (u32*)gf_malloc(sizeof(u32)*criteriaListCount); |
6951 | 0 | if (!tsel->attributeList) return GF_OUT_OF_MEM; |
6952 | 0 | memcpy(tsel->attributeList, criteriaList, sizeof(u32)*criteriaListCount); |
6953 | 0 | } |
6954 | 0 | return GF_OK; |
6955 | 0 | } |
6956 | | |
6957 | | void reset_tsel_box(GF_TrackBox *trak) |
6958 | 0 | { |
6959 | 0 | GF_UserDataMap *map; |
6960 | 0 | trak->Header->alternate_group = 0; |
6961 | 0 | map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); |
6962 | 0 | if (map) { |
6963 | 0 | gf_list_del_item(trak->udta->recordList, map); |
6964 | 0 | gf_isom_box_array_del(map->boxes); |
6965 | 0 | gf_free(map); |
6966 | 0 | } |
6967 | |
|
6968 | 0 | } |
6969 | | |
6970 | | GF_EXPORT |
6971 | | GF_Err gf_isom_reset_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, Bool reset_all_group) |
6972 | 0 | { |
6973 | 0 | GF_TrackBox *trak; |
6974 | 0 | u32 alternateGroupID = 0; |
6975 | |
|
6976 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
6977 | 0 | if (!trak) return GF_BAD_PARAM; |
6978 | 0 | if (!trak->Header->alternate_group) return GF_OK; |
6979 | | |
6980 | 0 | alternateGroupID = trak->Header->alternate_group; |
6981 | 0 | if (reset_all_group) { |
6982 | 0 | u32 i=0; |
6983 | 0 | while (i< gf_isom_get_track_count(movie) ) { |
6984 | | //locate first available ID |
6985 | 0 | GF_TrackBox *a_trak = gf_isom_get_track_box(movie, i+1); |
6986 | 0 | if (a_trak->Header->alternate_group == alternateGroupID) reset_tsel_box(a_trak); |
6987 | 0 | i++; |
6988 | 0 | } |
6989 | 0 | } else { |
6990 | 0 | reset_tsel_box(trak); |
6991 | 0 | } |
6992 | 0 | return GF_OK; |
6993 | 0 | } |
6994 | | |
6995 | | |
6996 | | GF_EXPORT |
6997 | | GF_Err gf_isom_reset_switch_parameters(GF_ISOFile *movie) |
6998 | 0 | { |
6999 | 0 | u32 i=0; |
7000 | 0 | while (i< gf_isom_get_track_count(movie) ) { |
7001 | | //locate first available ID |
7002 | 0 | GF_TrackBox *a_trak = gf_isom_get_track_box(movie, i+1); |
7003 | 0 | reset_tsel_box(a_trak); |
7004 | 0 | i++; |
7005 | 0 | } |
7006 | 0 | return GF_OK; |
7007 | 0 | } |
7008 | | |
7009 | | |
7010 | | GF_Err gf_isom_add_subsample(GF_ISOFile *movie, u32 track, u32 sampleNumber, u32 flags, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable) |
7011 | 0 | { |
7012 | 0 | u32 i, count; |
7013 | 0 | GF_SubSampleInformationBox *sub_samples; |
7014 | 0 | GF_TrackBox *trak; |
7015 | 0 | GF_Err e; |
7016 | |
|
7017 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7018 | 0 | if (e) return e; |
7019 | | |
7020 | 0 | trak = gf_isom_get_track_box(movie, track); |
7021 | 0 | if (!trak || !trak->Media || !trak->Media->information->sampleTable) |
7022 | 0 | return GF_BAD_PARAM; |
7023 | | |
7024 | 0 | if (!trak->Media->information->sampleTable->sub_samples) { |
7025 | 0 | trak->Media->information->sampleTable->sub_samples=gf_list_new(); |
7026 | 0 | } |
7027 | |
|
7028 | 0 | sub_samples = NULL; |
7029 | 0 | count = gf_list_count(trak->Media->information->sampleTable->sub_samples); |
7030 | 0 | for (i=0; i<count; i++) { |
7031 | 0 | sub_samples = gf_list_get(trak->Media->information->sampleTable->sub_samples, i); |
7032 | 0 | if (sub_samples->flags==flags) break; |
7033 | 0 | sub_samples = NULL; |
7034 | 0 | } |
7035 | 0 | if (!sub_samples) { |
7036 | 0 | sub_samples = (GF_SubSampleInformationBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_SUBS); |
7037 | 0 | if (!sub_samples) return GF_OUT_OF_MEM; |
7038 | 0 | gf_list_add(trak->Media->information->sampleTable->sub_samples, sub_samples); |
7039 | 0 | sub_samples->version = (subSampleSize>0xFFFF) ? 1 : 0; |
7040 | 0 | sub_samples->flags = flags; |
7041 | 0 | } |
7042 | 0 | return gf_isom_add_subsample_info(sub_samples, sampleNumber, subSampleSize, priority, reserved, discardable); |
7043 | 0 | } |
7044 | | |
7045 | | |
7046 | | GF_EXPORT |
7047 | | GF_Err gf_isom_set_rvc_config(GF_ISOFile *movie, u32 track, u32 sampleDescriptionIndex, u16 rvc_predefined, char *mime, u8 *data, u32 size) |
7048 | 0 | { |
7049 | 0 | GF_MPEGVisualSampleEntryBox *entry; |
7050 | 0 | GF_Err e; |
7051 | 0 | GF_TrackBox *trak; |
7052 | |
|
7053 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7054 | 0 | if (e) return e; |
7055 | | |
7056 | 0 | trak = gf_isom_get_track_box(movie, track); |
7057 | 0 | if (!trak) return GF_BAD_PARAM; |
7058 | | |
7059 | | |
7060 | 0 | entry = (GF_MPEGVisualSampleEntryBox *) gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex-1); |
7061 | 0 | if (!entry ) return GF_BAD_PARAM; |
7062 | 0 | if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_BAD_PARAM; |
7063 | | |
7064 | 0 | GF_RVCConfigurationBox *rvcc = (GF_RVCConfigurationBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_RVCC); |
7065 | 0 | if (rvcc && rvcc->rvc_meta_idx) { |
7066 | 0 | gf_isom_remove_meta_item(movie, GF_FALSE, track, rvcc->rvc_meta_idx, GF_FALSE, NULL); |
7067 | 0 | rvcc->rvc_meta_idx = 0; |
7068 | 0 | } |
7069 | |
|
7070 | 0 | if (!rvcc) { |
7071 | 0 | rvcc = (GF_RVCConfigurationBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_RVCC); |
7072 | 0 | if (!rvcc) return GF_OUT_OF_MEM; |
7073 | 0 | } |
7074 | 0 | rvcc->predefined_rvc_config = rvc_predefined; |
7075 | 0 | if (!rvc_predefined) { |
7076 | 0 | u32 it_id=0; |
7077 | 0 | e = gf_isom_set_meta_type(movie, GF_FALSE, track, GF_META_TYPE_RVCI); |
7078 | 0 | if (e) return e; |
7079 | 0 | gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_ISO2, GF_TRUE); |
7080 | 0 | e = gf_isom_add_meta_item_memory(movie, GF_FALSE, track, "rvcconfig.xml", &it_id, GF_META_ITEM_TYPE_MIME, mime, NULL, NULL, data, size, NULL); |
7081 | 0 | if (e) return e; |
7082 | 0 | rvcc->rvc_meta_idx = gf_isom_get_meta_item_count(movie, GF_FALSE, track); |
7083 | 0 | } |
7084 | 0 | return GF_OK; |
7085 | 0 | } |
7086 | | |
7087 | | /*for now not exported*/ |
7088 | | /*expands sampleGroup table for the given grouping type and sample_number. If sample_number is 0, just appends an entry at the end of the table*/ |
7089 | | static GF_Err gf_isom_add_sample_group_entry(GF_List *sampleGroups, u32 sample_number, GF_SampleGroupDescriptionBox *sgdesc, u32 grouping_type_parameter, u32 sampleGroupDescriptionIndex, GF_List *parent, GF_SampleTableBox *stbl) |
7090 | 0 | { |
7091 | 0 | GF_SampleGroupBox *sgroup = NULL; |
7092 | 0 | u32 i, count, last_sample_in_entry; |
7093 | 0 | Bool all_samples = GF_FALSE; |
7094 | 0 | gf_assert(sampleGroups); |
7095 | 0 | if (!sgdesc) return GF_BAD_PARAM; |
7096 | 0 | count = gf_list_count(sampleGroups); |
7097 | 0 | for (i=0; i<count; i++) { |
7098 | 0 | sgroup = (GF_SampleGroupBox*)gf_list_get(sampleGroups, i); |
7099 | 0 | if (sgroup->grouping_type==sgdesc->grouping_type) break; |
7100 | 0 | sgroup = NULL; |
7101 | 0 | } |
7102 | 0 | if (!sgroup) { |
7103 | 0 | sgroup = (GF_SampleGroupBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SBGP); |
7104 | 0 | if (!sgroup) return GF_OUT_OF_MEM; |
7105 | 0 | sgroup->grouping_type = sgdesc->grouping_type; |
7106 | 0 | sgroup->grouping_type_parameter = grouping_type_parameter; |
7107 | | // gf_list_add(sampleGroups, sgroup); |
7108 | | //crude patch to align old arch and filters |
7109 | 0 | gf_list_insert(sampleGroups, sgroup, 0); |
7110 | 0 | gf_assert(parent); |
7111 | 0 | gf_list_add(parent, sgroup); |
7112 | 0 | } |
7113 | 0 | u32 def_insert_value = (sgdesc && (sgdesc->version==2)) ? sgdesc->default_description_index : 0; |
7114 | | |
7115 | | /*used in fragments, means we are adding the last sample*/ |
7116 | 0 | if (!sample_number) { |
7117 | 0 | sample_number = 1; |
7118 | 0 | if (sgroup->entry_count) { |
7119 | 0 | for (i=0; i<sgroup->entry_count; i++) { |
7120 | 0 | sample_number += sgroup->sample_entries[i].sample_count; |
7121 | 0 | } |
7122 | 0 | } |
7123 | 0 | } else if (sample_number==(u32) -1) { |
7124 | 0 | all_samples = GF_TRUE; |
7125 | 0 | sample_number = 1; |
7126 | 0 | } |
7127 | |
|
7128 | 0 | if (!sgroup->entry_count) { |
7129 | 0 | u32 idx = 0; |
7130 | 0 | sgroup->entry_count = (sample_number>1) ? 2 : 1; |
7131 | 0 | sgroup->sample_entries = (GF_SampleGroupEntry*)gf_malloc(sizeof(GF_SampleGroupEntry) * sgroup->entry_count ); |
7132 | 0 | if (!sgroup->sample_entries) return GF_OUT_OF_MEM; |
7133 | 0 | if (sample_number>1) { |
7134 | 0 | sgroup->sample_entries[0].sample_count = sample_number-1; |
7135 | 0 | sgroup->sample_entries[0].group_description_index = sampleGroupDescriptionIndex ? def_insert_value : 1; |
7136 | 0 | idx = 1; |
7137 | 0 | } |
7138 | 0 | sgroup->sample_entries[idx].sample_count = 1; |
7139 | 0 | sgroup->sample_entries[idx].group_description_index = sampleGroupDescriptionIndex; |
7140 | 0 | if (all_samples && stbl) { |
7141 | 0 | sgroup->sample_entries[idx].sample_count = stbl->SampleSize->sampleCount; |
7142 | 0 | } |
7143 | 0 | return GF_OK; |
7144 | 0 | } |
7145 | 0 | if (all_samples && stbl) { |
7146 | 0 | sgroup->entry_count = 1; |
7147 | 0 | sgroup->sample_entries[0].group_description_index = sampleGroupDescriptionIndex; |
7148 | 0 | sgroup->sample_entries[0].sample_count = stbl->SampleSize->sampleCount; |
7149 | 0 | return GF_OK; |
7150 | 0 | } |
7151 | 0 | last_sample_in_entry = 0; |
7152 | 0 | for (i=0; i<sgroup->entry_count; i++) { |
7153 | | /*TODO*/ |
7154 | 0 | if (last_sample_in_entry + sgroup->sample_entries[i].sample_count > sample_number) |
7155 | 0 | return GF_NOT_SUPPORTED; |
7156 | 0 | last_sample_in_entry += sgroup->sample_entries[i].sample_count; |
7157 | 0 | } |
7158 | | |
7159 | 0 | if (last_sample_in_entry == sample_number) { |
7160 | 0 | if (sgroup->sample_entries[sgroup->entry_count-1].group_description_index==sampleGroupDescriptionIndex) |
7161 | 0 | return GF_OK; |
7162 | 0 | else |
7163 | 0 | return GF_NOT_SUPPORTED; |
7164 | 0 | } |
7165 | | |
7166 | 0 | if ((sgroup->sample_entries[sgroup->entry_count-1].group_description_index==sampleGroupDescriptionIndex) && (last_sample_in_entry+1==sample_number)) { |
7167 | 0 | sgroup->sample_entries[sgroup->entry_count-1].sample_count++; |
7168 | 0 | return GF_OK; |
7169 | 0 | } |
7170 | | /*last entry was an empty desc (no group associated), just add the number of samples missing until new one, then add new one*/ |
7171 | 0 | if (! sgroup->sample_entries[sgroup->entry_count-1].group_description_index) { |
7172 | 0 | sgroup->sample_entries[sgroup->entry_count-1].sample_count += sample_number - 1 - last_sample_in_entry; |
7173 | 0 | sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 1) ); |
7174 | 0 | sgroup->sample_entries[sgroup->entry_count].sample_count = 1; |
7175 | 0 | sgroup->sample_entries[sgroup->entry_count].group_description_index = sampleGroupDescriptionIndex; |
7176 | 0 | sgroup->entry_count++; |
7177 | 0 | return GF_OK; |
7178 | 0 | } |
7179 | | /*we are adding a sample with no desc, add entry at the end*/ |
7180 | 0 | if (!sampleGroupDescriptionIndex || (sample_number - 1 - last_sample_in_entry==0) ) { |
7181 | 0 | sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 1) ); |
7182 | 0 | sgroup->sample_entries[sgroup->entry_count].sample_count = 1; |
7183 | 0 | sgroup->sample_entries[sgroup->entry_count].group_description_index = sampleGroupDescriptionIndex; |
7184 | 0 | sgroup->entry_count++; |
7185 | 0 | return GF_OK; |
7186 | 0 | } |
7187 | | /*need to insert two entries ...*/ |
7188 | 0 | sgroup->sample_entries = (GF_SampleGroupEntry*)gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 2) ); |
7189 | |
|
7190 | 0 | sgroup->sample_entries[sgroup->entry_count].sample_count = sample_number - 1 - last_sample_in_entry; |
7191 | 0 | sgroup->sample_entries[sgroup->entry_count].group_description_index = def_insert_value; |
7192 | |
|
7193 | 0 | sgroup->sample_entries[sgroup->entry_count+1].sample_count = 1; |
7194 | 0 | sgroup->sample_entries[sgroup->entry_count+1].group_description_index = sampleGroupDescriptionIndex; |
7195 | 0 | sgroup->entry_count+=2; |
7196 | 0 | return GF_OK; |
7197 | 0 | } |
7198 | | |
7199 | | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7200 | | static GF_SampleGroupDescriptionBox *get_sgdp(GF_SampleTableBox *stbl, GF_TrackFragmentBox *traf, u32 grouping_type, Bool *is_traf_sgdp) |
7201 | | #else |
7202 | | static GF_SampleGroupDescriptionBox *get_sgdp(GF_SampleTableBox *stbl, void *traf, u32 grouping_type, Bool *is_traf_sgdp) |
7203 | | #endif /* GPAC_DISABLE_ISOM_FRAGMENTS */ |
7204 | 0 | { |
7205 | 0 | GF_List *groupList=NULL; |
7206 | 0 | GF_List **parent=NULL; |
7207 | 0 | GF_SampleGroupDescriptionBox *sgdesc=NULL; |
7208 | 0 | u32 count, i; |
7209 | |
|
7210 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7211 | 0 | if (!stbl && traf && traf->trex->track->Media->information->sampleTable) |
7212 | 0 | stbl = traf->trex->track->Media->information->sampleTable; |
7213 | 0 | #endif |
7214 | 0 | if (stbl) { |
7215 | 0 | if (!stbl->sampleGroupsDescription && !traf) |
7216 | 0 | stbl->sampleGroupsDescription = gf_list_new(); |
7217 | |
|
7218 | 0 | groupList = stbl->sampleGroupsDescription; |
7219 | 0 | if (is_traf_sgdp) *is_traf_sgdp = GF_FALSE; |
7220 | 0 | parent = &stbl->child_boxes; |
7221 | |
|
7222 | 0 | count = gf_list_count(groupList); |
7223 | 0 | for (i=0; i<count; i++) { |
7224 | 0 | sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(groupList, i); |
7225 | 0 | if (sgdesc->grouping_type==grouping_type) break; |
7226 | 0 | sgdesc = NULL; |
7227 | 0 | } |
7228 | 0 | } |
7229 | |
|
7230 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7231 | | /*look in stbl or traf for sample sampleGroupsDescription*/ |
7232 | 0 | if (!sgdesc && traf) { |
7233 | 0 | if (!traf->sampleGroupsDescription) |
7234 | 0 | traf->sampleGroupsDescription = gf_list_new(); |
7235 | 0 | groupList = traf->sampleGroupsDescription; |
7236 | 0 | parent = &traf->child_boxes; |
7237 | 0 | if (is_traf_sgdp) *is_traf_sgdp = GF_TRUE; |
7238 | |
|
7239 | 0 | count = gf_list_count(groupList); |
7240 | 0 | for (i=0; i<count; i++) { |
7241 | 0 | sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(groupList, i); |
7242 | 0 | if (sgdesc->grouping_type==grouping_type) break; |
7243 | 0 | sgdesc = NULL; |
7244 | 0 | } |
7245 | 0 | } |
7246 | 0 | #endif |
7247 | |
|
7248 | 0 | if (!sgdesc) { |
7249 | 0 | sgdesc = (GF_SampleGroupDescriptionBox *) gf_isom_box_new_parent(parent, GF_ISOM_BOX_TYPE_SGPD); |
7250 | 0 | if (!sgdesc) return NULL; |
7251 | 0 | sgdesc->grouping_type = grouping_type; |
7252 | 0 | gf_assert(groupList); |
7253 | 0 | gf_list_add(groupList, sgdesc); |
7254 | 0 | } |
7255 | 0 | return sgdesc; |
7256 | 0 | } |
7257 | | |
7258 | | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7259 | | static GF_Err gf_isom_set_sample_group_info_ex(GF_SampleTableBox *stbl, GF_TrackFragmentBox *traf, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *udta, void *(*sg_create_entry)(void *udta), Bool (*sg_compare_entry)(void *udta, void *entry)) |
7260 | | #else |
7261 | | static GF_Err gf_isom_set_sample_group_info_ex(GF_SampleTableBox *stbl, void *traf, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *udta, void *(*sg_create_entry)(void *udta), Bool (*sg_compare_entry)(void *udta, void *entry)) |
7262 | | #endif /* GPAC_DISABLE_ISOM_FRAGMENTS */ |
7263 | 0 | { |
7264 | 0 | GF_List *groupList, *parent; |
7265 | 0 | void *entry; |
7266 | 0 | Bool is_traf_sgpd; |
7267 | 0 | GF_SampleGroupDescriptionBox *sgdesc = NULL; |
7268 | 0 | u32 i, entry_idx; |
7269 | |
|
7270 | 0 | if (!stbl && !traf) return GF_BAD_PARAM; |
7271 | | |
7272 | 0 | sgdesc = get_sgdp(stbl, traf, grouping_type, &is_traf_sgpd); |
7273 | 0 | if (!sgdesc) return GF_OUT_OF_MEM; |
7274 | | |
7275 | 0 | entry = NULL; |
7276 | 0 | if (sg_compare_entry) { |
7277 | 0 | for (i=0; i<gf_list_count(sgdesc->group_descriptions); i++) { |
7278 | 0 | entry = gf_list_get(sgdesc->group_descriptions, i); |
7279 | 0 | if (sg_compare_entry(udta, entry)) break; |
7280 | 0 | entry = NULL; |
7281 | 0 | } |
7282 | 0 | } |
7283 | 0 | if (!entry && sg_create_entry) { |
7284 | 0 | entry = sg_create_entry(udta); |
7285 | 0 | if (!entry) return GF_IO_ERR; |
7286 | 0 | if (traf && !is_traf_sgpd) { |
7287 | 0 | sgdesc = get_sgdp(NULL, traf, grouping_type, &is_traf_sgpd); |
7288 | 0 | } |
7289 | 0 | gf_list_add(sgdesc->group_descriptions, entry); |
7290 | 0 | } |
7291 | 0 | if (!entry) |
7292 | 0 | entry_idx = 0; |
7293 | 0 | else |
7294 | 0 | entry_idx = 1 + gf_list_find(sgdesc->group_descriptions, entry); |
7295 | | |
7296 | | /*look in stbl or traf for sample sampleGroups*/ |
7297 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7298 | 0 | if (traf) { |
7299 | 0 | if (!traf->sampleGroups) |
7300 | 0 | traf->sampleGroups = gf_list_new(); |
7301 | 0 | groupList = traf->sampleGroups; |
7302 | 0 | parent = traf->child_boxes; |
7303 | 0 | if (entry_idx && is_traf_sgpd) |
7304 | 0 | entry_idx |= 0x10000; |
7305 | 0 | } else |
7306 | 0 | #endif |
7307 | 0 | { |
7308 | 0 | if (!stbl->sampleGroups) |
7309 | 0 | stbl->sampleGroups = gf_list_new(); |
7310 | 0 | groupList = stbl->sampleGroups; |
7311 | 0 | parent = stbl->child_boxes; |
7312 | 0 | } |
7313 | |
|
7314 | 0 | return gf_isom_add_sample_group_entry(groupList, sample_number, sgdesc, grouping_type_parameter, entry_idx, parent, stbl); |
7315 | 0 | } |
7316 | | |
7317 | | static GF_Err gf_isom_set_sample_group_info_internal(GF_ISOFile *movie, u32 track, u32 trafID, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *udta, void *(*sg_create_entry)(void *udta), Bool (*sg_compare_entry)(void *udta, void *entry)) |
7318 | 0 | { |
7319 | 0 | GF_Err e; |
7320 | 0 | GF_TrackBox *trak=NULL; |
7321 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7322 | 0 | GF_TrackFragmentBox *traf=NULL; |
7323 | 0 | #endif |
7324 | 0 | if (!trafID && (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) { |
7325 | 0 | trak = gf_isom_get_track_box(movie, track); |
7326 | 0 | if (!trak) return GF_BAD_PARAM; |
7327 | 0 | trafID = trak->Header->trackID; |
7328 | 0 | } |
7329 | | |
7330 | 0 | if (trafID) { |
7331 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7332 | 0 | if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) |
7333 | 0 | return GF_BAD_PARAM; |
7334 | | |
7335 | 0 | traf = gf_isom_get_traf(movie, trafID); |
7336 | | #else |
7337 | | return GF_NOT_SUPPORTED; |
7338 | | #endif |
7339 | |
|
7340 | 0 | } else if (track) { |
7341 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7342 | 0 | if (e) return e; |
7343 | | |
7344 | 0 | trak = gf_isom_get_track_box(movie, track); |
7345 | 0 | if (!trak) return GF_BAD_PARAM; |
7346 | 0 | } |
7347 | | |
7348 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7349 | 0 | return gf_isom_set_sample_group_info_ex(trak ? trak->Media->information->sampleTable : NULL, traf, sample_number, grouping_type, grouping_type_parameter, udta, sg_create_entry, sg_compare_entry); |
7350 | | #else |
7351 | | return gf_isom_set_sample_group_info_ex(trak->Media->information->sampleTable, NULL, sample_number, grouping_type, grouping_type_parameter, udta, sg_create_entry, sg_compare_entry); |
7352 | | #endif |
7353 | |
|
7354 | 0 | } |
7355 | | |
7356 | | void *sgpd_parse_entry(GF_SampleGroupDescriptionBox *p, GF_BitStream *bs, s32 bytes_in_box, u32 entry_size, u32 *total_bytes); |
7357 | | |
7358 | | GF_Err gf_isom_add_sample_group_info_internal(GF_ISOFile *movie, u32 track, u32 grouping_type, void *data, u32 data_size, u32 sgpd_flags, u32 *sampleGroupDescriptionIndex, Bool *is_traf_sgpd, Bool check_access, Bool *use_default, GF_SampleGroupDescriptionBox **out_sgdesc) |
7359 | 0 | { |
7360 | 0 | GF_Err e; |
7361 | 0 | GF_TrackBox *trak=NULL; |
7362 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7363 | 0 | GF_TrackFragmentBox *traf=NULL; |
7364 | | #else |
7365 | | void *traf=NULL; |
7366 | | #endif |
7367 | 0 | u32 trafID=0; |
7368 | 0 | GF_DefaultSampleGroupDescriptionEntry *entry=NULL; |
7369 | 0 | GF_SampleGroupDescriptionBox *sgdesc = NULL; |
7370 | 0 | Bool is_default = sgpd_flags & 0x80000000; |
7371 | |
|
7372 | 0 | if (sampleGroupDescriptionIndex) *sampleGroupDescriptionIndex = 0; |
7373 | |
|
7374 | 0 | if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) { |
7375 | 0 | trak = gf_isom_get_track_box(movie, track); |
7376 | 0 | if (!trak) return GF_BAD_PARAM; |
7377 | 0 | trafID = trak->Header->trackID; |
7378 | 0 | } |
7379 | | |
7380 | 0 | if (trafID) { |
7381 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7382 | 0 | if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) |
7383 | 0 | return GF_BAD_PARAM; |
7384 | | |
7385 | 0 | traf = gf_isom_get_traf(movie, trafID); |
7386 | | #else |
7387 | | return GF_NOT_SUPPORTED; |
7388 | | #endif |
7389 | 0 | } else { |
7390 | 0 | if (check_access) { |
7391 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7392 | 0 | if (e) return e; |
7393 | 0 | } |
7394 | | |
7395 | 0 | trak = gf_isom_get_track_box(movie, track); |
7396 | 0 | } |
7397 | 0 | if (!trak) return GF_BAD_PARAM; |
7398 | | |
7399 | | //get sample group desc for this grouping type |
7400 | 0 | sgdesc = get_sgdp(trak->Media->information->sampleTable, traf, grouping_type, is_traf_sgpd); |
7401 | 0 | if (!sgdesc) return GF_OUT_OF_MEM; |
7402 | | //first time we create the sample group description, set flags |
7403 | 0 | if (!gf_list_count(sgdesc->group_descriptions) && !traf) { |
7404 | 0 | if (sgpd_flags&1) sgdesc->flags |= 1; |
7405 | 0 | if (sgpd_flags&2) sgdesc->flags |= 2; |
7406 | 0 | if (sgpd_flags&0x40000000) sgdesc->version=3; |
7407 | 0 | } |
7408 | | |
7409 | |
|
7410 | 0 | GF_BitStream *bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); |
7411 | 0 | u32 bytes; |
7412 | 0 | entry = sgpd_parse_entry(sgdesc, bs, data_size, data_size, &bytes); |
7413 | 0 | gf_bs_del(bs); |
7414 | 0 | if (!entry) return GF_NON_COMPLIANT_BITSTREAM; |
7415 | | |
7416 | 0 | if (out_sgdesc) *out_sgdesc = sgdesc; |
7417 | | |
7418 | | |
7419 | | //find the same entry |
7420 | 0 | u32 k; |
7421 | 0 | for (k=0; k<gf_list_count(sgdesc->group_descriptions); k++) { |
7422 | 0 | void *sgde_dst = gf_list_get(sgdesc->group_descriptions, k); |
7423 | 0 | if (gf_isom_is_identical_sgpd(entry, sgde_dst, sgdesc->grouping_type)) { |
7424 | 0 | if (sampleGroupDescriptionIndex) *sampleGroupDescriptionIndex = k+1; |
7425 | 0 | sgpd_del_entry(sgdesc->grouping_type, entry); |
7426 | 0 | if (use_default) { |
7427 | 0 | u32 idx = k+1; |
7428 | 0 | if (is_traf_sgpd && *is_traf_sgpd) idx |= 0x10000; |
7429 | 0 | *use_default = (sgdesc->default_description_index==idx) ? GF_TRUE : GF_FALSE; |
7430 | 0 | } |
7431 | 0 | return GF_OK; |
7432 | 0 | } |
7433 | 0 | } |
7434 | | |
7435 | 0 | if (traf && ! *is_traf_sgpd) { |
7436 | 0 | sgdesc = get_sgdp(NULL, traf, grouping_type, is_traf_sgpd); |
7437 | 0 | if (!sgdesc) return GF_OUT_OF_MEM; |
7438 | 0 | } |
7439 | 0 | if (out_sgdesc) *out_sgdesc = sgdesc; |
7440 | |
|
7441 | 0 | e = gf_list_add(sgdesc->group_descriptions, entry); |
7442 | 0 | if (e) { |
7443 | 0 | sgpd_del_entry(sgdesc->grouping_type, entry); |
7444 | 0 | return e; |
7445 | 0 | } |
7446 | | |
7447 | | #if 0 |
7448 | | if (grouping_type==GF_ISOM_SAMPLE_GROUP_OINF) { |
7449 | | GF_OperatingPointsInformation *ptr = gf_isom_oinf_new_entry(); |
7450 | | GF_BitStream *bs=gf_bs_new(data, data_size, GF_BITSTREAM_READ); |
7451 | | e = gf_isom_oinf_read_entry(ptr, bs); |
7452 | | gf_bs_del(bs); |
7453 | | if (e) { |
7454 | | gf_isom_oinf_del_entry(ptr); |
7455 | | return e; |
7456 | | } |
7457 | | //not in track, create new sgdp |
7458 | | if (traf && ! *is_traf_sgpd) { |
7459 | | sgdesc = get_sgdp(NULL, traf, grouping_type, is_traf_sgpd); |
7460 | | if (!sgdesc) return GF_OUT_OF_MEM; |
7461 | | } |
7462 | | e = gf_list_add(sgdesc->group_descriptions, ptr); |
7463 | | if (e) return e; |
7464 | | entry = (GF_DefaultSampleGroupDescriptionEntry *) ptr; |
7465 | | } else if (grouping_type==GF_ISOM_SAMPLE_GROUP_LINF) { |
7466 | | GF_LHVCLayerInformation *ptr = gf_isom_linf_new_entry(); |
7467 | | GF_BitStream *bs=gf_bs_new(data, data_size, GF_BITSTREAM_READ); |
7468 | | e = gf_isom_linf_read_entry(ptr, bs); |
7469 | | gf_bs_del(bs); |
7470 | | if (e) { |
7471 | | gf_isom_linf_del_entry(ptr); |
7472 | | return e; |
7473 | | } |
7474 | | |
7475 | | if (traf && ! *is_traf_sgpd) { |
7476 | | sgdesc = get_sgdp(NULL, traf, grouping_type, is_traf_sgpd); |
7477 | | if (!sgdesc) return GF_OUT_OF_MEM; |
7478 | | } |
7479 | | |
7480 | | e = gf_list_add(sgdesc->group_descriptions, ptr); |
7481 | | if (e) return e; |
7482 | | entry = (GF_DefaultSampleGroupDescriptionEntry *) ptr; |
7483 | | } else { |
7484 | | u32 i, count=gf_list_count(sgdesc->group_descriptions); |
7485 | | for (i=0; i<count; i++) { |
7486 | | GF_DefaultSampleGroupDescriptionEntry *ent = gf_list_get(sgdesc->group_descriptions, i); |
7487 | | if ((ent->length == data_size) && !memcmp(ent->data, data, data_size)) { |
7488 | | entry = ent; |
7489 | | break; |
7490 | | } |
7491 | | entry=NULL; |
7492 | | } |
7493 | | if (!entry) { |
7494 | | GF_SAFEALLOC(entry, GF_DefaultSampleGroupDescriptionEntry); |
7495 | | if (!entry) return GF_OUT_OF_MEM; |
7496 | | entry->data = (u8*)gf_malloc(sizeof(char) * data_size); |
7497 | | if (!entry->data) { |
7498 | | gf_free(entry); |
7499 | | return GF_OUT_OF_MEM; |
7500 | | } |
7501 | | entry->length = data_size; |
7502 | | memcpy(entry->data, data, sizeof(char) * data_size); |
7503 | | |
7504 | | if (traf && ! *is_traf_sgpd) { |
7505 | | sgdesc = get_sgdp(NULL, traf, grouping_type, is_traf_sgpd); |
7506 | | if (!sgdesc) return GF_OUT_OF_MEM; |
7507 | | } |
7508 | | |
7509 | | e = gf_list_add(sgdesc->group_descriptions, entry); |
7510 | | if (e) { |
7511 | | gf_free(entry->data); |
7512 | | gf_free(entry); |
7513 | | return e; |
7514 | | } |
7515 | | } |
7516 | | } |
7517 | | #endif |
7518 | | |
7519 | | |
7520 | 0 | if (is_default && !sgdesc->default_description_index) { |
7521 | 0 | sgdesc->default_description_index = 1 + gf_list_find(sgdesc->group_descriptions, entry); |
7522 | 0 | if (sgdesc->version < 2) sgdesc->version = 2; |
7523 | 0 | if (is_traf_sgpd && *is_traf_sgpd) { |
7524 | 0 | sgdesc->default_description_index |= 0x10000; |
7525 | 0 | } |
7526 | 0 | } |
7527 | 0 | u32 grp_idx = 1 + gf_list_find(sgdesc->group_descriptions, entry); |
7528 | 0 | if (sampleGroupDescriptionIndex) *sampleGroupDescriptionIndex = grp_idx; |
7529 | 0 | if (use_default) { |
7530 | 0 | if (*is_traf_sgpd) |
7531 | 0 | grp_idx |= 0x10000; |
7532 | 0 | *use_default = (sgdesc->default_description_index==grp_idx) ? GF_TRUE : GF_FALSE; |
7533 | 0 | } |
7534 | 0 | return GF_OK; |
7535 | 0 | } |
7536 | | GF_EXPORT |
7537 | | GF_Err gf_isom_add_sample_group_info(GF_ISOFile *movie, u32 track, u32 grouping_type, void *data, u32 data_size, Bool is_default, u32 *sampleGroupDescriptionIndex) |
7538 | 0 | { |
7539 | 0 | return gf_isom_add_sample_group_info_internal(movie, track, grouping_type, data, data_size, is_default ? 0x80000000 : 0, sampleGroupDescriptionIndex, NULL, GF_TRUE, NULL, NULL); |
7540 | 0 | } |
7541 | | |
7542 | | GF_Err gf_isom_set_sample_group_description_internal(GF_ISOFile *movie, u32 track, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *data, u32 data_size, Bool check_access, u32 sgpd_flags) |
7543 | 0 | { |
7544 | 0 | u32 sampleGroupDescriptionIndex, trafID=0; |
7545 | 0 | GF_Err e; |
7546 | 0 | GF_SampleGroupDescriptionBox *sgdesc=NULL; |
7547 | 0 | Bool is_traf_sgpd, use_default=GF_FALSE; |
7548 | 0 | GF_List *groupList=NULL, *parent=NULL; |
7549 | |
|
7550 | 0 | e = gf_isom_add_sample_group_info_internal(movie, track, grouping_type, data, data_size, sgpd_flags, &sampleGroupDescriptionIndex, &is_traf_sgpd, check_access, &use_default, &sgdesc); |
7551 | 0 | if (e) return e; |
7552 | 0 | if (use_default) return GF_OK; |
7553 | | |
7554 | 0 | GF_SampleTableBox *stbl=NULL; |
7555 | 0 | GF_TrackBox *trak=NULL; |
7556 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7557 | 0 | GF_TrackFragmentBox *traf=NULL; |
7558 | 0 | #endif |
7559 | 0 | if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) { |
7560 | 0 | trak = gf_isom_get_track_box(movie, track); |
7561 | 0 | if (!trak) return GF_BAD_PARAM; |
7562 | 0 | trafID = trak->Header->trackID; |
7563 | 0 | } |
7564 | | |
7565 | 0 | if (trafID) { |
7566 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7567 | 0 | if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) |
7568 | 0 | return GF_BAD_PARAM; |
7569 | | |
7570 | 0 | traf = gf_isom_get_traf(movie, trafID); |
7571 | | #else |
7572 | | return GF_NOT_SUPPORTED; |
7573 | | #endif |
7574 | |
|
7575 | 0 | } else if (track) { |
7576 | 0 | if (check_access) { |
7577 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7578 | 0 | if (e) return e; |
7579 | 0 | } |
7580 | | |
7581 | 0 | trak = gf_isom_get_track_box(movie, track); |
7582 | 0 | if (!trak) return GF_BAD_PARAM; |
7583 | 0 | } |
7584 | | |
7585 | | /*look in stbl or traf for sample sampleGroups*/ |
7586 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
7587 | 0 | if (traf) { |
7588 | 0 | if (!traf->sampleGroups) |
7589 | 0 | traf->sampleGroups = gf_list_new(); |
7590 | 0 | groupList = traf->sampleGroups; |
7591 | 0 | parent = traf->child_boxes; |
7592 | 0 | if (sampleGroupDescriptionIndex && is_traf_sgpd) |
7593 | 0 | sampleGroupDescriptionIndex |= 0x10000; |
7594 | 0 | } else |
7595 | 0 | #endif |
7596 | 0 | { |
7597 | 0 | stbl = trak->Media->information->sampleTable; |
7598 | 0 | if (!stbl->sampleGroups) |
7599 | 0 | stbl->sampleGroups = gf_list_new(); |
7600 | 0 | groupList = stbl->sampleGroups; |
7601 | 0 | parent = stbl->child_boxes; |
7602 | 0 | } |
7603 | |
|
7604 | 0 | return gf_isom_add_sample_group_entry(groupList, sample_number, sgdesc, grouping_type_parameter, sampleGroupDescriptionIndex, parent, stbl); |
7605 | |
|
7606 | 0 | } |
7607 | | |
7608 | | GF_Err gf_isom_set_sample_group_description(GF_ISOFile *movie, u32 track, u32 sample_number, u32 grouping_type, u32 grouping_type_parameter, void *data, u32 data_size, u32 sgpd_flags) |
7609 | 0 | { |
7610 | 0 | return gf_isom_set_sample_group_description_internal(movie, track, sample_number, grouping_type, grouping_type_parameter, data, data_size, GF_TRUE, sgpd_flags); |
7611 | 0 | } |
7612 | | |
7613 | | GF_EXPORT |
7614 | | GF_Err gf_isom_remove_sample_group(GF_ISOFile *movie, u32 track, u32 grouping_type) |
7615 | 0 | { |
7616 | 0 | GF_Err e; |
7617 | 0 | GF_TrackBox *trak; |
7618 | 0 | u32 count, i; |
7619 | |
|
7620 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7621 | 0 | if (e) return e; |
7622 | | |
7623 | 0 | trak = gf_isom_get_track_box(movie, track); |
7624 | 0 | if (!trak) return GF_BAD_PARAM; |
7625 | | |
7626 | 0 | if (trak->Media->information->sampleTable->sampleGroupsDescription) { |
7627 | 0 | count = gf_list_count(trak->Media->information->sampleTable->sampleGroupsDescription); |
7628 | 0 | for (i=0; i<count; i++) { |
7629 | 0 | GF_SampleGroupDescriptionBox *sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(trak->Media->information->sampleTable->sampleGroupsDescription, i); |
7630 | 0 | if (sgdesc->grouping_type==grouping_type) { |
7631 | 0 | gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box*)sgdesc); |
7632 | 0 | gf_list_rem(trak->Media->information->sampleTable->sampleGroupsDescription, i); |
7633 | 0 | i--; |
7634 | 0 | count--; |
7635 | 0 | } |
7636 | 0 | } |
7637 | 0 | } |
7638 | 0 | if (trak->Media->information->sampleTable->sampleGroups) { |
7639 | 0 | count = gf_list_count(trak->Media->information->sampleTable->sampleGroups); |
7640 | 0 | for (i=0; i<count; i++) { |
7641 | 0 | GF_SampleGroupBox *sgroup = gf_list_get(trak->Media->information->sampleTable->sampleGroups, i); |
7642 | 0 | if (sgroup->grouping_type==grouping_type) { |
7643 | 0 | gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box*)sgroup); |
7644 | 0 | gf_list_rem(trak->Media->information->sampleTable->sampleGroups, i); |
7645 | 0 | i--; |
7646 | 0 | count--; |
7647 | 0 | } |
7648 | 0 | } |
7649 | 0 | } |
7650 | 0 | return GF_OK; |
7651 | 0 | } |
7652 | | |
7653 | | GF_EXPORT |
7654 | | GF_Err gf_isom_add_sample_info(GF_ISOFile *movie, u32 track, u32 sample_number, u32 grouping_type, u32 sampleGroupDescriptionIndex, u32 grouping_type_parameter) |
7655 | 0 | { |
7656 | 0 | GF_Err e; |
7657 | 0 | GF_TrackBox *trak; |
7658 | 0 | GF_List *groupList; |
7659 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
7660 | 0 | if (e) return e; |
7661 | | |
7662 | 0 | trak = gf_isom_get_track_box(movie, track); |
7663 | 0 | if (!trak) return GF_BAD_PARAM; |
7664 | | |
7665 | 0 | if (!trak->Media->information->sampleTable->sampleGroups) |
7666 | 0 | trak->Media->information->sampleTable->sampleGroups = gf_list_new(); |
7667 | | |
7668 | |
|
7669 | 0 | GF_SampleGroupDescriptionBox *sgdesc = get_sgdp(trak->Media->information->sampleTable, NULL, grouping_type, NULL); |
7670 | 0 | if (!sgdesc) return GF_BAD_PARAM; |
7671 | | |
7672 | 0 | groupList = trak->Media->information->sampleTable->sampleGroups; |
7673 | 0 | return gf_isom_add_sample_group_entry(groupList, sample_number, sgdesc, grouping_type_parameter, sampleGroupDescriptionIndex, trak->Media->information->sampleTable->child_boxes, trak->Media->information->sampleTable); |
7674 | 0 | } |
7675 | | |
7676 | | |
7677 | | void *sg_rap_create_entry(void *udta) |
7678 | 0 | { |
7679 | 0 | GF_VisualRandomAccessEntry *entry; |
7680 | 0 | u32 *num_leading_samples = (u32 *) udta; |
7681 | 0 | gf_assert(udta); |
7682 | 0 | GF_SAFEALLOC(entry, GF_VisualRandomAccessEntry); |
7683 | 0 | if (!entry) return NULL; |
7684 | 0 | entry->num_leading_samples = *num_leading_samples; |
7685 | 0 | entry->num_leading_samples_known = entry->num_leading_samples ? 1 : 0; |
7686 | 0 | return entry; |
7687 | 0 | } |
7688 | | |
7689 | | Bool sg_rap_compare_entry(void *udta, void *entry) |
7690 | 0 | { |
7691 | 0 | u32 *num_leading_samples = (u32 *) udta; |
7692 | 0 | if (*num_leading_samples == ((GF_VisualRandomAccessEntry *)entry)->num_leading_samples) return GF_TRUE; |
7693 | 0 | return GF_FALSE; |
7694 | 0 | } |
7695 | | |
7696 | | GF_EXPORT |
7697 | | GF_Err gf_isom_set_sample_rap_group(GF_ISOFile *movie, u32 track, u32 sample_number, Bool is_rap, u32 num_leading_samples) |
7698 | 0 | { |
7699 | 0 | return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_RAP, 0, &num_leading_samples, is_rap ? sg_rap_create_entry : NULL, is_rap ? sg_rap_compare_entry : NULL); |
7700 | 0 | } |
7701 | | |
7702 | | GF_Err gf_isom_fragment_set_sample_rap_group(GF_ISOFile *movie, GF_ISOTrackID trackID, u32 sample_number_in_frag, Bool is_rap, u32 num_leading_samples) |
7703 | 0 | { |
7704 | 0 | return gf_isom_set_sample_group_info_internal(movie, 0, trackID, sample_number_in_frag, GF_ISOM_SAMPLE_GROUP_RAP, 0, &num_leading_samples, is_rap ? sg_rap_create_entry : NULL, is_rap ? sg_rap_compare_entry : NULL); |
7705 | 0 | } |
7706 | | |
7707 | | |
7708 | | void *sg_av1s_create_entry(void *udta) |
7709 | 0 | { |
7710 | 0 | GF_AV1SwitchingEntry *entry; |
7711 | 0 | GF_SAFEALLOC(entry, GF_AV1SwitchingEntry); |
7712 | 0 | if (!entry) return NULL; |
7713 | 0 | return entry; |
7714 | 0 | } |
7715 | | |
7716 | | Bool sg_av1s_compare_entry(void *udta, void *entry) |
7717 | 0 | { |
7718 | 0 | return GF_TRUE; |
7719 | 0 | } |
7720 | | |
7721 | | GF_EXPORT |
7722 | | GF_Err gf_isom_set_sample_av1_switch_frame_group(GF_ISOFile *movie, u32 track, u32 sample_number, Bool is_switch_Frame) |
7723 | 0 | { |
7724 | 0 | return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_AV1S, 0, NULL, is_switch_Frame ? sg_av1s_create_entry : NULL, is_switch_Frame ? sg_av1s_compare_entry : NULL); |
7725 | 0 | } |
7726 | | |
7727 | | GF_Err gf_isom_fragment_set_sample_av1_switch_frame_group(GF_ISOFile *movie, GF_ISOTrackID trackID, u32 sample_number_in_frag, Bool is_switch_Frame) |
7728 | 0 | { |
7729 | 0 | return gf_isom_set_sample_group_info_internal(movie, 0, trackID, sample_number_in_frag, GF_ISOM_SAMPLE_GROUP_AV1S, 0, NULL, is_switch_Frame ? sg_av1s_create_entry : NULL, is_switch_Frame ? sg_av1s_compare_entry : NULL); |
7730 | 0 | } |
7731 | | |
7732 | | |
7733 | | |
7734 | | void *sg_roll_create_entry(void *udta) |
7735 | 0 | { |
7736 | 0 | GF_RollRecoveryEntry *entry; |
7737 | 0 | s16 *roll_distance = (s16 *) udta; |
7738 | 0 | GF_SAFEALLOC(entry, GF_RollRecoveryEntry); |
7739 | 0 | if (!entry) return NULL; |
7740 | 0 | entry->roll_distance = *roll_distance; |
7741 | 0 | return entry; |
7742 | 0 | } |
7743 | | |
7744 | | Bool sg_roll_compare_entry(void *udta, void *entry) |
7745 | 0 | { |
7746 | 0 | s16 *roll_distance = (s16 *) udta; |
7747 | 0 | if (*roll_distance == ((GF_RollRecoveryEntry *)entry)->roll_distance) return GF_TRUE; |
7748 | 0 | return GF_FALSE; |
7749 | 0 | } |
7750 | | |
7751 | | GF_EXPORT |
7752 | | GF_Err gf_isom_set_sample_roll_group(GF_ISOFile *movie, u32 track, u32 sample_number, GF_ISOSampleRollType roll_type, s16 roll_distance) |
7753 | 0 | { |
7754 | 0 | u32 grp_type = (roll_type>=GF_ISOM_SAMPLE_PREROLL) ? GF_ISOM_SAMPLE_GROUP_PROL : GF_ISOM_SAMPLE_GROUP_ROLL; |
7755 | 0 | if (roll_type==GF_ISOM_SAMPLE_PREROLL_NONE) |
7756 | 0 | roll_type = 0; |
7757 | |
|
7758 | 0 | return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, grp_type, 0, &roll_distance, roll_type ? sg_roll_create_entry : NULL, roll_type ? sg_roll_compare_entry : NULL); |
7759 | 0 | } |
7760 | | |
7761 | | GF_EXPORT |
7762 | | GF_Err gf_isom_fragment_set_sample_roll_group(GF_ISOFile *movie, GF_ISOTrackID trackID, u32 sample_number_in_frag, GF_ISOSampleRollType roll_type, s16 roll_distance) |
7763 | 0 | { |
7764 | 0 | u32 grp_type = (roll_type>=GF_ISOM_SAMPLE_PREROLL) ? GF_ISOM_SAMPLE_GROUP_PROL : GF_ISOM_SAMPLE_GROUP_ROLL; |
7765 | 0 | if (roll_type==GF_ISOM_SAMPLE_PREROLL_NONE) |
7766 | 0 | roll_type = 0; |
7767 | |
|
7768 | 0 | return gf_isom_set_sample_group_info_internal(movie, 0, trackID, sample_number_in_frag, grp_type, 0, &roll_distance, roll_type ? sg_roll_create_entry : NULL, roll_type ? sg_roll_compare_entry : NULL); |
7769 | 0 | } |
7770 | | |
7771 | | |
7772 | | void *sg_encryption_create_entry(void *udta) |
7773 | 0 | { |
7774 | 0 | GF_CENCSampleEncryptionGroupEntry *entry, *from_entry; |
7775 | 0 | GF_SAFEALLOC(entry, GF_CENCSampleEncryptionGroupEntry); |
7776 | 0 | if (!entry) return NULL; |
7777 | 0 | from_entry = (GF_CENCSampleEncryptionGroupEntry *)udta; |
7778 | 0 | memcpy(entry, from_entry, sizeof(GF_CENCSampleEncryptionGroupEntry) ); |
7779 | 0 | entry->key_info = gf_malloc(sizeof(u8) * entry->key_info_size); |
7780 | 0 | if (!entry->key_info) { |
7781 | 0 | gf_free(entry); |
7782 | 0 | return NULL; |
7783 | 0 | } |
7784 | 0 | memcpy(entry->key_info, from_entry->key_info, entry->key_info_size); |
7785 | 0 | return entry; |
7786 | 0 | } |
7787 | | |
7788 | | Bool sg_encryption_compare_entry(void *udta, void *_entry) |
7789 | 0 | { |
7790 | 0 | GF_CENCSampleEncryptionGroupEntry *entry = (GF_CENCSampleEncryptionGroupEntry *)_entry; |
7791 | 0 | GF_CENCSampleEncryptionGroupEntry *with_entry = (GF_CENCSampleEncryptionGroupEntry *)udta; |
7792 | |
|
7793 | 0 | if (entry->IsProtected != with_entry->IsProtected) return GF_FALSE; |
7794 | 0 | if (entry->skip_byte_block != with_entry->skip_byte_block) return GF_FALSE; |
7795 | 0 | if (entry->crypt_byte_block != with_entry->crypt_byte_block) return GF_FALSE; |
7796 | 0 | if (entry->key_info_size != with_entry->key_info_size) return GF_FALSE; |
7797 | | |
7798 | 0 | if (!memcmp(entry->key_info, with_entry->key_info, with_entry->key_info_size)) |
7799 | 0 | return GF_TRUE; |
7800 | 0 | return GF_FALSE; |
7801 | 0 | } |
7802 | | |
7803 | | |
7804 | | /*sample encryption information group can be in stbl or traf*/ |
7805 | | GF_EXPORT |
7806 | | GF_Err gf_isom_set_sample_cenc_group(GF_ISOFile *movie, u32 track, u32 sample_number, u8 isEncrypted, u32 crypt_byte_block, u32 skip_byte_block, u8 *key_info, u32 key_info_size) |
7807 | 0 | { |
7808 | 0 | GF_CENCSampleEncryptionGroupEntry entry; |
7809 | 0 | if (!key_info || (key_info_size<19)) |
7810 | 0 | return GF_BAD_PARAM; |
7811 | | |
7812 | 0 | memset(&entry, 0, sizeof(GF_CENCSampleEncryptionGroupEntry)); |
7813 | 0 | entry.crypt_byte_block = crypt_byte_block; |
7814 | 0 | entry.skip_byte_block = skip_byte_block; |
7815 | 0 | entry.IsProtected = isEncrypted; |
7816 | 0 | entry.key_info = key_info; |
7817 | 0 | entry.key_info_size = key_info_size; |
7818 | |
|
7819 | 0 | return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_SEIG, 0, &entry, sg_encryption_create_entry, sg_encryption_compare_entry); |
7820 | 0 | } |
7821 | | |
7822 | | |
7823 | | |
7824 | | GF_EXPORT |
7825 | | GF_Err gf_isom_set_sample_cenc_default_group(GF_ISOFile *movie, u32 track, u32 sample_number) |
7826 | 0 | { |
7827 | 0 | return gf_isom_set_sample_group_info_internal(movie, track, 0, sample_number, GF_ISOM_SAMPLE_GROUP_SEIG, 0, NULL, NULL, NULL); |
7828 | 0 | } |
7829 | | |
7830 | | GF_Err gf_isom_force_ctts(GF_ISOFile *file, u32 track) |
7831 | 0 | { |
7832 | 0 | GF_TrackBox *trak; |
7833 | 0 | GF_Err e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
7834 | 0 | if (e) return e; |
7835 | 0 | trak = gf_isom_get_track_box(file, track); |
7836 | 0 | if (!trak) return GF_BAD_PARAM; |
7837 | 0 | if (trak->Media->information->sampleTable->CompositionOffset) return GF_OK; |
7838 | | |
7839 | 0 | trak->Media->information->sampleTable->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_CTTS); |
7840 | 0 | if (!trak->Media->information->sampleTable->CompositionOffset) return GF_OUT_OF_MEM; |
7841 | 0 | trak->Media->information->sampleTable->CompositionOffset->nb_entries = 1; |
7842 | 0 | trak->Media->information->sampleTable->CompositionOffset->entries = gf_malloc(sizeof(GF_DttsEntry)); |
7843 | 0 | trak->Media->information->sampleTable->CompositionOffset->entries[0].decodingOffset = 0; |
7844 | 0 | trak->Media->information->sampleTable->CompositionOffset->entries[0].sampleCount = trak->Media->information->sampleTable->SampleSize->sampleCount; |
7845 | 0 | return GF_OK; |
7846 | 0 | } |
7847 | | |
7848 | | GF_EXPORT |
7849 | | GF_Err gf_isom_set_ctts_v1(GF_ISOFile *file, u32 track, u32 ctts_shift) |
7850 | 0 | { |
7851 | 0 | u32 i, shift; |
7852 | 0 | u64 duration; |
7853 | 0 | GF_CompositionOffsetBox *ctts; |
7854 | 0 | GF_CompositionToDecodeBox *cslg; |
7855 | 0 | s32 leastCTTS, greatestCTTS; |
7856 | 0 | GF_TrackBox *trak; |
7857 | 0 | GF_Err e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
7858 | 0 | if (e) return e; |
7859 | | |
7860 | 0 | trak = gf_isom_get_track_box(file, track); |
7861 | 0 | if (!trak) return GF_BAD_PARAM; |
7862 | | |
7863 | 0 | ctts = trak->Media->information->sampleTable->CompositionOffset; |
7864 | 0 | if (ctts->version) { |
7865 | 0 | shift = ctts_shift; |
7866 | 0 | } else { |
7867 | 0 | shift = ctts->nb_entries ? ctts->entries[0].decodingOffset : 0; |
7868 | 0 | } |
7869 | 0 | leastCTTS = GF_INT_MAX; |
7870 | 0 | greatestCTTS = 0; |
7871 | 0 | for (i=0; i<ctts->nb_entries; i++) { |
7872 | 0 | if (!ctts->version) |
7873 | 0 | ctts->entries[i].decodingOffset -= shift; |
7874 | |
|
7875 | 0 | if ((s32)ctts->entries[i].decodingOffset < leastCTTS) |
7876 | 0 | leastCTTS = ctts->entries[i].decodingOffset; |
7877 | 0 | if ((s32)ctts->entries[i].decodingOffset > greatestCTTS) |
7878 | 0 | greatestCTTS = ctts->entries[i].decodingOffset; |
7879 | 0 | } |
7880 | 0 | if (!ctts->version) { |
7881 | 0 | ctts->version = 1; |
7882 | | //if we had edit lists, shift all media times by the given amount |
7883 | 0 | if (trak->editBox && trak->editBox->editList) { |
7884 | 0 | for (i=0; i<gf_list_count(trak->editBox->editList->entryList); i++) { |
7885 | 0 | GF_EdtsEntry *ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, i); |
7886 | | //empty edit |
7887 | 0 | if (ent->mediaTime<0) continue; |
7888 | 0 | if (ent->mediaTime>=shift) ent->mediaTime -= shift; |
7889 | 0 | else ent->mediaTime = 0; |
7890 | | //no offset and last entry, trash edit |
7891 | 0 | if (!ent->mediaTime && (gf_list_count(trak->editBox->editList->entryList)==1)) { |
7892 | 0 | gf_isom_box_del_parent(&trak->child_boxes, (GF_Box *)trak->editBox); |
7893 | 0 | trak->editBox = NULL; |
7894 | 0 | break; |
7895 | 0 | } |
7896 | 0 | } |
7897 | 0 | SetTrackDuration(trak); |
7898 | 0 | } |
7899 | 0 | } |
7900 | |
|
7901 | 0 | if (!trak->Media->information->sampleTable->CompositionToDecode) { |
7902 | 0 | trak->Media->information->sampleTable->CompositionToDecode = (GF_CompositionToDecodeBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_CSLG); |
7903 | 0 | if (!trak->Media->information->sampleTable->CompositionToDecode) |
7904 | 0 | return GF_OUT_OF_MEM; |
7905 | 0 | } |
7906 | | |
7907 | 0 | cslg = trak->Media->information->sampleTable->CompositionToDecode; |
7908 | 0 | if (cslg) { |
7909 | 0 | cslg->compositionToDTSShift = shift; |
7910 | 0 | cslg->leastDecodeToDisplayDelta = leastCTTS; |
7911 | 0 | cslg->greatestDecodeToDisplayDelta = greatestCTTS; |
7912 | 0 | cslg->compositionStartTime = 0; |
7913 | | /*for our use case (first CTS set to 0), the composition end time is the media duration if it fits on 32 bits*/ |
7914 | 0 | duration = gf_isom_get_media_duration(file, track); |
7915 | 0 | cslg->compositionEndTime = (duration<0x7FFFFFFF) ? (s32) duration : 0; |
7916 | 0 | } |
7917 | |
|
7918 | 0 | gf_isom_modify_alternate_brand(file, GF_ISOM_BRAND_ISO4, GF_TRUE); |
7919 | 0 | return GF_OK; |
7920 | 0 | } |
7921 | | |
7922 | | static GF_Err gf_isom_set_ctts_v0(GF_ISOFile *file, GF_TrackBox *trak) |
7923 | 0 | { |
7924 | 0 | u32 i; |
7925 | 0 | s32 shift; |
7926 | 0 | GF_CompositionOffsetBox *ctts; |
7927 | 0 | GF_CompositionToDecodeBox *cslg; |
7928 | |
|
7929 | 0 | ctts = trak->Media->information->sampleTable->CompositionOffset; |
7930 | |
|
7931 | 0 | if (!trak->Media->information->sampleTable->CompositionToDecode) |
7932 | 0 | { |
7933 | 0 | shift = 0; |
7934 | 0 | for (i=0; i<ctts->nb_entries; i++) { |
7935 | 0 | if (-ctts->entries[i].decodingOffset > shift) |
7936 | 0 | shift = -ctts->entries[i].decodingOffset; |
7937 | 0 | } |
7938 | 0 | if (shift > 0) |
7939 | 0 | { |
7940 | 0 | for (i=0; i<ctts->nb_entries; i++) { |
7941 | 0 | s64 new_ts = ctts->entries[i].decodingOffset; |
7942 | 0 | new_ts += shift; |
7943 | 0 | ctts->entries[i].decodingOffset = (s32) new_ts; |
7944 | 0 | } |
7945 | 0 | } |
7946 | 0 | } |
7947 | 0 | else |
7948 | 0 | { |
7949 | 0 | cslg = trak->Media->information->sampleTable->CompositionToDecode; |
7950 | 0 | shift = (s32) cslg->compositionToDTSShift; |
7951 | 0 | for (i=0; i<ctts->nb_entries; i++) { |
7952 | 0 | s64 new_ts = ctts->entries[i].decodingOffset; |
7953 | 0 | new_ts += shift; |
7954 | 0 | ctts->entries[i].decodingOffset = (s32) new_ts; |
7955 | 0 | } |
7956 | 0 | gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box *)cslg); |
7957 | 0 | trak->Media->information->sampleTable->CompositionToDecode = NULL; |
7958 | 0 | } |
7959 | 0 | if (shift>0) { |
7960 | | //no edits, insert one |
7961 | 0 | if (! trak->editBox) { |
7962 | 0 | u64 dur = trak->Media->mediaHeader->duration; |
7963 | 0 | dur *= file->moov->mvhd->timeScale; |
7964 | 0 | dur /= trak->Media->mediaHeader->timeScale; |
7965 | 0 | gf_isom_set_edit(file, gf_list_find(file->moov->trackList, trak)+1, 0, dur, shift, GF_ISOM_EDIT_NORMAL); |
7966 | 0 | } else { |
7967 | | //otherwise shift media times in all entries |
7968 | 0 | for (i=0; i<gf_list_count(trak->editBox->editList->entryList); i++) { |
7969 | 0 | GF_EdtsEntry *ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, i); |
7970 | | //empty edit |
7971 | 0 | if (ent->mediaTime<0) continue; |
7972 | 0 | ent->mediaTime += shift; |
7973 | 0 | } |
7974 | 0 | SetTrackDuration(trak); |
7975 | 0 | } |
7976 | 0 | } |
7977 | 0 | ctts->version = 0; |
7978 | 0 | gf_isom_modify_alternate_brand(file, GF_ISOM_BRAND_ISO4, GF_FALSE); |
7979 | 0 | return GF_OK; |
7980 | 0 | } |
7981 | | |
7982 | | GF_EXPORT |
7983 | | GF_Err gf_isom_set_composition_offset_mode(GF_ISOFile *file, u32 track, Bool use_negative_offsets) |
7984 | 0 | { |
7985 | 0 | GF_Err e; |
7986 | 0 | GF_TrackBox *trak; |
7987 | 0 | GF_CompositionOffsetBox *ctts; |
7988 | |
|
7989 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
7990 | 0 | if (e) return e; |
7991 | | |
7992 | 0 | trak = gf_isom_get_track_box(file, track); |
7993 | 0 | if (!trak) return GF_BAD_PARAM; |
7994 | | |
7995 | 0 | ctts = trak->Media->information->sampleTable->CompositionOffset; |
7996 | 0 | if (!ctts) { |
7997 | 0 | if (!use_negative_offsets && trak->Media->information->sampleTable->CompositionToDecode) { |
7998 | 0 | gf_isom_box_del_parent(&trak->Media->information->sampleTable->child_boxes, (GF_Box *)trak->Media->information->sampleTable->CompositionToDecode); |
7999 | 0 | trak->Media->information->sampleTable->CompositionToDecode = NULL; |
8000 | 0 | } |
8001 | 0 | return GF_OK; |
8002 | 0 | } |
8003 | | |
8004 | 0 | if (use_negative_offsets) { |
8005 | 0 | return gf_isom_set_ctts_v1(file, track, 0); |
8006 | 0 | } else { |
8007 | 0 | if (ctts->version==0) return GF_OK; |
8008 | 0 | return gf_isom_set_ctts_v0(file, trak); |
8009 | 0 | } |
8010 | 0 | } |
8011 | | |
8012 | | #if 0 //unused |
8013 | | GF_Err gf_isom_set_sync_table(GF_ISOFile *file, u32 track) |
8014 | | { |
8015 | | GF_Err e; |
8016 | | GF_TrackBox *trak; |
8017 | | |
8018 | | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8019 | | if (e) return e; |
8020 | | |
8021 | | trak = gf_isom_get_track_box(file, track); |
8022 | | if (!trak) return GF_BAD_PARAM; |
8023 | | |
8024 | | if (!trak->Media->information->sampleTable->SyncSample) { |
8025 | | trak->Media->information->sampleTable->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STSS); |
8026 | | |
8027 | | if (!trak->Media->information->sampleTable->SyncSample) |
8028 | | return GF_OUT_OF_MEM; |
8029 | | } |
8030 | | return GF_OK; |
8031 | | } |
8032 | | #endif |
8033 | | |
8034 | | GF_Err gf_isom_set_sample_flags(GF_ISOFile *file, u32 track, u32 sampleNumber, u32 isLeading, u32 dependsOn, u32 dependedOn, u32 redundant) |
8035 | 0 | { |
8036 | 0 | GF_Err e; |
8037 | 0 | GF_TrackBox *trak; |
8038 | |
|
8039 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8040 | 0 | if (e) return e; |
8041 | | |
8042 | 0 | trak = gf_isom_get_track_box(file, track); |
8043 | 0 | if (!trak) return GF_BAD_PARAM; |
8044 | 0 | return stbl_SetDependencyType(trak->Media->information->sampleTable, sampleNumber, isLeading, dependsOn, dependedOn, redundant); |
8045 | 0 | } |
8046 | | |
8047 | | #if 0 //unused |
8048 | | GF_Err gf_isom_sample_set_dep_info(GF_ISOFile *file, u32 track, u32 sampleNumber, u32 isLeading, u32 dependsOn, u32 dependedOn, u32 redundant) |
8049 | | { |
8050 | | GF_TrackBox *trak; |
8051 | | |
8052 | | trak = gf_isom_get_track_box(file, track); |
8053 | | if (!trak) return GF_BAD_PARAM; |
8054 | | |
8055 | | return stbl_AddDependencyType(trak->Media->information->sampleTable, sampleNumber, isLeading, dependsOn, dependedOn, redundant); |
8056 | | } |
8057 | | #endif |
8058 | | |
8059 | | GF_EXPORT |
8060 | | GF_Err gf_isom_copy_sample_info(GF_ISOFile *dst, u32 dst_track, GF_ISOFile *src, u32 src_track, u32 sampleNumber) |
8061 | 0 | { |
8062 | 0 | u32 i, count, idx, dst_sample_num, subs_flags; |
8063 | 0 | GF_SubSampleInfoEntry *sub_sample; |
8064 | 0 | GF_Err e; |
8065 | 0 | GF_TrackBox *src_trak, *dst_trak; |
8066 | |
|
8067 | 0 | src_trak = gf_isom_get_track_box(src, src_track); |
8068 | 0 | if (!src_trak) return GF_BAD_PARAM; |
8069 | | |
8070 | 0 | dst_trak = gf_isom_get_track_box(dst, dst_track); |
8071 | 0 | if (!dst_trak) return GF_BAD_PARAM; |
8072 | | |
8073 | 0 | dst_sample_num = dst_trak->Media->information->sampleTable->SampleSize->sampleCount; |
8074 | | |
8075 | | /*modify depends flags*/ |
8076 | 0 | if (src_trak->Media->information->sampleTable->SampleDep) { |
8077 | 0 | u32 isLeading, dependsOn, dependedOn, redundant; |
8078 | |
|
8079 | 0 | isLeading = dependsOn = dependedOn = redundant = 0; |
8080 | |
|
8081 | 0 | e = stbl_GetSampleDepType(src_trak->Media->information->sampleTable->SampleDep, sampleNumber, &isLeading, &dependsOn, &dependedOn, &redundant); |
8082 | 0 | if (e) return e; |
8083 | | |
8084 | 0 | e = stbl_AppendDependencyType(dst_trak->Media->information->sampleTable, isLeading, dependsOn, dependedOn, redundant); |
8085 | 0 | if (e) return e; |
8086 | 0 | } |
8087 | | |
8088 | | /*copy subsample info if any*/ |
8089 | 0 | idx=1; |
8090 | 0 | while (gf_isom_get_subsample_types(src, src_track, idx, &subs_flags)) { |
8091 | 0 | GF_SubSampleInformationBox *dst_subs=NULL; |
8092 | 0 | idx++; |
8093 | |
|
8094 | 0 | if ( ! gf_isom_sample_get_subsample_entry(src, src_track, sampleNumber, subs_flags, &sub_sample)) |
8095 | 0 | continue; |
8096 | | |
8097 | | /*create subsample if needed*/ |
8098 | 0 | if (!dst_trak->Media->information->sampleTable->sub_samples) { |
8099 | 0 | dst_trak->Media->information->sampleTable->sub_samples = gf_list_new(); |
8100 | 0 | } |
8101 | 0 | count = gf_list_count(dst_trak->Media->information->sampleTable->sub_samples); |
8102 | 0 | for (i=0; i<count; i++) { |
8103 | 0 | dst_subs = gf_list_get(dst_trak->Media->information->sampleTable->sub_samples, i); |
8104 | 0 | if (dst_subs->flags==subs_flags) break; |
8105 | 0 | dst_subs=NULL; |
8106 | 0 | } |
8107 | 0 | if (!dst_subs) { |
8108 | 0 | dst_subs = (GF_SubSampleInformationBox *) gf_isom_box_new_parent(&dst_trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_SUBS); |
8109 | 0 | if (!dst_subs) return GF_OUT_OF_MEM; |
8110 | 0 | dst_subs->version=0; |
8111 | 0 | dst_subs->flags = subs_flags; |
8112 | 0 | gf_list_add(dst_trak->Media->information->sampleTable->sub_samples, dst_subs); |
8113 | 0 | } |
8114 | | |
8115 | 0 | count = gf_list_count(sub_sample->SubSamples); |
8116 | 0 | for (i=0; i<count; i++) { |
8117 | 0 | GF_SubSampleEntry *entry = (GF_SubSampleEntry*)gf_list_get(sub_sample->SubSamples, i); |
8118 | 0 | e = gf_isom_add_subsample_info(dst_subs, dst_sample_num, entry->subsample_size, entry->subsample_priority, entry->reserved, entry->discardable); |
8119 | 0 | if (e) return e; |
8120 | 0 | } |
8121 | 0 | } |
8122 | | |
8123 | | /*copy sampleToGroup info if any*/ |
8124 | 0 | count = 0; |
8125 | 0 | if (src_trak->Media->information->sampleTable->sampleGroups) |
8126 | 0 | count = gf_list_count(src_trak->Media->information->sampleTable->sampleGroups); |
8127 | |
|
8128 | 0 | for (i=0; i<count; i++) { |
8129 | 0 | GF_SampleGroupBox *sg; |
8130 | 0 | u32 j, k, default_index; |
8131 | 0 | u32 first_sample_in_entry, last_sample_in_entry, group_desc_index_src, group_desc_index_dst; |
8132 | 0 | first_sample_in_entry = 1; |
8133 | |
|
8134 | 0 | sg = (GF_SampleGroupBox*)gf_list_get(src_trak->Media->information->sampleTable->sampleGroups, i); |
8135 | 0 | for (j=0; j<sg->entry_count; j++) { |
8136 | 0 | GF_SampleGroupDescriptionBox *sgd_dst = NULL; |
8137 | 0 | last_sample_in_entry = first_sample_in_entry + sg->sample_entries[j].sample_count - 1; |
8138 | 0 | if ((sampleNumber<first_sample_in_entry) || (sampleNumber>last_sample_in_entry)) { |
8139 | 0 | first_sample_in_entry = last_sample_in_entry+1; |
8140 | 0 | continue; |
8141 | 0 | } |
8142 | | |
8143 | 0 | if (!dst_trak->Media->information->sampleTable->sampleGroups) |
8144 | 0 | dst_trak->Media->information->sampleTable->sampleGroups = gf_list_new(); |
8145 | |
|
8146 | 0 | group_desc_index_src = group_desc_index_dst = sg->sample_entries[j].group_description_index; |
8147 | |
|
8148 | 0 | if (group_desc_index_src) { |
8149 | 0 | GF_SampleGroupDescriptionBox *sgd_src; |
8150 | 0 | void *sgde_src, *sgde_dst; |
8151 | |
|
8152 | 0 | group_desc_index_dst = 0; |
8153 | | //check that the sample group description exists !! |
8154 | 0 | sgde_src = gf_isom_get_sample_group_info_entry(src, src_trak, sg->grouping_type, sg->sample_entries[j].group_description_index, &default_index, &sgd_src); |
8155 | |
|
8156 | 0 | if (!sgde_src) break; |
8157 | | |
8158 | 0 | if (!dst_trak->Media->information->sampleTable->sampleGroupsDescription) |
8159 | 0 | dst_trak->Media->information->sampleTable->sampleGroupsDescription = gf_list_new(); |
8160 | |
|
8161 | 0 | sgd_dst = NULL; |
8162 | 0 | for (k=0; k< gf_list_count(dst_trak->Media->information->sampleTable->sampleGroupsDescription); k++) { |
8163 | 0 | sgd_dst = gf_list_get(dst_trak->Media->information->sampleTable->sampleGroupsDescription, k); |
8164 | 0 | if (sgd_dst->grouping_type==sgd_src->grouping_type) break; |
8165 | 0 | sgd_dst = NULL; |
8166 | 0 | } |
8167 | 0 | if (!sgd_dst) { |
8168 | 0 | gf_isom_clone_box( (GF_Box *) sgd_src, (GF_Box **) &sgd_dst); |
8169 | 0 | if (!sgd_dst) return GF_OUT_OF_MEM; |
8170 | 0 | gf_list_add(dst_trak->Media->information->sampleTable->sampleGroupsDescription, sgd_dst); |
8171 | 0 | gf_list_add(dst_trak->Media->information->sampleTable->child_boxes, sgd_dst); |
8172 | 0 | } |
8173 | | |
8174 | | //find the same entry |
8175 | 0 | for (k=0; k<gf_list_count(sgd_dst->group_descriptions); k++) { |
8176 | 0 | sgde_dst = gf_list_get(sgd_dst->group_descriptions, k); |
8177 | 0 | if (gf_isom_is_identical_sgpd(sgde_src, sgde_dst, sgd_src->grouping_type)) { |
8178 | 0 | group_desc_index_dst = k+1; |
8179 | 0 | break; |
8180 | 0 | } |
8181 | 0 | } |
8182 | 0 | if (!group_desc_index_dst) { |
8183 | 0 | GF_SampleGroupDescriptionBox *cloned=NULL; |
8184 | 0 | gf_isom_clone_box( (GF_Box *) sgd_src, (GF_Box **) &cloned); |
8185 | 0 | if (!cloned) return GF_OUT_OF_MEM; |
8186 | 0 | sgde_dst = gf_list_get(cloned->group_descriptions, group_desc_index_dst); |
8187 | 0 | gf_list_rem(cloned->group_descriptions, group_desc_index_dst); |
8188 | 0 | gf_isom_box_del( (GF_Box *) cloned); |
8189 | 0 | gf_list_add(sgd_dst->group_descriptions, sgde_dst); |
8190 | 0 | group_desc_index_dst = gf_list_count(sgd_dst->group_descriptions); |
8191 | 0 | } |
8192 | 0 | } else { |
8193 | 0 | gf_isom_get_sample_group_info_entry(dst, dst_trak, sg->grouping_type, 1, NULL, &sgd_dst); |
8194 | 0 | if (!sgd_dst) continue; |
8195 | 0 | } |
8196 | | |
8197 | | /*found our sample, add it to trak->sampleGroups*/ |
8198 | 0 | e = gf_isom_add_sample_group_entry(dst_trak->Media->information->sampleTable->sampleGroups, dst_sample_num, sgd_dst, sg->grouping_type_parameter, group_desc_index_dst, dst_trak->Media->information->sampleTable->child_boxes, NULL); |
8199 | 0 | if (e) return e; |
8200 | 0 | break; |
8201 | 0 | } |
8202 | 0 | } |
8203 | | |
8204 | | //copy auxiliary info |
8205 | 0 | count = gf_list_count(src_trak->Media->information->sampleTable->sai_sizes); |
8206 | 0 | for (i=0; i<count; i++) { |
8207 | 0 | u32 j; |
8208 | 0 | GF_SampleAuxiliaryInfoOffsetBox *saio = NULL; |
8209 | 0 | GF_SampleAuxiliaryInfoSizeBox *saiz = gf_list_get(src_trak->Media->information->sampleTable->sai_sizes, i); |
8210 | |
|
8211 | 0 | switch (saiz->aux_info_type) { |
8212 | 0 | case GF_ISOM_CENC_SCHEME: |
8213 | 0 | case GF_ISOM_CBC_SCHEME: |
8214 | 0 | case GF_ISOM_CENS_SCHEME: |
8215 | 0 | case GF_ISOM_CBCS_SCHEME: |
8216 | 0 | case GF_ISOM_PIFF_SCHEME: |
8217 | 0 | case 0: |
8218 | 0 | continue; |
8219 | 0 | default: |
8220 | 0 | break; |
8221 | 0 | } |
8222 | | //no aux sample associated |
8223 | 0 | if (saiz->sample_count<sampleNumber) continue; |
8224 | | //no size associated |
8225 | 0 | if (!saiz->default_sample_info_size && !saiz->sample_info_size[sampleNumber-1]) continue; |
8226 | | |
8227 | 0 | for (j=0; j<gf_list_count(src_trak->Media->information->sampleTable->sai_offsets); j++) { |
8228 | 0 | saio = gf_list_get(src_trak->Media->information->sampleTable->sai_offsets, j); |
8229 | 0 | if ((saio->aux_info_type==saiz->aux_info_type) && (saio->aux_info_type_parameter==saiz->aux_info_type_parameter)) break; |
8230 | 0 | saio=NULL; |
8231 | 0 | } |
8232 | 0 | if (!saio) continue; |
8233 | 0 | if (!saio->offsets && !saio->sai_data) continue; |
8234 | | |
8235 | 0 | u64 offset = saio->offsets ? saio->offsets[0] : 0; |
8236 | 0 | u32 nb_saio = saio->entry_count; |
8237 | 0 | if ((nb_saio>1) && (saio->entry_count != saiz->sample_count)) continue; |
8238 | | |
8239 | 0 | u32 size; |
8240 | |
|
8241 | 0 | if (nb_saio == 1) { |
8242 | 0 | for (j=0; j < sampleNumber-1; j++) { |
8243 | 0 | size = saiz->default_sample_info_size ? saiz->default_sample_info_size : saiz->sample_info_size[j]; |
8244 | 0 | offset += size; |
8245 | 0 | } |
8246 | 0 | } else { |
8247 | 0 | offset = saio->offsets[sampleNumber-1]; |
8248 | 0 | } |
8249 | |
|
8250 | 0 | size = saiz->default_sample_info_size ? saiz->default_sample_info_size : saiz->sample_info_size[j]; |
8251 | |
|
8252 | 0 | if (saio->sai_data) { |
8253 | 0 | e = gf_isom_add_sample_aux_info_internal(dst_trak, NULL, j+1, saiz->aux_info_type, saiz->aux_info_type_parameter, saio->sai_data->data + offset, size); |
8254 | 0 | } else { |
8255 | 0 | u8 *sai = gf_malloc(size); |
8256 | 0 | if (!sai) return GF_OUT_OF_MEM; |
8257 | | |
8258 | 0 | u64 cur_position = gf_bs_get_position(src_trak->moov->mov->movieFileMap->bs); |
8259 | 0 | gf_bs_seek(src_trak->moov->mov->movieFileMap->bs, offset); |
8260 | |
|
8261 | 0 | gf_bs_read_data(src_trak->moov->mov->movieFileMap->bs, sai, size); |
8262 | 0 | gf_bs_seek(src_trak->moov->mov->movieFileMap->bs, cur_position); |
8263 | 0 | e = gf_isom_add_sample_aux_info_internal(dst_trak, NULL, j+1, saiz->aux_info_type, saiz->aux_info_type_parameter, sai, size); |
8264 | 0 | gf_free(sai); |
8265 | 0 | } |
8266 | | |
8267 | 0 | if (e) { |
8268 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] Failed to clone sai data: %s\n", gf_error_to_string(e) )); |
8269 | 0 | } |
8270 | 0 | } |
8271 | 0 | return GF_OK; |
8272 | 0 | } |
8273 | | |
8274 | | GF_EXPORT |
8275 | | GF_Err gf_isom_text_set_display_flags(GF_ISOFile *file, u32 track, u32 desc_index, u32 flags, GF_TextFlagsMode op_type) |
8276 | 0 | { |
8277 | 0 | u32 i; |
8278 | 0 | GF_Err e; |
8279 | 0 | GF_TrackBox *trak; |
8280 | |
|
8281 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8282 | 0 | if (e) return e; |
8283 | | |
8284 | 0 | trak = gf_isom_get_track_box(file, track); |
8285 | 0 | if (!trak) return GF_BAD_PARAM; |
8286 | | |
8287 | 0 | for (i=0; i < gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes); i++) { |
8288 | 0 | GF_Tx3gSampleEntryBox *txt; |
8289 | 0 | if (desc_index && (i+1 != desc_index)) continue; |
8290 | | |
8291 | 0 | txt = (GF_Tx3gSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, i); |
8292 | 0 | if (txt->type != GF_ISOM_BOX_TYPE_TX3G) continue; |
8293 | | |
8294 | 0 | switch (op_type) { |
8295 | 0 | case GF_ISOM_TEXT_FLAGS_TOGGLE: |
8296 | 0 | txt->displayFlags |= flags; |
8297 | 0 | break; |
8298 | 0 | case GF_ISOM_TEXT_FLAGS_UNTOGGLE: |
8299 | 0 | txt->displayFlags &= ~flags; |
8300 | 0 | break; |
8301 | 0 | default: |
8302 | 0 | txt->displayFlags = flags; |
8303 | 0 | break; |
8304 | 0 | } |
8305 | 0 | } |
8306 | 0 | return GF_OK; |
8307 | |
|
8308 | 0 | } |
8309 | | |
8310 | | |
8311 | | GF_EXPORT |
8312 | | GF_Err gf_isom_update_duration(GF_ISOFile *movie) |
8313 | 0 | { |
8314 | 0 | u32 i; |
8315 | 0 | u64 maxDur; |
8316 | 0 | GF_TrackBox *trak; |
8317 | |
|
8318 | 0 | if (!movie || !movie->moov) return GF_BAD_PARAM; |
8319 | | |
8320 | | //if file was open in Write or Edit mode, recompute the duration |
8321 | | //the duration of a movie is the MaxDuration of all the tracks... |
8322 | | |
8323 | 0 | maxDur = 0; |
8324 | 0 | i=0; |
8325 | 0 | while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { |
8326 | 0 | if( (movie->LastError = SetTrackDuration(trak)) ) return movie->LastError; |
8327 | 0 | if (trak->Header && (trak->Header->duration > maxDur)) |
8328 | 0 | maxDur = trak->Header->duration; |
8329 | 0 | } |
8330 | 0 | movie->moov->mvhd->duration = maxDur; |
8331 | |
|
8332 | 0 | return GF_OK; |
8333 | 0 | } |
8334 | | |
8335 | | GF_EXPORT |
8336 | | GF_Err gf_isom_update_edit_list_duration(GF_ISOFile *file, u32 track) |
8337 | 0 | { |
8338 | 0 | u32 i; |
8339 | 0 | u64 trackDuration; |
8340 | 0 | GF_EdtsEntry *ent; |
8341 | 0 | GF_Err e; |
8342 | 0 | GF_TrackBox *trak; |
8343 | |
|
8344 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8345 | 0 | if (e) return e; |
8346 | | |
8347 | 0 | trak = gf_isom_get_track_box(file, track); |
8348 | 0 | if (!trak) return GF_BAD_PARAM; |
8349 | | |
8350 | | |
8351 | | //the total duration is the media duration: adjust it in case... |
8352 | 0 | e = Media_SetDuration(trak); |
8353 | 0 | if (e) return e; |
8354 | | |
8355 | | //assert the timeScales are non-NULL |
8356 | 0 | if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) return GF_ISOM_INVALID_FILE; |
8357 | 0 | trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale; |
8358 | | |
8359 | | //if we have an edit list, the duration is the sum of all the editList |
8360 | | //entries' duration (always expressed in MovieTimeScale) |
8361 | 0 | if (trak->editBox && trak->editBox->editList) { |
8362 | 0 | u64 editListDuration = 0; |
8363 | 0 | GF_EditListBox *elst = trak->editBox->editList; |
8364 | 0 | i=0; |
8365 | 0 | while ((ent = (GF_EdtsEntry*)gf_list_enum(elst->entryList, &i))) { |
8366 | 0 | if ((ent->mediaTime>=0) && (ent->mediaRate==0x10000) && (ent->segmentDuration > trackDuration)) |
8367 | 0 | ent->segmentDuration = trackDuration; |
8368 | |
|
8369 | 0 | if (!ent->segmentDuration) { |
8370 | 0 | u64 diff; |
8371 | 0 | ent->segmentDuration = trackDuration; |
8372 | 0 | if (ent->mediaTime>0) { |
8373 | 0 | diff = ent->mediaTime; |
8374 | 0 | diff *= trak->moov->mvhd->timeScale; |
8375 | 0 | diff /= trak->Media->mediaHeader->timeScale; |
8376 | 0 | if (diff < ent->segmentDuration) |
8377 | 0 | ent->segmentDuration -= diff; |
8378 | | /* |
8379 | | else |
8380 | | diff = 0; |
8381 | | */ |
8382 | 0 | } |
8383 | 0 | } |
8384 | 0 | if ((ent->mediaTime>=0) && ((u64) ent->mediaTime>=trak->Media->mediaHeader->duration)) { |
8385 | 0 | ent->mediaTime = trak->Media->mediaHeader->duration; |
8386 | 0 | } |
8387 | 0 | editListDuration += ent->segmentDuration; |
8388 | 0 | } |
8389 | 0 | trackDuration = editListDuration; |
8390 | 0 | } |
8391 | 0 | if (!trackDuration) { |
8392 | 0 | trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale; |
8393 | 0 | } |
8394 | 0 | trak->Header->duration = trackDuration; |
8395 | |
|
8396 | 0 | return GF_OK; |
8397 | |
|
8398 | 0 | } |
8399 | | |
8400 | | |
8401 | | GF_EXPORT |
8402 | 0 | GF_Err gf_isom_clone_pssh(GF_ISOFile *output, GF_ISOFile *input, Bool in_moof) { |
8403 | 0 | GF_Box *a; |
8404 | 0 | u32 i; |
8405 | 0 | i = 0; |
8406 | |
|
8407 | 0 | while ((a = (GF_Box *)gf_list_enum(input->moov->child_boxes, &i))) { |
8408 | 0 | if (a->type == GF_ISOM_BOX_TYPE_PSSH) { |
8409 | 0 | GF_List **child_boxes = &output->moov->child_boxes; |
8410 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
8411 | 0 | if (in_moof) |
8412 | 0 | child_boxes = &output->moof->child_boxes; |
8413 | 0 | #endif |
8414 | |
|
8415 | 0 | GF_ProtectionSystemHeaderBox *pssh = (GF_ProtectionSystemHeaderBox *)gf_isom_box_new_parent(child_boxes, GF_ISOM_BOX_TYPE_PSSH); |
8416 | 0 | if (!pssh) return GF_OUT_OF_MEM; |
8417 | 0 | memmove(pssh->SystemID, ((GF_ProtectionSystemHeaderBox *)a)->SystemID, 16); |
8418 | 0 | if (((GF_ProtectionSystemHeaderBox *)a)->KIDs && ((GF_ProtectionSystemHeaderBox *)a)->KID_count > 0) { |
8419 | 0 | pssh->KID_count = ((GF_ProtectionSystemHeaderBox *)a)->KID_count; |
8420 | 0 | pssh->KIDs = (bin128 *)gf_malloc(pssh->KID_count*sizeof(bin128)); |
8421 | 0 | if (!pssh->KIDs) return GF_OUT_OF_MEM; |
8422 | 0 | memmove(pssh->KIDs, ((GF_ProtectionSystemHeaderBox *)a)->KIDs, pssh->KID_count*sizeof(bin128)); |
8423 | 0 | } |
8424 | 0 | pssh->private_data_size = ((GF_ProtectionSystemHeaderBox *)a)->private_data_size; |
8425 | 0 | if (!pssh->private_data_size) { |
8426 | 0 | pssh->private_data = NULL; |
8427 | 0 | } else { |
8428 | 0 | pssh->private_data = (u8 *)gf_malloc(pssh->private_data_size*sizeof(char)); |
8429 | 0 | if (!pssh->private_data) return GF_OUT_OF_MEM; |
8430 | 0 | memmove(pssh->private_data, ((GF_ProtectionSystemHeaderBox *)a)->private_data, pssh->private_data_size); |
8431 | 0 | } |
8432 | 0 | } |
8433 | 0 | } |
8434 | 0 | return GF_OK; |
8435 | 0 | } |
8436 | | |
8437 | | GF_EXPORT |
8438 | | GF_Err gf_isom_set_track_group(GF_ISOFile *file, u32 track_number, u32 track_group_id, u32 group_type, Bool do_add) |
8439 | 0 | { |
8440 | 0 | u32 i, j; |
8441 | 0 | GF_TrackGroupTypeBox *trgt; |
8442 | 0 | GF_Err e; |
8443 | 0 | GF_TrackBox *trak; |
8444 | |
|
8445 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8446 | 0 | if (e) return e; |
8447 | | |
8448 | 0 | trak = gf_isom_get_track_box(file, track_number); |
8449 | 0 | if (!trak) return GF_BAD_PARAM; |
8450 | 0 | if (!trak->groups) trak->groups = (GF_TrackGroupBox*) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TRGR); |
8451 | 0 | if (!trak->groups) return GF_OUT_OF_MEM; |
8452 | | |
8453 | 0 | for (j=0; j<gf_list_count(file->moov->trackList); j++) { |
8454 | 0 | GF_TrackBox *a_trak = gf_list_get(file->moov->trackList, j); |
8455 | 0 | if (!a_trak->groups) continue; |
8456 | | |
8457 | 0 | for (i=0; i<gf_list_count(a_trak->groups->groups); i++) { |
8458 | 0 | trgt = gf_list_get(a_trak->groups->groups, i); |
8459 | |
|
8460 | 0 | if (trgt->track_group_id==track_group_id) { |
8461 | 0 | if (trgt->group_type != group_type) { |
8462 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("A track with same group ID is already defined for different group type %s\n", gf_4cc_to_str(trgt->group_type) )); |
8463 | 0 | return GF_BAD_PARAM; |
8464 | 0 | } |
8465 | 0 | if (a_trak==trak) { |
8466 | 0 | if (!do_add) { |
8467 | 0 | gf_list_rem(trak->groups->groups, i); |
8468 | 0 | gf_isom_box_del_parent(&trak->groups->child_boxes, (GF_Box *)trgt); |
8469 | 0 | } |
8470 | 0 | return GF_OK; |
8471 | 0 | } |
8472 | 0 | } |
8473 | 0 | } |
8474 | 0 | } |
8475 | | //not found, add new group |
8476 | 0 | trgt = (GF_TrackGroupTypeBox*) gf_isom_box_new_parent(&trak->groups->child_boxes, GF_ISOM_BOX_TYPE_TRGT); |
8477 | 0 | if (!trgt) return GF_OUT_OF_MEM; |
8478 | 0 | trgt->track_group_id = track_group_id; |
8479 | 0 | trgt->group_type = group_type; |
8480 | 0 | return gf_list_add(trak->groups->groups, trgt); |
8481 | 0 | } |
8482 | | |
8483 | | GF_EXPORT |
8484 | | GF_Err gf_isom_set_nalu_length_field(GF_ISOFile *file, u32 track, u32 StreamDescriptionIndex, u32 nalu_size_length) |
8485 | 0 | { |
8486 | 0 | GF_Err e; |
8487 | 0 | GF_TrackBox *trak; |
8488 | 0 | GF_SampleEntryBox *entry; |
8489 | 0 | GF_MPEGVisualSampleEntryBox *ve; |
8490 | 0 | GF_SampleDescriptionBox *stsd; |
8491 | |
|
8492 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8493 | 0 | if (e) return e; |
8494 | | |
8495 | 0 | trak = gf_isom_get_track_box(file, track); |
8496 | 0 | if (!trak) return GF_BAD_PARAM; |
8497 | | |
8498 | 0 | stsd = trak->Media->information->sampleTable->SampleDescription; |
8499 | 0 | if (!stsd || !StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) { |
8500 | 0 | return GF_BAD_PARAM; |
8501 | 0 | } |
8502 | | |
8503 | 0 | entry = (GF_SampleEntryBox *)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1); |
8504 | | //no support for generic sample entries (eg, no MPEG4 descriptor) |
8505 | 0 | if (!entry || ! gf_isom_is_nalu_based_entry(trak->Media, entry)) { |
8506 | 0 | return GF_BAD_PARAM; |
8507 | 0 | } |
8508 | | |
8509 | 0 | ve = (GF_MPEGVisualSampleEntryBox*)entry; |
8510 | 0 | if (ve->avc_config) ve->avc_config->config->nal_unit_size = nalu_size_length; |
8511 | 0 | if (ve->svc_config) ve->svc_config->config->nal_unit_size = nalu_size_length; |
8512 | 0 | if (ve->hevc_config) ve->hevc_config->config->nal_unit_size = nalu_size_length; |
8513 | 0 | if (ve->lhvc_config) ve->lhvc_config->config->nal_unit_size = nalu_size_length; |
8514 | 0 | if (ve->vvc_config) ve->vvc_config->config->nal_unit_size = nalu_size_length; |
8515 | 0 | return GF_OK; |
8516 | 0 | } |
8517 | | |
8518 | | GF_EXPORT |
8519 | | GF_Err gf_isom_set_sample_group_in_traf(GF_ISOFile *file) |
8520 | 0 | { |
8521 | 0 | GF_Err e; |
8522 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
8523 | 0 | if (e) return e; |
8524 | | |
8525 | 0 | file->sample_groups_in_traf = GF_TRUE; |
8526 | 0 | return GF_OK; |
8527 | 0 | } |
8528 | | |
8529 | | GF_EXPORT |
8530 | | void gf_isom_set_progress_callback(GF_ISOFile *file, void (*progress_cbk)(void *udta, u64 nb_done, u64 nb_total), void *progress_cbk_udta) |
8531 | 0 | { |
8532 | 0 | if (file) { |
8533 | 0 | file->progress_cbk = progress_cbk; |
8534 | 0 | file->progress_cbk_udta = progress_cbk_udta; |
8535 | |
|
8536 | 0 | } |
8537 | 0 | } |
8538 | | |
8539 | | GF_Err gf_isom_update_video_sample_entry_fields(GF_ISOFile *file, u32 track, u32 stsd_idx, u16 revision, u32 vendor, u32 temporalQ, u32 spatialQ, u32 horiz_res, u32 vert_res, u16 frames_per_sample, const char *compressor_name, s16 color_table_index) |
8540 | 0 | { |
8541 | |
|
8542 | 0 | GF_TrackBox *trak; |
8543 | 0 | GF_MPEGVisualSampleEntryBox *vid_ent; |
8544 | | |
8545 | | /*get orig sample desc and clone it*/ |
8546 | 0 | trak = gf_isom_get_track_box(file, track); |
8547 | 0 | if (!trak || !stsd_idx) return GF_BAD_PARAM; |
8548 | | |
8549 | 0 | if (!trak->Media || !trak->Media->handler || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription) |
8550 | 0 | return GF_ISOM_INVALID_FILE; |
8551 | | |
8552 | 0 | switch (trak->Media->handler->handlerType) { |
8553 | 0 | case GF_ISOM_MEDIA_VISUAL: |
8554 | 0 | case GF_ISOM_MEDIA_AUXV: |
8555 | 0 | case GF_ISOM_MEDIA_PICT: |
8556 | 0 | break; |
8557 | 0 | default: |
8558 | 0 | return GF_BAD_PARAM; |
8559 | 0 | } |
8560 | 0 | vid_ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, stsd_idx-1); |
8561 | 0 | if (!vid_ent) |
8562 | 0 | return GF_BAD_PARAM; |
8563 | | |
8564 | 0 | vid_ent->revision = revision; |
8565 | 0 | vid_ent->vendor = vendor; |
8566 | 0 | vid_ent->temporal_quality = temporalQ; |
8567 | 0 | vid_ent->spatial_quality = spatialQ; |
8568 | 0 | vid_ent->horiz_res = horiz_res; |
8569 | 0 | vid_ent->vert_res = vert_res; |
8570 | 0 | vid_ent->frames_per_sample = frames_per_sample; |
8571 | 0 | if (compressor_name) |
8572 | 0 | strncpy(vid_ent->compressor_name, compressor_name, 32); |
8573 | |
|
8574 | 0 | vid_ent->color_table_index = color_table_index; |
8575 | 0 | return GF_OK; |
8576 | 0 | } |
8577 | | |
8578 | | GF_EXPORT |
8579 | | GF_Err gf_isom_update_sample_description_from_template(GF_ISOFile *file, u32 track, u32 sampleDescriptionIndex, u8 *data, u32 size) |
8580 | 0 | { |
8581 | 0 | GF_BitStream *bs; |
8582 | 0 | GF_TrackBox *trak; |
8583 | 0 | GF_Box *ent, *tpl_ent; |
8584 | 0 | GF_Err e; |
8585 | | /*get orig sample desc and clone it*/ |
8586 | 0 | trak = gf_isom_get_track_box(file, track); |
8587 | 0 | if (!trak || !sampleDescriptionIndex) return GF_BAD_PARAM; |
8588 | | |
8589 | 0 | if (!trak->Media || !trak->Media->handler || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription) |
8590 | 0 | return GF_ISOM_INVALID_FILE; |
8591 | | |
8592 | 0 | ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex-1); |
8593 | 0 | if (!ent) return GF_BAD_PARAM; |
8594 | | |
8595 | 0 | bs = gf_bs_new(data, size, GF_BITSTREAM_READ); |
8596 | | // e = gf_isom_box_parse(&tpl_ent, bs); |
8597 | 0 | e = gf_isom_box_parse_ex(&tpl_ent, bs, GF_ISOM_BOX_TYPE_STSD, GF_FALSE, 0); |
8598 | 0 | gf_bs_del(bs); |
8599 | 0 | if (e) return e; |
8600 | | |
8601 | 0 | while (gf_list_count(tpl_ent->child_boxes)) { |
8602 | 0 | u32 j=0; |
8603 | 0 | Bool found = GF_FALSE; |
8604 | 0 | GF_Box *abox = gf_list_pop_front(tpl_ent->child_boxes); |
8605 | |
|
8606 | 0 | switch (abox->type) { |
8607 | 0 | case GF_ISOM_BOX_TYPE_SINF: |
8608 | 0 | case GF_ISOM_BOX_TYPE_RINF: |
8609 | 0 | case GF_ISOM_BOX_TYPE_BTRT: |
8610 | 0 | found = GF_TRUE; |
8611 | 0 | break; |
8612 | 0 | } |
8613 | | |
8614 | 0 | if (found) { |
8615 | 0 | gf_isom_box_del(abox); |
8616 | 0 | continue; |
8617 | 0 | } |
8618 | | |
8619 | 0 | if (!ent->child_boxes) ent->child_boxes = gf_list_new(); |
8620 | 0 | for (j=0; j<gf_list_count(ent->child_boxes); j++) { |
8621 | 0 | GF_Box *b = gf_list_get(ent->child_boxes, j); |
8622 | 0 | if (b->type == abox->type) { |
8623 | 0 | found = GF_TRUE; |
8624 | 0 | break; |
8625 | 0 | } |
8626 | 0 | } |
8627 | 0 | if (!found) { |
8628 | 0 | gf_list_add(ent->child_boxes, abox); |
8629 | 0 | } else { |
8630 | 0 | gf_isom_box_del(abox); |
8631 | 0 | } |
8632 | 0 | } |
8633 | 0 | gf_isom_box_del(tpl_ent); |
8634 | | |
8635 | | //patch for old export |
8636 | 0 | GF_Box *abox = gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_SINF); |
8637 | 0 | if (abox) { |
8638 | 0 | gf_list_del_item(ent->child_boxes, abox); |
8639 | 0 | gf_list_add(ent->child_boxes, abox); |
8640 | 0 | } |
8641 | 0 | return GF_OK; |
8642 | 0 | } |
8643 | | |
8644 | | |
8645 | | #include <gpac/xml.h> |
8646 | | GF_EXPORT |
8647 | | GF_Err gf_isom_apply_box_patch(GF_ISOFile *file, GF_ISOTrackID globalTrackID, const char *box_patch_filename, Bool for_fragments) |
8648 | 0 | { |
8649 | 0 | GF_Err e; |
8650 | 0 | GF_DOMParser *dom; |
8651 | 0 | u32 i; |
8652 | 0 | GF_XMLNode *root; |
8653 | 0 | u8 *box_data=NULL; |
8654 | 0 | u32 box_data_size; |
8655 | 0 | if (!file || !box_patch_filename) return GF_BAD_PARAM; |
8656 | | #ifdef GPAC_DISABLE_ISOM_FRAGMENTS |
8657 | | if (for_fragments) return GF_NOT_SUPPORTED; |
8658 | | #endif |
8659 | 0 | dom = gf_xml_dom_new(); |
8660 | 0 | if (strstr(box_patch_filename, "<?xml")) { |
8661 | 0 | e = gf_xml_dom_parse_string(dom, (char *) box_patch_filename); |
8662 | 0 | } else { |
8663 | 0 | e = gf_xml_dom_parse(dom, box_patch_filename, NULL, NULL); |
8664 | 0 | } |
8665 | 0 | if (e) goto err_exit; |
8666 | | |
8667 | 0 | root = gf_xml_dom_get_root(dom); |
8668 | 0 | if (!root || strcmp(root->name, "GPACBOXES")) { |
8669 | 0 | e = GF_NON_COMPLIANT_BITSTREAM; |
8670 | 0 | goto err_exit; |
8671 | 0 | } |
8672 | | |
8673 | | //compute size of each child boxes to freeze the order |
8674 | 0 | if (for_fragments) { |
8675 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
8676 | 0 | u32 count = file->moof ? gf_list_count(file->moof->child_boxes) : 0; |
8677 | 0 | for (i=0; i<count; i++) { |
8678 | 0 | GF_Box *box = gf_list_get(file->moof->child_boxes, i); |
8679 | 0 | if (!(box->internal_flags & GF_ISOM_ORDER_FREEZE)) { |
8680 | 0 | gf_isom_box_size(box); |
8681 | 0 | gf_isom_box_freeze_order(box); |
8682 | 0 | } |
8683 | 0 | } |
8684 | 0 | #endif |
8685 | 0 | } else { |
8686 | 0 | for (i=0; i<gf_list_count(file->TopBoxes); i++) { |
8687 | 0 | GF_Box *box = gf_list_get(file->TopBoxes, i); |
8688 | 0 | if (!(box->internal_flags & GF_ISOM_ORDER_FREEZE)) { |
8689 | 0 | gf_isom_box_size(box); |
8690 | 0 | gf_isom_box_freeze_order(box); |
8691 | 0 | } |
8692 | 0 | } |
8693 | 0 | } |
8694 | |
|
8695 | 0 | for (i=0; i<gf_list_count(root->content); i++) { |
8696 | 0 | u32 j; |
8697 | 0 | u32 path_len; |
8698 | 0 | Bool essential_prop=GF_FALSE; |
8699 | 0 | u32 trackID=globalTrackID; |
8700 | 0 | u32 item_id=trackID; |
8701 | 0 | Bool is_frag_box; |
8702 | 0 | char *box_path=NULL; |
8703 | 0 | GF_Box *parent_box = NULL; |
8704 | 0 | GF_XMLNode *box_edit = gf_list_get(root->content, i); |
8705 | 0 | if (!box_edit->name || strcmp(box_edit->name, "Box")) continue; |
8706 | | |
8707 | 0 | for (j=0; j<gf_list_count(box_edit->attributes);j++) { |
8708 | 0 | GF_XMLAttribute *att = gf_list_get(box_edit->attributes, j); |
8709 | 0 | if (!strcmp(att->name, "path")) box_path = att->value; |
8710 | 0 | else if (!strcmp(att->name, "essential")) { |
8711 | 0 | if (!strcmp(att->value, "yes") || !strcmp(att->value, "true") || !strcmp(att->value, "1")) { |
8712 | 0 | essential_prop=GF_TRUE; |
8713 | 0 | } |
8714 | 0 | } |
8715 | 0 | else if (!strcmp(att->name, "itemID")) |
8716 | 0 | item_id = atoi(att->value); |
8717 | 0 | else if (!globalTrackID && !strcmp(att->name, "trackID")) |
8718 | 0 | trackID = atoi(att->value); |
8719 | 0 | } |
8720 | |
|
8721 | 0 | if (!box_path) continue; |
8722 | 0 | path_len = (u32) strlen(box_path); |
8723 | |
|
8724 | 0 | is_frag_box = !strncmp(box_path, "traf", 4) ? GF_TRUE : GF_FALSE; |
8725 | |
|
8726 | 0 | if (for_fragments && !is_frag_box) continue; |
8727 | 0 | else if (!for_fragments && is_frag_box) continue; |
8728 | | |
8729 | 0 | gf_xml_parse_bit_sequence(box_edit, box_patch_filename, &box_data, &box_data_size); |
8730 | 0 | if (box_data_size && (box_data_size<4) ) { |
8731 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Wrong BS specification for box, shall either be empty or at least 4 bytes for box type\n")); |
8732 | 0 | e = GF_NON_COMPLIANT_BITSTREAM; |
8733 | 0 | goto err_exit; |
8734 | 0 | } |
8735 | | |
8736 | 0 | while (box_path && (path_len>=4)) { |
8737 | 0 | u32 parent_list_box_type; |
8738 | 0 | GF_List **parent_list; |
8739 | 0 | u32 box_type = GF_4CC(box_path[0],box_path[1],box_path[2],box_path[3]); |
8740 | 0 | GF_Box *box=NULL; |
8741 | 0 | GF_BitStream *bs; |
8742 | 0 | s32 insert_pos = -1; |
8743 | 0 | box_path+=4; |
8744 | 0 | path_len-=4; |
8745 | |
|
8746 | 0 | if (!parent_box) { |
8747 | 0 | box=gf_isom_box_find_child(file->TopBoxes, box_type); |
8748 | 0 | if (!box) { |
8749 | 0 | if (box_type==GF_ISOM_BOX_TYPE_TRAK) { |
8750 | 0 | if (trackID) { |
8751 | 0 | box = (GF_Box *) gf_isom_get_track_box(file, gf_isom_get_track_by_id(file, trackID) ); |
8752 | 0 | } |
8753 | 0 | if (!box && gf_list_count(file->moov->trackList)==1) { |
8754 | 0 | box = gf_list_get(file->moov->trackList, 0); |
8755 | 0 | } |
8756 | 0 | } |
8757 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
8758 | 0 | else if (box_type==GF_ISOM_BOX_TYPE_TRAF) { |
8759 | 0 | if (trackID) { |
8760 | 0 | box = (GF_Box *) gf_isom_get_traf(file, trackID); |
8761 | 0 | } |
8762 | 0 | if (!box && file->moof && gf_list_count(file->moof->TrackList)==1) { |
8763 | 0 | box = gf_list_get(file->moof->TrackList, 0); |
8764 | 0 | } |
8765 | 0 | } |
8766 | 0 | #endif |
8767 | 0 | } |
8768 | 0 | if (!box) { |
8769 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Cannot locate box type %s at root or as track\n", gf_4cc_to_str(box_type) )); |
8770 | 0 | e = GF_BAD_PARAM; |
8771 | 0 | goto err_exit; |
8772 | 0 | } |
8773 | 0 | } else { |
8774 | 0 | box = gf_isom_box_find_child(parent_box->child_boxes, box_type); |
8775 | 0 | if (!box) { |
8776 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Cannot locate box type %s at child of %s\n", gf_4cc_to_str(box_type), gf_4cc_to_str(parent_box->type))); |
8777 | 0 | e = GF_BAD_PARAM; |
8778 | 0 | goto err_exit; |
8779 | 0 | } |
8780 | 0 | } |
8781 | | // '.' is child access |
8782 | 0 | if (path_len && (box_path[0]=='.')) { |
8783 | 0 | box_path += 1; |
8784 | 0 | path_len-=1; |
8785 | 0 | parent_box = box; |
8786 | 0 | continue; |
8787 | 0 | } |
8788 | | |
8789 | 0 | if (parent_box && !parent_box->child_boxes) parent_box->child_boxes = gf_list_new(); |
8790 | 0 | parent_list = parent_box ? &parent_box->child_boxes : &file->TopBoxes; |
8791 | 0 | parent_list_box_type = parent_box ? parent_box->type : 0; |
8792 | | |
8793 | | // '+' is append after, '-' is insert before |
8794 | 0 | if (path_len && ((box_path[0]=='-') || (box_path[0]=='+')) ) { |
8795 | 0 | s32 idx = gf_list_find(*parent_list, box); |
8796 | 0 | if (idx<0) { |
8797 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Invalid index for path %s\n", box_path)); |
8798 | 0 | e = GF_NON_COMPLIANT_BITSTREAM; |
8799 | 0 | goto err_exit; |
8800 | 0 | } |
8801 | 0 | if (box_path[0]=='+') insert_pos = idx+1; |
8802 | 0 | else insert_pos = idx; |
8803 | 0 | } |
8804 | 0 | else if (path_len) { |
8805 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Invalid path %s, expecting either '-', '+' or '.' as separators\n", box_path)); |
8806 | 0 | e = GF_NON_COMPLIANT_BITSTREAM; |
8807 | 0 | goto err_exit; |
8808 | 0 | } |
8809 | | |
8810 | 0 | if (!box_data) { |
8811 | 0 | if (insert_pos>=0) { |
8812 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[ISOBMFF] Invalid path %s for box removal, ignoring position\n", box_path)); |
8813 | 0 | } |
8814 | 0 | switch (box->type) { |
8815 | 0 | case GF_ISOM_BOX_TYPE_MOOV: |
8816 | 0 | file->moov = NULL; |
8817 | 0 | break; |
8818 | 0 | case GF_ISOM_BOX_TYPE_MDAT: |
8819 | 0 | file->mdat = NULL; |
8820 | 0 | break; |
8821 | 0 | case GF_ISOM_BOX_TYPE_PDIN: |
8822 | 0 | file->pdin = NULL; |
8823 | 0 | break; |
8824 | 0 | case GF_ISOM_BOX_TYPE_FTYP: |
8825 | 0 | file->brand = NULL; |
8826 | 0 | break; |
8827 | 0 | case GF_ISOM_BOX_TYPE_META: |
8828 | 0 | if ((GF_Box *) file->meta == box) file->meta = NULL; |
8829 | 0 | break; |
8830 | 0 | } |
8831 | 0 | if (parent_box) { |
8832 | 0 | gf_isom_box_remove_from_parent(parent_box, box); |
8833 | 0 | } |
8834 | 0 | gf_isom_box_del_parent(parent_list, box); |
8835 | 0 | } else { |
8836 | 0 | u32 size; |
8837 | |
|
8838 | 0 | bs = gf_bs_new(box_data, box_data_size, GF_BITSTREAM_READ); |
8839 | 0 | size = gf_bs_read_u32(bs); |
8840 | 0 | if (size != box_data_size) { |
8841 | 0 | GF_UnknownBox *new_box = (GF_UnknownBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UNKNOWN); |
8842 | 0 | new_box->original_4cc = size; |
8843 | 0 | new_box->dataSize = (u32) gf_bs_available(bs); |
8844 | 0 | new_box->data = gf_malloc(sizeof(u8)*new_box->dataSize); |
8845 | 0 | gf_bs_read_data(bs, new_box->data, new_box->dataSize); |
8846 | 0 | if (insert_pos<0) { |
8847 | 0 | gf_list_add(box->child_boxes, new_box); |
8848 | 0 | insert_pos = gf_list_find(box->child_boxes, new_box); |
8849 | 0 | } else { |
8850 | 0 | gf_list_insert(*parent_list, new_box, insert_pos); |
8851 | 0 | } |
8852 | |
|
8853 | 0 | if (parent_box && (parent_box->type==GF_ISOM_BOX_TYPE_IPRP)) { |
8854 | 0 | GF_ItemPropertyAssociationBox *ipma = (GF_ItemPropertyAssociationBox *) gf_isom_box_find_child(parent_box->child_boxes, GF_ISOM_BOX_TYPE_IPMA); |
8855 | 0 | if (!item_id) { |
8856 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[ISOBMFF] Inserting box in ipco without itemID, no association added\n")); |
8857 | 0 | } else if (ipma) { |
8858 | 0 | u32 nb_asso, k, insert_pos; |
8859 | 0 | GF_ItemPropertyAssociationEntry *entry = NULL; |
8860 | 0 | nb_asso = gf_list_count(ipma->entries); |
8861 | 0 | insert_pos = 0; |
8862 | 0 | for (k=0; k<nb_asso;k++) { |
8863 | 0 | entry = gf_list_get(ipma->entries, k); |
8864 | 0 | if (entry->item_id==item_id) break; |
8865 | | // item ids must appear in increasing order |
8866 | 0 | if (item_id>entry->item_id) ++insert_pos; |
8867 | 0 | entry = NULL; |
8868 | 0 | } |
8869 | 0 | if (!entry) { |
8870 | 0 | GF_SAFEALLOC(entry, GF_ItemPropertyAssociationEntry); |
8871 | 0 | if (!entry) return GF_OUT_OF_MEM; |
8872 | 0 | gf_list_insert(ipma->entries, entry, insert_pos); |
8873 | 0 | entry->item_id = item_id; |
8874 | 0 | } |
8875 | 0 | entry->associations = gf_realloc(entry->associations, sizeof(GF_ItemPropertyAssociationSlot) * (entry->nb_associations+1)); |
8876 | 0 | entry->associations[entry->nb_associations].essential = essential_prop; |
8877 | 0 | entry->associations[entry->nb_associations].index = 1+insert_pos; |
8878 | 0 | entry->nb_associations++; |
8879 | 0 | } |
8880 | 0 | } |
8881 | 0 | } else { |
8882 | 0 | u32 box_idx = 0; |
8883 | |
|
8884 | 0 | gf_bs_seek(bs, 0); |
8885 | 0 | while (gf_bs_available(bs)) { |
8886 | 0 | GF_Box *new_box; |
8887 | 0 | e = gf_isom_box_parse_ex(&new_box, bs, (insert_pos<0) ? box->type : parent_list_box_type, parent_box ? GF_FALSE : GF_TRUE, 0); |
8888 | 0 | if (e) { |
8889 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] failed to parse box\n", box_path)); |
8890 | 0 | gf_bs_del(bs); |
8891 | 0 | goto err_exit; |
8892 | 0 | } |
8893 | 0 | if (insert_pos<0) { |
8894 | 0 | gf_list_add(box->child_boxes, new_box); |
8895 | 0 | } else { |
8896 | 0 | gf_list_insert(*parent_list, new_box, insert_pos+box_idx); |
8897 | 0 | box_idx++; |
8898 | 0 | } |
8899 | 0 | } |
8900 | 0 | } |
8901 | 0 | gf_bs_del(bs); |
8902 | |
|
8903 | 0 | } |
8904 | 0 | gf_free(box_data); |
8905 | 0 | box_data = NULL; |
8906 | 0 | box_path = NULL; |
8907 | 0 | } |
8908 | 0 | } |
8909 | | |
8910 | 0 | err_exit: |
8911 | |
|
8912 | 0 | gf_xml_dom_del(dom); |
8913 | 0 | if (box_data) gf_free(box_data); |
8914 | 0 | return e; |
8915 | 0 | } |
8916 | | |
8917 | | GF_EXPORT |
8918 | | GF_Err gf_isom_set_track_magic(GF_ISOFile *movie, u32 trackNumber, u64 magic) |
8919 | 0 | { |
8920 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
8921 | 0 | if (!trak) return GF_BAD_PARAM; |
8922 | 0 | trak->magic = magic; |
8923 | 0 | return GF_OK; |
8924 | 0 | } |
8925 | | |
8926 | | GF_EXPORT |
8927 | | GF_Err gf_isom_set_track_index(GF_ISOFile *movie, u32 trackNumber, u32 index, void (*track_num_changed)(void *udta, u32 old_track_num, u32 new_track_num), void *udta) |
8928 | 0 | { |
8929 | 0 | u32 i, j, count; |
8930 | 0 | GF_List *tracks; |
8931 | 0 | GF_TrackBox *trak = gf_isom_get_track_box(movie, trackNumber); |
8932 | 0 | if (!trak || !index) return GF_BAD_PARAM; |
8933 | 0 | trak->index = index; |
8934 | 0 | tracks = gf_list_new(); |
8935 | 0 | count = gf_list_count(movie->moov->trackList); |
8936 | | //sort tracks in new list |
8937 | 0 | for (i=0; i<count; i++) { |
8938 | 0 | GF_TrackBox *a_tk = gf_list_get(movie->moov->trackList, i); |
8939 | 0 | if (!a_tk->index) { |
8940 | 0 | gf_list_insert(tracks, a_tk, 0); |
8941 | 0 | } else { |
8942 | 0 | for (j=0; j<gf_list_count(tracks); j++) { |
8943 | 0 | GF_TrackBox *a_tki = gf_list_get(tracks, j); |
8944 | 0 | if (a_tki->index<a_tk->index) continue; |
8945 | 0 | gf_list_insert(tracks, a_tk, j); |
8946 | 0 | a_tk = NULL; |
8947 | 0 | break; |
8948 | 0 | } |
8949 | 0 | if (a_tk) |
8950 | 0 | gf_list_add(tracks, a_tk); |
8951 | 0 | } |
8952 | 0 | } |
8953 | 0 | if (gf_list_count(tracks) != count) { |
8954 | 0 | gf_list_del(tracks); |
8955 | 0 | return GF_OUT_OF_MEM; |
8956 | 0 | } |
8957 | 0 | if (track_num_changed) { |
8958 | 0 | for (i=0; i<count; i++) { |
8959 | 0 | GF_TrackBox *a_tk = gf_list_get(tracks, i); |
8960 | 0 | s32 old_pos = gf_list_find(movie->moov->trackList, a_tk); |
8961 | 0 | gf_assert(old_pos>=0); |
8962 | 0 | if (old_pos != i) |
8963 | 0 | track_num_changed(udta, old_pos+1, i+1); |
8964 | 0 | } |
8965 | 0 | } |
8966 | 0 | gf_list_del(movie->moov->trackList); |
8967 | 0 | movie->moov->trackList = tracks; |
8968 | 0 | for (j=0; j<gf_list_count(tracks); j++) { |
8969 | 0 | GF_TrackBox *tki = gf_list_get(tracks, j); |
8970 | 0 | if (tki->index != 0xFFFE) // special value meaning always last |
8971 | 0 | tki->index = j + 1; |
8972 | 0 | } |
8973 | 0 | return GF_OK; |
8974 | 0 | } |
8975 | | |
8976 | | GF_EXPORT |
8977 | | GF_Err gf_isom_set_ipod_compatible(GF_ISOFile *the_file, u32 trackNumber) |
8978 | 0 | { |
8979 | 0 | GF_TrackBox *trak; |
8980 | 0 | GF_Err e; |
8981 | 0 | GF_MPEGVisualSampleEntryBox *entry; |
8982 | |
|
8983 | 0 | e = gf_isom_can_access_movie(the_file, GF_ISOM_OPEN_WRITE); |
8984 | 0 | if (e) return e; |
8985 | 0 | trak = gf_isom_get_track_box(the_file, trackNumber); |
8986 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
8987 | 0 | entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, 0); |
8988 | 0 | if (!entry) return GF_OK; |
8989 | 0 | switch (entry->type) { |
8990 | 0 | case GF_ISOM_BOX_TYPE_AVC1: |
8991 | 0 | case GF_ISOM_BOX_TYPE_AVC2: |
8992 | 0 | case GF_ISOM_BOX_TYPE_AVC3: |
8993 | 0 | case GF_ISOM_BOX_TYPE_AVC4: |
8994 | 0 | case GF_ISOM_BOX_TYPE_SVC1: |
8995 | 0 | case GF_ISOM_BOX_TYPE_MVC1: |
8996 | 0 | case GF_ISOM_BOX_TYPE_HVC1: |
8997 | 0 | case GF_ISOM_BOX_TYPE_HEV1: |
8998 | 0 | case GF_ISOM_BOX_TYPE_HVT1: |
8999 | 0 | break; |
9000 | 0 | default: |
9001 | 0 | return GF_OK; |
9002 | 0 | } |
9003 | | |
9004 | 0 | if (!entry->ipod_ext) { |
9005 | 0 | entry->ipod_ext = (GF_UnknownUUIDBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_UUID); |
9006 | 0 | if (!entry->ipod_ext) return GF_OUT_OF_MEM; |
9007 | 0 | } |
9008 | 0 | memcpy(entry->ipod_ext->uuid, GF_ISOM_IPOD_EXT, sizeof(u8)*16); |
9009 | 0 | entry->ipod_ext->dataSize = 4; |
9010 | 0 | entry->ipod_ext->data = gf_malloc(sizeof(u8)*4); |
9011 | 0 | if (!entry->ipod_ext->data) return GF_OUT_OF_MEM; |
9012 | 0 | memset(entry->ipod_ext->data, 0, sizeof(u8)*4); |
9013 | 0 | return GF_OK; |
9014 | 0 | } |
9015 | | |
9016 | | GF_EXPORT |
9017 | | Bool gf_isom_is_inplace_rewrite(GF_ISOFile *movie) |
9018 | 0 | { |
9019 | 0 | if (!movie) return GF_FALSE; |
9020 | 0 | if (!movie->no_inplace_rewrite) { |
9021 | | //things where added to the file, no inplace rewrite |
9022 | 0 | if (movie->editFileMap && gf_bs_get_size(movie->editFileMap->bs)) |
9023 | 0 | movie->no_inplace_rewrite = GF_TRUE; |
9024 | | //block redirect (used by mp4mx), no inplace rewrite |
9025 | 0 | else if (movie->on_block_out || (movie->finalName && !strcmp(movie->finalName, "_gpac_isobmff_redirect"))) |
9026 | 0 | movie->no_inplace_rewrite = GF_TRUE; |
9027 | | //stdout redirect, no inplace rewrite |
9028 | 0 | else if (movie->finalName && !strcmp(movie->finalName, "std")) |
9029 | 0 | movie->no_inplace_rewrite = GF_TRUE; |
9030 | | //new file, no inplace rewrite |
9031 | 0 | else if (!movie->fileName) |
9032 | 0 | movie->no_inplace_rewrite = GF_TRUE; |
9033 | 0 | } |
9034 | 0 | if (movie->no_inplace_rewrite) return GF_FALSE; |
9035 | | |
9036 | 0 | return GF_TRUE; |
9037 | 0 | } |
9038 | | |
9039 | | GF_EXPORT |
9040 | | void gf_isom_disable_inplace_rewrite(GF_ISOFile *movie) |
9041 | 1.89k | { |
9042 | 1.89k | if (movie) |
9043 | 1.89k | movie->no_inplace_rewrite = GF_TRUE; |
9044 | 1.89k | } |
9045 | | |
9046 | | GF_EXPORT |
9047 | | GF_Err gf_isom_set_y3d_info(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, GF_ISOM_Y3D_Info *info) |
9048 | 0 | { |
9049 | 0 | GF_Err e; |
9050 | 0 | u32 proj_type; |
9051 | 0 | GF_SampleEntryBox *ent; |
9052 | 0 | GF_TrackBox *trak; |
9053 | |
|
9054 | 0 | e = gf_isom_can_access_movie(movie, GF_ISOM_OPEN_WRITE); |
9055 | 0 | if (e) return e; |
9056 | | |
9057 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
9058 | 0 | if (!trak || !trak->Media || !info) return GF_BAD_PARAM; |
9059 | | |
9060 | 0 | ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescriptionIndex-1); |
9061 | 0 | if (!ent) return GF_BAD_PARAM; |
9062 | | |
9063 | 0 | if (info->projection_type > GF_PROJ360_EQR) return GF_NOT_SUPPORTED; |
9064 | | |
9065 | 0 | GF_Stereo3DBox *st3d = (GF_Stereo3DBox *) gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_ST3D); |
9066 | 0 | if (st3d) { |
9067 | 0 | if (info->stereo_type) { |
9068 | 0 | st3d->stereo_type = info->stereo_type; |
9069 | 0 | } else { |
9070 | 0 | gf_isom_box_del_parent(&ent->child_boxes, (GF_Box *) st3d); |
9071 | 0 | } |
9072 | 0 | } else if (info->stereo_type) { |
9073 | 0 | st3d = (GF_Stereo3DBox *) gf_isom_box_new_parent(&ent->child_boxes, GF_ISOM_BOX_TYPE_ST3D); |
9074 | 0 | if (!st3d) return GF_OUT_OF_MEM; |
9075 | 0 | st3d->stereo_type = info->stereo_type; |
9076 | 0 | } |
9077 | | |
9078 | | |
9079 | 0 | GF_Box *sv3d = gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_SV3D); |
9080 | 0 | if (sv3d && !info->projection_type) { |
9081 | 0 | gf_isom_box_del_parent(&ent->child_boxes, sv3d); |
9082 | 0 | return GF_OK; |
9083 | 0 | } |
9084 | | |
9085 | 0 | if (!sv3d && !info->projection_type) { |
9086 | 0 | return GF_OK; |
9087 | 0 | } |
9088 | 0 | if (!sv3d) { |
9089 | 0 | sv3d = gf_isom_box_new_parent(&ent->child_boxes, GF_ISOM_BOX_TYPE_SV3D); |
9090 | 0 | if (!sv3d) return GF_OUT_OF_MEM; |
9091 | 0 | } |
9092 | | |
9093 | | //svhd mandatory |
9094 | 0 | GF_SphericalVideoInfoBox *svhd = (GF_SphericalVideoInfoBox *) gf_isom_box_find_child(sv3d->child_boxes, GF_ISOM_BOX_TYPE_SVHD); |
9095 | 0 | if (svhd) { |
9096 | 0 | if (svhd->string) gf_free(svhd->string); |
9097 | 0 | } else { |
9098 | 0 | svhd = (GF_SphericalVideoInfoBox *) gf_isom_box_new_parent(&sv3d->child_boxes, GF_ISOM_BOX_TYPE_SVHD); |
9099 | 0 | if (!svhd) return GF_OUT_OF_MEM; |
9100 | 0 | } |
9101 | 0 | svhd->string = gf_strdup(info->meta_data ? info->meta_data : ""); |
9102 | | |
9103 | | //proj mandatory |
9104 | 0 | GF_Box *proj = gf_isom_box_find_child(sv3d->child_boxes, GF_ISOM_BOX_TYPE_PROJ); |
9105 | 0 | if (!proj) { |
9106 | 0 | proj = gf_isom_box_new_parent(&sv3d->child_boxes, GF_ISOM_BOX_TYPE_PROJ); |
9107 | 0 | if (!proj) return GF_OUT_OF_MEM; |
9108 | 0 | } |
9109 | | |
9110 | 0 | GF_ProjectionHeaderBox *projh = (GF_ProjectionHeaderBox *) gf_isom_box_find_child(proj->child_boxes, GF_ISOM_BOX_TYPE_PRHD); |
9111 | | //prj header mandatory |
9112 | 0 | if (!projh) { |
9113 | 0 | projh = (GF_ProjectionHeaderBox *) gf_isom_box_new_parent(&proj->child_boxes, GF_ISOM_BOX_TYPE_PRHD); |
9114 | 0 | if (!projh) return GF_OUT_OF_MEM; |
9115 | 0 | } |
9116 | 0 | projh->yaw = info->yaw; |
9117 | 0 | projh->pitch = info->pitch; |
9118 | 0 | projh->roll = info->roll; |
9119 | |
|
9120 | 0 | proj_type = (info->projection_type==GF_PROJ360_CUBE_MAP) ? GF_ISOM_BOX_TYPE_CBMP : GF_ISOM_BOX_TYPE_EQUI; |
9121 | |
|
9122 | 0 | GF_ProjectionTypeBox *projt = (GF_ProjectionTypeBox *) gf_isom_box_find_child(proj->child_boxes, proj_type); |
9123 | 0 | if (!projt) { |
9124 | 0 | projt = (GF_ProjectionTypeBox *) gf_isom_box_new_parent(&proj->child_boxes, proj_type); |
9125 | 0 | if (!projt) return GF_OUT_OF_MEM; |
9126 | 0 | } |
9127 | 0 | if (info->projection_type==GF_PROJ360_CUBE_MAP) { |
9128 | 0 | projt->layout = info->layout; |
9129 | 0 | projt->padding = info->padding; |
9130 | 0 | } else { |
9131 | 0 | projt->bounds_top = info->top; |
9132 | 0 | projt->bounds_bottom = info->bottom; |
9133 | 0 | projt->bounds_left = info->left; |
9134 | 0 | projt->bounds_right = info->right; |
9135 | 0 | } |
9136 | | |
9137 | | //remove other ones |
9138 | 0 | GF_Box *b = gf_isom_box_new_parent(&proj->child_boxes, GF_ISOM_BOX_TYPE_MSHP); |
9139 | 0 | if (b) gf_isom_box_del_parent(&proj->child_boxes, b); |
9140 | 0 | if (info->projection_type==GF_PROJ360_CUBE_MAP) { |
9141 | 0 | b = gf_isom_box_new_parent(&proj->child_boxes, GF_ISOM_BOX_TYPE_EQUI); |
9142 | 0 | if (b) gf_isom_box_del_parent(&proj->child_boxes, b); |
9143 | 0 | } else { |
9144 | 0 | b = gf_isom_box_new_parent(&proj->child_boxes, GF_ISOM_BOX_TYPE_EQUI); |
9145 | 0 | if (b) gf_isom_box_del_parent(&proj->child_boxes, b); |
9146 | |
|
9147 | 0 | } |
9148 | 0 | return GF_OK; |
9149 | 0 | } |
9150 | | |
9151 | | #endif //!defined(GPAC_DISABLE_ISOM_WRITE) |
9152 | | |
9153 | | #ifndef GPAC_DISABLE_ISOM |
9154 | | |
9155 | | GF_Err gf_isom_add_sample_aux_info_internal(GF_TrackBox *trak, void *_traf, u32 sampleNumber, u32 aux_type, u32 aux_info, u8 *data, u32 size) |
9156 | 0 | { |
9157 | 0 | u32 i, count; |
9158 | 0 | GF_List **child_box_cont, **child_box_sai, **child_box_saiz, **child_box_saio; |
9159 | 0 | GF_UnknownBox *sai_cont = NULL; |
9160 | |
|
9161 | 0 | if (!trak && !_traf) return GF_BAD_PARAM; |
9162 | | |
9163 | 0 | if (trak) { |
9164 | 0 | child_box_cont = &trak->child_boxes; |
9165 | 0 | child_box_sai = &trak->Media->information->sampleTable->child_boxes; |
9166 | 0 | child_box_saiz = &trak->Media->information->sampleTable->sai_sizes; |
9167 | 0 | child_box_saio = &trak->Media->information->sampleTable->sai_offsets; |
9168 | 0 | } else { |
9169 | 0 | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
9170 | 0 | GF_TrackFragmentBox *traf = (GF_TrackFragmentBox *)_traf; |
9171 | |
|
9172 | 0 | child_box_cont = &traf->child_boxes; |
9173 | 0 | child_box_sai = &traf->child_boxes; |
9174 | 0 | child_box_saiz = &traf->sai_sizes; |
9175 | 0 | child_box_saio = &traf->sai_offsets; |
9176 | 0 | #endif |
9177 | 0 | } |
9178 | |
|
9179 | 0 | count = gf_list_count(*child_box_cont); |
9180 | 0 | for (i=0; i<count; i++) { |
9181 | 0 | GF_UnknownBox *unkn = gf_list_get(*child_box_cont, i); |
9182 | 0 | if (unkn->type != GF_ISOM_BOX_TYPE_UNKNOWN) continue; |
9183 | 0 | if (unkn->original_4cc != GF_ISOM_BOX_TYPE_GDAT) continue; |
9184 | 0 | if (unkn->sai_type != aux_type) continue; |
9185 | 0 | if (unkn->sai_aux_info != aux_info) continue; |
9186 | 0 | sai_cont = unkn; |
9187 | 0 | break; |
9188 | 0 | } |
9189 | 0 | if (!sai_cont) { |
9190 | 0 | sai_cont = (GF_UnknownBox *) gf_isom_box_new_parent(child_box_cont, GF_ISOM_BOX_TYPE_UNKNOWN); |
9191 | 0 | if (!sai_cont) return GF_OUT_OF_MEM; |
9192 | 0 | sai_cont->original_4cc = GF_ISOM_BOX_TYPE_GDAT; |
9193 | 0 | sai_cont->sai_type = aux_type; |
9194 | 0 | sai_cont->sai_aux_info = aux_info; |
9195 | 0 | } |
9196 | 0 | sai_cont->data = gf_realloc(sai_cont->data, (size+sai_cont->dataSize)); |
9197 | 0 | if (!sai_cont->data) return GF_OUT_OF_MEM; |
9198 | 0 | memcpy(sai_cont->data+sai_cont->dataSize, data, size); |
9199 | 0 | sai_cont->dataSize += size; |
9200 | |
|
9201 | 0 | GF_SampleAuxiliaryInfoSizeBox *saiz=NULL; |
9202 | 0 | GF_SampleAuxiliaryInfoOffsetBox *saio=NULL; |
9203 | 0 | count = gf_list_count(*child_box_saiz); |
9204 | 0 | for (i=0; i<count; i++) { |
9205 | 0 | saiz = gf_list_get(*child_box_saiz, i); |
9206 | 0 | if ((saiz->aux_info_type==aux_type) && (saiz->aux_info_type_parameter==aux_info)) break; |
9207 | 0 | saiz = NULL; |
9208 | 0 | } |
9209 | 0 | if (!saiz) { |
9210 | 0 | saiz = (GF_SampleAuxiliaryInfoSizeBox *) gf_isom_box_new_parent(child_box_sai, GF_ISOM_BOX_TYPE_SAIZ); |
9211 | 0 | if (!saiz) return GF_OUT_OF_MEM; |
9212 | 0 | if (! *child_box_saiz) *child_box_saiz = gf_list_new(); |
9213 | 0 | gf_list_add(*child_box_saiz, saiz); |
9214 | |
|
9215 | 0 | saiz->aux_info_type = aux_type; |
9216 | 0 | saiz->aux_info_type_parameter = aux_info; |
9217 | 0 | } |
9218 | | |
9219 | 0 | if (saiz->sample_count >= sampleNumber) |
9220 | 0 | return GF_BAD_PARAM; |
9221 | | |
9222 | 0 | if ( (!saiz->sample_count && (sampleNumber==1)) |
9223 | 0 | || ((saiz->default_sample_info_size==size) && size) |
9224 | 0 | ) { |
9225 | 0 | saiz->sample_count ++; |
9226 | 0 | saiz->default_sample_info_size = size; |
9227 | 0 | } else { |
9228 | 0 | if (sampleNumber > saiz->sample_alloc) { |
9229 | 0 | saiz->sample_alloc = sampleNumber+10; |
9230 | 0 | saiz->sample_info_size = (u8*)gf_realloc(saiz->sample_info_size, sizeof(u8)*(saiz->sample_alloc)); |
9231 | 0 | } |
9232 | |
|
9233 | 0 | if (saiz->default_sample_info_size) { |
9234 | 0 | for (i=0; i<saiz->sample_count; i++) |
9235 | 0 | saiz->sample_info_size[i] = saiz->default_sample_info_size; |
9236 | 0 | saiz->default_sample_info_size = 0; |
9237 | 0 | } |
9238 | 0 | for (i=saiz->sample_count; i<sampleNumber-1; i++) |
9239 | 0 | saiz->sample_info_size[i] = 0; |
9240 | |
|
9241 | 0 | saiz->sample_info_size[sampleNumber-1] = size; |
9242 | 0 | saiz->sample_count = sampleNumber; |
9243 | 0 | } |
9244 | | |
9245 | |
|
9246 | 0 | count = gf_list_count(*child_box_saio); |
9247 | 0 | for (i=0; i<count; i++) { |
9248 | 0 | saio = gf_list_get(*child_box_saio, i); |
9249 | 0 | if ((saio->aux_info_type==aux_type) && (saio->aux_info_type_parameter==aux_info)) break; |
9250 | 0 | saio = NULL; |
9251 | 0 | } |
9252 | 0 | if (!saio) { |
9253 | 0 | saio = (GF_SampleAuxiliaryInfoOffsetBox *) gf_isom_box_new_parent(child_box_sai, GF_ISOM_BOX_TYPE_SAIO); |
9254 | 0 | if (!saio) return GF_OUT_OF_MEM; |
9255 | 0 | if (! *child_box_saio) *child_box_saio = gf_list_new(); |
9256 | 0 | gf_list_add(*child_box_saio, saio); |
9257 | 0 | saio->aux_info_type = aux_type; |
9258 | 0 | saio->aux_info_type_parameter = aux_info; |
9259 | 0 | } |
9260 | 0 | if (!saio->sai_data) saio->sai_data = sai_cont; |
9261 | 0 | saio->version = 1; |
9262 | 0 | saio->entry_count = 1; |
9263 | |
|
9264 | 0 | return GF_OK; |
9265 | 0 | } |
9266 | | #endif // GPAC_DISABLE_ISOM |
9267 | | |
9268 | | |
9269 | | #if !defined(GPAC_DISABLE_ISOM_WRITE) |
9270 | | |
9271 | | #ifndef GPAC_DISABLE_ISOM_FRAGMENTS |
9272 | | GF_Err gf_isom_fragment_set_sample_aux_info(GF_ISOFile *movie, u32 trackID, u32 sample_number_in_frag, u32 aux_type, u32 aux_info, u8 *data, u32 size) |
9273 | 0 | { |
9274 | 0 | GF_TrackFragmentBox *traf; |
9275 | 0 | if (!movie || !movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM; |
9276 | | |
9277 | 0 | traf = gf_isom_get_traf(movie, trackID); |
9278 | 0 | if (!traf) return GF_BAD_PARAM; |
9279 | 0 | return gf_isom_add_sample_aux_info_internal(NULL, traf, sample_number_in_frag, aux_type, aux_info, data, size); |
9280 | 0 | } |
9281 | | #endif |
9282 | | |
9283 | | GF_Err gf_isom_add_sample_aux_info(GF_ISOFile *file, u32 track, u32 sampleNumber, u32 aux_type, u32 aux_info, u8 *data, u32 size) |
9284 | 0 | { |
9285 | 0 | GF_Err e; |
9286 | 0 | GF_TrackBox *trak; |
9287 | |
|
9288 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
9289 | 0 | if (e) return e; |
9290 | | |
9291 | 0 | trak = gf_isom_get_track_box(file, track); |
9292 | 0 | if (!trak) return GF_BAD_PARAM; |
9293 | | |
9294 | 0 | return gf_isom_add_sample_aux_info_internal(trak, NULL, sampleNumber, aux_type, aux_info, data, size); |
9295 | 0 | } |
9296 | | |
9297 | | |
9298 | | GF_Err gf_isom_set_meta_qt(GF_ISOFile *file) |
9299 | 0 | { |
9300 | 0 | u32 i, count; |
9301 | 0 | if (!file) return GF_BAD_PARAM; |
9302 | 0 | GF_Err e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
9303 | 0 | if (e) return e; |
9304 | 0 | if (file->moov->meta) |
9305 | 0 | file->moov->meta->write_qt = 1; |
9306 | |
|
9307 | 0 | count = gf_list_count(file->moov->trackList); |
9308 | 0 | for (i=0; i<count; i++) { |
9309 | 0 | GF_TrackBox *trak = gf_list_get(file->moov->trackList, i); |
9310 | 0 | if (trak->meta) |
9311 | 0 | trak->meta->write_qt = 1; |
9312 | 0 | } |
9313 | 0 | return GF_OK; |
9314 | 0 | } |
9315 | | |
9316 | | |
9317 | | GF_EXPORT |
9318 | | GF_Err gf_isom_set_mpegh_compatible_profiles(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescIndex, const u32 *profiles, u32 nb_compat_profiles) |
9319 | 0 | { |
9320 | 0 | u32 i, type; |
9321 | 0 | GF_SampleEntryBox *ent; |
9322 | 0 | GF_MHACompatibleProfilesBox *mhap; |
9323 | 0 | GF_TrackBox *trak; |
9324 | |
|
9325 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
9326 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
9327 | 0 | ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescIndex-1); |
9328 | 0 | if (!ent) return GF_BAD_PARAM; |
9329 | 0 | type = ent->type; |
9330 | 0 | if (type==GF_ISOM_BOX_TYPE_GNRA) |
9331 | 0 | type = ((GF_GenericAudioSampleEntryBox *)ent)->EntryType; |
9332 | |
|
9333 | 0 | switch (type) { |
9334 | 0 | case GF_ISOM_BOX_TYPE_MHA1: |
9335 | 0 | case GF_ISOM_BOX_TYPE_MHA2: |
9336 | 0 | case GF_ISOM_BOX_TYPE_MHM1: |
9337 | 0 | case GF_ISOM_BOX_TYPE_MHM2: |
9338 | 0 | break; |
9339 | 0 | default: |
9340 | 0 | return GF_BAD_PARAM; |
9341 | 0 | } |
9342 | 0 | mhap = (GF_MHACompatibleProfilesBox *) gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_MHAP); |
9343 | 0 | if (!mhap) { |
9344 | 0 | if (! profiles || !nb_compat_profiles) return GF_OK; |
9345 | 0 | mhap = (GF_MHACompatibleProfilesBox *) gf_isom_box_new_parent(&ent->child_boxes, GF_ISOM_BOX_TYPE_MHAP); |
9346 | 0 | } else if (! profiles || !nb_compat_profiles) { |
9347 | 0 | gf_isom_box_del_parent(&ent->child_boxes, (GF_Box*)mhap); |
9348 | 0 | return GF_OK; |
9349 | 0 | } |
9350 | 0 | if (mhap->compat_profiles) gf_free(mhap->compat_profiles); |
9351 | 0 | mhap->compat_profiles = gf_malloc(sizeof(u8) * nb_compat_profiles); |
9352 | 0 | if (!mhap->compat_profiles) return GF_OUT_OF_MEM; |
9353 | 0 | for (i=0; i<nb_compat_profiles; i++) { |
9354 | 0 | mhap->compat_profiles[i] = (u8) profiles[i]; |
9355 | 0 | } |
9356 | 0 | mhap->num_profiles = nb_compat_profiles; |
9357 | 0 | return GF_OK; |
9358 | 0 | } |
9359 | | |
9360 | | GF_Err gf_isom_set_sample_description_restricted(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescIndex, u32 scheme_type) |
9361 | 0 | { |
9362 | 0 | u32 type; |
9363 | 0 | GF_SampleEntryBox *ent; |
9364 | 0 | GF_TrackBox *trak; |
9365 | |
|
9366 | 0 | trak = gf_isom_get_track_box(movie, trackNumber); |
9367 | 0 | if (!trak || !trak->Media) return GF_BAD_PARAM; |
9368 | 0 | ent = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, sampleDescIndex-1); |
9369 | 0 | if (!ent) return GF_BAD_PARAM; |
9370 | 0 | type = ent->type; |
9371 | |
|
9372 | 0 | u32 original_format = type; |
9373 | 0 | u32 gnr_type=0; |
9374 | 0 | if (original_format==GF_ISOM_BOX_TYPE_GNRA) { |
9375 | 0 | gnr_type = original_format; |
9376 | 0 | type = ((GF_GenericAudioSampleEntryBox*)ent)->EntryType; |
9377 | 0 | } else if (original_format==GF_ISOM_BOX_TYPE_GNRV) { |
9378 | 0 | gnr_type = original_format; |
9379 | 0 | type = ((GF_GenericVisualSampleEntryBox*)ent)->EntryType; |
9380 | 0 | } else if (original_format==GF_ISOM_BOX_TYPE_GNRM) { |
9381 | 0 | gnr_type = original_format; |
9382 | 0 | type = ((GF_GenericSampleEntryBox*)ent)->EntryType; |
9383 | 0 | } |
9384 | |
|
9385 | 0 | switch (type) { |
9386 | 0 | case GF_ISOM_BOX_TYPE_RESV: |
9387 | 0 | case GF_ISOM_BOX_TYPE_RESA: |
9388 | 0 | case GF_ISOM_BOX_TYPE_RESM: |
9389 | 0 | case GF_ISOM_BOX_TYPE_REST: |
9390 | 0 | case GF_ISOM_BOX_TYPE_RESU: |
9391 | 0 | case GF_ISOM_BOX_TYPE_RESS: |
9392 | 0 | case GF_ISOM_BOX_TYPE_RESF: |
9393 | 0 | case GF_ISOM_BOX_TYPE_RESP: |
9394 | 0 | case GF_ISOM_BOX_TYPE_RES3: |
9395 | 0 | return GF_OK; |
9396 | 0 | case GF_ISOM_BOX_TYPE_STXT: |
9397 | 0 | type = GF_ISOM_BOX_TYPE_REST; |
9398 | 0 | break; |
9399 | 0 | case GF_ISOM_BOX_TYPE_STPP: |
9400 | 0 | type = GF_ISOM_BOX_TYPE_RESU; |
9401 | 0 | break; |
9402 | 0 | case GF_ISOM_BOX_TYPE_METX: |
9403 | 0 | case GF_ISOM_BOX_TYPE_METT: |
9404 | 0 | case GF_ISOM_BOX_TYPE_URIM: |
9405 | 0 | case GF_ISOM_BOX_TYPE_MEBX: |
9406 | 0 | type = GF_ISOM_BOX_TYPE_RESM; |
9407 | 0 | break; |
9408 | 0 | default: |
9409 | 0 | type=0; |
9410 | 0 | switch (trak->Media->handler->handlerType) { |
9411 | 0 | case GF_ISOM_MEDIA_VISUAL: |
9412 | 0 | case GF_ISOM_MEDIA_AUXV: |
9413 | 0 | case GF_ISOM_MEDIA_PICT: |
9414 | 0 | type = GF_ISOM_BOX_TYPE_RESV; |
9415 | 0 | break; |
9416 | 0 | case GF_ISOM_MEDIA_AUDIO: |
9417 | 0 | type = GF_ISOM_BOX_TYPE_RESA; |
9418 | 0 | break; |
9419 | 0 | case GF_ISOM_MEDIA_OD: |
9420 | 0 | case GF_ISOM_MEDIA_OCR: |
9421 | 0 | case GF_ISOM_MEDIA_SCENE: |
9422 | 0 | case GF_ISOM_MEDIA_MPEG7: |
9423 | 0 | case GF_ISOM_MEDIA_OCI: |
9424 | 0 | case GF_ISOM_MEDIA_IPMP: |
9425 | 0 | case GF_ISOM_MEDIA_MPEGJ: |
9426 | 0 | type = GF_ISOM_BOX_TYPE_RESS; |
9427 | 0 | break; |
9428 | 0 | } |
9429 | 0 | break; |
9430 | 0 | } |
9431 | 0 | if (!type) return GF_NOT_SUPPORTED; |
9432 | | |
9433 | 0 | GF_ProtectionSchemeInfoBox *rinf; |
9434 | 0 | rinf = (GF_ProtectionSchemeInfoBox *) gf_isom_box_find_child(ent->child_boxes, GF_ISOM_BOX_TYPE_RINF); |
9435 | 0 | if (rinf) gf_isom_box_del_parent(&ent->child_boxes, (GF_Box *)rinf); |
9436 | |
|
9437 | 0 | rinf = (GF_ProtectionSchemeInfoBox *)gf_isom_box_new_parent(&ent->child_boxes, GF_ISOM_BOX_TYPE_RINF); |
9438 | 0 | if (!rinf) return GF_OUT_OF_MEM; |
9439 | | |
9440 | | |
9441 | 0 | rinf->original_format = (GF_OriginalFormatBox *)gf_isom_box_new_parent(&rinf->child_boxes, GF_ISOM_BOX_TYPE_FRMA); |
9442 | 0 | if (!rinf->original_format) return GF_OUT_OF_MEM; |
9443 | 0 | if (gnr_type) { |
9444 | 0 | rinf->original_format->data_format = gnr_type; |
9445 | 0 | rinf->original_format->gnr_type = original_format; |
9446 | 0 | } else { |
9447 | 0 | rinf->original_format->data_format = original_format; |
9448 | 0 | } |
9449 | | //common to isma, cenc and oma |
9450 | 0 | rinf->scheme_type = (GF_SchemeTypeBox *)gf_isom_box_new_parent(&rinf->child_boxes, GF_ISOM_BOX_TYPE_SCHM); |
9451 | 0 | if (!rinf->scheme_type) return GF_OUT_OF_MEM; |
9452 | 0 | rinf->scheme_type->scheme_type = scheme_type; |
9453 | |
|
9454 | 0 | ent->type = type; |
9455 | 0 | return GF_OK; |
9456 | 0 | } |
9457 | | |
9458 | | GF_Err isom_sample_refs_push(GF_SampleReferences *sref, s32 refID, u32 nb_refs, s32 *refs) |
9459 | 0 | { |
9460 | 0 | GF_SampleRefEntry *ent; |
9461 | 0 | GF_SAFEALLOC(ent, GF_SampleRefEntry); |
9462 | 0 | if (!ent) return GF_OUT_OF_MEM; |
9463 | 0 | refID += sref->id_shift; |
9464 | 0 | if (refID<0) { |
9465 | 0 | u32 new_shift = -refID; |
9466 | 0 | sref->id_shift += -refID; |
9467 | 0 | refID = 0; |
9468 | 0 | u32 i, j, count = gf_list_count(sref->entries); |
9469 | 0 | for (i=0; i<count; i++) { |
9470 | 0 | GF_SampleRefEntry *a = gf_list_get(sref->entries, i); |
9471 | 0 | a->sampleID += new_shift; |
9472 | 0 | for (j=0; j<a->nb_refs; j++) |
9473 | 0 | a->sample_refs[j] += new_shift; |
9474 | 0 | } |
9475 | 0 | } |
9476 | |
|
9477 | 0 | ent->sampleID = refID; |
9478 | 0 | if (nb_refs) { |
9479 | 0 | u32 j; |
9480 | 0 | ent->nb_refs = nb_refs; |
9481 | 0 | ent->sample_refs = gf_malloc(sizeof(u32)*nb_refs); |
9482 | 0 | memcpy(ent->sample_refs, refs, sizeof(s32)*nb_refs); |
9483 | 0 | if (sref->id_shift) { |
9484 | 0 | for (j=0; j<ent->nb_refs; j++) |
9485 | 0 | ent->sample_refs[j] += sref->id_shift; |
9486 | 0 | } |
9487 | 0 | } |
9488 | 0 | sref->cdrf_cache_size = 0; |
9489 | 0 | return gf_list_add(sref->entries, ent); |
9490 | 0 | } |
9491 | | |
9492 | | GF_Err gf_isom_set_sample_references(GF_ISOFile *file, u32 track, u32 sampleNumber, s32 refID, u32 nb_refs, s32 *refs) |
9493 | 0 | { |
9494 | 0 | GF_Err e; |
9495 | 0 | GF_TrackBox *trak; |
9496 | 0 | GF_SampleTableBox *stbl; |
9497 | |
|
9498 | 0 | e = gf_isom_can_access_movie(file, GF_ISOM_OPEN_WRITE); |
9499 | 0 | if (e) return e; |
9500 | | |
9501 | 0 | trak = gf_isom_get_track_box(file, track); |
9502 | 0 | if (!trak) return GF_BAD_PARAM; |
9503 | 0 | stbl = trak->Media->information->sampleTable; |
9504 | 0 | if (sampleNumber != stbl->SampleSize->sampleCount) |
9505 | 0 | return GF_BAD_PARAM; |
9506 | | |
9507 | 0 | if (!stbl->SampleRefs) { |
9508 | 0 | stbl->SampleRefs = (GF_SampleReferences *)gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_CDRF); |
9509 | 0 | if (!stbl->SampleRefs) return GF_OUT_OF_MEM; |
9510 | 0 | } |
9511 | 0 | return isom_sample_refs_push(stbl->SampleRefs, refID, nb_refs, refs); |
9512 | |
|
9513 | 0 | } |
9514 | | |
9515 | | |
9516 | | #endif /*!defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)*/ |