write: Fix type size for mdcv luminance
[L-SMASH.git] / cli / remuxer.c
blobc6cf8e0abadf7cb553bb656baa77ca8d23507c9a
1 /*****************************************************************************
2 * remuxer.c
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. */
23 #include "cli.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <inttypes.h>
29 #include <stdarg.h>
31 typedef struct
33 uint32_t track_ID;
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;
41 } output_track_t;
43 typedef struct
45 output_track_t *track;
46 lsmash_movie_parameters_t param;
47 uint32_t num_tracks;
48 uint32_t current_track_number;
49 } output_movie_t;
51 typedef struct
53 const char *name;
54 lsmash_file_t *fh;
55 lsmash_file_parameters_t param;
56 lsmash_file_parameters_t seg_param;
57 output_movie_t movie;
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 * );
61 } output_file_t;
63 typedef struct
65 lsmash_root_t *root;
66 output_file_t file;
67 uint32_t current_seg_number;
68 } output_t;
70 typedef struct
72 int active;
73 lsmash_summary_t *summary;
74 } input_summary_t;
76 typedef struct
78 lsmash_file_t *fh;
79 lsmash_file_parameters_t param;
80 } input_data_ref_t;
82 typedef struct
84 lsmash_media_parameters_t param;
85 uint32_t num_data_refs;
86 input_data_ref_t *data_refs;
87 } input_media_t;
89 typedef struct
91 int active;
92 lsmash_sample_t *sample;
93 double dts;
94 uint64_t composition_delay;
95 uint64_t skip_duration;
96 int reach_end_of_media_timeline;
97 uint32_t track_ID;
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;
104 input_media_t media;
105 } input_track_t;
107 typedef struct
109 input_track_t *track;
110 lsmash_itunes_metadata_t *itunes_metadata;
111 lsmash_movie_parameters_t param;
112 uint32_t movie_ID;
113 uint32_t num_tracks;
114 uint32_t num_itunes_metadata;
115 uint32_t current_track_number;
116 } input_movie_t;
118 typedef struct
120 lsmash_file_t *fh;
121 lsmash_file_parameters_t param;
122 input_movie_t movie;
123 } input_file_t;
125 typedef struct
127 lsmash_root_t *root;
128 input_file_t file;
129 } input_t;
131 typedef struct
133 char *raw_track_option;
134 int remove;
135 int disable;
136 int16_t alternate_group;
137 uint16_t ISO_language;
138 uint32_t seek;
139 int consider_rap;
140 char *handler_name;
141 } track_media_option;
143 typedef struct
145 output_t *output;
146 input_t *input;
147 track_media_option **track_option;
148 int num_input;
149 int add_bom_to_chpl;
150 int ref_chap_available;
151 uint32_t chap_track;
152 char *chap_file;
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;
158 int dash;
159 int compact_size_table;
160 double min_frag_duration;
161 int dry_run;
162 } remuxer_t;
164 typedef struct
166 char *whole_track_option;
167 int num_track_delimiter;
168 } file_option;
170 static void cleanup_input_movie( input_t *input )
172 if( !input )
173 return;
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 );
202 input->root = NULL;
205 static void cleanup_output_movie( output_t *output )
207 if( !output )
208 return;
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 );
226 output->root = NULL;
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 );
247 REFRESH_CONSOLE;
248 eprintf( "[Error] " );
249 va_list args;
250 va_start( args, message );
251 vfprintf( stderr, message, args );
252 va_end( args );
253 return -1;
256 static int error_message( const char *message, ... )
258 REFRESH_CONSOLE;
259 eprintf( "[Error] " );
260 va_list args;
261 va_start( args, message );
262 vfprintf( stderr, message, args );
263 va_end( args );
264 return -1;
267 static int warning_message( const char *message, ... )
269 REFRESH_CONSOLE;
270 eprintf( "[Warning] " );
271 va_list args;
272 va_start( args, message );
273 vfprintf( stderr, message, args );
274 va_end( args );
275 return -1;
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 )
284 eprintf( "\n"
285 "L-SMASH isom/mov re-muliplexer rev%s %s\n"
286 "Built on %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 )
293 display_version();
294 eprintf( "\n"
295 "Usage: remuxer -i input1 [-i input2 -i input3 ...] -o output\n"
296 "Global options:\n"
297 " --help\n"
298 " Display help.\n"
299 " --version\n"
300 " Display version information.\n"
301 " --chapter <string>\n"
302 " Set chapters from the file.\n"
303 " --chpl-with-bom\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"
332 " --dry-run\n"
333 " Execute as a dry run.\n"
334 "Track options:\n"
335 " remove\n"
336 " Remove this track.\n"
337 " disable\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"
345 " seek=<integer>\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"
352 "For example:\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 )
358 if( !src )
359 return NULL;
360 int dst_size = strlen( src ) + 1;
361 char *dst = lsmash_malloc( dst_size );
362 if( !dst )
363 return NULL;
364 memcpy( dst, src, dst_size );
365 return dst;
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 ) )
372 return -1;
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) );
377 if( shadow.meaning )
379 metadata->meaning = duplicate_string( shadow.meaning );
380 if( !metadata->meaning )
381 return -1;
383 if( shadow.name )
385 metadata->name = duplicate_string( shadow.name );
386 if( !metadata->name )
387 goto fail;
389 if( shadow.type == ITUNES_METADATA_TYPE_STRING )
391 metadata->value.string = duplicate_string( shadow.value.string );
392 if( !metadata->value.string )
393 goto fail;
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 )
399 goto fail;
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;
404 else
405 metadata->value = shadow.value;
406 return 0;
407 fail:
408 lsmash_freep( &metadata->meaning );
409 lsmash_freep( &metadata->name );
410 return -1;
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
420 input_t *input,
421 uint32_t track_ID,
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" );
429 return -1;
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" );
436 return -1;
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" );
441 return -1;
443 return 0;
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();
452 if( !input->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 );
458 if( !in_file->fh )
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" );
476 continue;
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;
487 /* Create tracks. */
488 input_track_t *in_track = in_movie->track = lsmash_malloc_zero( num_tracks * sizeof(input_track_t) );
489 if( !in_track )
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" );
503 continue;
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" );
509 continue;
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" );
515 continue;
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" );
521 continue;
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" );
530 continue;
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 != '\\' )
541 --p;
542 int relative_path_length = p == input_name ? 2 : p - input_name;
543 char *location = lsmash_malloc( relative_path_length + location_length + 2 );
544 if( location )
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 );
556 if( ret < 0 )
557 continue;
559 else
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" );
568 continue;
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" );
573 continue;
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" );
579 continue;
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 );
587 if( !summary )
589 WARNING_MSG( "failed to get a summary.\n" );
590 continue;
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" );
596 continue;
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;
604 in_track[i].dts = 0;
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 ) );
609 return 0;
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 )
620 break;
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 );
631 char *track_option;
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. */
641 break;
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 );
671 else
672 return ERROR_MSG( "unknown track option %s\n", track_option );
675 return 0;
678 static int parse_cli_option( int argc, char **argv, remuxer_t *remuxer )
680 #define FAILED_PARSE_CLI_OPTION( ... ) \
681 do \
683 lsmash_free( input_file_option ); \
684 return ERROR_MSG( __VA_ARGS__ ); \
686 while( 0 )
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 */
698 if( ++i == argc )
699 FAILED_PARSE_CLI_OPTION( "-i requires an argument.\n" );
700 input_file_option[input_movie_number].num_track_delimiter = 0;
701 char *p = argv[i];
702 while( *p )
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 */
717 if( ++i == argc )
718 FAILED_PARSE_CLI_OPTION( "-o requires an argument.\n" );
719 output_t *output = remuxer->output;
720 output->root = lsmash_create_root();
721 if( !output->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 */
727 if( ++i == argc )
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 */
735 if( ++i == argc )
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" ) )
743 if( ++i == argc )
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" ) )
749 if( ++i == argc )
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" ) )
757 if( ++i == argc )
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" ) )
765 if( ++i == argc )
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" ) )
773 if( ++i == argc )
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" ) )
783 if( ++i == argc )
784 FAILED_PARSE_CLI_OPTION( "--dash requires an argument.\n" );
785 remuxer->subseg_per_seg = atoi( argv[i] );
786 remuxer->dash = 1;
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;
792 else
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 )
804 continue;
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 );
830 return 0;
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,
863 ISOM_BRAND_TYPE_QT ,
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++ )
897 int invalid = 1;
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 )
911 continue;
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;
917 if( version )
918 *version = 0x00000700;
920 else
921 *brand = LSMASH_4CC( '3', 'g', 'g', *brand & 0xFF );
923 if( remuxer->dash
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;
930 invalid = 0;
931 break;
934 if( invalid )
936 /* Replace with the 'mp42' brand. */
937 *brand = ISOM_BRAND_TYPE_MP42;
938 if( version )
939 *version = 0;
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)) );
947 if( !heap )
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) )
962 if( i <= j )
963 ++major_brand_count[num_major_brand];
964 else
966 /* This major_brand already exists. Skip this. */
967 major_brand_count[num_major_brand] = 0;
968 --num_major_brand;
969 break;
972 ++num_major_brand;
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];
982 lsmash_free( heap );
983 return 0;
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);
995 if( remuxer->dash )
997 if( remuxer->frag_base_track )
999 if( self_containd_segment )
1000 out_file->param.mode |= LSMASH_FILE_MODE_INDEX;
1001 else
1002 out_file->param.mode &= ~LSMASH_FILE_MODE_MEDIA;
1003 out_file->param.mode |= LSMASH_FILE_MODE_SEGMENT;
1005 else
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) );
1023 if( !input_brands )
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;
1050 break;
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 );
1059 if( !out_file->fh )
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' )
1075 break;
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;
1082 break;
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;
1095 break;
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" );
1108 continue;
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 )
1115 return 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 ) )
1119 if( consider_rap )
1120 return ERROR_MSG( "failed to get the first random accessible point.\n" );
1121 else
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;
1126 return 0;
1129 /* Get composition delay. */
1130 uint64_t rap_dts;
1131 uint64_t rap_cts;
1132 uint32_t ctd_shift;
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" );
1146 if( consider_rap )
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" );
1151 uint64_t seek_cts;
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;
1160 return 0;
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, ... )
1167 REFRESH_CONSOLE;
1168 eprintf( "[Warning] in %"PRIu32"/%"PRIu32" -> out %"PRIu32": ", in_movie->movie_ID, in_track->track_ID, out_track->track_ID );
1169 va_list args;
1170 va_start( args, message );
1171 vfprintf( stderr, message, args );
1172 va_end( 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;
1189 else
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 )
1227 continue;
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" );
1249 continue;
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" );
1254 continue;
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;
1265 continue;
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;
1276 continue;
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" );
1283 continue;
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" );
1289 continue;
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;
1301 return 0;
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 )
1324 continue;
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" );
1335 else
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 )
1339 break;
1342 return 0;
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 )
1349 return 0;
1350 REFRESH_CONSOLE;
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;
1354 return 0;
1357 static lsmash_adhoc_remux_t moov_to_front =
1359 .func = moov_to_front_callback,
1360 .buffer_size = 4 * 1024 * 1024, /* 4MiB */
1361 .param = NULL
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.
1368 * a.mp4
1369 * a_1.mp4
1370 * a_2.mp4
1371 * ...
1372 * a_N.mp4
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 != '\\' )
1380 --p;
1381 if( p < out_file->name )
1382 ++p;
1383 if( *p != '.' )
1384 p = end;
1385 int suffix_length = 1;
1386 for( uint32_t i = output->current_seg_number; i; i /= 10 )
1387 ++suffix_length;
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) );
1391 if( !seg_name )
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 );
1396 if( *p == '.' )
1397 memcpy( seg_name + suffixless_length + suffix_length, p, end - p );
1398 int ret = out_file->open( seg_name, 0, seg_param );
1399 if( ret == 0 )
1400 eprintf( "[Segment] out: %s\n", seg_name );
1401 lsmash_free( seg_name );
1402 return ret;
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) );
1418 if( !brands )
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;
1431 else
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 );
1438 if( !segment )
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;
1447 return 0;
1450 static int handle_segmentation( remuxer_t *remuxer )
1452 if( remuxer->subseg_per_seg == 0 )
1453 return 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" );
1461 return -1;
1463 output->file.current_subseg_number = 1;
1464 ++ output->current_seg_number;
1466 else
1467 ++ output->file.current_subseg_number;
1468 return 0;
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
1475 : sample->index;
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. */
1508 while( 1 )
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. */
1524 continue;
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. */
1531 if( !sample )
1533 sample = lsmash_get_sample_from_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number );
1534 if( sample )
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;
1542 else
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" );
1547 break;
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 */
1558 else
1560 ERROR_MSG( "failed to get a sample.\n" );
1561 break;
1565 if( sample )
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
1581 && over_duration )
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" );
1592 break;
1594 if( handle_segmentation( remuxer ) < 0 )
1595 break;
1596 if( lsmash_create_fragment_movie( output->root ) < 0 )
1598 ERROR_MSG( "failed to create a movie fragment.\n" );
1599 break;
1601 pending_flush_fragments = 0;
1604 /* Append a sample if meeting a condition. */
1605 int append = 0;
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 )
1615 append = 1;
1616 else
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 )
1621 append = 0;
1622 else
1623 append = (info.prop.ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE)
1624 && ((double)info.dts / in_track->media.param.timescale <= frag_base_dts);
1627 if( append )
1629 if( sample->index )
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 );
1656 else
1658 lsmash_delete_sample( sample );
1659 in_track->sample = NULL;
1660 in_track->current_sample_number += 1;
1663 else
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" );
1682 return 0;
1683 #undef LSMASH_MAX
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 )
1698 continue;
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;
1710 lsmash_edit_t edit;
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;
1724 else
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;
1734 return 0;
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. */
1744 REFRESH_CONSOLE;
1745 if( lsmash_finish_movie( output->root, &moov_to_front ) )
1746 return -1;
1747 return remuxer->frag_base_track ? 0 : lsmash_write_lsmash_indicator( output->root );
1750 int main( int argc, char *argv[] )
1752 if ( argc < 2 )
1754 display_help();
1755 return -1;
1757 else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) )
1759 display_help();
1760 return 0;
1762 else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) )
1764 display_version();
1765 return 0;
1767 else if( argc < 5 )
1769 display_help();
1770 return -1;
1773 lsmash_get_mainargs( &argc, &argv );
1774 int num_input = 0;
1775 for( int i = 1 ; i < argc ; i++ )
1776 if( !strcasecmp( argv[i], "-i" ) || !strcasecmp( argv[i], "--input" ) )
1777 num_input++;
1778 if( !num_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) );
1782 if( !input )
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 *) );
1785 if( !track_option )
1787 lsmash_free( input );
1788 return ERROR_MSG( "failed to allocate the track option handler.\n" );
1790 remuxer_t remuxer =
1792 .output = &output,
1793 .input = input,
1794 .track_option = track_option,
1795 .num_input = num_input,
1796 .add_bom_to_chpl = 0,
1797 .ref_chap_available = 0,
1798 .chap_track = 1,
1799 .chap_file = NULL,
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,
1805 .dash = 0,
1806 .compact_size_table = 0,
1807 .min_frag_duration = 0.0,
1808 .dry_run = 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" );
1822 REFRESH_CONSOLE;
1823 eprintf( "%s completed!\n", !remuxer.dash || remuxer.subseg_per_seg == 0 ? "Remuxing" : "Segmentation" );
1824 cleanup_remuxer( &remuxer );
1825 return 0;