1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2011-2017 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. */
34 uint32_t last_sample_delta
;
35 uint32_t current_sample_number
;
36 uint32_t *summary_remap
;
37 uint64_t skip_dt_interval
;
38 uint64_t last_sample_dts
;
39 lsmash_track_parameters_t track_param
;
40 lsmash_media_parameters_t media_param
;
45 output_track_t
*track
;
46 lsmash_movie_parameters_t param
;
48 uint32_t current_track_number
;
55 lsmash_file_parameters_t param
;
56 lsmash_file_parameters_t seg_param
;
58 uint32_t current_subseg_number
;
59 int (*open
)( const char *filename
, int open_mode
, lsmash_file_parameters_t
* );
60 int (*close
)( lsmash_file_parameters_t
* );
67 uint32_t current_seg_number
;
73 lsmash_summary_t
*summary
;
79 lsmash_file_parameters_t param
;
84 lsmash_media_parameters_t param
;
85 uint32_t num_data_refs
;
86 input_data_ref_t
*data_refs
;
92 lsmash_sample_t
*sample
;
94 uint64_t composition_delay
;
95 uint64_t skip_duration
;
96 int reach_end_of_media_timeline
;
98 uint32_t last_sample_delta
;
99 uint32_t current_sample_number
;
100 uint32_t current_sample_index
;
101 uint32_t num_summaries
;
102 input_summary_t
*summaries
;
103 lsmash_track_parameters_t track_param
;
109 input_track_t
*track
;
110 lsmash_itunes_metadata_t
*itunes_metadata
;
111 lsmash_movie_parameters_t param
;
114 uint32_t num_itunes_metadata
;
115 uint32_t current_track_number
;
121 lsmash_file_parameters_t param
;
133 char *raw_track_option
;
136 int16_t alternate_group
;
137 uint16_t ISO_language
;
141 } track_media_option
;
147 track_media_option
**track_option
;
150 int ref_chap_available
;
153 uint16_t default_language
;
154 uint64_t max_chunk_size
;
155 uint32_t max_chunk_duration_in_ms
;
156 uint32_t frag_base_track
;
157 uint32_t subseg_per_seg
;
159 int compact_size_table
;
160 double min_frag_duration
;
166 char *whole_track_option
;
167 int num_track_delimiter
;
170 static void cleanup_input_movie( input_t
*input
)
174 input_movie_t
*in_movie
= &input
->file
.movie
;
175 if( in_movie
->itunes_metadata
)
177 for( uint32_t i
= 0; i
< in_movie
->num_itunes_metadata
; i
++ )
178 lsmash_cleanup_itunes_metadata( &in_movie
->itunes_metadata
[i
] );
179 lsmash_freep( &in_movie
->itunes_metadata
);
181 if( in_movie
->track
)
183 for( uint32_t i
= 0; i
< in_movie
->num_tracks
; i
++ )
185 input_track_t
*in_track
= &in_movie
->track
[i
];
186 if( in_track
->summaries
)
188 for( uint32_t j
= 0; j
< in_track
->num_summaries
; j
++ )
189 lsmash_cleanup_summary( in_track
->summaries
[j
].summary
);
190 lsmash_free( in_track
->summaries
);
192 input_media_t
*in_media
= &in_track
->media
;
193 for( uint32_t j
= 0; j
< in_media
->num_data_refs
; j
++ )
194 if( input
->file
.fh
!= in_media
->data_refs
[j
].fh
)
195 lsmash_close_file( &in_media
->data_refs
[j
].param
);
196 lsmash_free( in_media
->data_refs
);
198 lsmash_freep( &in_movie
->track
);
200 lsmash_close_file( &input
->file
.param
);
201 lsmash_destroy_root( input
->root
);
205 static void cleanup_output_movie( output_t
*output
)
209 output_movie_t
*out_movie
= &output
->file
.movie
;
210 if( out_movie
->track
)
212 for( uint32_t i
= 0; i
< out_movie
->num_tracks
; i
++ )
213 lsmash_free( out_movie
->track
[i
].summary_remap
);
214 lsmash_freep( &out_movie
->track
);
216 if( !(output
->file
.seg_param
.mode
& LSMASH_FILE_MODE_INITIALIZATION
) )
218 lsmash_freep( &output
->file
.seg_param
.brands
);
219 if( output
->file
.close
)
220 output
->file
.close( &output
->file
.seg_param
);
222 lsmash_freep( &output
->file
.param
.brands
);
223 if( output
->file
.close
)
224 output
->file
.close( &output
->file
.param
);
225 lsmash_destroy_root( output
->root
);
229 static void cleanup_remuxer( remuxer_t
*remuxer
)
231 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
233 cleanup_input_movie( &remuxer
->input
[i
] );
234 lsmash_free( remuxer
->track_option
[i
] );
236 lsmash_free( remuxer
->track_option
);
237 lsmash_free( remuxer
->input
);
238 cleanup_output_movie( remuxer
->output
);
241 #define eprintf( ... ) fprintf( stderr, __VA_ARGS__ )
242 #define REFRESH_CONSOLE eprintf( " \r" )
244 static int remuxer_error( remuxer_t
*remuxer
, const char *message
, ... )
246 cleanup_remuxer( remuxer
);
248 eprintf( "[Error] " );
250 va_start( args
, message
);
251 vfprintf( stderr
, message
, args
);
256 static int error_message( const char *message
, ... )
259 eprintf( "[Error] " );
261 va_start( args
, message
);
262 vfprintf( stderr
, message
, args
);
267 static int warning_message( const char *message
, ... )
270 eprintf( "[Warning] " );
272 va_start( args
, message
);
273 vfprintf( stderr
, message
, args
);
278 #define REMUXER_ERR( ... ) remuxer_error( &remuxer, __VA_ARGS__ )
279 #define ERROR_MSG( ... ) error_message( __VA_ARGS__ )
280 #define WARNING_MSG( ... ) warning_message( __VA_ARGS__ )
282 static void display_version( void )
285 "L-SMASH isom/mov re-muliplexer rev%s %s\n"
287 "Copyright (C) 2011-2017 L-SMASH project\n",
288 LSMASH_REV
, LSMASH_GIT_HASH
, __DATE__
, __TIME__
);
291 static void display_help( void )
295 "Usage: remuxer -i input1 [-i input2 -i input3 ...] -o output\n"
300 " Display version information.\n"
301 " --chapter <string>\n"
302 " Set chapters from the file.\n"
304 " Add UTF-8 BOM to the chapter strings in the chapter list. (experimental)\n"
305 " --chapter-track <integer>\n"
306 " Set which track the chapter applies to.\n"
307 " This option takes effect only when reference chapter is available.\n"
308 " If this option is not used, it defaults to 1.\n"
309 " --language <string>\n"
310 " Specify the default language for all the output tracks.\n"
311 " This option is overridden by the track options.\n"
312 " --max-chunk-duration <integer>\n"
313 " Specify the maximum duration per chunk in milliseconds.\n"
314 " Chunk is the minimum unit for media interleaving.\n"
315 " If this option is not used, it defaults to 500.\n"
316 " --max-chunk-size <integer>\n"
317 " Specify the maximum size per chunk in bytes.\n"
318 " If this option is not used, it defaults to 4*1024*1024.\n"
319 " --fragment <integer>\n"
320 " Enable fragmentation per random accessible point.\n"
321 " Set which track the fragmentation is based on.\n"
322 " --min-frag-duration <float>\n"
323 " Specify the minimum duration which fragments are allowed to be.\n"
324 " This option requires --fragment.\n"
325 " --dash <integer>\n"
326 " Enable DASH ISOBMFF-based Media segmentation.\n"
327 " The value is the number of subsegments per segment.\n"
328 " If zero, Indexed self-initializing Media Segment is constructed.\n"
329 " This option requires --fragment.\n"
330 " --compact-size-table\n"
331 " Compress sample size tables if possible.\n"
333 " Execute as a dry run.\n"
336 " Remove this track.\n"
338 " Disable this track.\n"
339 " language=<string>\n"
340 " Specify media language.\n"
341 " alternate-group=<integer>\n"
342 " Specify alternate group.\n"
343 " handler=<string>\n"
344 " Set media handler name.\n"
346 " Specify starting point in media.\n"
347 " safe-seek=<integer>\n"
348 " Same as seek except for considering random accessible point\n"
349 " Media starts from the closest random accessible point.\n"
350 "How to use track options:\n"
351 " -i input?[track_number1]:[track_option1],[track_option2]?[track_number2]:...\n"
353 " remuxer -i input1 -i input2?2:alternate-group=1?3:language=jpn,alternate-group=1 -o output\n" );
356 static char *duplicate_string( char *src
)
360 int dst_size
= strlen( src
) + 1;
361 char *dst
= lsmash_malloc( dst_size
);
364 memcpy( dst
, src
, dst_size
);
368 static int get_itunes_metadata( lsmash_root_t
*root
, uint32_t metadata_number
, lsmash_itunes_metadata_t
*metadata
)
370 memset( metadata
, 0, sizeof(lsmash_itunes_metadata_t
) );
371 if( lsmash_get_itunes_metadata( root
, metadata_number
, metadata
) )
373 lsmash_itunes_metadata_t shadow
= *metadata
;
374 metadata
->meaning
= NULL
;
375 metadata
->name
= NULL
;
376 memset( &metadata
->value
, 0, sizeof(lsmash_itunes_metadata_value_t
) );
379 metadata
->meaning
= duplicate_string( shadow
.meaning
);
380 if( !metadata
->meaning
)
385 metadata
->name
= duplicate_string( shadow
.name
);
386 if( !metadata
->name
)
389 if( shadow
.type
== ITUNES_METADATA_TYPE_STRING
)
391 metadata
->value
.string
= duplicate_string( shadow
.value
.string
);
392 if( !metadata
->value
.string
)
395 else if( shadow
.type
== ITUNES_METADATA_TYPE_BINARY
)
397 metadata
->value
.binary
.data
= lsmash_malloc( shadow
.value
.binary
.size
);
398 if( !metadata
->value
.binary
.data
)
400 memcpy( metadata
->value
.binary
.data
, shadow
.value
.binary
.data
, shadow
.value
.binary
.size
);
401 metadata
->value
.binary
.size
= shadow
.value
.binary
.size
;
402 metadata
->value
.binary
.subtype
= shadow
.value
.binary
.subtype
;
405 metadata
->value
= shadow
.value
;
408 lsmash_freep( &metadata
->meaning
);
409 lsmash_freep( &metadata
->name
);
413 static inline int is_relative_path( const char *path
)
415 return path
[0] == '/' || path
[0] == '\\' || (path
[0] != '\0' && path
[1] == ':') ? 0 : 1;
418 static int input_data_reference
422 input_data_ref_t
*in_data_ref
,
423 lsmash_data_reference_t
*data_ref
426 if( lsmash_open_file( data_ref
->location
, 1, &in_data_ref
->param
) < 0 )
428 WARNING_MSG( "failed to open an external media file.\n" );
431 in_data_ref
->param
.mode
|= LSMASH_FILE_MODE_MEDIA
;
432 in_data_ref
->fh
= lsmash_set_file( input
->root
, &in_data_ref
->param
);
433 if( !in_data_ref
->fh
)
435 WARNING_MSG( "failed to set an external media file as a data reference.\n" );
438 if( lsmash_assign_data_reference( input
->root
, track_ID
, data_ref
->index
, in_data_ref
->fh
) < 0 )
440 WARNING_MSG( "failed to assign an external media a data reference.\n" );
446 static int get_movie( input_t
*input
, char *input_name
)
448 if( !strcmp( input_name
, "-" ) )
449 return ERROR_MSG( "standard input not supported.\n" );
450 /* Read an input file. */
451 input
->root
= lsmash_create_root();
453 return ERROR_MSG( "failed to create a ROOT for an input file.\n" );
454 input_file_t
*in_file
= &input
->file
;
455 if( lsmash_open_file( input_name
, 1, &in_file
->param
) < 0 )
456 return ERROR_MSG( "failed to open an input file.\n" );
457 in_file
->fh
= lsmash_set_file( input
->root
, &in_file
->param
);
459 return ERROR_MSG( "failed to add an input file into a ROOT.\n" );
460 if( lsmash_read_file( in_file
->fh
, &in_file
->param
) < 0 )
461 return ERROR_MSG( "failed to read an input file\n" );
462 /* Get iTunes metadata. */
463 input_movie_t
*in_movie
= &in_file
->movie
;
464 in_movie
->num_itunes_metadata
= lsmash_count_itunes_metadata( input
->root
);
465 if( in_movie
->num_itunes_metadata
)
467 in_movie
->itunes_metadata
= lsmash_malloc( in_movie
->num_itunes_metadata
* sizeof(lsmash_itunes_metadata_t
) );
468 if( !in_movie
->itunes_metadata
)
469 return ERROR_MSG( "failed to alloc iTunes metadata.\n" );
470 uint32_t itunes_metadata_count
= 0;
471 for( uint32_t i
= 1; i
<= in_movie
->num_itunes_metadata
; i
++ )
473 if( get_itunes_metadata( input
->root
, i
, &in_movie
->itunes_metadata
[itunes_metadata_count
] ) )
475 WARNING_MSG( "failed to get an iTunes metadata.\n" );
478 ++itunes_metadata_count
;
480 in_movie
->num_itunes_metadata
= itunes_metadata_count
;
482 in_movie
->current_track_number
= 1;
483 lsmash_initialize_movie_parameters( &in_movie
->param
);
484 if( lsmash_get_movie_parameters( input
->root
, &in_movie
->param
) )
485 return ERROR_MSG( "failed to get movie parameters.\n" );
486 uint32_t num_tracks
= in_movie
->num_tracks
= in_movie
->param
.number_of_tracks
;
488 input_track_t
*in_track
= in_movie
->track
= lsmash_malloc_zero( num_tracks
* sizeof(input_track_t
) );
490 return ERROR_MSG( "failed to alloc input tracks.\n" );
491 for( uint32_t i
= 0; i
< num_tracks
; i
++ )
493 in_track
[i
].track_ID
= lsmash_get_track_ID( input
->root
, i
+ 1 );
494 if( !in_track
[i
].track_ID
)
495 return ERROR_MSG( "failed to get track_ID.\n" );
497 for( uint32_t i
= 0; i
< num_tracks
; i
++ )
499 lsmash_initialize_track_parameters( &in_track
[i
].track_param
);
500 if( lsmash_get_track_parameters( input
->root
, in_track
[i
].track_ID
, &in_track
[i
].track_param
) )
502 WARNING_MSG( "failed to get track parameters.\n" );
505 lsmash_initialize_media_parameters( &in_track
[i
].media
.param
);
506 if( lsmash_get_media_parameters( input
->root
, in_track
[i
].track_ID
, &in_track
[i
].media
.param
) )
508 WARNING_MSG( "failed to get media parameters.\n" );
511 uint32_t data_ref_count
= lsmash_count_data_reference( input
->root
, in_track
[i
].track_ID
);
512 if( data_ref_count
== 0 )
514 WARNING_MSG( "failed to get the number of data references.\n" );
517 in_track
[i
].media
.data_refs
= lsmash_malloc_zero( data_ref_count
* sizeof(input_data_ref_t
) );
518 if( !in_track
[i
].media
.data_refs
)
520 WARNING_MSG( "failed to allocate handles of data reference.\n" );
523 for( uint32_t j
= 0; j
< data_ref_count
; j
++ )
525 input_data_ref_t
*in_data_ref
= &in_track
[i
].media
.data_refs
[j
];
526 lsmash_data_reference_t data_ref
= { .index
= j
+ 1 };
527 if( lsmash_get_data_reference( input
->root
, in_track
[i
].track_ID
, &data_ref
) < 0 )
529 WARNING_MSG( "failed to get a data references.\n" );
532 if( data_ref
.location
)
534 if( is_relative_path( data_ref
.location
) && !is_relative_path( input_name
) )
536 /* Append the directory path from the referencing file. */
537 int location_length
= strlen( data_ref
.location
);
538 int input_name_length
= strlen( input_name
);
539 char *p
= input_name
+ input_name_length
;
540 while( p
!= input_name
&& *p
!= '/' && *p
!= '\\' )
542 int relative_path_length
= p
== input_name
? 2 : p
- input_name
;
543 char *location
= lsmash_malloc( relative_path_length
+ location_length
+ 2 );
546 memcpy( location
, input_name
, relative_path_length
);
547 memcpy( location
+ relative_path_length
+ 1, data_ref
.location
, location_length
);
548 location
[relative_path_length
] = '/';
549 location
[relative_path_length
+ location_length
+ 1] = '\0';
550 lsmash_cleanup_data_reference( &data_ref
);
551 data_ref
.location
= location
;
554 int ret
= input_data_reference( input
, in_track
[i
].track_ID
, in_data_ref
, &data_ref
);
555 lsmash_cleanup_data_reference( &data_ref
);
561 in_data_ref
->fh
= in_file
->fh
;
562 in_data_ref
->param
= in_file
->param
;
565 if( lsmash_construct_timeline( input
->root
, in_track
[i
].track_ID
) )
567 WARNING_MSG( "failed to construct timeline.\n" );
570 if( lsmash_get_last_sample_delta_from_media_timeline( input
->root
, in_track
[i
].track_ID
, &in_track
[i
].last_sample_delta
) )
572 WARNING_MSG( "failed to get the last sample delta.\n" );
575 in_track
[i
].num_summaries
= lsmash_count_summary( input
->root
, in_track
[i
].track_ID
);
576 if( in_track
[i
].num_summaries
== 0 )
578 WARNING_MSG( "failed to find valid summaries.\n" );
581 in_track
[i
].summaries
= lsmash_malloc_zero( in_track
[i
].num_summaries
* sizeof(input_summary_t
) );
582 if( !in_track
[i
].summaries
)
583 return ERROR_MSG( "failed to alloc input summaries.\n" );
584 for( uint32_t j
= 0; j
< in_track
[i
].num_summaries
; j
++ )
586 lsmash_summary_t
*summary
= lsmash_get_summary( input
->root
, in_track
[i
].track_ID
, j
+ 1 );
589 WARNING_MSG( "failed to get a summary.\n" );
592 if( !LSMASH_FLAGS_SATISFIED( lsmash_check_codec_support( summary
->sample_type
), LSMASH_CODEC_SUPPORT_FLAG_REMUX
) )
594 lsmash_cleanup_summary( summary
);
595 WARNING_MSG( "no support to remux this stream.\n" );
598 in_track
[i
].summaries
[j
].summary
= summary
;
599 in_track
[i
].summaries
[j
].active
= 1;
601 in_track
[i
].active
= 1;
602 in_track
[i
].current_sample_number
= 1;
603 in_track
[i
].sample
= NULL
;
605 in_track
[i
].composition_delay
= 0;
606 in_track
[i
].skip_duration
= 0;
608 lsmash_destroy_children( lsmash_file_as_box( in_file
->fh
) );
612 static int parse_track_option( remuxer_t
*remuxer
)
614 track_media_option
**track
= remuxer
->track_option
;
615 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
616 for( uint32_t j
= 0; j
< remuxer
->input
[i
].file
.movie
.num_tracks
; j
++ )
618 track_media_option
*current_track_opt
= &track
[i
][j
];
619 if( current_track_opt
->raw_track_option
== NULL
)
621 if( !strchr( current_track_opt
->raw_track_option
, ':' )
622 || strchr( current_track_opt
->raw_track_option
, ':' ) == current_track_opt
->raw_track_option
)
623 return ERROR_MSG( "track number is not specified in %s\n", current_track_opt
->raw_track_option
);
624 if( strchr( current_track_opt
->raw_track_option
, ':' ) != strrchr( current_track_opt
->raw_track_option
, ':' ) )
625 return ERROR_MSG( "multiple colons inside one track option in %s.\n", current_track_opt
->raw_track_option
);
626 uint32_t track_number
= atoi( strtok( current_track_opt
->raw_track_option
, ":" ) );
627 if( track_number
== 0 )
628 return ERROR_MSG( "%s is an invalid track number.\n", strtok( current_track_opt
->raw_track_option
, ":" ) );
629 if( track_number
> remuxer
->input
[i
].file
.movie
.num_tracks
)
630 return ERROR_MSG( "%d is an invalid track number.\n", track_number
);
632 while( (track_option
= strtok( NULL
, "," )) != NULL
)
634 if( strchr( track_option
, '=' ) != strrchr( track_option
, '=' ) )
635 return ERROR_MSG( "multiple equal signs inside one track option in %s\n", track_option
);
636 current_track_opt
= &track
[i
][track_number
- 1];
637 if( strstr( track_option
, "remove" ) )
639 current_track_opt
->remove
= 1;
640 /* No need to parse track options for this track anymore. */
643 else if( strstr( track_option
, "disable" ) )
644 current_track_opt
->disable
= 1;
645 else if( strstr( track_option
, "alternate-group=" ) )
647 char *track_parameter
= strchr( track_option
, '=' ) + 1;
648 current_track_opt
->alternate_group
= atoi( track_parameter
);
650 else if( strstr( track_option
, "language=" ) )
652 char *track_parameter
= strchr( track_option
, '=' ) + 1;
653 current_track_opt
->ISO_language
= lsmash_pack_iso_language( track_parameter
);
655 else if( strstr( track_option
, "handler=" ) )
657 char *track_parameter
= strchr( track_option
, '=' ) + 1;
658 current_track_opt
->handler_name
= track_parameter
;
660 else if( strstr( track_option
, "safe-seek=" ) )
662 char *track_parameter
= strchr( track_option
, '=' ) + 1;
663 current_track_opt
->seek
= atoi( track_parameter
);
664 current_track_opt
->consider_rap
= 1;
666 else if( strstr( track_option
, "seek=" ) )
668 char *track_parameter
= strchr( track_option
, '=' ) + 1;
669 current_track_opt
->seek
= atoi( track_parameter
);
672 return ERROR_MSG( "unknown track option %s\n", track_option
);
678 static int parse_cli_option( int argc
, char **argv
, remuxer_t
*remuxer
)
680 #define FAILED_PARSE_CLI_OPTION( ... ) \
683 lsmash_free( input_file_option ); \
684 return ERROR_MSG( __VA_ARGS__ ); \
687 input_t
*input
= remuxer
->input
;
688 track_media_option
**track_option
= remuxer
->track_option
;
689 file_option
*input_file_option
= lsmash_malloc( remuxer
->num_input
* sizeof(file_option
) );
690 if( !input_file_option
)
691 return ERROR_MSG( "failed to allocate per-file option.\n" );
692 int input_movie_number
= 0;
693 for( int i
= 1; i
< argc
; i
++ )
695 /* Get input movies. */
696 if( !strcasecmp( argv
[i
], "-i" ) || !strcasecmp( argv
[i
], "--input" ) ) /* input file */
699 FAILED_PARSE_CLI_OPTION( "-i requires an argument.\n" );
700 input_file_option
[input_movie_number
].num_track_delimiter
= 0;
703 input_file_option
[input_movie_number
].num_track_delimiter
+= (*p
++ == '?');
704 if( get_movie( &input
[input_movie_number
], strtok( argv
[i
], "?" ) ) )
705 FAILED_PARSE_CLI_OPTION( "failed to get input movie.\n" );
706 uint32_t num_tracks
= input
[input_movie_number
].file
.movie
.num_tracks
;
707 track_option
[input_movie_number
] = lsmash_malloc_zero( num_tracks
* sizeof(track_media_option
) );
708 if( !track_option
[input_movie_number
] )
709 FAILED_PARSE_CLI_OPTION( "couldn't allocate memory.\n" );
710 input_file_option
[input_movie_number
].whole_track_option
= strtok( NULL
, "" );
711 input
[input_movie_number
].file
.movie
.movie_ID
= input_movie_number
+ 1;
712 ++input_movie_number
;
714 /* Create output movie. */
715 else if( !strcasecmp( argv
[i
], "-o" ) || !strcasecmp( argv
[i
], "--output" ) ) /* output file */
718 FAILED_PARSE_CLI_OPTION( "-o requires an argument.\n" );
719 output_t
*output
= remuxer
->output
;
720 output
->root
= lsmash_create_root();
722 FAILED_PARSE_CLI_OPTION( "failed to create a ROOT.\n" );
723 output
->file
.name
= argv
[i
];
725 else if( !strcasecmp( argv
[i
], "--chapter" ) ) /* chapter file */
728 FAILED_PARSE_CLI_OPTION( "--chapter requires an argument.\n" );
729 remuxer
->chap_file
= argv
[i
];
731 else if( !strcasecmp( argv
[i
], "--chpl-with-bom" ) )
732 remuxer
->add_bom_to_chpl
= 1;
733 else if( !strcasecmp( argv
[i
], "--chapter-track" ) ) /* track to apply reference chapter to */
736 FAILED_PARSE_CLI_OPTION( "--chapter-track requires an argument.\n" );
737 remuxer
->chap_track
= atoi( argv
[i
] );
738 if( !remuxer
->chap_track
)
739 FAILED_PARSE_CLI_OPTION( "%s is an invalid track number.\n", argv
[i
] );
741 else if( !strcasecmp( argv
[i
], "--language" ) )
744 FAILED_PARSE_CLI_OPTION( "--chapter requires an argument.\n" );
745 remuxer
->default_language
= lsmash_pack_iso_language( argv
[i
] );
747 else if( !strcasecmp( argv
[i
], "--max-chunk-duration" ) )
750 FAILED_PARSE_CLI_OPTION( "--max-chunk-duration requires an argument.\n" );
751 remuxer
->max_chunk_duration_in_ms
= atoi( argv
[i
] );
752 if( remuxer
->max_chunk_duration_in_ms
== 0 )
753 FAILED_PARSE_CLI_OPTION( "%s is an invalid value for --max-chunk-duration.\n", argv
[i
] );
755 else if( !strcasecmp( argv
[i
], "--max-chunk-size" ) )
758 FAILED_PARSE_CLI_OPTION( "--max-chunk-size requires an argument.\n" );
759 remuxer
->max_chunk_size
= atoi( argv
[i
] );
760 if( remuxer
->max_chunk_size
== 0 )
761 FAILED_PARSE_CLI_OPTION( "%s is an invalid value for --max-chunk-size.\n", argv
[i
] );
763 else if( !strcasecmp( argv
[i
], "--fragment" ) )
766 FAILED_PARSE_CLI_OPTION( "--fragment requires an argument.\n" );
767 remuxer
->frag_base_track
= atoi( argv
[i
] );
768 if( remuxer
->frag_base_track
== 0 )
769 FAILED_PARSE_CLI_OPTION( "%s is an invalid track number.\n", argv
[i
] );
771 else if( !strcasecmp( argv
[i
], "--min-frag-duration" ) )
774 FAILED_PARSE_CLI_OPTION( "--min-frag-duration requires an argument.\n" );
775 remuxer
->min_frag_duration
= atof( argv
[i
] );
776 if( remuxer
->min_frag_duration
== 0.0 || remuxer
->min_frag_duration
== -0.0 )
777 FAILED_PARSE_CLI_OPTION( "%s is an invalid fragment duration.\n", argv
[i
] );
778 else if( remuxer
->frag_base_track
== 0 )
779 FAILED_PARSE_CLI_OPTION( "--min-frag-duration requires --fragment also be set.\n" );
781 else if( !strcasecmp( argv
[i
], "--dash" ) )
784 FAILED_PARSE_CLI_OPTION( "--dash requires an argument.\n" );
785 remuxer
->subseg_per_seg
= atoi( argv
[i
] );
788 else if( !strcasecmp( argv
[i
], "--compact-size-table" ) )
789 remuxer
->compact_size_table
= 1;
790 else if( !strcasecmp( argv
[i
], "--dry-run" ) )
791 remuxer
->dry_run
= 1;
793 FAILED_PARSE_CLI_OPTION( "unkown option found: %s\n", argv
[i
] );
795 if( !remuxer
->output
->root
)
796 FAILED_PARSE_CLI_OPTION( "output file name is not specified.\n" );
797 /* Parse track options */
798 /* Get the current track and media parameters */
799 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
800 for( uint32_t j
= 0; j
< input
[i
].file
.movie
.num_tracks
; j
++ )
802 input_track_t
*in_track
= &input
[i
].file
.movie
.track
[j
];
803 if( !in_track
->active
)
805 track_option
[i
][j
].alternate_group
= in_track
->track_param
.alternate_group
;
806 track_option
[i
][j
].ISO_language
= in_track
->media
.param
.ISO_language
;
807 track_option
[i
][j
].handler_name
= in_track
->media
.param
.media_handler_name
;
809 /* Set the default language */
810 if( remuxer
->default_language
)
811 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
812 for( uint32_t j
= 0; j
< input
[i
].file
.movie
.num_tracks
; j
++ )
813 track_option
[i
][j
].ISO_language
= remuxer
->default_language
;
814 /* Get the track and media parameters specified by users */
815 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
817 if( input_file_option
[i
].num_track_delimiter
> input
[i
].file
.movie
.num_tracks
)
818 FAILED_PARSE_CLI_OPTION( "more track options specified than the actual number of the tracks (%"PRIu32
").\n",
819 input
[i
].file
.movie
.num_tracks
);
820 if( input_file_option
[i
].num_track_delimiter
)
822 track_option
[i
][0].raw_track_option
= strtok( input_file_option
[i
].whole_track_option
, "?" );
823 for( int j
= 1; j
< input_file_option
[i
].num_track_delimiter
; j
++ )
824 track_option
[i
][j
].raw_track_option
= strtok( NULL
, "?" );
827 if( parse_track_option( remuxer
) )
828 FAILED_PARSE_CLI_OPTION( "failed to parse track options.\n" );
829 lsmash_free( input_file_option
);
831 #undef FAILED_PARSE_CLI_OPTION
834 static void replace_with_valid_brand( remuxer_t
*remuxer
)
836 static const lsmash_brand_type brand_filter_list
[] =
838 ISOM_BRAND_TYPE_3G2A
,
839 ISOM_BRAND_TYPE_3GG6
,
840 ISOM_BRAND_TYPE_3GG9
,
841 ISOM_BRAND_TYPE_3GP4
,
842 ISOM_BRAND_TYPE_3GP5
,
843 ISOM_BRAND_TYPE_3GP6
,
844 ISOM_BRAND_TYPE_3GP7
,
845 ISOM_BRAND_TYPE_3GP8
,
846 ISOM_BRAND_TYPE_3GP9
,
847 ISOM_BRAND_TYPE_3GR6
,
848 ISOM_BRAND_TYPE_3GR9
,
849 ISOM_BRAND_TYPE_M4A
,
850 ISOM_BRAND_TYPE_M4B
,
851 ISOM_BRAND_TYPE_M4V
,
852 ISOM_BRAND_TYPE_AVC1
,
853 ISOM_BRAND_TYPE_DBY1
,
854 ISOM_BRAND_TYPE_ISO2
,
855 ISOM_BRAND_TYPE_ISO3
,
856 ISOM_BRAND_TYPE_ISO4
,
857 ISOM_BRAND_TYPE_ISO5
,
858 ISOM_BRAND_TYPE_ISO6
,
859 ISOM_BRAND_TYPE_ISO7
,
860 ISOM_BRAND_TYPE_ISOM
,
861 ISOM_BRAND_TYPE_MP41
,
862 ISOM_BRAND_TYPE_MP42
,
866 input_t
*input
= remuxer
->input
;
867 /* Check the number of video and audio tracks, and the number of video
868 * and audio sample descriptions for the restrictions of 3GPP Basic Profile.
869 * - the maximum number of tracks shall be one for video (or alternatively
870 * one for scene description), one for audio and one for text
871 * - the maximum number of sample entries shall be one per track for video
872 * and audio (but unrestricted for text and scene description) */
873 uint32_t video_track_count
= 0;
874 uint32_t audio_track_count
= 0;
875 uint32_t video_num_summaries
= 0;
876 uint32_t audio_num_summaries
= 0;
877 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
879 input_movie_t
*movie
= &input
[i
].file
.movie
;
880 for( int j
= 0; j
< movie
->num_tracks
; j
++ )
882 if( movie
->track
[j
].media
.param
.handler_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
)
884 if( ++video_track_count
== 1 )
885 video_num_summaries
= movie
->track
[j
].num_summaries
;
887 else if( movie
->track
[j
].media
.param
.handler_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
)
889 if( ++audio_track_count
== 1 )
890 audio_num_summaries
= movie
->track
[j
].num_summaries
;
894 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
895 for( uint32_t j
= 0; j
<= input
[i
].file
.param
.brand_count
; j
++ )
898 uint32_t *brand
= j
? &input
[i
].file
.param
.brands
[j
- 1] : &input
[i
].file
.param
.major_brand
;
899 uint32_t *version
= j
? NULL
: &input
[i
].file
.param
.minor_version
;
900 for( int k
= 0; brand_filter_list
[k
]; k
++ )
902 if( *brand
== brand_filter_list
[k
] )
904 if( ((*brand
>> 24) & 0xFF) == '3'
905 && ((*brand
>> 16) & 0xFF) == 'g'
906 && (((*brand
>> 8) & 0xFF) == 'p' || ((*brand
>> 8) & 0xFF) == 'r') )
908 if( remuxer
->frag_base_track
== 0 /* Movie fragments are not allowed in '3gp4' and '3gp5'. */
909 && video_track_count
<= 1 && audio_track_count
<= 1
910 && video_num_summaries
<= 1 && audio_num_summaries
<= 1 )
912 /* Replace with the General Profile for maximum compatibility. */
913 if( (*brand
& 0xFF) < '6' )
915 /* 3GPP version 6.7.0 General Profile */
916 *brand
= ISOM_BRAND_TYPE_3GG6
;
918 *version
= 0x00000700;
921 *brand
= LSMASH_4CC( '3', 'g', 'g', *brand
& 0xFF );
924 && (*brand
== ISOM_BRAND_TYPE_AVC1
925 || (((*brand
>> 24) & 0xFF) == 'i'
926 && ((*brand
>> 16) & 0xFF) == 's'
927 && ((*brand
>> 8) & 0xFF) == 'o'
928 && ((*brand
& 0xFF) == 'm' || (*brand
& 0xFF) < '6'))) )
929 *brand
= ISOM_BRAND_TYPE_ISO6
;
936 /* Replace with the 'mp42' brand. */
937 *brand
= ISOM_BRAND_TYPE_MP42
;
944 static int pick_most_used_major_brand( input_t
*input
, output_file_t
*out_file
, int num_input
)
946 void *heap
= lsmash_malloc( num_input
* (sizeof(lsmash_brand_type
) + 2 * sizeof(uint32_t)) );
948 return ERROR_MSG( "failed to allocate memory.\n" );
949 lsmash_brand_type
*major_brand
= (lsmash_brand_type
*)heap
;
950 uint32_t *minor_version
= (uint32_t *)((lsmash_brand_type
*)heap
+ num_input
);
951 uint32_t *major_brand_count
= minor_version
+ num_input
;
952 uint32_t num_major_brand
= 0;
953 for( int i
= 0; i
< num_input
; i
++ )
955 major_brand
[num_major_brand
] = input
[i
].file
.param
.major_brand
;
956 minor_version
[num_major_brand
] = input
[i
].file
.param
.minor_version
;
957 major_brand_count
[num_major_brand
] = 0;
958 for( int j
= 0; j
< num_input
; j
++ )
959 if( (major_brand
[num_major_brand
] == input
[j
].file
.param
.major_brand
)
960 && (minor_version
[num_major_brand
] == input
[j
].file
.param
.minor_version
) )
963 ++major_brand_count
[num_major_brand
];
966 /* This major_brand already exists. Skip this. */
967 major_brand_count
[num_major_brand
] = 0;
974 uint32_t most_used_count
= 0;
975 for( uint32_t i
= 0; i
< num_major_brand
; i
++ )
976 if( major_brand_count
[i
] > most_used_count
)
978 most_used_count
= major_brand_count
[i
];
979 out_file
->param
.major_brand
= major_brand
[i
];
980 out_file
->param
.minor_version
= minor_version
[i
];
986 static int set_movie_parameters( remuxer_t
*remuxer
)
988 int num_input
= remuxer
->num_input
;
989 input_t
*input
= remuxer
->input
;
990 output_t
*output
= remuxer
->output
;
991 output_file_t
*out_file
= &output
->file
;
992 if( remuxer
->frag_base_track
)
993 out_file
->param
.mode
|= LSMASH_FILE_MODE_FRAGMENTED
;
994 int self_containd_segment
= (remuxer
->dash
&& remuxer
->subseg_per_seg
== 0);
997 if( remuxer
->frag_base_track
)
999 if( self_containd_segment
)
1000 out_file
->param
.mode
|= LSMASH_FILE_MODE_INDEX
;
1002 out_file
->param
.mode
&= ~LSMASH_FILE_MODE_MEDIA
;
1003 out_file
->param
.mode
|= LSMASH_FILE_MODE_SEGMENT
;
1006 WARNING_MSG( "--dash requires --fragment.\n" );
1008 out_file
->param
.max_chunk_duration
= remuxer
->max_chunk_duration_in_ms
* 1e-3;
1009 out_file
->param
.max_chunk_size
= remuxer
->max_chunk_size
;
1010 replace_with_valid_brand( remuxer
);
1011 if( self_containd_segment
)
1013 out_file
->param
.major_brand
= ISOM_BRAND_TYPE_DASH
;
1014 out_file
->param
.minor_version
= 0;
1016 else if( pick_most_used_major_brand( input
, out_file
, num_input
) < 0 )
1017 return ERROR_MSG( "failed to pick the most used major brand.\n" );
1018 /* Deduplicate compatible brands. */
1019 uint32_t num_input_brands
= num_input
+ (self_containd_segment
? 1 : 0);
1020 for( int i
= 0; i
< num_input
; i
++ )
1021 num_input_brands
+= input
[i
].file
.param
.brand_count
;
1022 lsmash_brand_type
*input_brands
= lsmash_malloc( num_input_brands
* sizeof(lsmash_brand_type
) );
1024 return ERROR_MSG( "failed to allocate brands for an input file.\n" );
1025 num_input_brands
= 0;
1026 if( self_containd_segment
)
1027 input_brands
[num_input_brands
++] = ISOM_BRAND_TYPE_DASH
;
1028 for( int i
= 0; i
< num_input
; i
++ )
1030 input_brands
[num_input_brands
++] = input
[i
].file
.param
.major_brand
;
1031 for( uint32_t j
= 0; j
< input
[i
].file
.param
.brand_count
; j
++ )
1032 if( input
[i
].file
.param
.brands
[j
] )
1033 input_brands
[num_input_brands
++] = input
[i
].file
.param
.brands
[j
];
1035 lsmash_brand_type
*output_brands
= lsmash_malloc_zero( num_input_brands
* sizeof(lsmash_brand_type
) );
1036 if( !output_brands
)
1038 lsmash_free( input_brands
);
1039 return ERROR_MSG( "failed to allocate brands for an output file.\n" );
1041 uint32_t num_output_brands
= 0;
1042 for( uint32_t i
= 0; i
< num_input_brands
; i
++ )
1044 output_brands
[num_output_brands
] = input_brands
[i
];
1045 for( uint32_t j
= 0; j
< num_output_brands
; j
++ )
1046 if( output_brands
[num_output_brands
] == output_brands
[j
] )
1048 /* This brand already exists. Skip this. */
1049 --num_output_brands
;
1052 ++num_output_brands
;
1054 lsmash_free( input_brands
);
1055 out_file
->param
.brand_count
= num_output_brands
;
1056 out_file
->param
.brands
= output_brands
;
1057 /* Set up a file. */
1058 out_file
->fh
= lsmash_set_file( output
->root
, &out_file
->param
);
1060 return ERROR_MSG( "failed to add an output file into a ROOT.\n" );
1061 out_file
->seg_param
= out_file
->param
;
1062 /* Check whether a reference chapter track is allowed or not. */
1063 if( remuxer
->chap_file
)
1064 for( uint32_t i
= 0; i
< out_file
->param
.brand_count
; i
++ )
1066 uint32_t brand
= out_file
->param
.brands
[i
];
1067 /* According to the restrictions of 3GPP Basic Profile,
1068 * - there shall be no references between tracks, e.g., a scene description track
1069 * shall not refer to a media track since all tracks are on equal footing and
1070 * played in parallel by a conforming player.
1071 * Therefore, the referenced chapter track is forbidden to use for 3GPP Basic Profile. */
1072 if( ((brand
>> 24) & 0xFF) == '3'
1073 && ((brand
>> 16) & 0xFF) == 'g'
1074 && ((brand
>> 8) & 0xFF) == 'p' )
1076 /* QuickTime file and iTunes MP4 file can contain the referenced chapter track. */
1077 if( brand
== ISOM_BRAND_TYPE_QT
|| brand
== ISOM_BRAND_TYPE_M4A
1078 || brand
== ISOM_BRAND_TYPE_M4B
|| brand
== ISOM_BRAND_TYPE_M4P
1079 || brand
== ISOM_BRAND_TYPE_M4V
)
1081 remuxer
->ref_chap_available
= 1;
1085 /* Set the movie timescale in order to match the media timescale if only one track is there. */
1086 lsmash_initialize_movie_parameters( &out_file
->movie
.param
);
1087 if( out_file
->movie
.num_tracks
== 1 )
1088 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
1090 input_movie_t
*in_movie
= &input
[i
].file
.movie
;
1091 for( uint32_t j
= 0; j
< in_movie
->num_tracks
; j
++ )
1092 if( in_movie
->track
[j
].active
)
1094 out_file
->movie
.param
.timescale
= in_movie
->track
[j
].media
.param
.timescale
;
1098 return lsmash_set_movie_parameters( output
->root
, &out_file
->movie
.param
);
1101 static void set_itunes_metadata( output_t
*output
, input_t
*input
, int num_input
)
1103 for( int i
= 0; i
< num_input
; i
++ )
1104 for( uint32_t j
= 0; j
< input
[i
].file
.movie
.num_itunes_metadata
; j
++ )
1105 if( lsmash_set_itunes_metadata( output
->root
, input
[i
].file
.movie
.itunes_metadata
[j
] ) )
1107 WARNING_MSG( "failed to set an iTunes metadata.\n" );
1112 static int set_starting_point( input_t
*input
, input_track_t
*in_track
, uint32_t seek_point
, int consider_rap
)
1114 if( seek_point
== 0 )
1116 uint32_t rap_number
;
1117 if( lsmash_get_closest_random_accessible_point_from_media_timeline( input
->root
, in_track
->track_ID
, 1, &rap_number
) )
1120 return ERROR_MSG( "failed to get the first random accessible point.\n" );
1123 WARNING_MSG( "no random access point!\n" );
1124 /* Set number of the first sample to be muxed. */
1125 in_track
->current_sample_number
= seek_point
;
1129 /* Get composition delay. */
1133 if( lsmash_get_dts_from_media_timeline( input
->root
, in_track
->track_ID
, rap_number
, &rap_dts
) )
1134 return ERROR_MSG( "failed to get CTS of the first random accessible sample of seek point.\n" );
1135 if( lsmash_get_cts_from_media_timeline( input
->root
, in_track
->track_ID
, rap_number
, &rap_cts
) )
1136 return ERROR_MSG( "failed to get CTS of the first random accessible sample of seek point.\n" );
1137 if( lsmash_get_composition_to_decode_shift_from_media_timeline( input
->root
, in_track
->track_ID
, &ctd_shift
) )
1138 return ERROR_MSG( "failed to get composition to decode timeline shfit.\n" );
1139 in_track
->composition_delay
= rap_cts
- rap_dts
+ ctd_shift
;
1140 /* Check if starting point is random accessible. */
1141 if( lsmash_get_closest_random_accessible_point_from_media_timeline( input
->root
, in_track
->track_ID
, seek_point
, &rap_number
) )
1142 return ERROR_MSG( "failed to get a random accessible point.\n" );
1143 if( rap_number
!= seek_point
)
1145 WARNING_MSG( "starting point you specified is not a random accessible point.\n" );
1148 /* Get duration that should be skipped. */
1149 if( lsmash_get_cts_from_media_timeline( input
->root
, in_track
->track_ID
, rap_number
, &rap_cts
) )
1150 return ERROR_MSG( "failed to get CTS of the closest and past random accessible sample of starting point.\n" );
1152 if( lsmash_get_cts_from_media_timeline( input
->root
, in_track
->track_ID
, seek_point
, &seek_cts
) )
1153 return ERROR_MSG( "failed to get CTS of starting point.\n" );
1154 if( rap_cts
< seek_cts
)
1155 in_track
->skip_duration
= seek_cts
- rap_cts
;
1158 /* Set number of the first sample to be muxed. */
1159 in_track
->current_sample_number
= consider_rap
? rap_number
: seek_point
;
1163 static void exclude_invalid_output_track( output_t
*output
, output_track_t
*out_track
,
1164 input_movie_t
*in_movie
, input_track_t
*in_track
,
1165 const char *message
, ... )
1168 eprintf( "[Warning] in %"PRIu32
"/%"PRIu32
" -> out %"PRIu32
": ", in_movie
->movie_ID
, in_track
->track_ID
, out_track
->track_ID
);
1170 va_start( args
, message
);
1171 vfprintf( stderr
, message
, args
);
1173 lsmash_delete_track( output
->root
, out_track
->track_ID
);
1174 -- output
->file
.movie
.num_tracks
;
1175 in_track
->active
= 0;
1178 static int prepare_output( remuxer_t
*remuxer
)
1180 input_t
*input
= remuxer
->input
;
1181 output_t
*output
= remuxer
->output
;
1182 output_movie_t
*out_movie
= &output
->file
.movie
;
1183 /* Try to open an output file. */
1184 if( remuxer
->dry_run
)
1186 output
->file
.open
= dry_open_file
;
1187 output
->file
.close
= dry_close_file
;
1191 output
->file
.open
= lsmash_open_file
;
1192 output
->file
.close
= lsmash_close_file
;
1194 if( output
->file
.open( output
->file
.name
, 0, &output
->file
.param
) < 0 )
1195 return ERROR_MSG( "failed to open an output file.\n" );
1196 /* Count the number of output tracks. */
1197 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
1198 out_movie
->num_tracks
+= input
[i
].file
.movie
.num_tracks
;
1199 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
1201 input_movie_t
*in_movie
= &input
[i
].file
.movie
;
1202 for( uint32_t j
= 0; j
< in_movie
->num_tracks
; j
++ )
1204 /* Don't remux tracks specified as 'remove' by a user. */
1205 if( remuxer
->track_option
[i
][j
].remove
)
1206 in_movie
->track
[j
].active
= 0;
1207 if( !in_movie
->track
[j
].active
)
1208 -- out_movie
->num_tracks
;
1211 if( set_movie_parameters( remuxer
) < 0 )
1212 return ERROR_MSG( "failed to set output movie parameters.\n" );
1213 set_itunes_metadata( output
, input
, remuxer
->num_input
);
1214 /* Allocate output tracks. */
1215 out_movie
->track
= lsmash_malloc( out_movie
->num_tracks
* sizeof(output_track_t
) );
1216 if( !out_movie
->track
)
1217 return ERROR_MSG( "failed to alloc output tracks.\n" );
1218 out_movie
->current_track_number
= 1;
1219 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
1221 input_movie_t
*in_movie
= &input
[i
].file
.movie
;
1222 for( uint32_t j
= 0; j
< in_movie
->num_tracks
; j
++ )
1224 track_media_option
*current_track_opt
= &remuxer
->track_option
[i
][j
];
1225 input_track_t
*in_track
= &in_movie
->track
[j
];
1226 if( !in_track
->active
)
1228 output_track_t
*out_track
= &out_movie
->track
[ out_movie
->current_track_number
- 1 ];
1229 out_track
->summary_remap
= lsmash_malloc_zero( in_track
->num_summaries
* sizeof(uint32_t) );
1230 if( !out_track
->summary_remap
)
1231 return ERROR_MSG( "failed to create summary mapping for a track.\n" );
1232 out_track
->track_ID
= lsmash_create_track( output
->root
, in_track
->media
.param
.handler_type
);
1233 if( !out_track
->track_ID
)
1234 return ERROR_MSG( "failed to create a track.\n" );
1235 /* Copy track and media parameters except for track_ID. */
1236 out_track
->track_param
= in_track
->track_param
;
1237 out_track
->media_param
= in_track
->media
.param
;
1238 /* Set track and media parameters specified by users */
1239 out_track
->track_param
.alternate_group
= current_track_opt
->alternate_group
;
1240 out_track
->media_param
.ISO_language
= current_track_opt
->ISO_language
;
1241 out_track
->media_param
.media_handler_name
= current_track_opt
->handler_name
;
1242 out_track
->media_param
.compact_sample_size_table
= remuxer
->compact_size_table
;
1243 out_track
->track_param
.track_ID
= out_track
->track_ID
;
1244 if( current_track_opt
->disable
)
1245 out_track
->track_param
.mode
&= ~ISOM_TRACK_ENABLED
;
1246 if( lsmash_set_track_parameters( output
->root
, out_track
->track_ID
, &out_track
->track_param
) < 0 )
1248 exclude_invalid_output_track( output
, out_track
, in_movie
, in_track
, "failed to set track parameters.\n" );
1251 if( lsmash_set_media_parameters( output
->root
, out_track
->track_ID
, &out_track
->media_param
) < 0 )
1253 exclude_invalid_output_track( output
, out_track
, in_movie
, in_track
, "failed to set media parameters.\n" );
1256 lsmash_data_reference_t data_ref
= { .index
= 1, .location
= NULL
};
1257 if( lsmash_create_data_reference( output
->root
, out_track
->track_ID
, &data_ref
, output
->file
.fh
) < 0 )
1258 return ERROR_MSG( "failed to create a data reference for output movie.\n" );
1259 uint32_t valid_summary_count
= 0;
1260 for( uint32_t k
= 0; k
< in_track
->num_summaries
; k
++ )
1262 if( !in_track
->summaries
[k
].active
)
1264 out_track
->summary_remap
[k
] = 0;
1267 lsmash_summary_t
*summary
= in_track
->summaries
[k
].summary
;
1268 summary
->data_ref_index
= 1;
1269 if( lsmash_add_sample_entry( output
->root
, out_track
->track_ID
, summary
) == 0 )
1271 WARNING_MSG( "failed to append a summary.\n" );
1272 lsmash_cleanup_summary( summary
);
1273 in_track
->summaries
[k
].summary
= NULL
;
1274 in_track
->summaries
[k
].active
= 0;
1275 out_track
->summary_remap
[k
] = 0;
1278 out_track
->summary_remap
[k
] = ++valid_summary_count
;
1280 if( valid_summary_count
== 0 )
1282 exclude_invalid_output_track( output
, out_track
, in_movie
, in_track
, "failed to append all summaries.\n" );
1285 out_track
->last_sample_delta
= in_track
->last_sample_delta
;
1286 if( set_starting_point( input
, in_track
, current_track_opt
->seek
, current_track_opt
->consider_rap
) < 0 )
1288 exclude_invalid_output_track( output
, out_track
, in_movie
, in_track
, "failed to set starting point.\n" );
1291 out_track
->current_sample_number
= 1;
1292 out_track
->skip_dt_interval
= 0;
1293 out_track
->last_sample_dts
= 0;
1294 ++ out_movie
->current_track_number
;
1297 if( out_movie
->num_tracks
== 0 )
1298 return ERROR_MSG( "failed to create the output movie.\n" );
1299 out_movie
->current_track_number
= 1;
1300 output
->current_seg_number
= 1;
1304 static void set_reference_chapter_track( remuxer_t
*remuxer
)
1306 if( remuxer
->ref_chap_available
)
1307 lsmash_create_reference_chapter_track( remuxer
->output
->root
, remuxer
->chap_track
, remuxer
->chap_file
);
1310 static int flush_movie_fragment( remuxer_t
*remuxer
)
1312 input_t
*inputs
= remuxer
->input
;
1313 output_t
*output
= remuxer
->output
;
1314 output_movie_t
*out_movie
= &output
->file
.movie
;
1315 uint32_t out_current_track_number
= 1;
1316 for( uint32_t i
= 1; i
<= remuxer
->num_input
; i
++ )
1318 input_t
*in
= &inputs
[i
- 1];
1319 input_movie_t
*in_movie
= &in
->file
.movie
;
1320 for( uint32_t j
= 1; j
<= in_movie
->num_tracks
; j
++ )
1322 input_track_t
*in_track
= &in_movie
->track
[j
- 1];
1323 if( !in_track
->active
)
1325 output_track_t
*out_track
= &out_movie
->track
[out_current_track_number
- 1];
1326 if( !in_track
->reach_end_of_media_timeline
)
1328 lsmash_sample_t sample
;
1329 if( lsmash_get_sample_info_from_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
, &sample
) < 0 )
1330 return ERROR_MSG( "failed to get the information of the next sample.\n" );
1331 uint64_t sample_dts
= sample
.dts
- out_track
->skip_dt_interval
;
1332 if( lsmash_flush_pooled_samples( output
->root
, out_track
->track_ID
, sample_dts
- out_track
->last_sample_dts
) < 0 )
1333 return ERROR_MSG( "failed to flush the rest of samples in a fragment.\n" );
1336 if( lsmash_flush_pooled_samples( output
->root
, out_track
->track_ID
, out_track
->last_sample_delta
) < 0 )
1337 return ERROR_MSG( "failed to flush the rest of samples in a fragment.\n" );
1338 if( ++out_current_track_number
> out_movie
->num_tracks
)
1345 static int moov_to_front_callback( void *param
, uint64_t written_movie_size
, uint64_t total_movie_size
)
1347 static uint32_t progress_pos
= 0;
1348 if ( (written_movie_size
>> 24) <= progress_pos
)
1351 eprintf( "Finalizing: [%5.2lf%%]\r", ((double)written_movie_size
/ total_movie_size
) * 100.0 );
1352 /* Print, per 16 megabytes */
1353 progress_pos
= written_movie_size
>> 24;
1357 static lsmash_adhoc_remux_t moov_to_front
=
1359 .func
= moov_to_front_callback
,
1360 .buffer_size
= 4 * 1024 * 1024, /* 4MiB */
1364 static int open_media_segment( output_t
*output
, lsmash_file_parameters_t
*seg_param
)
1366 /* Open a media segment file.
1367 * Each file is named as follows.
1373 * N is the number of segment files excluding the initialization segment file.
1375 output_file_t
*out_file
= &output
->file
;
1376 int out_file_name_length
= strlen( out_file
->name
);
1377 const char *end
= &out_file
->name
[ out_file_name_length
];
1378 const char *p
= end
;
1379 while( p
>= out_file
->name
&& *p
!= '.' && *p
!= '/' && *p
!= '\\' )
1381 if( p
< out_file
->name
)
1385 int suffix_length
= 1;
1386 for( uint32_t i
= output
->current_seg_number
; i
; i
/= 10 )
1388 int seg_name_length
= out_file_name_length
+ suffix_length
;
1389 int suffixless_length
= p
- out_file
->name
;
1390 char *seg_name
= lsmash_malloc( (seg_name_length
+ 1) * sizeof(char) );
1392 return ERROR_MSG( "failed to allocate to store a segment filename.\n" );
1393 seg_name
[ seg_name_length
] = '\0';
1394 memcpy( seg_name
, out_file
->name
, suffixless_length
);
1395 sprintf( seg_name
+ suffixless_length
, "_%"PRIu32
, output
->current_seg_number
);
1397 memcpy( seg_name
+ suffixless_length
+ suffix_length
, p
, end
- p
);
1398 int ret
= out_file
->open( seg_name
, 0, seg_param
);
1400 eprintf( "[Segment] out: %s\n", seg_name
);
1401 lsmash_free( seg_name
);
1405 static int switch_segment( remuxer_t
*remuxer
)
1407 output_t
*output
= remuxer
->output
;
1408 output_file_t
*out_file
= &output
->file
;
1409 lsmash_file_parameters_t seg_param
= { 0 };
1410 if( open_media_segment( output
, &seg_param
) < 0 )
1411 return ERROR_MSG( "failed to open an output file for segmentation.\n" );
1412 /* Set up the media segment file.
1413 * Copy the parameters of the previous segment if the previous is not the initialization segment. */
1414 if( out_file
->seg_param
.mode
& LSMASH_FILE_MODE_INITIALIZATION
)
1416 uint32_t brand_count
= out_file
->param
.brand_count
+ 2;
1417 lsmash_brand_type
*brands
= lsmash_malloc_zero( brand_count
* sizeof(lsmash_brand_type
) );
1419 return ERROR_MSG( "failed to allocate brands for an output segment file.\n" );
1420 brands
[0] = ISOM_BRAND_TYPE_MSDH
;
1421 brands
[1] = ISOM_BRAND_TYPE_MSIX
;
1422 for( uint32_t i
= 0; i
< out_file
->param
.brand_count
; i
++ )
1423 brands
[i
+ 2] = out_file
->param
.brands
[i
];
1424 seg_param
.major_brand
= ISOM_BRAND_TYPE_MSDH
;
1425 seg_param
.brand_count
= brand_count
;
1426 seg_param
.brands
= brands
;
1427 seg_param
.mode
= LSMASH_FILE_MODE_WRITE
| LSMASH_FILE_MODE_FRAGMENTED
1428 | LSMASH_FILE_MODE_BOX
| LSMASH_FILE_MODE_MEDIA
1429 | LSMASH_FILE_MODE_INDEX
| LSMASH_FILE_MODE_SEGMENT
;
1433 void *opaque
= seg_param
.opaque
;
1434 seg_param
= out_file
->seg_param
;
1435 seg_param
.opaque
= opaque
;
1437 lsmash_file_t
*segment
= lsmash_set_file( output
->root
, &seg_param
);
1439 return ERROR_MSG( "failed to add an output segment file into a ROOT.\n" );
1440 /* Switch to the next segment.
1441 * After switching, close the previous segment if the previous is not the initialization segment. */
1442 if( lsmash_switch_media_segment( output
->root
, segment
, &moov_to_front
) < 0 )
1443 return ERROR_MSG( "failed to switch to the next segment.\n" );
1444 if( !(out_file
->seg_param
.mode
& LSMASH_FILE_MODE_INITIALIZATION
) )
1445 return out_file
->close( &out_file
->seg_param
);
1446 out_file
->seg_param
= seg_param
;
1450 static int handle_segmentation( remuxer_t
*remuxer
)
1452 if( remuxer
->subseg_per_seg
== 0 )
1454 output_t
*output
= remuxer
->output
;
1455 if( remuxer
->subseg_per_seg
== output
->file
.current_subseg_number
1456 || output
->current_seg_number
== 1 )
1458 if( switch_segment( remuxer
) < 0 )
1460 ERROR_MSG( "failed to switch to a segment.\n" );
1463 output
->file
.current_subseg_number
= 1;
1464 ++ output
->current_seg_number
;
1467 ++ output
->file
.current_subseg_number
;
1471 static void adapt_description_index( output_track_t
*out_track
, input_track_t
*in_track
, lsmash_sample_t
*sample
)
1473 sample
->index
= sample
->index
> in_track
->num_summaries
? in_track
->num_summaries
1474 : sample
->index
== 0 ? 1
1476 sample
->index
= out_track
->summary_remap
[ sample
->index
- 1 ];
1477 if( in_track
->current_sample_index
== 0 )
1478 in_track
->current_sample_index
= sample
->index
;
1481 static void adjust_timestamp( output_track_t
*out_track
, lsmash_sample_t
*sample
)
1483 /* The first DTS must be 0. */
1484 if( out_track
->current_sample_number
== 1 )
1485 out_track
->skip_dt_interval
= sample
->dts
;
1486 if( out_track
->skip_dt_interval
)
1488 sample
->dts
-= out_track
->skip_dt_interval
;
1489 sample
->cts
-= out_track
->skip_dt_interval
;
1493 static int do_remux( remuxer_t
*remuxer
)
1495 #define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b))
1496 input_t
*inputs
= remuxer
->input
;
1497 output_t
*output
= remuxer
->output
;
1498 output_movie_t
*out_movie
= &output
->file
.movie
;
1499 set_reference_chapter_track( remuxer
);
1500 double largest_dts
= 0; /* in seconds */
1501 double frag_base_dts
= 0; /* in seconds */
1502 uint32_t input_movie_number
= 1;
1503 uint32_t num_consecutive_sample_skip
= 0;
1504 uint32_t num_active_input_tracks
= out_movie
->num_tracks
;
1505 uint64_t total_media_size
= 0;
1506 uint32_t progress_pos
= 0;
1507 uint8_t pending_flush_fragments
= (remuxer
->frag_base_track
!= 0); /* For non-fragmented movie, always set to 0. */
1510 input_t
*in
= &inputs
[input_movie_number
- 1];
1511 input_movie_t
*in_movie
= &in
->file
.movie
;
1512 input_track_t
*in_track
= &in_movie
->track
[ in_movie
->current_track_number
- 1 ];
1513 if( !in_track
->active
)
1515 /* Move the next track. */
1516 if( ++ in_movie
->current_track_number
> in_movie
->num_tracks
)
1518 /* Move the next input movie. */
1519 in_movie
->current_track_number
= 1;
1520 ++input_movie_number
;
1522 if( input_movie_number
> remuxer
->num_input
)
1523 input_movie_number
= 1; /* Back the first input movie. */
1526 /* Try append a sample in an input track where we didn't reach the end of media timeline. */
1527 if( !in_track
->reach_end_of_media_timeline
)
1529 lsmash_sample_t
*sample
= in_track
->sample
;
1530 /* Get a new sample data if the track doesn't hold any one. */
1533 sample
= lsmash_get_sample_from_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
);
1536 output_track_t
*out_track
= &out_movie
->track
[ out_movie
->current_track_number
- 1 ];
1537 adapt_description_index( out_track
, in_track
, sample
);
1538 adjust_timestamp( out_track
, sample
);
1539 in_track
->sample
= sample
;
1540 in_track
->dts
= (double)sample
->dts
/ in_track
->media
.param
.timescale
;
1544 if( lsmash_check_sample_existence_in_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
) )
1546 ERROR_MSG( "failed to get a sample.\n" );
1549 lsmash_sample_t sample_info
= { 0 };
1550 if( lsmash_get_sample_info_from_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
, &sample_info
) < 0 )
1552 /* No more appendable samples in this track. */
1553 in_track
->sample
= NULL
;
1554 in_track
->reach_end_of_media_timeline
= 1;
1555 if( --num_active_input_tracks
== 0 )
1556 break; /* end of muxing */
1560 ERROR_MSG( "failed to get a sample.\n" );
1567 /* Flushing the active movie fragment is pending until random accessible point sample within all active tracks are ready. */
1568 if( remuxer
->frag_base_track
)
1570 if( pending_flush_fragments
== 0 )
1572 int over_duration
= 1;
1573 if( remuxer
->min_frag_duration
!= 0.0 )
1575 lsmash_sample_t info
;
1576 if( lsmash_get_sample_info_from_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
+ 1, &info
) >= 0 )
1577 over_duration
= ((double)info
.dts
/ in_track
->media
.param
.timescale
) - frag_base_dts
>= remuxer
->min_frag_duration
;
1579 if( remuxer
->frag_base_track
== out_movie
->current_track_number
1580 && sample
->prop
.ra_flags
!= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
1583 pending_flush_fragments
= 1;
1584 frag_base_dts
= in_track
->dts
;
1587 else if( num_consecutive_sample_skip
== num_active_input_tracks
|| total_media_size
== 0 )
1589 if( flush_movie_fragment( remuxer
) < 0 )
1591 ERROR_MSG( "failed to flush a movie fragment.\n" );
1594 if( handle_segmentation( remuxer
) < 0 )
1596 if( lsmash_create_fragment_movie( output
->root
) < 0 )
1598 ERROR_MSG( "failed to create a movie fragment.\n" );
1601 pending_flush_fragments
= 0;
1604 /* Append a sample if meeting a condition. */
1606 int need_new_fragment
= (remuxer
->frag_base_track
&& sample
->index
!= in_track
->current_sample_index
);
1607 if( pending_flush_fragments
== 0 )
1608 append
= (in_track
->dts
<= largest_dts
|| num_consecutive_sample_skip
== num_active_input_tracks
) && !need_new_fragment
;
1609 else if( remuxer
->frag_base_track
!= out_movie
->current_track_number
&& !need_new_fragment
)
1611 /* Wait as much as possible both to make the last sample within each track fragment close to the DTS of
1612 * the first sample within the track fragment corresponding to the base track within the next movie
1613 * fragment and to make all the track fragments within the next movie fragment start with RAP. */
1614 if( sample
->prop
.ra_flags
== ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
1618 /* Check the DTS and random accessibilities of the next sample. */
1619 lsmash_sample_t info
;
1620 if( lsmash_get_sample_info_from_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
+ 1, &info
) < 0 )
1623 append
= (info
.prop
.ra_flags
!= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
1624 && ((double)info
.dts
/ in_track
->media
.param
.timescale
<= frag_base_dts
);
1631 output_track_t
*out_track
= &out_movie
->track
[ out_movie
->current_track_number
- 1 ];
1632 uint64_t sample_size
= sample
->length
; /* sample might be deleted internally after appending. */
1633 uint64_t last_sample_dts
= sample
->dts
; /* same as above */
1634 uint32_t sample_index
= sample
->index
; /* same as above */
1635 /* Append a sample into output movie. */
1636 if( lsmash_append_sample( output
->root
, out_track
->track_ID
, sample
) < 0 )
1638 lsmash_delete_sample( sample
);
1639 return ERROR_MSG( "failed to append a sample.\n" );
1641 largest_dts
= LSMASH_MAX( largest_dts
, in_track
->dts
);
1642 in_track
->sample
= NULL
;
1643 in_track
->current_sample_number
+= 1;
1644 in_track
->current_sample_index
= sample_index
;
1645 out_track
->current_sample_number
+= 1;
1646 out_track
->last_sample_dts
= last_sample_dts
;
1647 num_consecutive_sample_skip
= 0;
1648 total_media_size
+= sample_size
;
1649 /* Print, per 4 megabytes, total size of imported media. */
1650 if( (total_media_size
>> 22) > progress_pos
)
1652 progress_pos
= total_media_size
>> 22;
1653 eprintf( "Importing: %"PRIu64
" bytes\r", total_media_size
);
1658 lsmash_delete_sample( sample
);
1659 in_track
->sample
= NULL
;
1660 in_track
->current_sample_number
+= 1;
1664 ++num_consecutive_sample_skip
; /* Skip appendig sample. */
1667 /* Move the next track. */
1668 if( ++ in_movie
->current_track_number
> in_movie
->num_tracks
)
1670 /* Move the next input movie. */
1671 in_movie
->current_track_number
= 1;
1672 ++input_movie_number
;
1674 if( input_movie_number
> remuxer
->num_input
)
1675 input_movie_number
= 1; /* Back the first input movie. */
1676 if( ++ out_movie
->current_track_number
> out_movie
->num_tracks
)
1677 out_movie
->current_track_number
= 1; /* Back the first track in the output movie. */
1679 for( uint32_t i
= 0; i
< out_movie
->num_tracks
; i
++ )
1680 if( lsmash_flush_pooled_samples( output
->root
, out_movie
->track
[i
].track_ID
, out_movie
->track
[i
].last_sample_delta
) )
1681 return ERROR_MSG( "failed to flush samples.\n" );
1686 static int construct_timeline_maps( remuxer_t
*remuxer
)
1688 input_t
*input
= remuxer
->input
;
1689 output_t
*output
= remuxer
->output
;
1690 output_movie_t
*out_movie
= &output
->file
.movie
;
1691 track_media_option
**track_option
= remuxer
->track_option
;
1692 out_movie
->current_track_number
= 1;
1693 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
1694 for( uint32_t j
= 0; j
< input
[i
].file
.movie
.num_tracks
; j
++ )
1696 input_track_t
*in_track
= &input
[i
].file
.movie
.track
[j
];
1697 if( !in_track
->active
)
1699 output_track_t
*out_track
= &out_movie
->track
[ out_movie
->current_track_number
++ - 1 ];
1700 if( track_option
[i
][j
].seek
)
1702 /* Reconstruct timeline maps. */
1703 if( lsmash_delete_explicit_timeline_map( output
->root
, out_track
->track_ID
) )
1704 return ERROR_MSG( "failed to delete explicit timeline maps.\n" );
1705 uint32_t movie_timescale
= lsmash_get_movie_timescale( output
->root
);
1706 uint32_t media_timescale
= lsmash_get_media_timescale( output
->root
, out_track
->track_ID
);
1707 if( !media_timescale
)
1708 return ERROR_MSG( "media timescale is broken.\n" );
1709 double timescale_convert_multiplier
= (double)movie_timescale
/ media_timescale
;
1711 edit
.start_time
= in_track
->composition_delay
+ in_track
->skip_duration
;
1712 if( edit
.start_time
)
1714 uint64_t empty_duration
= edit
.start_time
+ lsmash_get_composition_to_decode_shift( output
->root
, out_track
->track_ID
);
1715 lsmash_edit_t empty_edit
;
1716 empty_edit
.duration
= empty_duration
* timescale_convert_multiplier
+ 0.5;
1717 empty_edit
.start_time
= ISOM_EDIT_MODE_EMPTY
;
1718 empty_edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
1719 if( lsmash_create_explicit_timeline_map( output
->root
, out_track
->track_ID
, empty_edit
) )
1720 return ERROR_MSG( "failed to create a empty duration.\n" );
1722 if( remuxer
->frag_base_track
== 0 )
1723 edit
.duration
= (out_track
->last_sample_dts
+ out_track
->last_sample_delta
- in_track
->skip_duration
) * timescale_convert_multiplier
;
1725 edit
.duration
= ISOM_EDIT_DURATION_IMPLICIT
;
1726 edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
1727 if( lsmash_create_explicit_timeline_map( output
->root
, out_track
->track_ID
, edit
) )
1728 return ERROR_MSG( "failed to create a explicit timeline map.\n" );
1730 else if( lsmash_copy_timeline_map( output
->root
, out_track
->track_ID
, input
[i
].root
, in_track
->track_ID
) )
1731 return ERROR_MSG( "failed to copy timeline maps.\n" );
1733 out_movie
->current_track_number
= 1;
1737 static int finish_movie( remuxer_t
*remuxer
)
1739 output_t
*output
= remuxer
->output
;
1740 /* Set chapter list */
1741 if( remuxer
->chap_file
)
1742 lsmash_set_tyrant_chapter( output
->root
, remuxer
->chap_file
, remuxer
->add_bom_to_chpl
);
1743 /* Finish muxing. */
1745 if( lsmash_finish_movie( output
->root
, &moov_to_front
) )
1747 return remuxer
->frag_base_track
? 0 : lsmash_write_lsmash_indicator( output
->root
);
1750 int main( int argc
, char *argv
[] )
1757 else if( !strcasecmp( argv
[1], "-h" ) || !strcasecmp( argv
[1], "--help" ) )
1762 else if( !strcasecmp( argv
[1], "-v" ) || !strcasecmp( argv
[1], "--version" ) )
1773 lsmash_get_mainargs( &argc
, &argv
);
1775 for( int i
= 1 ; i
< argc
; i
++ )
1776 if( !strcasecmp( argv
[i
], "-i" ) || !strcasecmp( argv
[i
], "--input" ) )
1779 return ERROR_MSG( "no input file specified.\n" );
1780 output_t output
= { 0 };
1781 input_t
*input
= lsmash_malloc_zero( num_input
* sizeof(input_t
) );
1783 return ERROR_MSG( "failed to allocate the input handler.\n" );
1784 track_media_option
**track_option
= lsmash_malloc_zero( num_input
* sizeof(track_media_option
*) );
1787 lsmash_free( input
);
1788 return ERROR_MSG( "failed to allocate the track option handler.\n" );
1794 .track_option
= track_option
,
1795 .num_input
= num_input
,
1796 .add_bom_to_chpl
= 0,
1797 .ref_chap_available
= 0,
1800 .default_language
= 0,
1801 .max_chunk_size
= 4*1024*1024,
1802 .max_chunk_duration_in_ms
= 500,
1803 .frag_base_track
= 0,
1804 .subseg_per_seg
= 0,
1806 .compact_size_table
= 0,
1807 .min_frag_duration
= 0.0,
1810 if( parse_cli_option( argc
, argv
, &remuxer
) )
1811 return REMUXER_ERR( "failed to parse command line options.\n" );
1812 if( prepare_output( &remuxer
) )
1813 return REMUXER_ERR( "failed to set up preparation for output.\n" );
1814 if( remuxer
.frag_base_track
&& construct_timeline_maps( &remuxer
) )
1815 return REMUXER_ERR( "failed to construct timeline maps.\n" );
1816 if( do_remux( &remuxer
) )
1817 return REMUXER_ERR( "failed to remux movies.\n" );
1818 if( remuxer
.frag_base_track
== 0 && construct_timeline_maps( &remuxer
) )
1819 return REMUXER_ERR( "failed to construct timeline maps.\n" );
1820 if( finish_movie( &remuxer
) )
1821 return REMUXER_ERR( "failed to finish output movie.\n" );
1823 eprintf( "%s completed!\n", !remuxer
.dash
|| remuxer
.subseg_per_seg
== 0 ? "Remuxing" : "Segmentation" );
1824 cleanup_remuxer( &remuxer
);