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