fragment: Get 'tfra' box only when 'mfra' box is present.
[L-SMASH.git] / core / file.c
blob6477027dbe3a0d013672be75997c3146151397c4
1 /*****************************************************************************
2 * file.c
3 *****************************************************************************
4 * Copyright (C) 2010-2014 L-SMASH project
6 * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *****************************************************************************/
21 /* This file is available under an ISC license. */
23 #include "common/internal.h" /* must be placed first */
25 #include <string.h>
27 #include "box.h"
28 #include "read.h"
30 int isom_check_compatibility
32 lsmash_file_t *file
35 if( !file )
36 return -1;
37 /* Clear flags for compatibility. */
38 ptrdiff_t compat_offset = offsetof( lsmash_file_t, qt_compatible );
39 memset( (int8_t *)file + compat_offset, 0, sizeof(lsmash_file_t) - compat_offset );
40 file->min_isom_version = UINT8_MAX; /* undefined value */
41 /* Get the brand container. */
42 isom_ftyp_t *ftyp = file->ftyp ? file->ftyp : (isom_ftyp_t *)lsmash_get_entry_data( &file->styp_list, 1 );
43 /* Check brand to decide mandatory boxes. */
44 if( !ftyp )
46 /* No brand declaration means this file is a MP4 version 1 or QuickTime file format. */
47 if( file->moov
48 && file->moov->iods )
50 file->mp4_version1 = 1;
51 file->isom_compatible = 1;
53 else
55 file->qt_compatible = 1;
56 file->undefined_64_ver = 1;
58 return 0;
60 for( uint32_t i = 0; i <= ftyp->brand_count; i++ )
62 uint32_t brand = (i == ftyp->brand_count ? ftyp->major_brand : ftyp->compatible_brands[i]);
63 switch( brand )
65 case ISOM_BRAND_TYPE_QT :
66 file->qt_compatible = 1;
67 break;
68 case ISOM_BRAND_TYPE_MP41 :
69 file->mp4_version1 = 1;
70 break;
71 case ISOM_BRAND_TYPE_MP42 :
72 file->mp4_version2 = 1;
73 break;
74 case ISOM_BRAND_TYPE_AVC1 :
75 case ISOM_BRAND_TYPE_ISOM :
76 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 1 );
77 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 1 );
78 break;
79 case ISOM_BRAND_TYPE_ISO2 :
80 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 2 );
81 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 2 );
82 break;
83 case ISOM_BRAND_TYPE_ISO3 :
84 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 3 );
85 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 3 );
86 break;
87 case ISOM_BRAND_TYPE_ISO4 :
88 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 4 );
89 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 4 );
90 break;
91 case ISOM_BRAND_TYPE_ISO5 :
92 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 5 );
93 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 5 );
94 break;
95 case ISOM_BRAND_TYPE_ISO6 :
96 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 6 );
97 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 6 );
98 break;
99 case ISOM_BRAND_TYPE_ISO7 :
100 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 7 );
101 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 7 );
102 break;
103 case ISOM_BRAND_TYPE_M4A :
104 case ISOM_BRAND_TYPE_M4B :
105 case ISOM_BRAND_TYPE_M4P :
106 case ISOM_BRAND_TYPE_M4V :
107 file->itunes_movie = 1;
108 break;
109 case ISOM_BRAND_TYPE_3GP4 :
110 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 4 );
111 break;
112 case ISOM_BRAND_TYPE_3GP5 :
113 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 5 );
114 break;
115 case ISOM_BRAND_TYPE_3GE6 :
116 case ISOM_BRAND_TYPE_3GG6 :
117 case ISOM_BRAND_TYPE_3GP6 :
118 case ISOM_BRAND_TYPE_3GR6 :
119 case ISOM_BRAND_TYPE_3GS6 :
120 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 6 );
121 break;
122 case ISOM_BRAND_TYPE_3GP7 :
123 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 7 );
124 break;
125 case ISOM_BRAND_TYPE_3GP8 :
126 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 8 );
127 break;
128 case ISOM_BRAND_TYPE_3GE9 :
129 case ISOM_BRAND_TYPE_3GF9 :
130 case ISOM_BRAND_TYPE_3GG9 :
131 case ISOM_BRAND_TYPE_3GH9 :
132 case ISOM_BRAND_TYPE_3GM9 :
133 case ISOM_BRAND_TYPE_3GP9 :
134 case ISOM_BRAND_TYPE_3GR9 :
135 case ISOM_BRAND_TYPE_3GS9 :
136 case ISOM_BRAND_TYPE_3GT9 :
137 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 9 );
138 break;
139 default :
140 break;
142 switch( brand )
144 case ISOM_BRAND_TYPE_AVC1 :
145 case ISOM_BRAND_TYPE_ISO2 :
146 case ISOM_BRAND_TYPE_ISO3 :
147 case ISOM_BRAND_TYPE_ISO4 :
148 case ISOM_BRAND_TYPE_ISO5 :
149 case ISOM_BRAND_TYPE_ISO6 :
150 file->avc_extensions = 1;
151 break;
152 case ISOM_BRAND_TYPE_3GP4 :
153 case ISOM_BRAND_TYPE_3GP5 :
154 case ISOM_BRAND_TYPE_3GP6 :
155 case ISOM_BRAND_TYPE_3GP7 :
156 case ISOM_BRAND_TYPE_3GP8 :
157 case ISOM_BRAND_TYPE_3GP9 :
158 file->forbid_tref = 1;
159 break;
160 case ISOM_BRAND_TYPE_3GH9 :
161 case ISOM_BRAND_TYPE_3GM9 :
162 case ISOM_BRAND_TYPE_DASH :
163 case ISOM_BRAND_TYPE_DSMS :
164 case ISOM_BRAND_TYPE_LMSG :
165 case ISOM_BRAND_TYPE_MSDH :
166 case ISOM_BRAND_TYPE_MSIX :
167 case ISOM_BRAND_TYPE_SIMS :
168 file->media_segment = 1;
169 break;
170 default :
171 break;
174 file->isom_compatible = !file->qt_compatible
175 || file->mp4_version1
176 || file->mp4_version2
177 || file->itunes_movie
178 || file->max_3gpp_version;
179 file->undefined_64_ver = file->qt_compatible || file->itunes_movie;
180 if( file->flags & LSMASH_FILE_MODE_WRITE )
182 /* Media Segment is incompatible with ISO Base Media File Format version 4 or former must be compatible with
183 * version 6 or later since it requires default-base-is-moof and Track Fragment Base Media Decode Time Box. */
184 if( file->media_segment && (file->min_isom_version < 5 || (file->max_isom_version && file->max_isom_version < 6)) )
185 return -1;
186 file->allow_moof_base = (file->max_isom_version >= 5 && file->min_isom_version >= 5)
187 || (file->max_isom_version == 0 && file->min_isom_version == UINT8_MAX && file->media_segment);
189 return 0;
192 int isom_check_mandatory_boxes
194 lsmash_file_t *file
197 if( !file
198 || !file->moov
199 || !file->moov->mvhd )
200 return -1;
201 /* A movie requires at least one track. */
202 if( !file->moov->trak_list.head )
203 return -1;
204 for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
206 isom_trak_t *trak = (isom_trak_t *)entry->data;
207 if( !trak
208 || !trak->tkhd
209 || !trak->mdia
210 || !trak->mdia->mdhd
211 || !trak->mdia->hdlr
212 || !trak->mdia->minf
213 || !trak->mdia->minf->dinf
214 || !trak->mdia->minf->dinf->dref
215 || !trak->mdia->minf->stbl
216 || !trak->mdia->minf->stbl->stsd
217 || !trak->mdia->minf->stbl->stsz
218 || !trak->mdia->minf->stbl->stts
219 || !trak->mdia->minf->stbl->stsc
220 || !trak->mdia->minf->stbl->stco )
221 return -1;
222 if( file->qt_compatible && !trak->mdia->minf->hdlr )
223 return -1;
224 isom_stbl_t *stbl = trak->mdia->minf->stbl;
225 if( !stbl->stsd->list.head )
226 return -1;
227 if( !file->fragment
228 && (!stbl->stsd->list.head
229 || !stbl->stts->list || !stbl->stts->list->head
230 || !stbl->stsc->list || !stbl->stsc->list->head
231 || !stbl->stco->list || !stbl->stco->list->head) )
232 return -1;
234 if( !file->fragment )
235 return 0;
236 if( !file->moov->mvex )
237 return -1;
238 for( lsmash_entry_t *entry = file->moov->mvex->trex_list.head; entry; entry = entry->next )
239 if( !entry->data ) /* trex */
240 return -1;
241 return 0;
244 static int isom_set_brands
246 lsmash_file_t *file,
247 lsmash_brand_type major_brand,
248 uint32_t minor_version,
249 lsmash_brand_type *brands,
250 uint32_t brand_count
253 if( brand_count > 50 )
254 return -1; /* We support setting brands up to 50. */
255 if( major_brand == 0 || brand_count == 0 )
257 /* Absence of File Type Box means this file is a QuickTime or MP4 version 1 format file. */
258 isom_remove_box_by_itself( file->ftyp );
259 /* Anyway we use QTFF as a default file format. */
260 file->qt_compatible = 1;
261 return 0;
263 isom_ftyp_t *ftyp;
264 if( file->flags & LSMASH_FILE_MODE_INITIALIZATION )
266 /* Add File Type Box if absent yet. */
267 if( !file->ftyp && !isom_add_ftyp( file ) )
268 return -1;
269 ftyp = file->ftyp;
271 else
273 /* Add Segment Type Box if absent yet. */
274 ftyp = file->styp_list.head && file->styp_list.head->data
275 ? (isom_styp_t *)file->styp_list.head->data
276 : isom_add_styp( file );
277 if( !ftyp )
278 return -1;
280 /* Allocate an array of compatible brands.
281 * ISO/IEC 14496-12 doesn't forbid the absence of brands in the compatible brand list.
282 * For a reason of safety, however, we set at least one brand in the list. */
283 size_t alloc_size = (brand_count ? brand_count : 1) * sizeof(uint32_t);
284 lsmash_brand_type *compatible_brands;
285 if( !file->compatible_brands )
286 compatible_brands = lsmash_malloc( alloc_size );
287 else
288 compatible_brands = lsmash_realloc( file->compatible_brands, alloc_size );
289 if( !compatible_brands )
290 return -1;
291 /* Set compatible brands. */
292 if( brand_count )
293 for( uint32_t i = 0; i < brand_count; i++ )
294 compatible_brands[i] = brands[i];
295 else
297 /* At least one compatible brand. */
298 compatible_brands[0] = major_brand;
299 brand_count = 1;
301 file->compatible_brands = compatible_brands;
302 /* Duplicate an array of compatible brands. */
303 lsmash_free( ftyp->compatible_brands );
304 ftyp->compatible_brands = lsmash_memdup( compatible_brands, alloc_size );
305 if( !ftyp->compatible_brands )
307 lsmash_freep( &file->compatible_brands );
308 return -1;
310 ftyp->size = ISOM_BASEBOX_COMMON_SIZE + 8 + brand_count * 4;
311 ftyp->major_brand = major_brand;
312 ftyp->minor_version = minor_version;
313 ftyp->brand_count = brand_count;
314 file->brand_count = brand_count;
315 return isom_check_compatibility( file );
318 /*******************************
319 public interfaces
320 *******************************/
322 void lsmash_discard_boxes
324 lsmash_root_t *root
327 if( !root || !root->file )
328 return;
329 isom_remove_all_extension_boxes( &root->file->extensions );
332 int lsmash_open_file
334 const char *filename,
335 int open_mode,
336 lsmash_file_parameters_t *param
339 if( !filename || !param )
340 return -1;
341 char mode[4] = { 0 };
342 lsmash_file_mode file_mode = 0;
343 if( open_mode == 0 )
345 memcpy( mode, "w+b", 4 );
346 file_mode = LSMASH_FILE_MODE_WRITE
347 | LSMASH_FILE_MODE_BOX
348 | LSMASH_FILE_MODE_INITIALIZATION
349 | LSMASH_FILE_MODE_MEDIA;
351 #ifdef LSMASH_DEMUXER_ENABLED
352 else if( open_mode == 1 )
354 memcpy( mode, "rb", 3 );
355 file_mode = LSMASH_FILE_MODE_READ;
357 #endif
358 if( file_mode == 0 )
359 return -1;
360 FILE *stream = NULL;
361 int seekable = 1;
362 if( !strcmp( filename, "-" ) )
364 if( file_mode & LSMASH_FILE_MODE_READ )
366 stream = stdin;
367 seekable = 0;
369 else if( file_mode & LSMASH_FILE_MODE_WRITE )
371 stream = stdout;
372 seekable = 0;
373 file_mode |= LSMASH_FILE_MODE_FRAGMENTED;
376 else
377 stream = lsmash_fopen( filename, mode );
378 if( !stream )
379 return -1;
380 memset( param, 0, sizeof(lsmash_file_parameters_t) );
381 param->mode = file_mode;
382 param->opaque = (void *)stream;
383 param->read = lsmash_fread_wrapper;
384 param->write = lsmash_fwrite_wrapper;
385 param->seek = seekable ? lsmash_fseek_wrapper : NULL;
386 param->major_brand = 0;
387 param->brands = NULL;
388 param->brand_count = 0;
389 param->minor_version = 0;
390 param->max_chunk_duration = 0.5;
391 param->max_async_tolerance = 2.0;
392 param->max_chunk_size = 4 * 1024 * 1024;
393 param->max_read_size = 4 * 1024 * 1024;
394 return 0;
397 int lsmash_close_file
399 lsmash_file_parameters_t *param
402 if( !param )
403 return -1;
404 if( !param->opaque )
405 return 0;
406 int ret = fclose( (FILE *)param->opaque );
407 param->opaque = NULL;
408 return ret;
411 lsmash_file_t *lsmash_set_file
413 lsmash_root_t *root,
414 lsmash_file_parameters_t *param
417 if( !root || !param )
418 return NULL;
419 lsmash_file_t *file = isom_add_file( root );
420 if( !file )
421 return NULL;
422 lsmash_bs_t *bs = lsmash_bs_create();
423 if( !bs )
424 goto fail;
425 file->bs = bs;
426 file->flags = param->mode;
427 file->bs->stream = param->opaque;
428 file->bs->read = param->read;
429 file->bs->write = param->write;
430 file->bs->seek = param->seek;
431 file->bs->unseekable = (param->seek == NULL);
432 file->bs->buffer.max_size = param->max_read_size;
433 file->max_chunk_duration = param->max_chunk_duration;
434 file->max_async_tolerance = LSMASH_MAX( param->max_async_tolerance, 2 * param->max_chunk_duration );
435 file->max_chunk_size = param->max_chunk_size;
436 if( (file->flags & LSMASH_FILE_MODE_WRITE)
437 && (file->flags & LSMASH_FILE_MODE_BOX) )
439 /* Establish the fragment handler if required. */
440 if( file->flags & LSMASH_FILE_MODE_FRAGMENTED )
442 file->fragment = lsmash_malloc_zero( sizeof(isom_fragment_manager_t) );
443 if( !file->fragment )
444 goto fail;
445 file->fragment->pool = lsmash_create_entry_list();
446 if( !file->fragment->pool )
447 goto fail;
449 else if( file->bs->unseekable )
450 /* For unseekable output operations, LSMASH_FILE_MODE_FRAGMENTED shall be set. */
451 goto fail;
452 /* Establish file types. */
453 if( isom_set_brands( file, param->major_brand,
454 param->minor_version,
455 param->brands, param->brand_count ) < 0 )
456 goto fail;
457 /* Create the movie header if the initialization of the streams is required. */
458 if( file->flags & LSMASH_FILE_MODE_INITIALIZATION )
460 if( !isom_add_moov( file )
461 || !isom_add_mvhd( file->moov ) )
462 goto fail;
463 /* Default Movie Header Box. */
464 isom_mvhd_t *mvhd = file->moov->mvhd;
465 mvhd->rate = 0x00010000;
466 mvhd->volume = 0x0100;
467 mvhd->matrix[0] = 0x00010000;
468 mvhd->matrix[4] = 0x00010000;
469 mvhd->matrix[8] = 0x40000000;
470 mvhd->next_track_ID = 1;
473 if( !root->file )
474 root->file = file;
475 return file;
476 fail:
477 isom_remove_box_by_itself( file );
478 return NULL;
481 int64_t lsmash_read_file
483 lsmash_file_t *file,
484 lsmash_file_parameters_t *param
487 #ifdef LSMASH_DEMUXER_ENABLED
488 if( !file || !file->bs )
489 return -1LL;
490 int64_t ret = -1;
491 if( file->flags & (LSMASH_FILE_MODE_READ | LSMASH_FILE_MODE_DUMP) )
493 /* Get the file size if seekable when reading. */
494 if( !file->bs->unseekable )
496 ret = lsmash_bs_read_seek( file->bs, 0, SEEK_END );
497 if( ret < 0 )
498 return ret;
499 file->bs->written = ret;
500 lsmash_bs_read_seek( file->bs, 0, SEEK_SET );
502 else
503 ret = 0;
504 /* Read whole boxes. */
505 if( isom_read_file( file ) < 0 )
506 return -1;
507 if( param )
509 if( file->ftyp )
511 /* file types */
512 isom_ftyp_t *ftyp = file->ftyp;
513 param->major_brand = ftyp->major_brand ? ftyp->major_brand : ISOM_BRAND_TYPE_QT;
514 param->minor_version = ftyp->minor_version;
515 param->brands = file->compatible_brands;
516 param->brand_count = file->brand_count;
518 else if( file->styp_list.head && file->styp_list.head->data )
520 /* segment types */
521 isom_styp_t *styp = (isom_styp_t *)file->styp_list.head->data;
522 param->major_brand = styp->major_brand ? styp->major_brand : ISOM_BRAND_TYPE_QT;
523 param->minor_version = styp->minor_version;
524 param->brands = file->compatible_brands;
525 param->brand_count = file->brand_count;
527 else
529 param->major_brand = file->mp4_version1 ? ISOM_BRAND_TYPE_MP41 : ISOM_BRAND_TYPE_QT;
530 param->minor_version = 0;
531 param->brands = NULL;
532 param->brand_count = 0;
536 return ret;
537 #else
538 return -1LL;
539 #endif
542 int lsmash_activate_file
544 lsmash_root_t *root,
545 lsmash_file_t *file
548 if( !root || !file || file->root != root )
549 return -1;
550 root->file = file;
551 return 0;