alac: Utilize meaningful error values.
[L-SMASH.git] / cli / remuxer.c
blob22939374e338a5c54e962b1fc05c340fc5e18eb1
1 /*****************************************************************************
2 * remuxer.c:
3 *****************************************************************************
4 * Copyright (C) 2011-2014 L-SMASH project
6 * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *****************************************************************************/
21 /* This file is available under an ISC license. */
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <strings.h>
27 #include <inttypes.h>
28 #include <stdarg.h>
30 #include "lsmash.h"
31 #include "cli.h"
33 #include "config.h"
35 typedef struct
37 uint32_t track_ID;
38 uint32_t last_sample_delta;
39 uint32_t current_sample_number;
40 uint32_t *summary_remap;
41 uint64_t skip_dt_interval;
42 uint64_t last_sample_dts;
43 lsmash_track_parameters_t track_param;
44 lsmash_media_parameters_t media_param;
45 } output_track_t;
47 typedef struct
49 output_track_t *track;
50 lsmash_movie_parameters_t param;
51 uint32_t num_tracks;
52 uint32_t current_track_number;
53 } output_movie_t;
55 typedef struct
57 const char *name;
58 lsmash_file_t *fh;
59 lsmash_file_parameters_t param;
60 lsmash_file_parameters_t seg_param;
61 output_movie_t movie;
62 uint32_t current_subseg_number;
63 } output_file_t;
65 typedef struct
67 lsmash_root_t *root;
68 output_file_t file;
69 uint32_t current_seg_number;
70 } output_t;
72 typedef struct
74 int active;
75 lsmash_summary_t *summary;
76 } input_summary_t;
78 typedef struct
80 lsmash_file_t *fh;
81 lsmash_file_parameters_t param;
82 } input_data_ref_t;
84 typedef struct
86 lsmash_media_parameters_t param;
87 uint32_t num_data_refs;
88 input_data_ref_t *data_refs;
89 } input_media_t;
91 typedef struct
93 int active;
94 lsmash_sample_t *sample;
95 double dts;
96 uint64_t composition_delay;
97 uint64_t skip_duration;
98 int reach_end_of_media_timeline;
99 uint32_t track_ID;
100 uint32_t last_sample_delta;
101 uint32_t current_sample_number;
102 uint32_t current_sample_index;
103 uint32_t num_summaries;
104 input_summary_t *summaries;
105 lsmash_track_parameters_t track_param;
106 input_media_t media;
107 } input_track_t;
109 typedef struct
111 input_track_t *track;
112 lsmash_itunes_metadata_t *itunes_metadata;
113 lsmash_movie_parameters_t param;
114 uint32_t movie_ID;
115 uint32_t num_tracks;
116 uint32_t num_itunes_metadata;
117 uint32_t current_track_number;
118 } input_movie_t;
120 typedef struct
122 lsmash_file_t *fh;
123 lsmash_file_parameters_t param;
124 input_movie_t movie;
125 } input_file_t;
127 typedef struct
129 lsmash_root_t *root;
130 input_file_t file;
131 } input_t;
133 typedef struct
135 char *raw_track_option;
136 int remove;
137 int disable;
138 int16_t alternate_group;
139 uint16_t ISO_language;
140 uint32_t seek;
141 int consider_rap;
142 char *handler_name;
143 } track_media_option;
145 typedef struct
147 output_t *output;
148 input_t *input;
149 track_media_option **track_option;
150 int num_input;
151 int add_bom_to_chpl;
152 int ref_chap_available;
153 uint32_t chap_track;
154 char *chap_file;
155 uint16_t default_language;
156 uint32_t frag_base_track;
157 uint32_t subseg_per_seg;
158 int dash;
159 } remuxer_t;
161 typedef struct
163 char *whole_track_option;
164 int num_track_delimiter;
165 } file_option;
167 static void cleanup_input_movie( input_t *input )
169 if( !input )
170 return;
171 input_movie_t *in_movie = &input->file.movie;
172 if( in_movie->itunes_metadata )
174 for( uint32_t i = 0; i < in_movie->num_itunes_metadata; i++ )
175 lsmash_cleanup_itunes_metadata( &in_movie->itunes_metadata[i] );
176 lsmash_freep( &in_movie->itunes_metadata );
178 if( in_movie->track )
180 for( uint32_t i = 0; i < in_movie->num_tracks; i++ )
182 input_track_t *in_track = &in_movie->track[i];
183 if( in_track->summaries )
185 for( uint32_t j = 0; j < in_track->num_summaries; j++ )
186 lsmash_cleanup_summary( in_track->summaries[j].summary );
187 lsmash_free( in_track->summaries );
189 input_media_t *in_media = &in_track->media;
190 for( uint32_t j = 0; j < in_media->num_data_refs; j++ )
191 if( input->file.fh != in_media->data_refs[j].fh )
192 lsmash_close_file( &in_media->data_refs[j].param );
193 lsmash_free( in_media->data_refs );
195 lsmash_freep( &in_movie->track );
197 lsmash_close_file( &input->file.param );
198 lsmash_destroy_root( input->root );
199 input->root = NULL;
202 static void cleanup_output_movie( output_t *output )
204 if( !output )
205 return;
206 output_movie_t *out_movie = &output->file.movie;
207 if( out_movie->track )
209 for( uint32_t i = 0; i < out_movie->num_tracks; i++ )
210 lsmash_free( out_movie->track[i].summary_remap );
211 lsmash_freep( &out_movie->track );
213 if( !(output->file.seg_param.mode & LSMASH_FILE_MODE_INITIALIZATION) )
215 lsmash_freep( &output->file.seg_param.brands );
216 lsmash_close_file( &output->file.seg_param );
218 lsmash_freep( &output->file.param.brands );
219 lsmash_close_file( &output->file.param );
220 lsmash_destroy_root( output->root );
221 output->root = NULL;
224 static void cleanup_remuxer( remuxer_t *remuxer )
226 for( int i = 0; i < remuxer->num_input; i++ )
228 cleanup_input_movie( &remuxer->input[i] );
229 if( remuxer->track_option[i] )
230 lsmash_free( remuxer->track_option[i] );
232 cleanup_output_movie( remuxer->output );
235 #define eprintf( ... ) fprintf( stderr, __VA_ARGS__ )
236 #define REFRESH_CONSOLE eprintf( " \r" )
238 static int remuxer_error( remuxer_t *remuxer, const char *message, ... )
240 cleanup_remuxer( remuxer );
241 REFRESH_CONSOLE;
242 eprintf( "[Error] " );
243 va_list args;
244 va_start( args, message );
245 vfprintf( stderr, message, args );
246 va_end( args );
247 return -1;
250 static int error_message( const char *message, ... )
252 REFRESH_CONSOLE;
253 eprintf( "[Error] " );
254 va_list args;
255 va_start( args, message );
256 vfprintf( stderr, message, args );
257 va_end( args );
258 return -1;
261 static int warning_message( const char *message, ... )
263 REFRESH_CONSOLE;
264 eprintf( "[Warning] " );
265 va_list args;
266 va_start( args, message );
267 vfprintf( stderr, message, args );
268 va_end( args );
269 return -1;
272 #define REMUXER_ERR( ... ) remuxer_error( &remuxer, __VA_ARGS__ )
273 #define ERROR_MSG( ... ) error_message( __VA_ARGS__ )
274 #define WARNING_MSG( ... ) warning_message( __VA_ARGS__ )
276 static void display_version( void )
278 eprintf( "\n"
279 "L-SMASH isom/mov re-muliplexer rev%s %s\n"
280 "Built on %s %s\n"
281 "Copyright (C) 2011-2014 L-SMASH project\n",
282 LSMASH_REV, LSMASH_GIT_HASH, __DATE__, __TIME__ );
285 static void display_help( void )
287 display_version();
288 eprintf( "\n"
289 "Usage: remuxer -i input1 [-i input2 -i input3 ...] -o output\n"
290 "Global options:\n"
291 " --help Display help.\n"
292 " --version Display version information.\n"
293 " --chapter <string> Set chapters from the file.\n"
294 " --chpl-with-bom Add UTF-8 BOM to the chapter strings\n"
295 " in the chapter list. (experimental)\n"
296 " --chapter-track <integer> Set which track the chapter applies to.\n"
297 " This option takes effect only when reference\n"
298 " chapter is available.\n"
299 " If this option is not used, it defaults to 1.\n"
300 " --language <string> Specify the default language for all the output tracks.\n"
301 " This option is overridden by the track options.\n"
302 " --fragment <integer> Enable fragmentation per random accessible point.\n"
303 " Set which track the fragmentation is based on.\n"
304 " --dash <integer> Enable DASH ISOBMFF-based Media segmentation.\n"
305 " The value is the number of subsegments per segment.\n"
306 " If zero, Indexed self-initializing Media Segment.\n"
307 " This option requires --fragment.\n"
308 "Track options:\n"
309 " remove Remove this track\n"
310 " disable Disable this track\n"
311 " language=<string> Specify media language\n"
312 " alternate-group=<integer> Specify alternate group\n"
313 " handler=<string> Set media handler name\n"
314 " seek=<integer> Specify starting point in media\n"
315 " safe-seek=<integer> Same as seek except for considering random accessible point\n"
316 " Media starts from the closest random accessible point\n"
317 "How to use track options:\n"
318 " -i input?[track_number1]:[track_option1],[track_option2]?[track_number2]:...\n"
319 "For example:\n"
320 " remuxer -i input1 -i input2?2:alternate-group=1?3:language=jpn,alternate-group=1 -o output\n" );
323 static char *duplicate_string( char *src )
325 if( !src )
326 return NULL;
327 int dst_size = strlen( src ) + 1;
328 char *dst = lsmash_malloc( dst_size );
329 if( !dst )
330 return NULL;
331 memcpy( dst, src, dst_size );
332 return dst;
335 static int get_itunes_metadata( lsmash_root_t *root, uint32_t metadata_number, lsmash_itunes_metadata_t *metadata )
337 memset( metadata, 0, sizeof(lsmash_itunes_metadata_t) );
338 if( lsmash_get_itunes_metadata( root, metadata_number, metadata ) )
339 return -1;
340 lsmash_itunes_metadata_t shadow = *metadata;
341 metadata->meaning = NULL;
342 metadata->name = NULL;
343 memset( &metadata->value, 0, sizeof(lsmash_itunes_metadata_value_t) );
344 if( shadow.meaning )
346 metadata->meaning = duplicate_string( shadow.meaning );
347 if( !metadata->meaning )
348 return -1;
350 if( shadow.name )
352 metadata->name = duplicate_string( shadow.name );
353 if( !metadata->name )
354 goto fail;
356 if( shadow.type == ITUNES_METADATA_TYPE_STRING )
358 metadata->value.string = duplicate_string( shadow.value.string );
359 if( !metadata->value.string )
360 goto fail;
362 else if( shadow.type == ITUNES_METADATA_TYPE_BINARY )
364 metadata->value.binary.data = lsmash_malloc( shadow.value.binary.size );
365 if( !metadata->value.binary.data )
366 goto fail;
367 memcpy( metadata->value.binary.data, shadow.value.binary.data, shadow.value.binary.size );
368 metadata->value.binary.size = shadow.value.binary.size;
369 metadata->value.binary.subtype = shadow.value.binary.subtype;
371 else
372 metadata->value = shadow.value;
373 return 0;
374 fail:
375 lsmash_freep( &metadata->meaning );
376 lsmash_freep( &metadata->name );
377 return -1;
380 static inline int is_relative_path( const char *path )
382 return path[0] == '/' || path[0] == '\\' || (path[0] != '\0' && path[1] == ':') ? 0 : 1;
385 static int input_data_reference
387 input_t *input,
388 uint32_t track_ID,
389 input_data_ref_t *in_data_ref,
390 lsmash_data_reference_t *data_ref
393 if( lsmash_open_file( data_ref->location, 1, &in_data_ref->param ) < 0 )
395 WARNING_MSG( "failed to open an external media file.\n" );
396 return -1;
398 in_data_ref->param.mode |= LSMASH_FILE_MODE_MEDIA;
399 in_data_ref->fh = lsmash_set_file( input->root, &in_data_ref->param );
400 if( !in_data_ref->fh )
402 WARNING_MSG( "failed to set an external media file as a data reference.\n" );
403 return -1;
405 if( lsmash_assign_data_reference( input->root, track_ID, data_ref->index, in_data_ref->fh ) < 0 )
407 WARNING_MSG( "failed to assign an external media a data reference.\n" );
408 return -1;
410 return 0;
413 static int get_movie( input_t *input, char *input_name )
415 if( !strcmp( input_name, "-" ) )
416 return ERROR_MSG( "standard input not supported.\n" );
417 /* Read an input file. */
418 input->root = lsmash_create_root();
419 if( !input->root )
420 return ERROR_MSG( "failed to create a ROOT for an input file.\n" );
421 input_file_t *in_file = &input->file;
422 if( lsmash_open_file( input_name, 1, &in_file->param ) < 0 )
423 return ERROR_MSG( "failed to open an input file.\n" );
424 in_file->fh = lsmash_set_file( input->root, &in_file->param );
425 if( !in_file->fh )
426 return ERROR_MSG( "failed to add an input file into a ROOT.\n" );
427 if( lsmash_read_file( in_file->fh, &in_file->param ) < 0 )
428 return ERROR_MSG( "failed to read an input file\n" );
429 /* Get iTunes metadata. */
430 input_movie_t *in_movie = &in_file->movie;
431 in_movie->num_itunes_metadata = lsmash_count_itunes_metadata( input->root );
432 if( in_movie->num_itunes_metadata )
434 in_movie->itunes_metadata = lsmash_malloc( in_movie->num_itunes_metadata * sizeof(lsmash_itunes_metadata_t) );
435 if( !in_movie->itunes_metadata )
436 return ERROR_MSG( "failed to alloc iTunes metadata.\n" );
437 uint32_t itunes_metadata_count = 0;
438 for( uint32_t i = 1; i <= in_movie->num_itunes_metadata; i++ )
440 if( get_itunes_metadata( input->root, i, &in_movie->itunes_metadata[itunes_metadata_count] ) )
442 WARNING_MSG( "failed to get an iTunes metadata.\n" );
443 continue;
445 ++itunes_metadata_count;
447 in_movie->num_itunes_metadata = itunes_metadata_count;
449 in_movie->current_track_number = 1;
450 lsmash_initialize_movie_parameters( &in_movie->param );
451 if( lsmash_get_movie_parameters( input->root, &in_movie->param ) )
452 return ERROR_MSG( "failed to get movie parameters.\n" );
453 uint32_t num_tracks = in_movie->num_tracks = in_movie->param.number_of_tracks;
454 /* Create tracks. */
455 input_track_t *in_track = in_movie->track = lsmash_malloc_zero( num_tracks * sizeof(input_track_t) );
456 if( !in_track )
457 return ERROR_MSG( "failed to alloc input tracks.\n" );
458 for( uint32_t i = 0; i < num_tracks; i++ )
460 in_track[i].track_ID = lsmash_get_track_ID( input->root, i + 1 );
461 if( !in_track[i].track_ID )
462 return ERROR_MSG( "failed to get track_ID.\n" );
464 for( uint32_t i = 0; i < num_tracks; i++ )
466 lsmash_initialize_track_parameters( &in_track[i].track_param );
467 if( lsmash_get_track_parameters( input->root, in_track[i].track_ID, &in_track[i].track_param ) )
469 WARNING_MSG( "failed to get track parameters.\n" );
470 continue;
472 lsmash_initialize_media_parameters( &in_track[i].media.param );
473 if( lsmash_get_media_parameters( input->root, in_track[i].track_ID, &in_track[i].media.param ) )
475 WARNING_MSG( "failed to get media parameters.\n" );
476 continue;
478 uint32_t data_ref_count = lsmash_count_data_reference( input->root, in_track[i].track_ID );
479 if( data_ref_count == 0 )
481 WARNING_MSG( "failed to get the number of data references.\n" );
482 continue;
484 in_track[i].media.data_refs = lsmash_malloc_zero( data_ref_count * sizeof(input_data_ref_t) );
485 if( !in_track[i].media.data_refs )
487 WARNING_MSG( "failed to allocate handles of data reference.\n" );
488 continue;
490 for( uint32_t j = 0; j < data_ref_count; j++ )
492 input_data_ref_t *in_data_ref = &in_track[i].media.data_refs[j];
493 lsmash_data_reference_t data_ref = { .index = j + 1 };
494 if( lsmash_get_data_reference( input->root, in_track[i].track_ID, &data_ref ) < 0 )
496 WARNING_MSG( "failed to get a data references.\n" );
497 continue;
499 if( data_ref.location )
501 if( is_relative_path( data_ref.location ) && !is_relative_path( input_name ) )
503 /* Append the directory path from the referencing file. */
504 int location_length = strlen( data_ref.location );
505 int input_name_length = strlen( input_name );
506 char *p = input_name + input_name_length;
507 while( p != input_name && *p != '/' && *p != '\\' )
508 --p;
509 int relative_path_length = p == input_name ? 2 : p - input_name;
510 char *location = lsmash_malloc( relative_path_length + location_length + 2 );
511 if( location )
513 memcpy( location, input_name, relative_path_length );
514 memcpy( location + relative_path_length + 1, data_ref.location, location_length );
515 location[relative_path_length] = '/';
516 location[relative_path_length + location_length + 1] = '\0';
517 lsmash_cleanup_data_reference( &data_ref );
518 data_ref.location = location;
521 int ret = input_data_reference( input, in_track[i].track_ID, in_data_ref, &data_ref );
522 lsmash_cleanup_data_reference( &data_ref );
523 if( ret < 0 )
524 continue;
526 else
528 in_data_ref->fh = in_file->fh;
529 in_data_ref->param = in_file->param;
532 if( lsmash_construct_timeline( input->root, in_track[i].track_ID ) )
534 WARNING_MSG( "failed to construct timeline.\n" );
535 continue;
537 if( lsmash_get_last_sample_delta_from_media_timeline( input->root, in_track[i].track_ID, &in_track[i].last_sample_delta ) )
539 WARNING_MSG( "failed to get the last sample delta.\n" );
540 continue;
542 in_track[i].num_summaries = lsmash_count_summary( input->root, in_track[i].track_ID );
543 if( in_track[i].num_summaries == 0 )
545 WARNING_MSG( "failed to find valid summaries.\n" );
546 continue;
548 in_track[i].summaries = lsmash_malloc_zero( in_track[i].num_summaries * sizeof(input_summary_t) );
549 if( !in_track[i].summaries )
550 return ERROR_MSG( "failed to alloc input summaries.\n" );
551 for( uint32_t j = 0; j < in_track[i].num_summaries; j++ )
553 lsmash_summary_t *summary = lsmash_get_summary( input->root, in_track[i].track_ID, j + 1 );
554 if( !summary )
556 WARNING_MSG( "failed to get a summary.\n" );
557 continue;
559 if( !LSMASH_FLAGS_SATISFIED( lsmash_check_codec_support( summary->sample_type ), LSMASH_CODEC_SUPPORT_FLAG_REMUX ) )
561 lsmash_cleanup_summary( summary );
562 WARNING_MSG( "no support to remux this stream.\n" );
563 continue;
565 in_track[i].summaries[j].summary = summary;
566 in_track[i].summaries[j].active = 1;
568 in_track[i].active = 1;
569 in_track[i].current_sample_number = 1;
570 in_track[i].sample = NULL;
571 in_track[i].dts = 0;
572 in_track[i].composition_delay = 0;
573 in_track[i].skip_duration = 0;
575 lsmash_destroy_children( lsmash_file_as_box( in_file->fh ) );
576 return 0;
579 static int parse_track_option( remuxer_t *remuxer )
581 track_media_option **track = remuxer->track_option;
582 for( int i = 0; i < remuxer->num_input; i++ )
583 for( uint32_t j = 0; j < remuxer->input[i].file.movie.num_tracks; j++ )
585 track_media_option *current_track_opt = &track[i][j];
586 if( current_track_opt->raw_track_option == NULL )
587 break;
588 if( !strchr( current_track_opt->raw_track_option, ':' )
589 || strchr( current_track_opt->raw_track_option, ':' ) == current_track_opt->raw_track_option )
590 return ERROR_MSG( "track number is not specified in %s\n", current_track_opt->raw_track_option );
591 if( strchr( current_track_opt->raw_track_option, ':' ) != strrchr( current_track_opt->raw_track_option, ':' ) )
592 return ERROR_MSG( "multiple colons inside one track option in %s.\n", current_track_opt->raw_track_option );
593 uint32_t track_number = atoi( strtok( current_track_opt->raw_track_option, ":" ) );
594 if( track_number == 0 )
595 return ERROR_MSG( "%s is an invalid track number.\n", strtok( current_track_opt->raw_track_option, ":" ) );
596 if( track_number > remuxer->input[i].file.movie.num_tracks )
597 return ERROR_MSG( "%d is an invalid track number.\n", track_number );
598 char *track_option;
599 while( (track_option = strtok( NULL, "," )) != NULL )
601 if( strchr( track_option, '=' ) != strrchr( track_option, '=' ) )
602 return ERROR_MSG( "multiple equal signs inside one track option in %s\n", track_option );
603 current_track_opt = &track[i][track_number - 1];
604 if( strstr( track_option, "remove" ) )
606 current_track_opt->remove = 1;
607 /* No need to parse track options for this track anymore. */
608 break;
610 else if( strstr( track_option, "disable" ) )
611 current_track_opt->disable = 1;
612 else if( strstr( track_option, "alternate-group=" ) )
614 char *track_parameter = strchr( track_option, '=' ) + 1;
615 current_track_opt->alternate_group = atoi( track_parameter );
617 else if( strstr( track_option, "language=" ) )
619 char *track_parameter = strchr( track_option, '=' ) + 1;
620 current_track_opt->ISO_language = lsmash_pack_iso_language( track_parameter );
622 else if( strstr( track_option, "handler=" ) )
624 char *track_parameter = strchr( track_option, '=' ) + 1;
625 current_track_opt->handler_name = track_parameter;
627 else if( strstr( track_option, "safe-seek=" ) )
629 char *track_parameter = strchr( track_option, '=' ) + 1;
630 current_track_opt->seek = atoi( track_parameter );
631 current_track_opt->consider_rap = 1;
633 else if( strstr( track_option, "seek=" ) )
635 char *track_parameter = strchr( track_option, '=' ) + 1;
636 current_track_opt->seek = atoi( track_parameter );
638 else
639 return ERROR_MSG( "unknown track option %s\n", track_option );
642 return 0;
645 static int parse_cli_option( int argc, char **argv, remuxer_t *remuxer )
647 input_t *input = remuxer->input;
648 track_media_option **track_option = remuxer->track_option;
649 file_option input_file_option[ remuxer->num_input ];
650 int input_movie_number = 0;
651 for( int i = 1; i < argc ; i++ )
653 /* Get input movies. */
654 if( !strcasecmp( argv[i], "-i" ) || !strcasecmp( argv[i], "--input" ) ) /* input file */
656 if( ++i == argc )
657 return ERROR_MSG( "-i requires an argument.\n" );
658 input_file_option[input_movie_number].num_track_delimiter = 0;
659 char *p = argv[i];
660 while( *p )
661 input_file_option[input_movie_number].num_track_delimiter += (*p++ == '?');
662 if( get_movie( &input[input_movie_number], strtok( argv[i], "?" ) ) )
663 return ERROR_MSG( "failed to get input movie.\n" );
664 uint32_t num_tracks = input[input_movie_number].file.movie.num_tracks;
665 track_option[input_movie_number] = lsmash_malloc_zero( num_tracks * sizeof(track_media_option) );
666 if( !track_option[input_movie_number] )
667 return ERROR_MSG( "couldn't allocate memory.\n" );
668 input_file_option[input_movie_number].whole_track_option = strtok( NULL, "" );
669 input[input_movie_number].file.movie.movie_ID = input_movie_number + 1;
670 ++input_movie_number;
672 /* Create output movie. */
673 else if( !strcasecmp( argv[i], "-o" ) || !strcasecmp( argv[i], "--output" ) ) /* output file */
675 if( ++i == argc )
676 return ERROR_MSG( "-o requires an argument.\n" );
677 output_t *output = remuxer->output;
678 output->root = lsmash_create_root();
679 if( !output->root )
680 return ERROR_MSG( "failed to create a ROOT.\n" );
681 if( lsmash_open_file( argv[i], 0, &output->file.param ) < 0 )
682 return ERROR_MSG( "failed to open an output file.\n" );
683 output->file.name = argv[i];
685 else if( !strcasecmp( argv[i], "--chapter" ) ) /* chapter file */
687 if( ++i == argc )
688 return ERROR_MSG( "--chapter requires an argument.\n" );
689 remuxer->chap_file = argv[i];
691 else if( !strcasecmp( argv[i], "--chpl-with-bom" ) )
692 remuxer->add_bom_to_chpl = 1;
693 else if( !strcasecmp( argv[i], "--chapter-track" ) ) /* track to apply reference chapter to */
695 if( ++i == argc )
696 return ERROR_MSG( "--chapter-track requires an argument.\n" );
697 remuxer->chap_track = atoi( argv[i] );
698 if( !remuxer->chap_track )
699 return ERROR_MSG( "%s is an invalid track number.\n", argv[i] );
701 else if( !strcasecmp( argv[i], "--language" ) )
703 if( ++i == argc )
704 return ERROR_MSG( "--chapter requires an argument.\n" );
705 remuxer->default_language = lsmash_pack_iso_language( argv[i] );
707 else if( !strcasecmp( argv[i], "--fragment" ) )
709 if( ++i == argc )
710 return ERROR_MSG( "--fragment requires an argument.\n" );
711 remuxer->frag_base_track = atoi( argv[i] );
712 if( remuxer->frag_base_track == 0 )
713 return ERROR_MSG( "%s is an invalid track number.\n", argv[i] );
715 else if( !strcasecmp( argv[i], "--dash" ) )
717 if( ++i == argc )
718 return ERROR_MSG( "--dash requires an argument.\n" );
719 remuxer->subseg_per_seg = atoi( argv[i] );
720 remuxer->dash = 1;
722 else
723 return ERROR_MSG( "unkown option found: %s\n", argv[i] );
725 if( !remuxer->output->root )
726 return ERROR_MSG( "output file name is not specified.\n" );
727 /* Parse track options */
728 /* Get the current track and media parameters */
729 for( int i = 0; i < remuxer->num_input; i++ )
730 for( uint32_t j = 0; j < input[i].file.movie.num_tracks; j++ )
732 input_track_t *in_track = &input[i].file.movie.track[j];
733 if( !in_track->active )
734 continue;
735 track_option[i][j].alternate_group = in_track->track_param.alternate_group;
736 track_option[i][j].ISO_language = in_track->media.param.ISO_language;
737 track_option[i][j].handler_name = in_track->media.param.media_handler_name;
739 /* Set the default language */
740 if( remuxer->default_language )
741 for( int i = 0; i < remuxer->num_input; i++ )
742 for( uint32_t j = 0; j < input[i].file.movie.num_tracks; j++ )
743 track_option[i][j].ISO_language = remuxer->default_language;
744 /* Get the track and media parameters specified by users */
745 for( int i = 0; i < remuxer->num_input; i++ )
747 if( input_file_option[i].num_track_delimiter > input[i].file.movie.num_tracks )
748 return ERROR_MSG( "more track options specified than the actual number of the tracks (%"PRIu32").\n",
749 input[i].file.movie.num_tracks );
750 if( input_file_option[i].num_track_delimiter )
752 track_option[i][0].raw_track_option = strtok( input_file_option[i].whole_track_option, "?" );
753 for( int j = 1; j < input_file_option[i].num_track_delimiter ; j++ )
754 track_option[i][j].raw_track_option = strtok( NULL, "?" );
757 if( parse_track_option( remuxer ) )
758 return ERROR_MSG( "failed to parse track options.\n" );
759 return 0;
762 static void replace_with_valid_brand( remuxer_t *remuxer )
764 static const lsmash_brand_type brand_filter_list[] =
766 ISOM_BRAND_TYPE_3G2A,
767 ISOM_BRAND_TYPE_3GG6,
768 ISOM_BRAND_TYPE_3GG9,
769 ISOM_BRAND_TYPE_3GP4,
770 ISOM_BRAND_TYPE_3GP5,
771 ISOM_BRAND_TYPE_3GP6,
772 ISOM_BRAND_TYPE_3GP7,
773 ISOM_BRAND_TYPE_3GP8,
774 ISOM_BRAND_TYPE_3GP9,
775 ISOM_BRAND_TYPE_3GR6,
776 ISOM_BRAND_TYPE_3GR9,
777 ISOM_BRAND_TYPE_M4A ,
778 ISOM_BRAND_TYPE_M4B ,
779 ISOM_BRAND_TYPE_M4V ,
780 ISOM_BRAND_TYPE_AVC1,
781 ISOM_BRAND_TYPE_DBY1,
782 ISOM_BRAND_TYPE_ISO2,
783 ISOM_BRAND_TYPE_ISO3,
784 ISOM_BRAND_TYPE_ISO4,
785 ISOM_BRAND_TYPE_ISO5,
786 ISOM_BRAND_TYPE_ISO6,
787 ISOM_BRAND_TYPE_ISO7,
788 ISOM_BRAND_TYPE_ISOM,
789 ISOM_BRAND_TYPE_MP41,
790 ISOM_BRAND_TYPE_MP42,
791 ISOM_BRAND_TYPE_QT ,
794 input_t *input = remuxer->input;
795 /* Check the number of video and audio tracks, and the number of video
796 * and audio sample descriptions for the restrictions of 3GPP Basic Profile.
797 * - the maximum number of tracks shall be one for video (or alternatively
798 * one for scene description), one for audio and one for text
799 * - the maximum number of sample entries shall be one per track for video
800 * and audio (but unrestricted for text and scene description) */
801 uint32_t video_track_count = 0;
802 uint32_t audio_track_count = 0;
803 uint32_t video_num_summaries = 0;
804 uint32_t audio_num_summaries = 0;
805 for( int i = 0; i < remuxer->num_input; i++ )
807 input_movie_t *movie = &input[i].file.movie;
808 for( int j = 0; j < movie->num_tracks; j++ )
810 if( movie->track[j].media.param.handler_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK )
812 if( ++video_track_count == 1 )
813 video_num_summaries = movie->track[j].num_summaries;
815 else if( movie->track[j].media.param.handler_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK )
817 if( ++audio_track_count == 1 )
818 audio_num_summaries = movie->track[j].num_summaries;
822 for( int i = 0; i < remuxer->num_input; i++ )
823 for( uint32_t j = 0; j <= input[i].file.param.brand_count; j++ )
825 int invalid = 1;
826 uint32_t *brand = j ? &input[i].file.param.brands[j - 1] : &input[i].file.param.major_brand;
827 uint32_t *version = j ? NULL : &input[i].file.param.minor_version;
828 for( int k = 0; brand_filter_list[k]; k++ )
830 if( *brand == brand_filter_list[k] )
832 if( ((*brand >> 24) & 0xFF) == '3'
833 && ((*brand >> 16) & 0xFF) == 'g'
834 && (((*brand >> 8) & 0xFF) == 'p' || ((*brand >> 8) & 0xFF) == 'r') )
836 if( remuxer->frag_base_track == 0 /* Movie fragments are not allowed in '3gp4' and '3gp5'. */
837 && video_track_count <= 1 && audio_track_count <= 1
838 && video_num_summaries <= 1 && audio_num_summaries <= 1 )
839 continue;
840 /* Replace with the General Profile for maximum compatibility. */
841 if( (*brand & 0xFF) < '6' )
843 /* 3GPP version 6.7.0 General Profile */
844 *brand = ISOM_BRAND_TYPE_3GG6;
845 if( version )
846 *version = 0x00000700;
848 else
849 *brand = LSMASH_4CC( '3', 'g', 'g', *brand & 0xFF );
851 if( remuxer->dash
852 && (*brand == ISOM_BRAND_TYPE_AVC1
853 || (((*brand >> 24) & 0xFF) == 'i'
854 && ((*brand >> 16) & 0xFF) == 's'
855 && ((*brand >> 8) & 0xFF) == 'o'
856 && ((*brand & 0xFF) == 'm' || (*brand & 0xFF) < '6'))) )
857 *brand = ISOM_BRAND_TYPE_ISO6;
858 invalid = 0;
859 break;
862 if( invalid )
864 /* Replace with the 'mp42' brand. */
865 *brand = ISOM_BRAND_TYPE_MP42;
866 if( version )
867 *version = 0;
872 static int set_movie_parameters( remuxer_t *remuxer )
874 int num_input = remuxer->num_input;
875 input_t *input = remuxer->input;
876 output_t *output = remuxer->output;
877 output_file_t *out_file = &output->file;
878 if( remuxer->frag_base_track )
879 out_file->param.mode |= LSMASH_FILE_MODE_FRAGMENTED;
880 int self_containd_segment = (remuxer->dash && remuxer->subseg_per_seg == 0);
881 if( remuxer->dash )
883 if( remuxer->frag_base_track )
885 if( self_containd_segment )
886 out_file->param.mode |= LSMASH_FILE_MODE_INDEX;
887 else
889 out_file->param.mode &= ~LSMASH_FILE_MODE_MEDIA;
890 out_file->param.mode |= LSMASH_FILE_MODE_SEGMENT;
893 else
894 WARNING_MSG( "--dash requires --fragment.\n" );
896 replace_with_valid_brand( remuxer );
897 if( self_containd_segment )
899 out_file->param.major_brand = ISOM_BRAND_TYPE_DASH;
900 out_file->param.minor_version = 0;
902 else
904 /* Pick the most used major_brands. */
905 lsmash_brand_type major_brand [num_input];
906 uint32_t minor_version [num_input];
907 uint32_t major_brand_count[num_input];
908 uint32_t num_major_brand = 0;
909 for( int i = 0; i < num_input; i++ )
911 major_brand [num_major_brand] = input[i].file.param.major_brand;
912 minor_version [num_major_brand] = input[i].file.param.minor_version;
913 major_brand_count[num_major_brand] = 0;
914 for( int j = 0; j < num_input; j++ )
915 if( (major_brand [num_major_brand] == input[j].file.param.major_brand)
916 && (minor_version[num_major_brand] == input[j].file.param.minor_version) )
918 if( i <= j )
919 ++major_brand_count[num_major_brand];
920 else
922 /* This major_brand already exists. Skip this. */
923 major_brand_count[num_major_brand] = 0;
924 --num_major_brand;
925 break;
928 ++num_major_brand;
930 uint32_t most_used_count = 0;
931 for( uint32_t i = 0; i < num_major_brand; i++ )
932 if( major_brand_count[i] > most_used_count )
934 most_used_count = major_brand_count[i];
935 out_file->param.major_brand = major_brand [i];
936 out_file->param.minor_version = minor_version[i];
939 /* Deduplicate compatible brands. */
940 uint32_t num_input_brands = num_input + (self_containd_segment ? 1 : 0);
941 for( int i = 0; i < num_input; i++ )
942 num_input_brands += input[i].file.param.brand_count;
943 lsmash_brand_type input_brands[num_input_brands];
944 num_input_brands = 0;
945 if( self_containd_segment )
946 input_brands[num_input_brands++] = ISOM_BRAND_TYPE_DASH;
947 for( int i = 0; i < num_input; i++ )
949 input_brands[num_input_brands++] = input[i].file.param.major_brand;
950 for( uint32_t j = 0; j < input[i].file.param.brand_count; j++ )
951 if( input[i].file.param.brands[j] )
952 input_brands[num_input_brands++] = input[i].file.param.brands[j];
954 lsmash_brand_type *output_brands = lsmash_malloc_zero( num_input_brands * sizeof(lsmash_brand_type) );
955 if( !output_brands )
956 return ERROR_MSG( "failed to allocate brands for an output file.\n" );
957 uint32_t num_output_brands = 0;
958 for( uint32_t i = 0; i < num_input_brands; i++ )
960 output_brands[num_output_brands] = input_brands[i];
961 for( uint32_t j = 0; j < num_output_brands; j++ )
962 if( output_brands[num_output_brands] == output_brands[j] )
964 /* This brand already exists. Skip this. */
965 --num_output_brands;
966 break;
968 ++num_output_brands;
970 out_file->param.brand_count = num_output_brands;
971 out_file->param.brands = output_brands;
972 /* Set up a file. */
973 out_file->fh = lsmash_set_file( output->root, &out_file->param );
974 if( !out_file->fh )
975 return ERROR_MSG( "failed to add an output file into a ROOT.\n" );
976 out_file->seg_param = out_file->param;
977 /* Check whether a reference chapter track is allowed or not. */
978 if( remuxer->chap_file )
979 for( uint32_t i = 0; i < out_file->param.brand_count; i++ )
981 uint32_t brand = out_file->param.brands[i];
982 /* According to the restrictions of 3GPP Basic Profile,
983 * - there shall be no references between tracks, e.g., a scene description track
984 * shall not refer to a media track since all tracks are on equal footing and
985 * played in parallel by a conforming player.
986 * Therefore, the referenced chapter track is forbidden to use for 3GPP Basic Profile. */
987 if( ((brand >> 24) & 0xFF) == '3'
988 && ((brand >> 16) & 0xFF) == 'g'
989 && ((brand >> 8) & 0xFF) == 'p' )
990 break;
991 /* QuickTime file and iTunes MP4 file can contain the referenced chapter track. */
992 if( brand == ISOM_BRAND_TYPE_QT || brand == ISOM_BRAND_TYPE_M4A
993 || brand == ISOM_BRAND_TYPE_M4B || brand == ISOM_BRAND_TYPE_M4P
994 || brand == ISOM_BRAND_TYPE_M4V )
996 remuxer->ref_chap_available = 1;
997 break;
1000 /* Set the movie timescale in order to match the media timescale if only one track is there. */
1001 lsmash_initialize_movie_parameters( &out_file->movie.param );
1002 if( out_file->movie.num_tracks == 1 )
1003 for( int i = 0; i < remuxer->num_input; i++ )
1005 input_movie_t *in_movie = &input[i].file.movie;
1006 for( uint32_t j = 0; j < in_movie->num_tracks; j++ )
1007 if( in_movie->track[j].active )
1009 out_file->movie.param.timescale = in_movie->track[j].media.param.timescale;
1010 break;
1013 return lsmash_set_movie_parameters( output->root, &out_file->movie.param );
1016 static void set_itunes_metadata( output_t *output, input_t *input, int num_input )
1018 for( int i = 0; i < num_input; i++ )
1019 for( uint32_t j = 0; j < input[i].file.movie.num_itunes_metadata; j++ )
1020 if( lsmash_set_itunes_metadata( output->root, input[i].file.movie.itunes_metadata[j] ) )
1022 WARNING_MSG( "failed to set an iTunes metadata.\n" );
1023 continue;
1027 static int set_starting_point( input_t *input, input_track_t *in_track, uint32_t seek_point, int consider_rap )
1029 if( seek_point == 0 )
1030 return 0;
1031 uint32_t rap_number;
1032 if( lsmash_get_closest_random_accessible_point_from_media_timeline( input->root, in_track->track_ID, 1, &rap_number ) )
1034 if( consider_rap )
1035 return ERROR_MSG( "failed to get the first random accessible point.\n" );
1036 else
1038 WARNING_MSG( "no random access point!\n" );
1039 /* Set number of the first sample to be muxed. */
1040 in_track->current_sample_number = seek_point;
1041 return 0;
1044 /* Get composition delay. */
1045 uint64_t rap_dts;
1046 uint64_t rap_cts;
1047 uint32_t ctd_shift;
1048 if( lsmash_get_dts_from_media_timeline( input->root, in_track->track_ID, rap_number, &rap_dts ) )
1049 return ERROR_MSG( "failed to get CTS of the first random accessible sample of seek point.\n" );
1050 if( lsmash_get_cts_from_media_timeline( input->root, in_track->track_ID, rap_number, &rap_cts ) )
1051 return ERROR_MSG( "failed to get CTS of the first random accessible sample of seek point.\n" );
1052 if( lsmash_get_composition_to_decode_shift_from_media_timeline( input->root, in_track->track_ID, &ctd_shift ) )
1053 return ERROR_MSG( "failed to get composition to decode timeline shfit.\n" );
1054 in_track->composition_delay = rap_cts - rap_dts + ctd_shift;
1055 /* Check if starting point is random accessible. */
1056 if( lsmash_get_closest_random_accessible_point_from_media_timeline( input->root, in_track->track_ID, seek_point, &rap_number ) )
1057 return ERROR_MSG( "failed to get a random accessible point.\n" );
1058 if( rap_number != seek_point )
1060 WARNING_MSG( "starting point you specified is not a random accessible point.\n" );
1061 if( consider_rap )
1063 /* Get duration that should be skipped. */
1064 if( lsmash_get_cts_from_media_timeline( input->root, in_track->track_ID, rap_number, &rap_cts ) )
1065 return ERROR_MSG( "failed to get CTS of the closest and past random accessible sample of starting point.\n" );
1066 uint64_t seek_cts;
1067 if( lsmash_get_cts_from_media_timeline( input->root, in_track->track_ID, seek_point, &seek_cts ) )
1068 return ERROR_MSG( "failed to get CTS of starting point.\n" );
1069 if( rap_cts < seek_cts )
1070 in_track->skip_duration = seek_cts - rap_cts;
1073 /* Set number of the first sample to be muxed. */
1074 in_track->current_sample_number = consider_rap ? rap_number : seek_point;
1075 return 0;
1078 static void exclude_invalid_output_track( output_t *output, output_track_t *out_track,
1079 input_movie_t *in_movie, input_track_t *in_track,
1080 const char *message, ... )
1082 REFRESH_CONSOLE;
1083 eprintf( "[Warning] in %"PRIu32"/%"PRIu32" -> out %"PRIu32": ", in_movie->movie_ID, in_track->track_ID, out_track->track_ID );
1084 va_list args;
1085 va_start( args, message );
1086 vfprintf( stderr, message, args );
1087 va_end( args );
1088 lsmash_delete_track( output->root, out_track->track_ID );
1089 -- output->file.movie.num_tracks;
1090 in_track->active = 0;
1093 static int prepare_output( remuxer_t *remuxer )
1095 input_t *input = remuxer->input;
1096 output_t *output = remuxer->output;
1097 output_movie_t *out_movie = &output->file.movie;
1098 /* Count the number of output tracks. */
1099 for( int i = 0; i < remuxer->num_input; i++ )
1100 out_movie->num_tracks += input[i].file.movie.num_tracks;
1101 for( int i = 0; i < remuxer->num_input; i++ )
1103 input_movie_t *in_movie = &input[i].file.movie;
1104 for( uint32_t j = 0; j < in_movie->num_tracks; j++ )
1106 /* Don't remux tracks specified as 'remove' by a user. */
1107 if( remuxer->track_option[i][j].remove )
1108 in_movie->track[j].active = 0;
1109 if( !in_movie->track[j].active )
1110 -- out_movie->num_tracks;
1113 if( set_movie_parameters( remuxer ) < 0 )
1114 return ERROR_MSG( "failed to set output movie parameters.\n" );
1115 set_itunes_metadata( output, input, remuxer->num_input );
1116 /* Allocate output tracks. */
1117 out_movie->track = lsmash_malloc( out_movie->num_tracks * sizeof(output_track_t) );
1118 if( !out_movie->track )
1119 return ERROR_MSG( "failed to alloc output tracks.\n" );
1120 out_movie->current_track_number = 1;
1121 for( int i = 0; i < remuxer->num_input; i++ )
1123 input_movie_t *in_movie = &input[i].file.movie;
1124 for( uint32_t j = 0; j < in_movie->num_tracks; j++ )
1126 track_media_option *current_track_opt = &remuxer->track_option[i][j];
1127 input_track_t *in_track = &in_movie->track[j];
1128 if( !in_track->active )
1129 continue;
1130 output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ];
1131 out_track->summary_remap = lsmash_malloc_zero( in_track->num_summaries * sizeof(uint32_t) );
1132 if( !out_track->summary_remap )
1133 return ERROR_MSG( "failed to create summary mapping for a track.\n" );
1134 out_track->track_ID = lsmash_create_track( output->root, in_track->media.param.handler_type );
1135 if( !out_track->track_ID )
1136 return ERROR_MSG( "failed to create a track.\n" );
1137 /* Copy track and media parameters except for track_ID. */
1138 out_track->track_param = in_track->track_param;
1139 out_track->media_param = in_track->media.param;
1140 /* Set track and media parameters specified by users */
1141 out_track->track_param.alternate_group = current_track_opt->alternate_group;
1142 out_track->media_param.ISO_language = current_track_opt->ISO_language;
1143 out_track->media_param.media_handler_name = current_track_opt->handler_name;
1144 out_track->track_param.track_ID = out_track->track_ID;
1145 if( current_track_opt->disable )
1146 out_track->track_param.mode &= ~ISOM_TRACK_ENABLED;
1147 if( lsmash_set_track_parameters( output->root, out_track->track_ID, &out_track->track_param ) < 0 )
1149 exclude_invalid_output_track( output, out_track, in_movie, in_track, "failed to set track parameters.\n" );
1150 continue;
1152 if( lsmash_set_media_parameters( output->root, out_track->track_ID, &out_track->media_param ) < 0 )
1154 exclude_invalid_output_track( output, out_track, in_movie, in_track, "failed to set media parameters.\n" );
1155 continue;
1157 lsmash_data_reference_t data_ref = { .index = 1, .location = NULL };
1158 if( lsmash_create_data_reference( output->root, out_track->track_ID, &data_ref, output->file.fh ) < 0 )
1159 return ERROR_MSG( "failed to create a data reference for output movie.\n" );
1160 uint32_t valid_summary_count = 0;
1161 for( uint32_t k = 0; k < in_track->num_summaries; k++ )
1163 if( !in_track->summaries[k].active )
1165 out_track->summary_remap[k] = 0;
1166 continue;
1168 lsmash_summary_t *summary = in_track->summaries[k].summary;
1169 summary->data_ref_index = 1;
1170 if( lsmash_add_sample_entry( output->root, out_track->track_ID, summary ) == 0 )
1172 WARNING_MSG( "failed to append a summary.\n" );
1173 lsmash_cleanup_summary( summary );
1174 in_track->summaries[k].summary = NULL;
1175 in_track->summaries[k].active = 0;
1176 out_track->summary_remap[k] = 0;
1177 continue;
1179 out_track->summary_remap[k] = ++valid_summary_count;
1181 if( valid_summary_count == 0 )
1183 exclude_invalid_output_track( output, out_track, in_movie, in_track, "failed to append all summaries.\n" );
1184 continue;
1186 out_track->last_sample_delta = in_track->last_sample_delta;
1187 if( set_starting_point( input, in_track, current_track_opt->seek, current_track_opt->consider_rap ) < 0 )
1189 exclude_invalid_output_track( output, out_track, in_movie, in_track, "failed to set starting point.\n" );
1190 continue;
1192 out_track->current_sample_number = 1;
1193 out_track->skip_dt_interval = 0;
1194 out_track->last_sample_dts = 0;
1195 ++ out_movie->current_track_number;
1198 if( out_movie->num_tracks == 0 )
1199 return ERROR_MSG( "failed to create the output movie.\n" );
1200 out_movie->current_track_number = 1;
1201 output->current_seg_number = 1;
1202 return 0;
1205 static void set_reference_chapter_track( remuxer_t *remuxer )
1207 if( remuxer->ref_chap_available )
1208 lsmash_create_reference_chapter_track( remuxer->output->root, remuxer->chap_track, remuxer->chap_file );
1211 static int flush_movie_fragment( remuxer_t *remuxer )
1213 input_t *inputs = remuxer->input;
1214 output_t *output = remuxer->output;
1215 output_movie_t *out_movie = &output->file.movie;
1216 uint32_t out_current_track_number = 1;
1217 for( uint32_t i = 1; i <= remuxer->num_input; i++ )
1219 input_t *in = &inputs[i - 1];
1220 input_movie_t *in_movie = &in->file.movie;
1221 for( uint32_t j = 1; j <= in_movie->num_tracks; j++ )
1223 input_track_t *in_track = &in_movie->track[j - 1];
1224 if( !in_track->active )
1225 continue;
1226 output_track_t *out_track = &out_movie->track[out_current_track_number - 1];
1227 if( !in_track->reach_end_of_media_timeline )
1229 lsmash_sample_t sample;
1230 if( lsmash_get_sample_info_from_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number, &sample ) < 0 )
1231 return ERROR_MSG( "failed to get the information of the next sample.\n" );
1232 uint64_t sample_dts = sample.dts - out_track->skip_dt_interval;
1233 if( lsmash_flush_pooled_samples( output->root, out_track->track_ID, sample_dts - out_track->last_sample_dts ) < 0 )
1234 return ERROR_MSG( "failed to flush the rest of samples in a fragment.\n" );
1236 else
1237 if( lsmash_flush_pooled_samples( output->root, out_track->track_ID, out_track->last_sample_delta ) < 0 )
1238 return ERROR_MSG( "failed to flush the rest of samples in a fragment.\n" );
1239 if( ++out_current_track_number > out_movie->num_tracks )
1240 break;
1243 return 0;
1246 static int moov_to_front_callback( void *param, uint64_t written_movie_size, uint64_t total_movie_size )
1248 REFRESH_CONSOLE;
1249 eprintf( "Finalizing: [%5.2lf%%]\r", ((double)written_movie_size / total_movie_size) * 100.0 );
1250 return 0;
1253 static lsmash_adhoc_remux_t moov_to_front =
1255 .func = moov_to_front_callback,
1256 .buffer_size = 4 * 1024 * 1024, /* 4MiB */
1257 .param = NULL
1260 static int open_media_segment( output_t *output, lsmash_file_parameters_t *seg_param )
1262 /* Open a media segment file.
1263 * Each file is named as follows.
1264 * a.mp4
1265 * a_1.mp4
1266 * a_2.mp4
1267 * ...
1268 * a_N.mp4
1269 * N is the number of segment files excluding the initialization segment file.
1271 output_file_t *out_file = &output->file;
1272 int out_file_name_length = strlen( out_file->name );
1273 const char *end = &out_file->name[ out_file_name_length ];
1274 const char *p = end;
1275 while( p >= out_file->name && *p != '.' && *p != '/' && *p != '\\' )
1276 --p;
1277 if( p < out_file->name )
1278 ++p;
1279 if( *p != '.' )
1280 p = end;
1281 int suffix_length = 1;
1282 for( uint32_t i = output->current_seg_number; i; i /= 10 )
1283 ++suffix_length;
1284 int seg_name_length = out_file_name_length + suffix_length;
1285 int suffixless_length = p - out_file->name;
1286 char seg_name[ seg_name_length + 1 ];
1287 seg_name[ seg_name_length ] = '\0';
1288 memcpy( seg_name, out_file->name, suffixless_length );
1289 sprintf( seg_name + suffixless_length, "_%"PRIu32, output->current_seg_number );
1290 if( *p == '.' )
1291 memcpy( seg_name + suffixless_length + suffix_length, p, end - p );
1292 int ret = lsmash_open_file( seg_name, 0, seg_param );
1293 if( ret == 0 )
1294 eprintf( "[Segment] out: %s\n", seg_name );
1295 return ret;
1298 static int switch_segment( remuxer_t *remuxer )
1300 output_t *output = remuxer->output;
1301 output_file_t *out_file = &output->file;
1302 lsmash_file_parameters_t seg_param = { 0 };
1303 if( open_media_segment( output, &seg_param ) < 0 )
1304 return ERROR_MSG( "failed to open an output file for segmentation.\n" );
1305 /* Set up the media segment file.
1306 * Copy the parameters of the previous segment if the previous is not the initialization segment. */
1307 if( out_file->seg_param.mode & LSMASH_FILE_MODE_INITIALIZATION )
1309 uint32_t brand_count = out_file->param.brand_count + 2;
1310 lsmash_brand_type *brands = lsmash_malloc_zero( brand_count * sizeof(lsmash_brand_type) );
1311 if( !brands )
1312 return ERROR_MSG( "failed to allocate brands for an output segment file.\n" );
1313 brands[0] = ISOM_BRAND_TYPE_MSDH;
1314 brands[1] = ISOM_BRAND_TYPE_MSIX;
1315 for( uint32_t i = 0; i < out_file->param.brand_count; i++ )
1316 brands[i + 2] = out_file->param.brands[i];
1317 seg_param.major_brand = ISOM_BRAND_TYPE_MSDH;
1318 seg_param.brand_count = brand_count;
1319 seg_param.brands = brands;
1320 seg_param.mode = LSMASH_FILE_MODE_WRITE | LSMASH_FILE_MODE_FRAGMENTED
1321 | LSMASH_FILE_MODE_BOX | LSMASH_FILE_MODE_MEDIA
1322 | LSMASH_FILE_MODE_INDEX | LSMASH_FILE_MODE_SEGMENT;
1324 else
1326 void *opaque = seg_param.opaque;
1327 seg_param = out_file->seg_param;
1328 seg_param.opaque = opaque;
1330 lsmash_file_t *segment = lsmash_set_file( output->root, &seg_param );
1331 if( !segment )
1332 return ERROR_MSG( "failed to add an output segment file into a ROOT.\n" );
1333 /* Switch to the next segment.
1334 * After switching, close the previous segment if the previous is not the initialization segment. */
1335 if( lsmash_switch_media_segment( output->root, segment, &moov_to_front ) < 0 )
1336 return ERROR_MSG( "failed to switch to the next segment.\n" );
1337 if( !(out_file->seg_param.mode & LSMASH_FILE_MODE_INITIALIZATION) )
1338 return lsmash_close_file( &out_file->seg_param );
1339 out_file->seg_param = seg_param;
1340 return 0;
1343 static int handle_segmentation( remuxer_t *remuxer )
1345 if( remuxer->subseg_per_seg == 0 )
1346 return 0;
1347 output_t *output = remuxer->output;
1348 if( remuxer->subseg_per_seg == output->file.current_subseg_number
1349 || output->current_seg_number == 1 )
1351 if( switch_segment( remuxer ) < 0 )
1353 ERROR_MSG( "failed to switch to a segment.\n" );
1354 return -1;
1356 output->file.current_subseg_number = 1;
1357 ++ output->current_seg_number;
1359 else
1360 ++ output->file.current_subseg_number;
1361 return 0;
1364 static void adapt_description_index( output_track_t *out_track, input_track_t *in_track, lsmash_sample_t *sample )
1366 sample->index = sample->index > in_track->num_summaries ? in_track->num_summaries
1367 : sample->index == 0 ? 1
1368 : sample->index;
1369 sample->index = out_track->summary_remap[ sample->index - 1 ];
1370 if( in_track->current_sample_index == 0 )
1371 in_track->current_sample_index = sample->index;
1374 static void adjust_timestamp( output_track_t *out_track, lsmash_sample_t *sample )
1376 /* The first DTS must be 0. */
1377 if( out_track->current_sample_number == 1 )
1378 out_track->skip_dt_interval = sample->dts;
1379 if( out_track->skip_dt_interval )
1381 sample->dts -= out_track->skip_dt_interval;
1382 sample->cts -= out_track->skip_dt_interval;
1386 static int do_remux( remuxer_t *remuxer )
1388 #define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b))
1389 input_t *inputs = remuxer->input;
1390 output_t *output = remuxer->output;
1391 output_movie_t *out_movie = &output->file.movie;
1392 set_reference_chapter_track( remuxer );
1393 double largest_dts = 0; /* in seconds */
1394 double frag_base_dts = 0; /* in seconds */
1395 uint32_t input_movie_number = 1;
1396 uint32_t num_consecutive_sample_skip = 0;
1397 uint32_t num_active_input_tracks = out_movie->num_tracks;
1398 uint64_t total_media_size = 0;
1399 uint8_t sample_count = 0;
1400 uint8_t pending_flush_fragments = (remuxer->frag_base_track != 0); /* For non-fragmented movie, always set to 0. */
1401 while( 1 )
1403 input_t *in = &inputs[input_movie_number - 1];
1404 input_movie_t *in_movie = &in->file.movie;
1405 input_track_t *in_track = &in_movie->track[ in_movie->current_track_number - 1 ];
1406 if( !in_track->active )
1408 /* Move the next track. */
1409 if( ++ in_movie->current_track_number > in_movie->num_tracks )
1411 /* Move the next input movie. */
1412 in_movie->current_track_number = 1;
1413 ++input_movie_number;
1415 if( input_movie_number > remuxer->num_input )
1416 input_movie_number = 1; /* Back the first input movie. */
1417 continue;
1419 /* Try append a sample in an input track where we didn't reach the end of media timeline. */
1420 if( !in_track->reach_end_of_media_timeline )
1422 lsmash_sample_t *sample = in_track->sample;
1423 /* Get a new sample data if the track doesn't hold any one. */
1424 if( !sample )
1426 sample = lsmash_get_sample_from_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number );
1427 if( sample )
1429 output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ];
1430 adapt_description_index( out_track, in_track, sample );
1431 adjust_timestamp( out_track, sample );
1432 in_track->sample = sample;
1433 in_track->dts = (double)sample->dts / in_track->media.param.timescale;
1435 else
1437 if( lsmash_check_sample_existence_in_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number ) )
1439 ERROR_MSG( "failed to get a sample.\n" );
1440 break;
1442 lsmash_sample_t sample_info = { 0 };
1443 if( lsmash_get_sample_info_from_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number, &sample_info ) < 0 )
1445 /* No more appendable samples in this track. */
1446 in_track->sample = NULL;
1447 in_track->reach_end_of_media_timeline = 1;
1448 if( --num_active_input_tracks == 0 )
1449 break; /* end of muxing */
1451 else
1453 ERROR_MSG( "failed to get a sample.\n" );
1454 break;
1458 if( sample )
1460 /* Flushing the active movie fragment is pending until random accessible point sample within all active tracks are ready. */
1461 if( remuxer->frag_base_track )
1463 if( pending_flush_fragments == 0 )
1465 if( remuxer->frag_base_track == out_movie->current_track_number
1466 && sample->prop.ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1468 pending_flush_fragments = 1;
1469 frag_base_dts = in_track->dts;
1472 else if( num_consecutive_sample_skip == num_active_input_tracks || total_media_size == 0 )
1474 if( flush_movie_fragment( remuxer ) < 0 )
1476 ERROR_MSG( "failed to flush a movie fragment.\n" );
1477 break;
1479 if( handle_segmentation( remuxer ) < 0 )
1480 break;
1481 if( lsmash_create_fragment_movie( output->root ) < 0 )
1483 ERROR_MSG( "failed to create a movie fragment.\n" );
1484 break;
1486 pending_flush_fragments = 0;
1489 /* Append a sample if meeting a condition. */
1490 int append = 0;
1491 int need_new_fragment = (remuxer->frag_base_track && sample->index != in_track->current_sample_index);
1492 if( pending_flush_fragments == 0 )
1493 append = (in_track->dts <= largest_dts || num_consecutive_sample_skip == num_active_input_tracks) && !need_new_fragment;
1494 else if( remuxer->frag_base_track != out_movie->current_track_number && !need_new_fragment )
1496 /* Wait as much as possible both to make the last sample within each track fragment close to the DTS of
1497 * the first sample within the track fragment corresponding to the base track within the next movie
1498 * fragment and to make all the track fragments within the next movie fragment start with RAP. */
1499 if( sample->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1500 append = 1;
1501 else
1503 /* Check the DTS and random accessibilities of the next sample. */
1504 lsmash_sample_t info;
1505 if( lsmash_get_sample_info_from_media_timeline( in->root, in_track->track_ID, in_track->current_sample_number + 1, &info ) < 0 )
1506 append = 0;
1507 else
1508 append = (info.prop.ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE)
1509 && ((double)info.dts / in_track->media.param.timescale <= frag_base_dts);
1512 if( append )
1514 if( sample->index )
1516 output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ];
1517 uint64_t sample_size = sample->length; /* sample might be deleted internally after appending. */
1518 uint64_t last_sample_dts = sample->dts; /* same as above */
1519 uint32_t sample_index = sample->index; /* same as above */
1520 /* Append a sample into output movie. */
1521 if( lsmash_append_sample( output->root, out_track->track_ID, sample ) < 0 )
1523 lsmash_delete_sample( sample );
1524 return ERROR_MSG( "failed to append a sample.\n" );
1526 largest_dts = LSMASH_MAX( largest_dts, in_track->dts );
1527 in_track->sample = NULL;
1528 in_track->current_sample_number += 1;
1529 in_track->current_sample_index = sample_index;
1530 out_track->current_sample_number += 1;
1531 out_track->last_sample_dts = last_sample_dts;
1532 num_consecutive_sample_skip = 0;
1533 total_media_size += sample_size;
1534 /* Print, per 256 samples, total size of imported media. */
1535 if( ++sample_count == 0 )
1536 eprintf( "Importing: %"PRIu64" bytes\r", total_media_size );
1538 else
1540 lsmash_delete_sample( sample );
1541 in_track->sample = NULL;
1542 in_track->current_sample_number += 1;
1545 else
1546 ++num_consecutive_sample_skip; /* Skip appendig sample. */
1549 /* Move the next track. */
1550 if( ++ in_movie->current_track_number > in_movie->num_tracks )
1552 /* Move the next input movie. */
1553 in_movie->current_track_number = 1;
1554 ++input_movie_number;
1556 if( input_movie_number > remuxer->num_input )
1557 input_movie_number = 1; /* Back the first input movie. */
1558 if( ++ out_movie->current_track_number > out_movie->num_tracks )
1559 out_movie->current_track_number = 1; /* Back the first track in the output movie. */
1561 for( uint32_t i = 0; i < out_movie->num_tracks; i++ )
1562 if( lsmash_flush_pooled_samples( output->root, out_movie->track[i].track_ID, out_movie->track[i].last_sample_delta ) )
1563 return ERROR_MSG( "failed to flush samples.\n" );
1564 return 0;
1565 #undef LSMASH_MAX
1568 static int construct_timeline_maps( remuxer_t *remuxer )
1570 input_t *input = remuxer->input;
1571 output_t *output = remuxer->output;
1572 output_movie_t *out_movie = &output->file.movie;
1573 track_media_option **track_option = remuxer->track_option;
1574 out_movie->current_track_number = 1;
1575 for( int i = 0; i < remuxer->num_input; i++ )
1576 for( uint32_t j = 0; j < input[i].file.movie.num_tracks; j++ )
1578 input_track_t *in_track = &input[i].file.movie.track[j];
1579 if( !in_track->active )
1580 continue;
1581 output_track_t *out_track = &out_movie->track[ out_movie->current_track_number ++ - 1 ];
1582 if( track_option[i][j].seek )
1584 /* Reconstruct timeline maps. */
1585 if( lsmash_delete_explicit_timeline_map( output->root, out_track->track_ID ) )
1586 return ERROR_MSG( "failed to delete explicit timeline maps.\n" );
1587 uint32_t movie_timescale = lsmash_get_movie_timescale( output->root );
1588 uint32_t media_timescale = lsmash_get_media_timescale( output->root, out_track->track_ID );
1589 if( !media_timescale )
1590 return ERROR_MSG( "media timescale is broken.\n" );
1591 double timescale_convert_multiplier = (double)movie_timescale / media_timescale;
1592 lsmash_edit_t edit;
1593 edit.start_time = in_track->composition_delay + in_track->skip_duration;
1594 if( edit.start_time )
1596 uint64_t empty_duration = edit.start_time + lsmash_get_composition_to_decode_shift( output->root, out_track->track_ID );
1597 lsmash_edit_t empty_edit;
1598 empty_edit.duration = empty_duration * timescale_convert_multiplier + 0.5;
1599 empty_edit.start_time = ISOM_EDIT_MODE_EMPTY;
1600 empty_edit.rate = ISOM_EDIT_MODE_NORMAL;
1601 if( lsmash_create_explicit_timeline_map( output->root, out_track->track_ID, empty_edit ) )
1602 return ERROR_MSG( "failed to create a empty duration.\n" );
1604 if( remuxer->frag_base_track == 0 )
1605 edit.duration = (out_track->last_sample_dts + out_track->last_sample_delta - in_track->skip_duration) * timescale_convert_multiplier;
1606 else
1607 edit.duration = ISOM_EDIT_DURATION_IMPLICIT;
1608 edit.rate = ISOM_EDIT_MODE_NORMAL;
1609 if( lsmash_create_explicit_timeline_map( output->root, out_track->track_ID, edit ) )
1610 return ERROR_MSG( "failed to create a explicit timeline map.\n" );
1612 else if( lsmash_copy_timeline_map( output->root, out_track->track_ID, input[i].root, in_track->track_ID ) )
1613 return ERROR_MSG( "failed to copy timeline maps.\n" );
1615 out_movie->current_track_number = 1;
1616 return 0;
1619 static int finish_movie( remuxer_t *remuxer )
1621 output_t *output = remuxer->output;
1622 /* Set chapter list */
1623 if( remuxer->chap_file )
1624 lsmash_set_tyrant_chapter( output->root, remuxer->chap_file, remuxer->add_bom_to_chpl );
1625 /* Finish muxing. */
1626 REFRESH_CONSOLE;
1627 if( lsmash_finish_movie( output->root, &moov_to_front ) )
1628 return -1;
1629 return remuxer->frag_base_track ? 0 : lsmash_write_lsmash_indicator( output->root );
1632 int main( int argc, char *argv[] )
1634 if ( argc < 2 )
1636 display_help();
1637 return -1;
1639 else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) )
1641 display_help();
1642 return 0;
1644 else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) )
1646 display_version();
1647 return 0;
1649 else if( argc < 5 )
1651 display_help();
1652 return -1;
1655 lsmash_get_mainargs( &argc, &argv );
1656 int num_input = 0;
1657 for( int i = 1 ; i < argc ; i++ )
1658 if( !strcasecmp( argv[i], "-i" ) || !strcasecmp( argv[i], "--input" ) )
1659 num_input++;
1660 if( !num_input )
1661 return ERROR_MSG( "no input file specified.\n" );
1662 output_t output = { 0 };
1663 input_t input[ num_input ];
1664 track_media_option *track_option[ num_input ];
1665 remuxer_t remuxer =
1667 .output = &output,
1668 .input = input,
1669 .track_option = track_option,
1670 .num_input = num_input,
1671 .add_bom_to_chpl = 0,
1672 .ref_chap_available = 0,
1673 .chap_track = 1,
1674 .chap_file = NULL,
1675 .default_language = 0,
1676 .frag_base_track = 0,
1677 .subseg_per_seg = 0,
1678 .dash = 0
1680 memset( input, 0, num_input * sizeof(input_t) );
1681 memset( track_option, 0, num_input * sizeof(track_media_option *) );
1682 if( parse_cli_option( argc, argv, &remuxer ) )
1683 return REMUXER_ERR( "failed to parse command line options.\n" );
1684 if( prepare_output( &remuxer ) )
1685 return REMUXER_ERR( "failed to set up preparation for output.\n" );
1686 if( remuxer.frag_base_track && construct_timeline_maps( &remuxer ) )
1687 return REMUXER_ERR( "failed to construct timeline maps.\n" );
1688 if( do_remux( &remuxer ) )
1689 return REMUXER_ERR( "failed to remux movies.\n" );
1690 if( remuxer.frag_base_track == 0 && construct_timeline_maps( &remuxer ) )
1691 return REMUXER_ERR( "failed to construct timeline maps.\n" );
1692 if( finish_movie( &remuxer ) )
1693 return REMUXER_ERR( "failed to finish output movie.\n" );
1694 REFRESH_CONSOLE;
1695 eprintf( "%s completed!\n", !remuxer.dash || remuxer.subseg_per_seg == 0 ? "Remuxing" : "Segmentation" );
1696 cleanup_remuxer( &remuxer );
1697 return 0;