1 /*****************************************************************************
2  * file.c
3  *****************************************************************************
4  * Copyright (C) 2010-2017 L-SMASH project
5  *
6  * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
7  *
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.
11  *
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  *****************************************************************************/
20 
21 /* This file is available under an ISC license. */
22 
23 #include "common/internal.h" /* must be placed first */
24 
25 /* for _setmode() */
26 #ifdef _WIN32
27 #include <io.h>
28 #endif
29 
30 #include <string.h>
31 #include <fcntl.h>
32 
33 #include "box.h"
34 #include "read.h"
35 #include "fragment.h"
36 
37 #include "importer/importer.h"
38 
isom_clear_compat_flags(lsmash_file_t * file)39 static void isom_clear_compat_flags
40 (
41     lsmash_file_t *file
42 )
43 {
44     /* Clear flags for compatibility. */
45     memset( (int8_t *)file + COMPAT_FLAGS_OFFSET, 0, sizeof(lsmash_file_t) - COMPAT_FLAGS_OFFSET );
46     file->min_isom_version = UINT8_MAX; /* undefined value */
47 }
48 
isom_check_compatibility(lsmash_file_t * file)49 int isom_check_compatibility
50 (
51     lsmash_file_t *file
52 )
53 {
54     if( LSMASH_IS_NON_EXISTING_BOX( file ) )
55         return LSMASH_ERR_FUNCTION_PARAM;
56     isom_clear_compat_flags( file );
57     /* Get the brand container. */
58     isom_ftyp_t *ftyp = LSMASH_IS_EXISTING_BOX( file->ftyp )
59                       ? file->ftyp
60                       : (isom_ftyp_t *)lsmash_list_get_entry_data( &file->styp_list, 1 );
61     /* Check brand to decide mandatory boxes. */
62     if( LSMASH_IS_NON_EXISTING_BOX( ftyp ) )
63     {
64         /* No brand declaration means this file is a MP4 version 1 or QuickTime file format. */
65         if( LSMASH_IS_EXISTING_BOX( file->moov->iods ) )
66         {
67             file->mp4_version1    = 1;
68             file->isom_compatible = 1;
69         }
70         else
71         {
72             file->qt_compatible    = 1;
73             file->undefined_64_ver = 1;
74         }
75         return 0;
76     }
77     for( uint32_t i = 0; i <= ftyp->brand_count; i++ )
78     {
79         uint32_t brand = (i == ftyp->brand_count ? ftyp->major_brand : ftyp->compatible_brands[i]);
80         switch( brand )
81         {
82             case ISOM_BRAND_TYPE_QT :
83                 file->qt_compatible = 1;
84                 break;
85             case ISOM_BRAND_TYPE_MP41 :
86                 file->mp4_version1 = 1;
87                 break;
88             case ISOM_BRAND_TYPE_MP42 :
89                 file->mp4_version2 = 1;
90                 break;
91             case ISOM_BRAND_TYPE_AVC1 :
92             case ISOM_BRAND_TYPE_ISOM :
93                 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 1 );
94                 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 1 );
95                 break;
96             case ISOM_BRAND_TYPE_ISO2 :
97                 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 2 );
98                 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 2 );
99                 break;
100             case ISOM_BRAND_TYPE_ISO3 :
101                 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 3 );
102                 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 3 );
103                 break;
104             case ISOM_BRAND_TYPE_ISO4 :
105                 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 4 );
106                 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 4 );
107                 break;
108             case ISOM_BRAND_TYPE_ISO5 :
109                 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 5 );
110                 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 5 );
111                 break;
112             case ISOM_BRAND_TYPE_ISO6 :
113                 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 6 );
114                 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 6 );
115                 break;
116             case ISOM_BRAND_TYPE_ISO7 :
117                 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 7 );
118                 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 7 );
119                 break;
120             case ISOM_BRAND_TYPE_M4A :
121             case ISOM_BRAND_TYPE_M4B :
122             case ISOM_BRAND_TYPE_M4P :
123             case ISOM_BRAND_TYPE_M4V :
124                 file->itunes_movie = 1;
125                 break;
126             case ISOM_BRAND_TYPE_3GP4 :
127                 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 4 );
128                 break;
129             case ISOM_BRAND_TYPE_3GP5 :
130                 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 5 );
131                 break;
132             case ISOM_BRAND_TYPE_3GE6 :
133             case ISOM_BRAND_TYPE_3GG6 :
134             case ISOM_BRAND_TYPE_3GP6 :
135             case ISOM_BRAND_TYPE_3GR6 :
136             case ISOM_BRAND_TYPE_3GS6 :
137                 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 6 );
138                 break;
139             case ISOM_BRAND_TYPE_3GP7 :
140                 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 7 );
141                 break;
142             case ISOM_BRAND_TYPE_3GP8 :
143                 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 8 );
144                 break;
145             case ISOM_BRAND_TYPE_3GE9 :
146             case ISOM_BRAND_TYPE_3GF9 :
147             case ISOM_BRAND_TYPE_3GG9 :
148             case ISOM_BRAND_TYPE_3GH9 :
149             case ISOM_BRAND_TYPE_3GM9 :
150             case ISOM_BRAND_TYPE_3GP9 :
151             case ISOM_BRAND_TYPE_3GR9 :
152             case ISOM_BRAND_TYPE_3GS9 :
153             case ISOM_BRAND_TYPE_3GT9 :
154                 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 9 );
155                 break;
156             default :
157                 break;
158         }
159         switch( brand )
160         {
161             case ISOM_BRAND_TYPE_AVC1 :
162             case ISOM_BRAND_TYPE_ISO2 :
163             case ISOM_BRAND_TYPE_ISO3 :
164             case ISOM_BRAND_TYPE_ISO4 :
165             case ISOM_BRAND_TYPE_ISO5 :
166             case ISOM_BRAND_TYPE_ISO6 :
167                 file->avc_extensions = 1;
168                 break;
169             case ISOM_BRAND_TYPE_3GP4 :
170             case ISOM_BRAND_TYPE_3GP5 :
171             case ISOM_BRAND_TYPE_3GP6 :
172             case ISOM_BRAND_TYPE_3GP7 :
173             case ISOM_BRAND_TYPE_3GP8 :
174             case ISOM_BRAND_TYPE_3GP9 :
175                 file->forbid_tref = 1;
176                 break;
177             case ISOM_BRAND_TYPE_3GH9 :
178             case ISOM_BRAND_TYPE_3GM9 :
179             case ISOM_BRAND_TYPE_DASH :
180             case ISOM_BRAND_TYPE_DSMS :
181             case ISOM_BRAND_TYPE_LMSG :
182             case ISOM_BRAND_TYPE_MSDH :
183             case ISOM_BRAND_TYPE_MSIX :
184             case ISOM_BRAND_TYPE_SIMS :
185                 file->media_segment = 1;
186                 break;
187             default :
188                 break;
189         }
190     }
191     file->isom_compatible = !file->qt_compatible
192                           || file->mp4_version1
193                           || file->mp4_version2
194                           || file->itunes_movie
195                           || file->max_3gpp_version;
196     file->undefined_64_ver = file->qt_compatible || file->itunes_movie;
197     if( file->flags & LSMASH_FILE_MODE_WRITE )
198     {
199         /* Media Segment is incompatible with ISO Base Media File Format version 4 or former must be compatible with
200          * version 6 or later since it requires default-base-is-moof and Track Fragment Base Media Decode Time Box. */
201         if( file->media_segment && (file->min_isom_version < 5 || (file->max_isom_version && file->max_isom_version < 6)) )
202             return LSMASH_ERR_INVALID_DATA;
203         file->allow_moof_base = (file->max_isom_version >= 5 && file->min_isom_version >= 5)
204                              || (file->max_isom_version == 0 && file->min_isom_version == UINT8_MAX && file->media_segment);
205     }
206     return 0;
207 }
208 
isom_check_mandatory_boxes(lsmash_file_t * file)209 int isom_check_mandatory_boxes
210 (
211     lsmash_file_t *file
212 )
213 {
214     assert( LSMASH_IS_EXISTING_BOX( file ) );
215     /* A movie requires at least one track. */
216     if( !file->moov->trak_list.head )
217         return LSMASH_ERR_INVALID_DATA;
218     for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
219     {
220         isom_trak_t *trak = (isom_trak_t *)entry->data;
221         if( LSMASH_IS_NON_EXISTING_BOX( trak )
222          || LSMASH_IS_NON_EXISTING_BOX( trak->tkhd )
223          || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->mdhd )
224          || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->hdlr )
225          || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->dinf->dref )
226          || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stsd )
227          || (LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stsz )
228           && LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stz2 ))
229          || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stts )
230          || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stsc )
231          || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stco ) )
232             return LSMASH_ERR_INVALID_DATA;
233         if( file->qt_compatible && LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->hdlr ) )
234             return LSMASH_ERR_INVALID_DATA;
235         isom_stbl_t *stbl = trak->mdia->minf->stbl;
236         if( !stbl->stsd->list.head )
237             return LSMASH_ERR_INVALID_DATA;
238         if( !file->fragment
239          && (!stbl->stsd->list.head
240           || !stbl->stts->list || !stbl->stts->list->head
241           || !stbl->stsc->list || !stbl->stsc->list->head
242           || !stbl->stco->list || !stbl->stco->list->head) )
243             return LSMASH_ERR_INVALID_DATA;
244     }
245     if( !file->fragment )
246         return 0;
247     if( LSMASH_IS_NON_EXISTING_BOX( file->moov->mvex ) )
248         return LSMASH_ERR_INVALID_DATA;
249     for( lsmash_entry_t *entry = file->moov->mvex->trex_list.head; entry; entry = entry->next )
250         if( LSMASH_IS_NON_EXISTING_BOX( (isom_trex_t *)entry->data ) )
251             return LSMASH_ERR_INVALID_DATA;
252     return 0;
253 }
254 
isom_rearrange_data(lsmash_file_t * file,lsmash_adhoc_remux_t * remux,uint8_t * buf[2],size_t read_num,size_t size,uint64_t read_pos,uint64_t write_pos,uint64_t file_size)255 int isom_rearrange_data
256 (
257     lsmash_file_t        *file,
258     lsmash_adhoc_remux_t *remux,
259     uint8_t              *buf[2],
260     size_t                read_num,
261     size_t                size,
262     uint64_t              read_pos,
263     uint64_t              write_pos,
264     uint64_t              file_size
265 )
266 {
267     assert( remux );
268     /* Copy-pastan */
269     int buf_switch = 1;
270     lsmash_bs_t *bs = file->bs;
271     int     ret;
272     int64_t ret64;
273     while( read_num == size )
274     {
275         ret64 = lsmash_bs_write_seek( bs, read_pos, SEEK_SET );
276         if( ret64 < 0 )
277             return ret64;
278         ret = lsmash_bs_read_data( bs, buf[buf_switch], &read_num );
279         if( ret < 0 )
280             return ret;
281         read_pos    = bs->offset;
282         buf_switch ^= 0x1;
283         ret64 = lsmash_bs_write_seek( bs, write_pos, SEEK_SET );
284         if( ret64 < 0 )
285             return ret64;
286         ret = lsmash_bs_write_data( bs, buf[buf_switch], size );
287         if( ret < 0 )
288             return ret;
289         write_pos = bs->offset;
290         if( remux->func )
291             remux->func( remux->param, write_pos, file_size ); // FIXME:
292     }
293     ret = lsmash_bs_write_data( bs, buf[buf_switch ^ 0x1], read_num );
294     if( ret < 0 )
295         return ret;
296     if( remux->func )
297         remux->func( remux->param, file_size, file_size ); // FIXME:
298     return 0;
299 }
300 
isom_set_brands(lsmash_file_t * file,lsmash_brand_type major_brand,uint32_t minor_version,lsmash_brand_type * brands,uint32_t brand_count)301 static int isom_set_brands
302 (
303     lsmash_file_t     *file,
304     lsmash_brand_type  major_brand,
305     uint32_t           minor_version,
306     lsmash_brand_type *brands,
307     uint32_t           brand_count
308 )
309 {
310     if( brand_count > 50 )
311         return LSMASH_ERR_FUNCTION_PARAM;   /* We support setting brands up to 50. */
312     if( major_brand == 0 && (!brands || brand_count == 0 || brands[0] == 0) )
313     {
314         if( file->flags & LSMASH_FILE_MODE_INITIALIZATION )
315         {
316             /* Absence of File Type Box means this file is a QuickTime or MP4 version 1 format file. */
317             isom_remove_box_by_itself( file->ftyp );
318             /* Anyway we use QTFF as a default file format. */
319             isom_clear_compat_flags( file );
320             file->qt_compatible = 1;
321         }
322         else
323         {
324             /* The absence of the Segment Type Box is allowed.
325              * We set brands from the initialization segment after switching to this segment. */
326             for( lsmash_entry_t *entry = file->styp_list.head; entry; entry = entry->next )
327                 isom_remove_box_by_itself( entry->data );
328             if( LSMASH_IS_EXISTING_BOX( file->initializer ) )
329             {
330                 /* Copy flags for compatibility. */
331                 memcpy( (int8_t *)file + COMPAT_FLAGS_OFFSET, file->initializer, sizeof(lsmash_file_t) - COMPAT_FLAGS_OFFSET );
332                 file->isom_compatible  = 1;
333                 file->allow_moof_base  = 1;
334                 file->media_segment    = 1;
335                 if( file->min_isom_version < 5 )
336                     file->min_isom_version = 5;
337                 if( file->max_isom_version < 6 )
338                     file->max_isom_version = 6;
339             }
340         }
341         return 0;
342     }
343     else if( major_brand == 0 )
344     {
345         major_brand = brands[0];
346         lsmash_log( NULL, LSMASH_LOG_WARNING,
347                     "major_brand is not specified. Use the first brand in the compatible brand list as major_brand.\n" );
348     }
349     else if( !brands )
350         brand_count = 0;
351     isom_ftyp_t *ftyp;
352     if( file->flags & LSMASH_FILE_MODE_INITIALIZATION )
353     {
354         /* Add File Type Box if absent yet. */
355         if( LSMASH_IS_NON_EXISTING_BOX( file->ftyp ) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_ftyp( file ) ) )
356             return LSMASH_ERR_NAMELESS;
357         ftyp = file->ftyp;
358     }
359     else
360     {
361         /* Add Segment Type Box if absent yet. */
362         ftyp = file->styp_list.head && LSMASH_IS_EXISTING_BOX( (isom_styp_t *)file->styp_list.head->data )
363              ? (isom_styp_t *)file->styp_list.head->data
364              : isom_add_styp( file );
365         if( LSMASH_IS_NON_EXISTING_BOX( ftyp ) )
366             return LSMASH_ERR_NAMELESS;
367     }
368     /* Allocate an array of compatible brands.
369      * ISO/IEC 14496-12 doesn't forbid the absence of brands in the compatible brand list.
370      * For a reason of safety, however, we set at least one brand in the list. */
371     size_t alloc_size = (brand_count ? brand_count : 1) * sizeof(uint32_t);
372     lsmash_brand_type *compatible_brands;
373     if( !file->compatible_brands )
374         compatible_brands = lsmash_malloc( alloc_size );
375     else
376         compatible_brands = lsmash_realloc( file->compatible_brands, alloc_size );
377     if( !compatible_brands )
378         return LSMASH_ERR_MEMORY_ALLOC;
379     /* Set compatible brands. */
380     if( brand_count )
381         for( uint32_t i = 0; i < brand_count; i++ )
382             compatible_brands[i] = brands[i];
383     else
384     {
385         /* At least one compatible brand. */
386         compatible_brands[0] = major_brand;
387         brand_count = 1;
388     }
389     file->compatible_brands = compatible_brands;
390     /* Duplicate an array of compatible brands. */
391     lsmash_free( ftyp->compatible_brands );
392     ftyp->compatible_brands = lsmash_memdup( compatible_brands, alloc_size );
393     if( !ftyp->compatible_brands )
394     {
395         lsmash_freep( &file->compatible_brands );
396         return LSMASH_ERR_MEMORY_ALLOC;
397     }
398     ftyp->size          = ISOM_BASEBOX_COMMON_SIZE + 8 + brand_count * 4;
399     ftyp->major_brand   = major_brand;
400     ftyp->minor_version = minor_version;
401     ftyp->brand_count   = brand_count;
402     file->brand_count   = brand_count;
403     return isom_check_compatibility( file );
404 }
405 
406 /*---- default I/O ----*/
407 typedef struct
408 {
409     FILE *file_ptr;
410     int   is_standard_stream;   /* If set to 1, 'file_ptr' points to standard stream (i.e. stdin, stdout or stderr).
411                                  * This flag prevents from accidentally closing standard streams. */
412     lsmash_file_mode file_mode;
413 } default_io_stream_t;
414 
default_io_stream_open(const char * filename,int open_mode)415 static default_io_stream_t *default_io_stream_open( const char *filename, int open_mode )
416 {
417 #ifdef _WIN32
418     _setmode( _fileno( stdin ),  _O_BINARY );
419     _setmode( _fileno( stdout ), _O_BINARY );
420     _setmode( _fileno( stderr ), _O_BINARY );
421 #endif
422     default_io_stream_t *stream = (default_io_stream_t *)lsmash_malloc_zero( sizeof(default_io_stream_t) );
423     if( !stream )
424         return NULL;
425     char mode[4] = { 0 };
426     if( open_mode == 0 )
427     {
428         memcpy( mode, "w+b", 4 );
429         stream->file_mode = LSMASH_FILE_MODE_WRITE
430                           | LSMASH_FILE_MODE_BOX
431                           | LSMASH_FILE_MODE_INITIALIZATION
432                           | LSMASH_FILE_MODE_MEDIA;
433     }
434     else if( open_mode == 1 )
435     {
436         memcpy( mode, "rb", 3 );
437         stream->file_mode = LSMASH_FILE_MODE_READ;
438     }
439     else
440         assert( 0 );
441     if( !strcmp( filename, "-" ) )
442     {
443         if( stream->file_mode & LSMASH_FILE_MODE_READ )
444         {
445             stream->file_ptr           = stdin;
446             stream->is_standard_stream = 1;
447         }
448         else if( stream->file_mode & LSMASH_FILE_MODE_WRITE )
449         {
450             stream->file_ptr           = stdout;
451             stream->is_standard_stream = 1;
452             stream->file_mode         |= LSMASH_FILE_MODE_FRAGMENTED;
453         }
454     }
455     else
456         stream->file_ptr = lsmash_fopen( filename, mode );
457     if( stream->file_ptr == NULL )
458         lsmash_freep( &stream );
459     return stream;
460 }
461 
default_io_stream_close(default_io_stream_t * stream)462 static int default_io_stream_close( default_io_stream_t *stream )
463 {
464     if( !stream )
465         return 0;
466     int ret = stream->is_standard_stream ? 0 : fclose( stream->file_ptr );
467     lsmash_free( stream );
468     return ret;
469 }
470 
default_io_stream_read(void * opaque,uint8_t * buf,int size)471 static int default_io_stream_read( void *opaque, uint8_t *buf, int size )
472 {
473     int read_size = fread( buf, 1, size, ((default_io_stream_t *)opaque)->file_ptr );
474     return ferror( ((default_io_stream_t *)opaque)->file_ptr ) ? LSMASH_ERR_NAMELESS : read_size;
475 }
476 
default_io_stream_write(void * opaque,uint8_t * buf,int size)477 static int default_io_stream_write( void *opaque, uint8_t *buf, int size )
478 {
479     return fwrite( buf, 1, size, ((default_io_stream_t *)opaque)->file_ptr );
480 }
481 
default_io_stream_seek(void * opaque,int64_t offset,int whence)482 static int64_t default_io_stream_seek( void *opaque, int64_t offset, int whence )
483 {
484     if( lsmash_fseek( ((default_io_stream_t *)opaque)->file_ptr, offset, whence ) != 0 )
485         return LSMASH_ERR_NAMELESS;
486     return lsmash_ftell( ((default_io_stream_t *)opaque)->file_ptr );
487 }
488 
489 /*******************************
490     public interfaces
491 *******************************/
492 
lsmash_discard_boxes(lsmash_root_t * root)493 void lsmash_discard_boxes
494 (
495     lsmash_root_t *root
496 )
497 {
498     if( LSMASH_IS_NON_EXISTING_BOX( root )
499      || LSMASH_IS_NON_EXISTING_BOX( root->file ) )
500         return;
501     isom_remove_all_extension_boxes( &root->file->extensions );
502 }
503 
lsmash_open_file(const char * filename,int open_mode,lsmash_file_parameters_t * param)504 int lsmash_open_file
505 (
506     const char               *filename,
507     int                       open_mode,
508     lsmash_file_parameters_t *param
509 )
510 {
511     if( !filename || !param || (open_mode != 0 && open_mode != 1) )
512         return LSMASH_ERR_FUNCTION_PARAM;
513     default_io_stream_t *stream = default_io_stream_open( filename, open_mode );
514     if( !stream )
515         return LSMASH_ERR_NAMELESS;
516     memset( param, 0, sizeof(lsmash_file_parameters_t) );
517     param->mode                = stream->file_mode;
518     param->opaque              = (void *)stream;
519     param->read                = default_io_stream_read;
520     param->write               = default_io_stream_write;
521     param->seek                = stream->is_standard_stream ? NULL : default_io_stream_seek;
522     param->major_brand         = 0;
523     param->brands              = NULL;
524     param->brand_count         = 0;
525     param->minor_version       = 0;
526     param->max_chunk_duration  = 0.5;
527     param->max_async_tolerance = 2.0;
528     param->max_chunk_size      = 4 * 1024 * 1024;
529     param->max_read_size       = 4 * 1024 * 1024;
530     return 0;
531 }
532 
lsmash_close_file(lsmash_file_parameters_t * param)533 int lsmash_close_file
534 (
535     lsmash_file_parameters_t *param
536 )
537 {
538     if( !param )
539         return LSMASH_ERR_NAMELESS;
540     int ret = default_io_stream_close( (default_io_stream_t *)param->opaque );
541     param->opaque = NULL;
542     return ret == 0 ? 0 : LSMASH_ERR_UNKNOWN;
543 }
544 
lsmash_set_file(lsmash_root_t * root,lsmash_file_parameters_t * param)545 lsmash_file_t *lsmash_set_file
546 (
547     lsmash_root_t            *root,
548     lsmash_file_parameters_t *param
549 )
550 {
551     if( LSMASH_IS_NON_EXISTING_BOX( root ) || !param )
552         return NULL;
553     lsmash_file_t *file = isom_add_file_abstract( root );
554     if( LSMASH_IS_NON_EXISTING_BOX( file ) )
555         return NULL;
556     lsmash_bs_t *bs = lsmash_bs_create();
557     if( !bs )
558         goto fail;
559     file->bs                  = bs;
560     file->flags               = param->mode;
561     file->bs->stream          = param->opaque;
562     file->bs->read            = param->read;
563     file->bs->write           = param->write;
564     file->bs->seek            = param->seek;
565     file->bs->unseekable      = (param->seek == NULL);
566     file->bs->buffer.max_size = param->max_read_size;
567     file->max_chunk_duration  = param->max_chunk_duration;
568     file->max_async_tolerance = LSMASH_MAX( param->max_async_tolerance, 2 * param->max_chunk_duration );
569     file->max_chunk_size      = param->max_chunk_size;
570     if( (file->flags & LSMASH_FILE_MODE_WRITE)
571      && (file->flags & LSMASH_FILE_MODE_BOX) )
572     {
573         /* Construction of Segment Index Box requires seekability at our current implementation.
574          * If segment is not so large, data rearrangement can be avoided by buffering i.e. the
575          * seekability is not essential, but at present we don't support buffering of all materials
576          * within segment. */
577         if( (file->flags & LSMASH_FILE_MODE_INDEX) && file->bs->unseekable )
578             goto fail;
579         /* Establish the fragment handler if required. */
580         if( file->flags & LSMASH_FILE_MODE_FRAGMENTED )
581         {
582             file->fragment = lsmash_malloc_zero( sizeof(isom_fragment_manager_t) );
583             if( !file->fragment )
584                 goto fail;
585             file->fragment->first_moof_pos = FIRST_MOOF_POS_UNDETERMINED;
586             file->fragment->pool = lsmash_list_create( isom_remove_sample_pool );
587             if( !file->fragment->pool )
588                 goto fail;
589         }
590         else if( file->bs->unseekable )
591             /* For unseekable output operations, LSMASH_FILE_MODE_FRAGMENTED shall be set. */
592             goto fail;
593         /* Establish file types. */
594         if( isom_set_brands( file, param->major_brand,
595                                    param->minor_version,
596                                    param->brands, param->brand_count ) < 0 )
597             goto fail;
598         /* Create the movie header if the initialization of the streams is required. */
599         if( (file->flags & LSMASH_FILE_MODE_INITIALIZATION) && !isom_movie_create( file ) )
600             goto fail;
601     }
602     if( LSMASH_IS_NON_EXISTING_BOX( root->file ) )
603         root->file = file;
604     return file;
605 fail:
606     isom_remove_box_by_itself( file );
607     return NULL;
608 }
609 
lsmash_read_file(lsmash_file_t * file,lsmash_file_parameters_t * param)610 int64_t lsmash_read_file
611 (
612     lsmash_file_t            *file,
613     lsmash_file_parameters_t *param
614 )
615 {
616     if( LSMASH_IS_NON_EXISTING_BOX( file ) )
617         return (int64_t)LSMASH_ERR_FUNCTION_PARAM;
618     if( !file->bs )
619         return (int64_t)LSMASH_ERR_NAMELESS;
620     int64_t ret = LSMASH_ERR_NAMELESS;
621     if( file->flags & (LSMASH_FILE_MODE_READ | LSMASH_FILE_MODE_DUMP) )
622     {
623         importer_t *importer = lsmash_importer_alloc( file->root );
624         if( !importer )
625             return (int64_t)LSMASH_ERR_MEMORY_ALLOC;
626         lsmash_importer_set_file( importer, file );
627         ret = lsmash_importer_find( importer, "ISOBMFF/QTFF", !file->bs->unseekable );
628         if( ret < 0 )
629             return ret;
630         if( param )
631         {
632             if( LSMASH_IS_EXISTING_BOX( file->ftyp ) )
633             {
634                 /* file types */
635                 isom_ftyp_t *ftyp = file->ftyp;
636                 param->major_brand   = ftyp->major_brand ? ftyp->major_brand : ISOM_BRAND_TYPE_QT;
637                 param->minor_version = ftyp->minor_version;
638                 param->brands        = file->compatible_brands;
639                 param->brand_count   = file->brand_count;
640             }
641             else if( file->styp_list.head && LSMASH_IS_EXISTING_BOX( (isom_styp_t *)file->styp_list.head->data ) )
642             {
643                 /* segment types */
644                 isom_styp_t *styp = (isom_styp_t *)file->styp_list.head->data;
645                 param->major_brand   = styp->major_brand ? styp->major_brand : ISOM_BRAND_TYPE_QT;
646                 param->minor_version = styp->minor_version;
647                 param->brands        = file->compatible_brands;
648                 param->brand_count   = file->brand_count;
649             }
650             else
651             {
652                 param->major_brand   = file->mp4_version1 ? ISOM_BRAND_TYPE_MP41 : ISOM_BRAND_TYPE_QT;
653                 param->minor_version = 0;
654                 param->brands        = NULL;
655                 param->brand_count   = 0;
656             }
657         }
658     }
659     return ret;
660 }
661 
lsmash_activate_file(lsmash_root_t * root,lsmash_file_t * file)662 int lsmash_activate_file
663 (
664     lsmash_root_t *root,
665     lsmash_file_t *file
666 )
667 {
668     if( !root || !file || file->root != root )
669         return LSMASH_ERR_FUNCTION_PARAM;
670     root->file = file;
671     return 0;
672 }
673 
lsmash_switch_media_segment(lsmash_root_t * root,lsmash_file_t * successor,lsmash_adhoc_remux_t * remux)674 int lsmash_switch_media_segment
675 (
676     lsmash_root_t        *root,
677     lsmash_file_t        *successor,
678     lsmash_adhoc_remux_t *remux
679 )
680 {
681     if( LSMASH_IS_NON_EXISTING_BOX( root ) || !remux )
682         return LSMASH_ERR_FUNCTION_PARAM;
683     lsmash_file_t *predecessor = root->file;
684     if( LSMASH_IS_NON_EXISTING_BOX( predecessor ) || LSMASH_IS_NON_EXISTING_BOX( successor )
685      || predecessor == successor
686      || predecessor->root != successor->root
687      || LSMASH_IS_NON_EXISTING_BOX( predecessor->root ) || LSMASH_IS_NON_EXISTING_BOX( successor->root )
688      || predecessor->root != root || successor->root != root
689      ||  (successor->flags & LSMASH_FILE_MODE_INITIALIZATION)
690      || !(successor->flags & LSMASH_FILE_MODE_MEDIA)
691      || !(predecessor->flags & LSMASH_FILE_MODE_WRITE)      || !(successor->flags & LSMASH_FILE_MODE_WRITE)
692      || !(predecessor->flags & LSMASH_FILE_MODE_BOX)        || !(successor->flags & LSMASH_FILE_MODE_BOX)
693      || !(predecessor->flags & LSMASH_FILE_MODE_FRAGMENTED) || !(successor->flags & LSMASH_FILE_MODE_FRAGMENTED)
694      || !(predecessor->flags & LSMASH_FILE_MODE_SEGMENT)    || !(successor->flags & LSMASH_FILE_MODE_SEGMENT)
695      || (!(predecessor->flags & LSMASH_FILE_MODE_MEDIA) && !(predecessor->flags & LSMASH_FILE_MODE_INITIALIZATION)) )
696         return LSMASH_ERR_FUNCTION_PARAM;
697     int ret = isom_finish_final_fragment_movie( predecessor, remux );
698     if( ret < 0 )
699         return ret;
700     if( predecessor->flags & LSMASH_FILE_MODE_INITIALIZATION )
701     {
702         if( predecessor->initializer != predecessor )
703             return LSMASH_ERR_INVALID_DATA;
704         successor->initializer = predecessor;
705     }
706     else
707         successor->initializer = predecessor->initializer;
708     isom_styp_t *styp = (isom_styp_t *)lsmash_list_get_entry_data( &successor->styp_list, 1 );
709     if( LSMASH_IS_NON_EXISTING_BOX( styp ) )
710     {
711         ret = isom_set_brands( successor, 0, 0, NULL, 0 );
712         if( ret < 0 )
713             return LSMASH_ERR_NAMELESS;
714     }
715     successor->fragment_count = predecessor->fragment_count;
716     root->file = successor;
717     return 0;
718 }
719