1 /*****************************************************************************
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. */
37 uint32_t last_sample_delta
;
38 uint32_t current_sample_number
;
39 uint32_t *summary_remap
;
40 uint64_t skip_dt_interval
;
41 uint64_t last_sample_dts
;
42 lsmash_track_parameters_t track_param
;
43 lsmash_media_parameters_t media_param
;
48 output_track_t
*track
;
49 lsmash_movie_parameters_t param
;
51 uint32_t current_track_number
;
58 lsmash_file_parameters_t param
;
59 lsmash_file_parameters_t seg_param
;
61 uint32_t current_subseg_number
;
68 uint32_t current_seg_number
;
74 lsmash_summary_t
*summary
;
80 lsmash_file_parameters_t param
;
85 lsmash_media_parameters_t param
;
86 uint32_t num_data_refs
;
87 input_data_ref_t
*data_refs
;
93 lsmash_sample_t
*sample
;
95 uint64_t composition_delay
;
96 uint64_t skip_duration
;
97 int reach_end_of_media_timeline
;
99 uint32_t last_sample_delta
;
100 uint32_t current_sample_number
;
101 uint32_t current_sample_index
;
102 uint32_t num_summaries
;
103 input_summary_t
*summaries
;
104 lsmash_track_parameters_t track_param
;
110 input_track_t
*track
;
111 lsmash_itunes_metadata_t
*itunes_metadata
;
112 lsmash_movie_parameters_t param
;
115 uint32_t num_itunes_metadata
;
116 uint32_t current_track_number
;
122 lsmash_file_parameters_t param
;
134 char *raw_track_option
;
137 int16_t alternate_group
;
138 uint16_t ISO_language
;
142 } track_media_option
;
148 track_media_option
**track_option
;
151 int ref_chap_available
;
154 uint16_t default_language
;
155 uint32_t frag_base_track
;
156 uint32_t subseg_per_seg
;
162 char *whole_track_option
;
163 int num_track_delimiter
;
166 static void cleanup_input_movie( input_t
*input
)
170 input_movie_t
*in_movie
= &input
->file
.movie
;
171 if( in_movie
->itunes_metadata
)
173 for( uint32_t i
= 0; i
< in_movie
->num_itunes_metadata
; i
++ )
174 lsmash_cleanup_itunes_metadata( &in_movie
->itunes_metadata
[i
] );
175 lsmash_freep( &in_movie
->itunes_metadata
);
177 if( in_movie
->track
)
179 for( uint32_t i
= 0; i
< in_movie
->num_tracks
; i
++ )
181 input_track_t
*in_track
= &in_movie
->track
[i
];
182 if( in_track
->summaries
)
184 for( uint32_t j
= 0; j
< in_track
->num_summaries
; j
++ )
185 lsmash_cleanup_summary( in_track
->summaries
[j
].summary
);
186 lsmash_free( in_track
->summaries
);
188 input_media_t
*in_media
= &in_track
->media
;
189 for( uint32_t j
= 0; j
< in_media
->num_data_refs
; j
++ )
190 if( input
->file
.fh
!= in_media
->data_refs
[j
].fh
)
191 lsmash_close_file( &in_media
->data_refs
[j
].param
);
192 lsmash_free( in_media
->data_refs
);
194 lsmash_freep( &in_movie
->track
);
196 lsmash_close_file( &input
->file
.param
);
197 lsmash_destroy_root( input
->root
);
201 static void cleanup_output_movie( output_t
*output
)
205 output_movie_t
*out_movie
= &output
->file
.movie
;
206 if( out_movie
->track
)
208 for( uint32_t i
= 0; i
< out_movie
->num_tracks
; i
++ )
209 lsmash_free( out_movie
->track
[i
].summary_remap
);
210 lsmash_freep( &out_movie
->track
);
212 if( !(output
->file
.seg_param
.mode
& LSMASH_FILE_MODE_INITIALIZATION
) )
214 lsmash_freep( &output
->file
.seg_param
.brands
);
215 lsmash_close_file( &output
->file
.seg_param
);
217 lsmash_freep( &output
->file
.param
.brands
);
218 lsmash_close_file( &output
->file
.param
);
219 lsmash_destroy_root( output
->root
);
223 static void cleanup_remuxer( remuxer_t
*remuxer
)
225 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
229 cleanup_input_movie( &remuxer
->input
[i
] );
230 lsmash_free( remuxer
->input
);
232 if( remuxer
->track_option
)
234 if( remuxer
->track_option
[i
] )
235 lsmash_free( remuxer
->track_option
[i
] );
236 lsmash_free( remuxer
->track_option
);
239 cleanup_output_movie( remuxer
->output
);
242 #define eprintf( ... ) fprintf( stderr, __VA_ARGS__ )
243 #define REFRESH_CONSOLE eprintf( " \r" )
245 static int remuxer_error( remuxer_t
*remuxer
, const char *message
, ... )
247 cleanup_remuxer( remuxer
);
249 eprintf( "[Error] " );
251 va_start( args
, message
);
252 vfprintf( stderr
, message
, args
);
257 static int error_message( const char *message
, ... )
260 eprintf( "[Error] " );
262 va_start( args
, message
);
263 vfprintf( stderr
, message
, args
);
268 static int warning_message( const char *message
, ... )
271 eprintf( "[Warning] " );
273 va_start( args
, message
);
274 vfprintf( stderr
, message
, args
);
279 #define REMUXER_ERR( ... ) remuxer_error( &remuxer, __VA_ARGS__ )
280 #define ERROR_MSG( ... ) error_message( __VA_ARGS__ )
281 #define WARNING_MSG( ... ) warning_message( __VA_ARGS__ )
283 static void display_version( void )
286 "L-SMASH isom/mov re-muliplexer rev%s %s\n"
288 "Copyright (C) 2011-2014 L-SMASH project\n",
289 LSMASH_REV
, LSMASH_GIT_HASH
, __DATE__
, __TIME__
);
292 static void display_help( void )
296 "Usage: remuxer -i input1 [-i input2 -i input3 ...] -o output\n"
298 " --help Display help.\n"
299 " --version Display version information.\n"
300 " --chapter <string> Set chapters from the file.\n"
301 " --chpl-with-bom Add UTF-8 BOM to the chapter strings\n"
302 " in the chapter list. (experimental)\n"
303 " --chapter-track <integer> Set which track the chapter applies to.\n"
304 " This option takes effect only when reference\n"
305 " chapter is available.\n"
306 " If this option is not used, it defaults to 1.\n"
307 " --language <string> Specify the default language for all the output tracks.\n"
308 " This option is overridden by the track options.\n"
309 " --fragment <integer> Enable fragmentation per random accessible point.\n"
310 " Set which track the fragmentation is based on.\n"
311 " --dash <integer> Enable DASH ISOBMFF-based Media segmentation.\n"
312 " The value is the number of subsegments per segment.\n"
313 " If zero, Indexed self-initializing Media Segment.\n"
314 " This option requires --fragment.\n"
316 " remove Remove this track\n"
317 " disable Disable this track\n"
318 " language=<string> Specify media language\n"
319 " alternate-group=<integer> Specify alternate group\n"
320 " handler=<string> Set media handler name\n"
321 " seek=<integer> Specify starting point in media\n"
322 " safe-seek=<integer> Same as seek except for considering random accessible point\n"
323 " Media starts from the closest random accessible point\n"
324 "How to use track options:\n"
325 " -i input?[track_number1]:[track_option1],[track_option2]?[track_number2]:...\n"
327 " remuxer -i input1 -i input2?2:alternate-group=1?3:language=jpn,alternate-group=1 -o output\n" );
330 static char *duplicate_string( char *src
)
334 int dst_size
= strlen( src
) + 1;
335 char *dst
= lsmash_malloc( dst_size
);
338 memcpy( dst
, src
, dst_size
);
342 static int get_itunes_metadata( lsmash_root_t
*root
, uint32_t metadata_number
, lsmash_itunes_metadata_t
*metadata
)
344 memset( metadata
, 0, sizeof(lsmash_itunes_metadata_t
) );
345 if( lsmash_get_itunes_metadata( root
, metadata_number
, metadata
) )
347 lsmash_itunes_metadata_t shadow
= *metadata
;
348 metadata
->meaning
= NULL
;
349 metadata
->name
= NULL
;
350 memset( &metadata
->value
, 0, sizeof(lsmash_itunes_metadata_value_t
) );
353 metadata
->meaning
= duplicate_string( shadow
.meaning
);
354 if( !metadata
->meaning
)
359 metadata
->name
= duplicate_string( shadow
.name
);
360 if( !metadata
->name
)
363 if( shadow
.type
== ITUNES_METADATA_TYPE_STRING
)
365 metadata
->value
.string
= duplicate_string( shadow
.value
.string
);
366 if( !metadata
->value
.string
)
369 else if( shadow
.type
== ITUNES_METADATA_TYPE_BINARY
)
371 metadata
->value
.binary
.data
= lsmash_malloc( shadow
.value
.binary
.size
);
372 if( !metadata
->value
.binary
.data
)
374 memcpy( metadata
->value
.binary
.data
, shadow
.value
.binary
.data
, shadow
.value
.binary
.size
);
375 metadata
->value
.binary
.size
= shadow
.value
.binary
.size
;
376 metadata
->value
.binary
.subtype
= shadow
.value
.binary
.subtype
;
379 metadata
->value
= shadow
.value
;
382 lsmash_freep( &metadata
->meaning
);
383 lsmash_freep( &metadata
->name
);
387 static inline int is_relative_path( const char *path
)
389 return path
[0] == '/' || path
[0] == '\\' || (path
[0] != '\0' && path
[1] == ':') ? 0 : 1;
392 static int input_data_reference
396 input_data_ref_t
*in_data_ref
,
397 lsmash_data_reference_t
*data_ref
400 if( lsmash_open_file( data_ref
->location
, 1, &in_data_ref
->param
) < 0 )
402 WARNING_MSG( "failed to open an external media file.\n" );
405 in_data_ref
->param
.mode
|= LSMASH_FILE_MODE_MEDIA
;
406 in_data_ref
->fh
= lsmash_set_file( input
->root
, &in_data_ref
->param
);
407 if( !in_data_ref
->fh
)
409 WARNING_MSG( "failed to set an external media file as a data reference.\n" );
412 if( lsmash_assign_data_reference( input
->root
, track_ID
, data_ref
->index
, in_data_ref
->fh
) < 0 )
414 WARNING_MSG( "failed to assign an external media a data reference.\n" );
420 static int get_movie( input_t
*input
, char *input_name
)
422 if( !strcmp( input_name
, "-" ) )
423 return ERROR_MSG( "standard input not supported.\n" );
424 /* Read an input file. */
425 input
->root
= lsmash_create_root();
427 return ERROR_MSG( "failed to create a ROOT for an input file.\n" );
428 input_file_t
*in_file
= &input
->file
;
429 if( lsmash_open_file( input_name
, 1, &in_file
->param
) < 0 )
430 return ERROR_MSG( "failed to open an input file.\n" );
431 in_file
->fh
= lsmash_set_file( input
->root
, &in_file
->param
);
433 return ERROR_MSG( "failed to add an input file into a ROOT.\n" );
434 if( lsmash_read_file( in_file
->fh
, &in_file
->param
) < 0 )
435 return ERROR_MSG( "failed to read an input file\n" );
436 /* Get iTunes metadata. */
437 input_movie_t
*in_movie
= &in_file
->movie
;
438 in_movie
->num_itunes_metadata
= lsmash_count_itunes_metadata( input
->root
);
439 if( in_movie
->num_itunes_metadata
)
441 in_movie
->itunes_metadata
= lsmash_malloc( in_movie
->num_itunes_metadata
* sizeof(lsmash_itunes_metadata_t
) );
442 if( !in_movie
->itunes_metadata
)
443 return ERROR_MSG( "failed to alloc iTunes metadata.\n" );
444 uint32_t itunes_metadata_count
= 0;
445 for( uint32_t i
= 1; i
<= in_movie
->num_itunes_metadata
; i
++ )
447 if( get_itunes_metadata( input
->root
, i
, &in_movie
->itunes_metadata
[itunes_metadata_count
] ) )
449 WARNING_MSG( "failed to get an iTunes metadata.\n" );
452 ++itunes_metadata_count
;
454 in_movie
->num_itunes_metadata
= itunes_metadata_count
;
456 in_movie
->current_track_number
= 1;
457 lsmash_initialize_movie_parameters( &in_movie
->param
);
458 if( lsmash_get_movie_parameters( input
->root
, &in_movie
->param
) )
459 return ERROR_MSG( "failed to get movie parameters.\n" );
460 uint32_t num_tracks
= in_movie
->num_tracks
= in_movie
->param
.number_of_tracks
;
462 input_track_t
*in_track
= in_movie
->track
= lsmash_malloc_zero( num_tracks
* sizeof(input_track_t
) );
464 return ERROR_MSG( "failed to alloc input tracks.\n" );
465 for( uint32_t i
= 0; i
< num_tracks
; i
++ )
467 in_track
[i
].track_ID
= lsmash_get_track_ID( input
->root
, i
+ 1 );
468 if( !in_track
[i
].track_ID
)
469 return ERROR_MSG( "failed to get track_ID.\n" );
471 for( uint32_t i
= 0; i
< num_tracks
; i
++ )
473 lsmash_initialize_track_parameters( &in_track
[i
].track_param
);
474 if( lsmash_get_track_parameters( input
->root
, in_track
[i
].track_ID
, &in_track
[i
].track_param
) )
476 WARNING_MSG( "failed to get track parameters.\n" );
479 lsmash_initialize_media_parameters( &in_track
[i
].media
.param
);
480 if( lsmash_get_media_parameters( input
->root
, in_track
[i
].track_ID
, &in_track
[i
].media
.param
) )
482 WARNING_MSG( "failed to get media parameters.\n" );
485 uint32_t data_ref_count
= lsmash_count_data_reference( input
->root
, in_track
[i
].track_ID
);
486 if( data_ref_count
== 0 )
488 WARNING_MSG( "failed to get the number of data references.\n" );
491 in_track
[i
].media
.data_refs
= lsmash_malloc_zero( data_ref_count
* sizeof(input_data_ref_t
) );
492 if( !in_track
[i
].media
.data_refs
)
494 WARNING_MSG( "failed to allocate handles of data reference.\n" );
497 for( uint32_t j
= 0; j
< data_ref_count
; j
++ )
499 input_data_ref_t
*in_data_ref
= &in_track
[i
].media
.data_refs
[j
];
500 lsmash_data_reference_t data_ref
= { .index
= j
+ 1 };
501 if( lsmash_get_data_reference( input
->root
, in_track
[i
].track_ID
, &data_ref
) < 0 )
503 WARNING_MSG( "failed to get a data references.\n" );
506 if( data_ref
.location
)
508 if( is_relative_path( data_ref
.location
) && !is_relative_path( input_name
) )
510 /* Append the directory path from the referencing file. */
511 int location_length
= strlen( data_ref
.location
);
512 int input_name_length
= strlen( input_name
);
513 char *p
= input_name
+ input_name_length
;
514 while( p
!= input_name
&& *p
!= '/' && *p
!= '\\' )
516 int relative_path_length
= p
== input_name
? 2 : p
- input_name
;
517 char *location
= lsmash_malloc( relative_path_length
+ location_length
+ 2 );
520 memcpy( location
, input_name
, relative_path_length
);
521 memcpy( location
+ relative_path_length
+ 1, data_ref
.location
, location_length
);
522 location
[relative_path_length
] = '/';
523 location
[relative_path_length
+ location_length
+ 1] = '\0';
524 lsmash_cleanup_data_reference( &data_ref
);
525 data_ref
.location
= location
;
528 int ret
= input_data_reference( input
, in_track
[i
].track_ID
, in_data_ref
, &data_ref
);
529 lsmash_cleanup_data_reference( &data_ref
);
535 in_data_ref
->fh
= in_file
->fh
;
536 in_data_ref
->param
= in_file
->param
;
539 if( lsmash_construct_timeline( input
->root
, in_track
[i
].track_ID
) )
541 WARNING_MSG( "failed to construct timeline.\n" );
544 if( lsmash_get_last_sample_delta_from_media_timeline( input
->root
, in_track
[i
].track_ID
, &in_track
[i
].last_sample_delta
) )
546 WARNING_MSG( "failed to get the last sample delta.\n" );
549 in_track
[i
].num_summaries
= lsmash_count_summary( input
->root
, in_track
[i
].track_ID
);
550 if( in_track
[i
].num_summaries
== 0 )
552 WARNING_MSG( "failed to find valid summaries.\n" );
555 in_track
[i
].summaries
= lsmash_malloc_zero( in_track
[i
].num_summaries
* sizeof(input_summary_t
) );
556 if( !in_track
[i
].summaries
)
557 return ERROR_MSG( "failed to alloc input summaries.\n" );
558 for( uint32_t j
= 0; j
< in_track
[i
].num_summaries
; j
++ )
560 lsmash_summary_t
*summary
= lsmash_get_summary( input
->root
, in_track
[i
].track_ID
, j
+ 1 );
563 WARNING_MSG( "failed to get a summary.\n" );
566 if( !LSMASH_FLAGS_SATISFIED( lsmash_check_codec_support( summary
->sample_type
), LSMASH_CODEC_SUPPORT_FLAG_REMUX
) )
568 lsmash_cleanup_summary( summary
);
569 WARNING_MSG( "no support to remux this stream.\n" );
572 in_track
[i
].summaries
[j
].summary
= summary
;
573 in_track
[i
].summaries
[j
].active
= 1;
575 in_track
[i
].active
= 1;
576 in_track
[i
].current_sample_number
= 1;
577 in_track
[i
].sample
= NULL
;
579 in_track
[i
].composition_delay
= 0;
580 in_track
[i
].skip_duration
= 0;
582 lsmash_destroy_children( lsmash_file_as_box( in_file
->fh
) );
586 static int parse_track_option( remuxer_t
*remuxer
)
588 track_media_option
**track
= remuxer
->track_option
;
589 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
590 for( uint32_t j
= 0; j
< remuxer
->input
[i
].file
.movie
.num_tracks
; j
++ )
592 track_media_option
*current_track_opt
= &track
[i
][j
];
593 if( current_track_opt
->raw_track_option
== NULL
)
595 if( !strchr( current_track_opt
->raw_track_option
, ':' )
596 || strchr( current_track_opt
->raw_track_option
, ':' ) == current_track_opt
->raw_track_option
)
597 return ERROR_MSG( "track number is not specified in %s\n", current_track_opt
->raw_track_option
);
598 if( strchr( current_track_opt
->raw_track_option
, ':' ) != strrchr( current_track_opt
->raw_track_option
, ':' ) )
599 return ERROR_MSG( "multiple colons inside one track option in %s.\n", current_track_opt
->raw_track_option
);
600 uint32_t track_number
= atoi( strtok( current_track_opt
->raw_track_option
, ":" ) );
601 if( track_number
== 0 )
602 return ERROR_MSG( "%s is an invalid track number.\n", strtok( current_track_opt
->raw_track_option
, ":" ) );
603 if( track_number
> remuxer
->input
[i
].file
.movie
.num_tracks
)
604 return ERROR_MSG( "%d is an invalid track number.\n", track_number
);
606 while( (track_option
= strtok( NULL
, "," )) != NULL
)
608 if( strchr( track_option
, '=' ) != strrchr( track_option
, '=' ) )
609 return ERROR_MSG( "multiple equal signs inside one track option in %s\n", track_option
);
610 current_track_opt
= &track
[i
][track_number
- 1];
611 if( strstr( track_option
, "remove" ) )
613 current_track_opt
->remove
= 1;
614 /* No need to parse track options for this track anymore. */
617 else if( strstr( track_option
, "disable" ) )
618 current_track_opt
->disable
= 1;
619 else if( strstr( track_option
, "alternate-group=" ) )
621 char *track_parameter
= strchr( track_option
, '=' ) + 1;
622 current_track_opt
->alternate_group
= atoi( track_parameter
);
624 else if( strstr( track_option
, "language=" ) )
626 char *track_parameter
= strchr( track_option
, '=' ) + 1;
627 current_track_opt
->ISO_language
= lsmash_pack_iso_language( track_parameter
);
629 else if( strstr( track_option
, "handler=" ) )
631 char *track_parameter
= strchr( track_option
, '=' ) + 1;
632 current_track_opt
->handler_name
= track_parameter
;
634 else if( strstr( track_option
, "safe-seek=" ) )
636 char *track_parameter
= strchr( track_option
, '=' ) + 1;
637 current_track_opt
->seek
= atoi( track_parameter
);
638 current_track_opt
->consider_rap
= 1;
640 else if( strstr( track_option
, "seek=" ) )
642 char *track_parameter
= strchr( track_option
, '=' ) + 1;
643 current_track_opt
->seek
= atoi( track_parameter
);
646 return ERROR_MSG( "unknown track option %s\n", track_option
);
652 static int parse_cli_option( int argc
, char **argv
, remuxer_t
*remuxer
)
654 #define FAILED_PARSE_CLI_OPTION( ... ) \
657 lsmash_free( input_file_option ); \
658 return ERROR_MSG( __VA_ARGS__ ); \
661 input_t
*input
= remuxer
->input
;
662 track_media_option
**track_option
= remuxer
->track_option
;
663 file_option
*input_file_option
= lsmash_malloc( remuxer
->num_input
* sizeof(file_option
) );
664 if( !input_file_option
)
665 return ERROR_MSG( "failed to allocate per-file option.\n" );
666 int input_movie_number
= 0;
667 for( int i
= 1; i
< argc
; i
++ )
669 /* Get input movies. */
670 if( !strcasecmp( argv
[i
], "-i" ) || !strcasecmp( argv
[i
], "--input" ) ) /* input file */
673 FAILED_PARSE_CLI_OPTION( "-i requires an argument.\n" );
674 input_file_option
[input_movie_number
].num_track_delimiter
= 0;
677 input_file_option
[input_movie_number
].num_track_delimiter
+= (*p
++ == '?');
678 if( get_movie( &input
[input_movie_number
], strtok( argv
[i
], "?" ) ) )
679 FAILED_PARSE_CLI_OPTION( "failed to get input movie.\n" );
680 uint32_t num_tracks
= input
[input_movie_number
].file
.movie
.num_tracks
;
681 track_option
[input_movie_number
] = lsmash_malloc_zero( num_tracks
* sizeof(track_media_option
) );
682 if( !track_option
[input_movie_number
] )
683 FAILED_PARSE_CLI_OPTION( "couldn't allocate memory.\n" );
684 input_file_option
[input_movie_number
].whole_track_option
= strtok( NULL
, "" );
685 input
[input_movie_number
].file
.movie
.movie_ID
= input_movie_number
+ 1;
686 ++input_movie_number
;
688 /* Create output movie. */
689 else if( !strcasecmp( argv
[i
], "-o" ) || !strcasecmp( argv
[i
], "--output" ) ) /* output file */
692 FAILED_PARSE_CLI_OPTION( "-o requires an argument.\n" );
693 output_t
*output
= remuxer
->output
;
694 output
->root
= lsmash_create_root();
696 FAILED_PARSE_CLI_OPTION( "failed to create a ROOT.\n" );
697 if( lsmash_open_file( argv
[i
], 0, &output
->file
.param
) < 0 )
698 FAILED_PARSE_CLI_OPTION( "failed to open an output file.\n" );
699 output
->file
.name
= argv
[i
];
701 else if( !strcasecmp( argv
[i
], "--chapter" ) ) /* chapter file */
704 FAILED_PARSE_CLI_OPTION( "--chapter requires an argument.\n" );
705 remuxer
->chap_file
= argv
[i
];
707 else if( !strcasecmp( argv
[i
], "--chpl-with-bom" ) )
708 remuxer
->add_bom_to_chpl
= 1;
709 else if( !strcasecmp( argv
[i
], "--chapter-track" ) ) /* track to apply reference chapter to */
712 FAILED_PARSE_CLI_OPTION( "--chapter-track requires an argument.\n" );
713 remuxer
->chap_track
= atoi( argv
[i
] );
714 if( !remuxer
->chap_track
)
715 FAILED_PARSE_CLI_OPTION( "%s is an invalid track number.\n", argv
[i
] );
717 else if( !strcasecmp( argv
[i
], "--language" ) )
720 FAILED_PARSE_CLI_OPTION( "--chapter requires an argument.\n" );
721 remuxer
->default_language
= lsmash_pack_iso_language( argv
[i
] );
723 else if( !strcasecmp( argv
[i
], "--fragment" ) )
726 FAILED_PARSE_CLI_OPTION( "--fragment requires an argument.\n" );
727 remuxer
->frag_base_track
= atoi( argv
[i
] );
728 if( remuxer
->frag_base_track
== 0 )
729 FAILED_PARSE_CLI_OPTION( "%s is an invalid track number.\n", argv
[i
] );
731 else if( !strcasecmp( argv
[i
], "--dash" ) )
734 FAILED_PARSE_CLI_OPTION( "--dash requires an argument.\n" );
735 remuxer
->subseg_per_seg
= atoi( argv
[i
] );
739 FAILED_PARSE_CLI_OPTION( "unkown option found: %s\n", argv
[i
] );
741 if( !remuxer
->output
->root
)
742 FAILED_PARSE_CLI_OPTION( "output file name is not specified.\n" );
743 /* Parse track options */
744 /* Get the current track and media parameters */
745 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
746 for( uint32_t j
= 0; j
< input
[i
].file
.movie
.num_tracks
; j
++ )
748 input_track_t
*in_track
= &input
[i
].file
.movie
.track
[j
];
749 if( !in_track
->active
)
751 track_option
[i
][j
].alternate_group
= in_track
->track_param
.alternate_group
;
752 track_option
[i
][j
].ISO_language
= in_track
->media
.param
.ISO_language
;
753 track_option
[i
][j
].handler_name
= in_track
->media
.param
.media_handler_name
;
755 /* Set the default language */
756 if( remuxer
->default_language
)
757 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
758 for( uint32_t j
= 0; j
< input
[i
].file
.movie
.num_tracks
; j
++ )
759 track_option
[i
][j
].ISO_language
= remuxer
->default_language
;
760 /* Get the track and media parameters specified by users */
761 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
763 if( input_file_option
[i
].num_track_delimiter
> input
[i
].file
.movie
.num_tracks
)
764 FAILED_PARSE_CLI_OPTION( "more track options specified than the actual number of the tracks (%"PRIu32
").\n",
765 input
[i
].file
.movie
.num_tracks
);
766 if( input_file_option
[i
].num_track_delimiter
)
768 track_option
[i
][0].raw_track_option
= strtok( input_file_option
[i
].whole_track_option
, "?" );
769 for( int j
= 1; j
< input_file_option
[i
].num_track_delimiter
; j
++ )
770 track_option
[i
][j
].raw_track_option
= strtok( NULL
, "?" );
773 if( parse_track_option( remuxer
) )
774 FAILED_PARSE_CLI_OPTION( "failed to parse track options.\n" );
775 lsmash_free( input_file_option
);
777 #undef FAILED_PARSE_CLI_OPTION
780 static void replace_with_valid_brand( remuxer_t
*remuxer
)
782 static const lsmash_brand_type brand_filter_list
[] =
784 ISOM_BRAND_TYPE_3G2A
,
785 ISOM_BRAND_TYPE_3GG6
,
786 ISOM_BRAND_TYPE_3GG9
,
787 ISOM_BRAND_TYPE_3GP4
,
788 ISOM_BRAND_TYPE_3GP5
,
789 ISOM_BRAND_TYPE_3GP6
,
790 ISOM_BRAND_TYPE_3GP7
,
791 ISOM_BRAND_TYPE_3GP8
,
792 ISOM_BRAND_TYPE_3GP9
,
793 ISOM_BRAND_TYPE_3GR6
,
794 ISOM_BRAND_TYPE_3GR9
,
795 ISOM_BRAND_TYPE_M4A
,
796 ISOM_BRAND_TYPE_M4B
,
797 ISOM_BRAND_TYPE_M4V
,
798 ISOM_BRAND_TYPE_AVC1
,
799 ISOM_BRAND_TYPE_DBY1
,
800 ISOM_BRAND_TYPE_ISO2
,
801 ISOM_BRAND_TYPE_ISO3
,
802 ISOM_BRAND_TYPE_ISO4
,
803 ISOM_BRAND_TYPE_ISO5
,
804 ISOM_BRAND_TYPE_ISO6
,
805 ISOM_BRAND_TYPE_ISO7
,
806 ISOM_BRAND_TYPE_ISOM
,
807 ISOM_BRAND_TYPE_MP41
,
808 ISOM_BRAND_TYPE_MP42
,
812 input_t
*input
= remuxer
->input
;
813 /* Check the number of video and audio tracks, and the number of video
814 * and audio sample descriptions for the restrictions of 3GPP Basic Profile.
815 * - the maximum number of tracks shall be one for video (or alternatively
816 * one for scene description), one for audio and one for text
817 * - the maximum number of sample entries shall be one per track for video
818 * and audio (but unrestricted for text and scene description) */
819 uint32_t video_track_count
= 0;
820 uint32_t audio_track_count
= 0;
821 uint32_t video_num_summaries
= 0;
822 uint32_t audio_num_summaries
= 0;
823 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
825 input_movie_t
*movie
= &input
[i
].file
.movie
;
826 for( int j
= 0; j
< movie
->num_tracks
; j
++ )
828 if( movie
->track
[j
].media
.param
.handler_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
)
830 if( ++video_track_count
== 1 )
831 video_num_summaries
= movie
->track
[j
].num_summaries
;
833 else if( movie
->track
[j
].media
.param
.handler_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
)
835 if( ++audio_track_count
== 1 )
836 audio_num_summaries
= movie
->track
[j
].num_summaries
;
840 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
841 for( uint32_t j
= 0; j
<= input
[i
].file
.param
.brand_count
; j
++ )
844 uint32_t *brand
= j
? &input
[i
].file
.param
.brands
[j
- 1] : &input
[i
].file
.param
.major_brand
;
845 uint32_t *version
= j
? NULL
: &input
[i
].file
.param
.minor_version
;
846 for( int k
= 0; brand_filter_list
[k
]; k
++ )
848 if( *brand
== brand_filter_list
[k
] )
850 if( ((*brand
>> 24) & 0xFF) == '3'
851 && ((*brand
>> 16) & 0xFF) == 'g'
852 && (((*brand
>> 8) & 0xFF) == 'p' || ((*brand
>> 8) & 0xFF) == 'r') )
854 if( remuxer
->frag_base_track
== 0 /* Movie fragments are not allowed in '3gp4' and '3gp5'. */
855 && video_track_count
<= 1 && audio_track_count
<= 1
856 && video_num_summaries
<= 1 && audio_num_summaries
<= 1 )
858 /* Replace with the General Profile for maximum compatibility. */
859 if( (*brand
& 0xFF) < '6' )
861 /* 3GPP version 6.7.0 General Profile */
862 *brand
= ISOM_BRAND_TYPE_3GG6
;
864 *version
= 0x00000700;
867 *brand
= LSMASH_4CC( '3', 'g', 'g', *brand
& 0xFF );
870 && (*brand
== ISOM_BRAND_TYPE_AVC1
871 || (((*brand
>> 24) & 0xFF) == 'i'
872 && ((*brand
>> 16) & 0xFF) == 's'
873 && ((*brand
>> 8) & 0xFF) == 'o'
874 && ((*brand
& 0xFF) == 'm' || (*brand
& 0xFF) < '6'))) )
875 *brand
= ISOM_BRAND_TYPE_ISO6
;
882 /* Replace with the 'mp42' brand. */
883 *brand
= ISOM_BRAND_TYPE_MP42
;
890 static int pick_most_used_major_brand( input_t
*input
, output_file_t
*out_file
, int num_input
)
892 void *heap
= lsmash_malloc( num_input
* (sizeof(lsmash_brand_type
) + 2 * sizeof(uint32_t)) );
894 return ERROR_MSG( "failed to allocate memory.\n" );
895 lsmash_brand_type
*major_brand
= (lsmash_brand_type
*)heap
;
896 uint32_t *minor_version
= (uint32_t *)((lsmash_brand_type
*)heap
+ num_input
);
897 uint32_t *major_brand_count
= minor_version
+ num_input
;
898 uint32_t num_major_brand
= 0;
899 for( int i
= 0; i
< num_input
; i
++ )
901 major_brand
[num_major_brand
] = input
[i
].file
.param
.major_brand
;
902 minor_version
[num_major_brand
] = input
[i
].file
.param
.minor_version
;
903 major_brand_count
[num_major_brand
] = 0;
904 for( int j
= 0; j
< num_input
; j
++ )
905 if( (major_brand
[num_major_brand
] == input
[j
].file
.param
.major_brand
)
906 && (minor_version
[num_major_brand
] == input
[j
].file
.param
.minor_version
) )
909 ++major_brand_count
[num_major_brand
];
912 /* This major_brand already exists. Skip this. */
913 major_brand_count
[num_major_brand
] = 0;
920 uint32_t most_used_count
= 0;
921 for( uint32_t i
= 0; i
< num_major_brand
; i
++ )
922 if( major_brand_count
[i
] > most_used_count
)
924 most_used_count
= major_brand_count
[i
];
925 out_file
->param
.major_brand
= major_brand
[i
];
926 out_file
->param
.minor_version
= minor_version
[i
];
932 static int set_movie_parameters( remuxer_t
*remuxer
)
934 int num_input
= remuxer
->num_input
;
935 input_t
*input
= remuxer
->input
;
936 output_t
*output
= remuxer
->output
;
937 output_file_t
*out_file
= &output
->file
;
938 if( remuxer
->frag_base_track
)
939 out_file
->param
.mode
|= LSMASH_FILE_MODE_FRAGMENTED
;
940 int self_containd_segment
= (remuxer
->dash
&& remuxer
->subseg_per_seg
== 0);
943 if( remuxer
->frag_base_track
)
945 if( self_containd_segment
)
946 out_file
->param
.mode
|= LSMASH_FILE_MODE_INDEX
;
949 out_file
->param
.mode
&= ~LSMASH_FILE_MODE_MEDIA
;
950 out_file
->param
.mode
|= LSMASH_FILE_MODE_SEGMENT
;
954 WARNING_MSG( "--dash requires --fragment.\n" );
956 replace_with_valid_brand( remuxer
);
957 if( self_containd_segment
)
959 out_file
->param
.major_brand
= ISOM_BRAND_TYPE_DASH
;
960 out_file
->param
.minor_version
= 0;
962 else if( pick_most_used_major_brand( input
, out_file
, num_input
) < 0 )
963 return ERROR_MSG( "failed to pick the most used major brand.\n" );
964 /* Deduplicate compatible brands. */
965 uint32_t num_input_brands
= num_input
+ (self_containd_segment
? 1 : 0);
966 for( int i
= 0; i
< num_input
; i
++ )
967 num_input_brands
+= input
[i
].file
.param
.brand_count
;
968 lsmash_brand_type
*input_brands
= lsmash_malloc( num_input_brands
* sizeof(lsmash_brand_type
) );
970 return ERROR_MSG( "failed to allocate brands for an input file.\n" );
971 num_input_brands
= 0;
972 if( self_containd_segment
)
973 input_brands
[num_input_brands
++] = ISOM_BRAND_TYPE_DASH
;
974 for( int i
= 0; i
< num_input
; i
++ )
976 input_brands
[num_input_brands
++] = input
[i
].file
.param
.major_brand
;
977 for( uint32_t j
= 0; j
< input
[i
].file
.param
.brand_count
; j
++ )
978 if( input
[i
].file
.param
.brands
[j
] )
979 input_brands
[num_input_brands
++] = input
[i
].file
.param
.brands
[j
];
981 lsmash_brand_type
*output_brands
= lsmash_malloc_zero( num_input_brands
* sizeof(lsmash_brand_type
) );
984 lsmash_free( input_brands
);
985 return ERROR_MSG( "failed to allocate brands for an output file.\n" );
987 uint32_t num_output_brands
= 0;
988 for( uint32_t i
= 0; i
< num_input_brands
; i
++ )
990 output_brands
[num_output_brands
] = input_brands
[i
];
991 for( uint32_t j
= 0; j
< num_output_brands
; j
++ )
992 if( output_brands
[num_output_brands
] == output_brands
[j
] )
994 /* This brand already exists. Skip this. */
1000 lsmash_free( input_brands
);
1001 out_file
->param
.brand_count
= num_output_brands
;
1002 out_file
->param
.brands
= output_brands
;
1003 /* Set up a file. */
1004 out_file
->fh
= lsmash_set_file( output
->root
, &out_file
->param
);
1006 return ERROR_MSG( "failed to add an output file into a ROOT.\n" );
1007 out_file
->seg_param
= out_file
->param
;
1008 /* Check whether a reference chapter track is allowed or not. */
1009 if( remuxer
->chap_file
)
1010 for( uint32_t i
= 0; i
< out_file
->param
.brand_count
; i
++ )
1012 uint32_t brand
= out_file
->param
.brands
[i
];
1013 /* According to the restrictions of 3GPP Basic Profile,
1014 * - there shall be no references between tracks, e.g., a scene description track
1015 * shall not refer to a media track since all tracks are on equal footing and
1016 * played in parallel by a conforming player.
1017 * Therefore, the referenced chapter track is forbidden to use for 3GPP Basic Profile. */
1018 if( ((brand
>> 24) & 0xFF) == '3'
1019 && ((brand
>> 16) & 0xFF) == 'g'
1020 && ((brand
>> 8) & 0xFF) == 'p' )
1022 /* QuickTime file and iTunes MP4 file can contain the referenced chapter track. */
1023 if( brand
== ISOM_BRAND_TYPE_QT
|| brand
== ISOM_BRAND_TYPE_M4A
1024 || brand
== ISOM_BRAND_TYPE_M4B
|| brand
== ISOM_BRAND_TYPE_M4P
1025 || brand
== ISOM_BRAND_TYPE_M4V
)
1027 remuxer
->ref_chap_available
= 1;
1031 /* Set the movie timescale in order to match the media timescale if only one track is there. */
1032 lsmash_initialize_movie_parameters( &out_file
->movie
.param
);
1033 if( out_file
->movie
.num_tracks
== 1 )
1034 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
1036 input_movie_t
*in_movie
= &input
[i
].file
.movie
;
1037 for( uint32_t j
= 0; j
< in_movie
->num_tracks
; j
++ )
1038 if( in_movie
->track
[j
].active
)
1040 out_file
->movie
.param
.timescale
= in_movie
->track
[j
].media
.param
.timescale
;
1044 return lsmash_set_movie_parameters( output
->root
, &out_file
->movie
.param
);
1047 static void set_itunes_metadata( output_t
*output
, input_t
*input
, int num_input
)
1049 for( int i
= 0; i
< num_input
; i
++ )
1050 for( uint32_t j
= 0; j
< input
[i
].file
.movie
.num_itunes_metadata
; j
++ )
1051 if( lsmash_set_itunes_metadata( output
->root
, input
[i
].file
.movie
.itunes_metadata
[j
] ) )
1053 WARNING_MSG( "failed to set an iTunes metadata.\n" );
1058 static int set_starting_point( input_t
*input
, input_track_t
*in_track
, uint32_t seek_point
, int consider_rap
)
1060 if( seek_point
== 0 )
1062 uint32_t rap_number
;
1063 if( lsmash_get_closest_random_accessible_point_from_media_timeline( input
->root
, in_track
->track_ID
, 1, &rap_number
) )
1066 return ERROR_MSG( "failed to get the first random accessible point.\n" );
1069 WARNING_MSG( "no random access point!\n" );
1070 /* Set number of the first sample to be muxed. */
1071 in_track
->current_sample_number
= seek_point
;
1075 /* Get composition delay. */
1079 if( lsmash_get_dts_from_media_timeline( input
->root
, in_track
->track_ID
, rap_number
, &rap_dts
) )
1080 return ERROR_MSG( "failed to get CTS of the first random accessible sample of seek point.\n" );
1081 if( lsmash_get_cts_from_media_timeline( input
->root
, in_track
->track_ID
, rap_number
, &rap_cts
) )
1082 return ERROR_MSG( "failed to get CTS of the first random accessible sample of seek point.\n" );
1083 if( lsmash_get_composition_to_decode_shift_from_media_timeline( input
->root
, in_track
->track_ID
, &ctd_shift
) )
1084 return ERROR_MSG( "failed to get composition to decode timeline shfit.\n" );
1085 in_track
->composition_delay
= rap_cts
- rap_dts
+ ctd_shift
;
1086 /* Check if starting point is random accessible. */
1087 if( lsmash_get_closest_random_accessible_point_from_media_timeline( input
->root
, in_track
->track_ID
, seek_point
, &rap_number
) )
1088 return ERROR_MSG( "failed to get a random accessible point.\n" );
1089 if( rap_number
!= seek_point
)
1091 WARNING_MSG( "starting point you specified is not a random accessible point.\n" );
1094 /* Get duration that should be skipped. */
1095 if( lsmash_get_cts_from_media_timeline( input
->root
, in_track
->track_ID
, rap_number
, &rap_cts
) )
1096 return ERROR_MSG( "failed to get CTS of the closest and past random accessible sample of starting point.\n" );
1098 if( lsmash_get_cts_from_media_timeline( input
->root
, in_track
->track_ID
, seek_point
, &seek_cts
) )
1099 return ERROR_MSG( "failed to get CTS of starting point.\n" );
1100 if( rap_cts
< seek_cts
)
1101 in_track
->skip_duration
= seek_cts
- rap_cts
;
1104 /* Set number of the first sample to be muxed. */
1105 in_track
->current_sample_number
= consider_rap
? rap_number
: seek_point
;
1109 static void exclude_invalid_output_track( output_t
*output
, output_track_t
*out_track
,
1110 input_movie_t
*in_movie
, input_track_t
*in_track
,
1111 const char *message
, ... )
1114 eprintf( "[Warning] in %"PRIu32
"/%"PRIu32
" -> out %"PRIu32
": ", in_movie
->movie_ID
, in_track
->track_ID
, out_track
->track_ID
);
1116 va_start( args
, message
);
1117 vfprintf( stderr
, message
, args
);
1119 lsmash_delete_track( output
->root
, out_track
->track_ID
);
1120 -- output
->file
.movie
.num_tracks
;
1121 in_track
->active
= 0;
1124 static int prepare_output( remuxer_t
*remuxer
)
1126 input_t
*input
= remuxer
->input
;
1127 output_t
*output
= remuxer
->output
;
1128 output_movie_t
*out_movie
= &output
->file
.movie
;
1129 /* Count the number of output tracks. */
1130 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
1131 out_movie
->num_tracks
+= input
[i
].file
.movie
.num_tracks
;
1132 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
1134 input_movie_t
*in_movie
= &input
[i
].file
.movie
;
1135 for( uint32_t j
= 0; j
< in_movie
->num_tracks
; j
++ )
1137 /* Don't remux tracks specified as 'remove' by a user. */
1138 if( remuxer
->track_option
[i
][j
].remove
)
1139 in_movie
->track
[j
].active
= 0;
1140 if( !in_movie
->track
[j
].active
)
1141 -- out_movie
->num_tracks
;
1144 if( set_movie_parameters( remuxer
) < 0 )
1145 return ERROR_MSG( "failed to set output movie parameters.\n" );
1146 set_itunes_metadata( output
, input
, remuxer
->num_input
);
1147 /* Allocate output tracks. */
1148 out_movie
->track
= lsmash_malloc( out_movie
->num_tracks
* sizeof(output_track_t
) );
1149 if( !out_movie
->track
)
1150 return ERROR_MSG( "failed to alloc output tracks.\n" );
1151 out_movie
->current_track_number
= 1;
1152 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
1154 input_movie_t
*in_movie
= &input
[i
].file
.movie
;
1155 for( uint32_t j
= 0; j
< in_movie
->num_tracks
; j
++ )
1157 track_media_option
*current_track_opt
= &remuxer
->track_option
[i
][j
];
1158 input_track_t
*in_track
= &in_movie
->track
[j
];
1159 if( !in_track
->active
)
1161 output_track_t
*out_track
= &out_movie
->track
[ out_movie
->current_track_number
- 1 ];
1162 out_track
->summary_remap
= lsmash_malloc_zero( in_track
->num_summaries
* sizeof(uint32_t) );
1163 if( !out_track
->summary_remap
)
1164 return ERROR_MSG( "failed to create summary mapping for a track.\n" );
1165 out_track
->track_ID
= lsmash_create_track( output
->root
, in_track
->media
.param
.handler_type
);
1166 if( !out_track
->track_ID
)
1167 return ERROR_MSG( "failed to create a track.\n" );
1168 /* Copy track and media parameters except for track_ID. */
1169 out_track
->track_param
= in_track
->track_param
;
1170 out_track
->media_param
= in_track
->media
.param
;
1171 /* Set track and media parameters specified by users */
1172 out_track
->track_param
.alternate_group
= current_track_opt
->alternate_group
;
1173 out_track
->media_param
.ISO_language
= current_track_opt
->ISO_language
;
1174 out_track
->media_param
.media_handler_name
= current_track_opt
->handler_name
;
1175 out_track
->track_param
.track_ID
= out_track
->track_ID
;
1176 if( current_track_opt
->disable
)
1177 out_track
->track_param
.mode
&= ~ISOM_TRACK_ENABLED
;
1178 if( lsmash_set_track_parameters( output
->root
, out_track
->track_ID
, &out_track
->track_param
) < 0 )
1180 exclude_invalid_output_track( output
, out_track
, in_movie
, in_track
, "failed to set track parameters.\n" );
1183 if( lsmash_set_media_parameters( output
->root
, out_track
->track_ID
, &out_track
->media_param
) < 0 )
1185 exclude_invalid_output_track( output
, out_track
, in_movie
, in_track
, "failed to set media parameters.\n" );
1188 lsmash_data_reference_t data_ref
= { .index
= 1, .location
= NULL
};
1189 if( lsmash_create_data_reference( output
->root
, out_track
->track_ID
, &data_ref
, output
->file
.fh
) < 0 )
1190 return ERROR_MSG( "failed to create a data reference for output movie.\n" );
1191 uint32_t valid_summary_count
= 0;
1192 for( uint32_t k
= 0; k
< in_track
->num_summaries
; k
++ )
1194 if( !in_track
->summaries
[k
].active
)
1196 out_track
->summary_remap
[k
] = 0;
1199 lsmash_summary_t
*summary
= in_track
->summaries
[k
].summary
;
1200 summary
->data_ref_index
= 1;
1201 if( lsmash_add_sample_entry( output
->root
, out_track
->track_ID
, summary
) == 0 )
1203 WARNING_MSG( "failed to append a summary.\n" );
1204 lsmash_cleanup_summary( summary
);
1205 in_track
->summaries
[k
].summary
= NULL
;
1206 in_track
->summaries
[k
].active
= 0;
1207 out_track
->summary_remap
[k
] = 0;
1210 out_track
->summary_remap
[k
] = ++valid_summary_count
;
1212 if( valid_summary_count
== 0 )
1214 exclude_invalid_output_track( output
, out_track
, in_movie
, in_track
, "failed to append all summaries.\n" );
1217 out_track
->last_sample_delta
= in_track
->last_sample_delta
;
1218 if( set_starting_point( input
, in_track
, current_track_opt
->seek
, current_track_opt
->consider_rap
) < 0 )
1220 exclude_invalid_output_track( output
, out_track
, in_movie
, in_track
, "failed to set starting point.\n" );
1223 out_track
->current_sample_number
= 1;
1224 out_track
->skip_dt_interval
= 0;
1225 out_track
->last_sample_dts
= 0;
1226 ++ out_movie
->current_track_number
;
1229 if( out_movie
->num_tracks
== 0 )
1230 return ERROR_MSG( "failed to create the output movie.\n" );
1231 out_movie
->current_track_number
= 1;
1232 output
->current_seg_number
= 1;
1236 static void set_reference_chapter_track( remuxer_t
*remuxer
)
1238 if( remuxer
->ref_chap_available
)
1239 lsmash_create_reference_chapter_track( remuxer
->output
->root
, remuxer
->chap_track
, remuxer
->chap_file
);
1242 static int flush_movie_fragment( remuxer_t
*remuxer
)
1244 input_t
*inputs
= remuxer
->input
;
1245 output_t
*output
= remuxer
->output
;
1246 output_movie_t
*out_movie
= &output
->file
.movie
;
1247 uint32_t out_current_track_number
= 1;
1248 for( uint32_t i
= 1; i
<= remuxer
->num_input
; i
++ )
1250 input_t
*in
= &inputs
[i
- 1];
1251 input_movie_t
*in_movie
= &in
->file
.movie
;
1252 for( uint32_t j
= 1; j
<= in_movie
->num_tracks
; j
++ )
1254 input_track_t
*in_track
= &in_movie
->track
[j
- 1];
1255 if( !in_track
->active
)
1257 output_track_t
*out_track
= &out_movie
->track
[out_current_track_number
- 1];
1258 if( !in_track
->reach_end_of_media_timeline
)
1260 lsmash_sample_t sample
;
1261 if( lsmash_get_sample_info_from_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
, &sample
) < 0 )
1262 return ERROR_MSG( "failed to get the information of the next sample.\n" );
1263 uint64_t sample_dts
= sample
.dts
- out_track
->skip_dt_interval
;
1264 if( lsmash_flush_pooled_samples( output
->root
, out_track
->track_ID
, sample_dts
- out_track
->last_sample_dts
) < 0 )
1265 return ERROR_MSG( "failed to flush the rest of samples in a fragment.\n" );
1268 if( lsmash_flush_pooled_samples( output
->root
, out_track
->track_ID
, out_track
->last_sample_delta
) < 0 )
1269 return ERROR_MSG( "failed to flush the rest of samples in a fragment.\n" );
1270 if( ++out_current_track_number
> out_movie
->num_tracks
)
1277 static int moov_to_front_callback( void *param
, uint64_t written_movie_size
, uint64_t total_movie_size
)
1280 eprintf( "Finalizing: [%5.2lf%%]\r", ((double)written_movie_size
/ total_movie_size
) * 100.0 );
1284 static lsmash_adhoc_remux_t moov_to_front
=
1286 .func
= moov_to_front_callback
,
1287 .buffer_size
= 4 * 1024 * 1024, /* 4MiB */
1291 static int open_media_segment( output_t
*output
, lsmash_file_parameters_t
*seg_param
)
1293 /* Open a media segment file.
1294 * Each file is named as follows.
1300 * N is the number of segment files excluding the initialization segment file.
1302 output_file_t
*out_file
= &output
->file
;
1303 int out_file_name_length
= strlen( out_file
->name
);
1304 const char *end
= &out_file
->name
[ out_file_name_length
];
1305 const char *p
= end
;
1306 while( p
>= out_file
->name
&& *p
!= '.' && *p
!= '/' && *p
!= '\\' )
1308 if( p
< out_file
->name
)
1312 int suffix_length
= 1;
1313 for( uint32_t i
= output
->current_seg_number
; i
; i
/= 10 )
1315 int seg_name_length
= out_file_name_length
+ suffix_length
;
1316 int suffixless_length
= p
- out_file
->name
;
1317 char *seg_name
= lsmash_malloc( (seg_name_length
+ 1) * sizeof(char) );
1319 return ERROR_MSG( "failed to allocate to store a segment filename.\n" );
1320 seg_name
[ seg_name_length
] = '\0';
1321 memcpy( seg_name
, out_file
->name
, suffixless_length
);
1322 sprintf( seg_name
+ suffixless_length
, "_%"PRIu32
, output
->current_seg_number
);
1324 memcpy( seg_name
+ suffixless_length
+ suffix_length
, p
, end
- p
);
1325 int ret
= lsmash_open_file( seg_name
, 0, seg_param
);
1327 eprintf( "[Segment] out: %s\n", seg_name
);
1328 lsmash_free( seg_name
);
1332 static int switch_segment( remuxer_t
*remuxer
)
1334 output_t
*output
= remuxer
->output
;
1335 output_file_t
*out_file
= &output
->file
;
1336 lsmash_file_parameters_t seg_param
= { 0 };
1337 if( open_media_segment( output
, &seg_param
) < 0 )
1338 return ERROR_MSG( "failed to open an output file for segmentation.\n" );
1339 /* Set up the media segment file.
1340 * Copy the parameters of the previous segment if the previous is not the initialization segment. */
1341 if( out_file
->seg_param
.mode
& LSMASH_FILE_MODE_INITIALIZATION
)
1343 uint32_t brand_count
= out_file
->param
.brand_count
+ 2;
1344 lsmash_brand_type
*brands
= lsmash_malloc_zero( brand_count
* sizeof(lsmash_brand_type
) );
1346 return ERROR_MSG( "failed to allocate brands for an output segment file.\n" );
1347 brands
[0] = ISOM_BRAND_TYPE_MSDH
;
1348 brands
[1] = ISOM_BRAND_TYPE_MSIX
;
1349 for( uint32_t i
= 0; i
< out_file
->param
.brand_count
; i
++ )
1350 brands
[i
+ 2] = out_file
->param
.brands
[i
];
1351 seg_param
.major_brand
= ISOM_BRAND_TYPE_MSDH
;
1352 seg_param
.brand_count
= brand_count
;
1353 seg_param
.brands
= brands
;
1354 seg_param
.mode
= LSMASH_FILE_MODE_WRITE
| LSMASH_FILE_MODE_FRAGMENTED
1355 | LSMASH_FILE_MODE_BOX
| LSMASH_FILE_MODE_MEDIA
1356 | LSMASH_FILE_MODE_INDEX
| LSMASH_FILE_MODE_SEGMENT
;
1360 void *opaque
= seg_param
.opaque
;
1361 seg_param
= out_file
->seg_param
;
1362 seg_param
.opaque
= opaque
;
1364 lsmash_file_t
*segment
= lsmash_set_file( output
->root
, &seg_param
);
1366 return ERROR_MSG( "failed to add an output segment file into a ROOT.\n" );
1367 /* Switch to the next segment.
1368 * After switching, close the previous segment if the previous is not the initialization segment. */
1369 if( lsmash_switch_media_segment( output
->root
, segment
, &moov_to_front
) < 0 )
1370 return ERROR_MSG( "failed to switch to the next segment.\n" );
1371 if( !(out_file
->seg_param
.mode
& LSMASH_FILE_MODE_INITIALIZATION
) )
1372 return lsmash_close_file( &out_file
->seg_param
);
1373 out_file
->seg_param
= seg_param
;
1377 static int handle_segmentation( remuxer_t
*remuxer
)
1379 if( remuxer
->subseg_per_seg
== 0 )
1381 output_t
*output
= remuxer
->output
;
1382 if( remuxer
->subseg_per_seg
== output
->file
.current_subseg_number
1383 || output
->current_seg_number
== 1 )
1385 if( switch_segment( remuxer
) < 0 )
1387 ERROR_MSG( "failed to switch to a segment.\n" );
1390 output
->file
.current_subseg_number
= 1;
1391 ++ output
->current_seg_number
;
1394 ++ output
->file
.current_subseg_number
;
1398 static void adapt_description_index( output_track_t
*out_track
, input_track_t
*in_track
, lsmash_sample_t
*sample
)
1400 sample
->index
= sample
->index
> in_track
->num_summaries
? in_track
->num_summaries
1401 : sample
->index
== 0 ? 1
1403 sample
->index
= out_track
->summary_remap
[ sample
->index
- 1 ];
1404 if( in_track
->current_sample_index
== 0 )
1405 in_track
->current_sample_index
= sample
->index
;
1408 static void adjust_timestamp( output_track_t
*out_track
, lsmash_sample_t
*sample
)
1410 /* The first DTS must be 0. */
1411 if( out_track
->current_sample_number
== 1 )
1412 out_track
->skip_dt_interval
= sample
->dts
;
1413 if( out_track
->skip_dt_interval
)
1415 sample
->dts
-= out_track
->skip_dt_interval
;
1416 sample
->cts
-= out_track
->skip_dt_interval
;
1420 static int do_remux( remuxer_t
*remuxer
)
1422 #define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b))
1423 input_t
*inputs
= remuxer
->input
;
1424 output_t
*output
= remuxer
->output
;
1425 output_movie_t
*out_movie
= &output
->file
.movie
;
1426 set_reference_chapter_track( remuxer
);
1427 double largest_dts
= 0; /* in seconds */
1428 double frag_base_dts
= 0; /* in seconds */
1429 uint32_t input_movie_number
= 1;
1430 uint32_t num_consecutive_sample_skip
= 0;
1431 uint32_t num_active_input_tracks
= out_movie
->num_tracks
;
1432 uint64_t total_media_size
= 0;
1433 uint8_t sample_count
= 0;
1434 uint8_t pending_flush_fragments
= (remuxer
->frag_base_track
!= 0); /* For non-fragmented movie, always set to 0. */
1437 input_t
*in
= &inputs
[input_movie_number
- 1];
1438 input_movie_t
*in_movie
= &in
->file
.movie
;
1439 input_track_t
*in_track
= &in_movie
->track
[ in_movie
->current_track_number
- 1 ];
1440 if( !in_track
->active
)
1442 /* Move the next track. */
1443 if( ++ in_movie
->current_track_number
> in_movie
->num_tracks
)
1445 /* Move the next input movie. */
1446 in_movie
->current_track_number
= 1;
1447 ++input_movie_number
;
1449 if( input_movie_number
> remuxer
->num_input
)
1450 input_movie_number
= 1; /* Back the first input movie. */
1453 /* Try append a sample in an input track where we didn't reach the end of media timeline. */
1454 if( !in_track
->reach_end_of_media_timeline
)
1456 lsmash_sample_t
*sample
= in_track
->sample
;
1457 /* Get a new sample data if the track doesn't hold any one. */
1460 sample
= lsmash_get_sample_from_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
);
1463 output_track_t
*out_track
= &out_movie
->track
[ out_movie
->current_track_number
- 1 ];
1464 adapt_description_index( out_track
, in_track
, sample
);
1465 adjust_timestamp( out_track
, sample
);
1466 in_track
->sample
= sample
;
1467 in_track
->dts
= (double)sample
->dts
/ in_track
->media
.param
.timescale
;
1471 if( lsmash_check_sample_existence_in_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
) )
1473 ERROR_MSG( "failed to get a sample.\n" );
1476 lsmash_sample_t sample_info
= { 0 };
1477 if( lsmash_get_sample_info_from_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
, &sample_info
) < 0 )
1479 /* No more appendable samples in this track. */
1480 in_track
->sample
= NULL
;
1481 in_track
->reach_end_of_media_timeline
= 1;
1482 if( --num_active_input_tracks
== 0 )
1483 break; /* end of muxing */
1487 ERROR_MSG( "failed to get a sample.\n" );
1494 /* Flushing the active movie fragment is pending until random accessible point sample within all active tracks are ready. */
1495 if( remuxer
->frag_base_track
)
1497 if( pending_flush_fragments
== 0 )
1499 if( remuxer
->frag_base_track
== out_movie
->current_track_number
1500 && sample
->prop
.ra_flags
!= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
1502 pending_flush_fragments
= 1;
1503 frag_base_dts
= in_track
->dts
;
1506 else if( num_consecutive_sample_skip
== num_active_input_tracks
|| total_media_size
== 0 )
1508 if( flush_movie_fragment( remuxer
) < 0 )
1510 ERROR_MSG( "failed to flush a movie fragment.\n" );
1513 if( handle_segmentation( remuxer
) < 0 )
1515 if( lsmash_create_fragment_movie( output
->root
) < 0 )
1517 ERROR_MSG( "failed to create a movie fragment.\n" );
1520 pending_flush_fragments
= 0;
1523 /* Append a sample if meeting a condition. */
1525 int need_new_fragment
= (remuxer
->frag_base_track
&& sample
->index
!= in_track
->current_sample_index
);
1526 if( pending_flush_fragments
== 0 )
1527 append
= (in_track
->dts
<= largest_dts
|| num_consecutive_sample_skip
== num_active_input_tracks
) && !need_new_fragment
;
1528 else if( remuxer
->frag_base_track
!= out_movie
->current_track_number
&& !need_new_fragment
)
1530 /* Wait as much as possible both to make the last sample within each track fragment close to the DTS of
1531 * the first sample within the track fragment corresponding to the base track within the next movie
1532 * fragment and to make all the track fragments within the next movie fragment start with RAP. */
1533 if( sample
->prop
.ra_flags
== ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
1537 /* Check the DTS and random accessibilities of the next sample. */
1538 lsmash_sample_t info
;
1539 if( lsmash_get_sample_info_from_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
+ 1, &info
) < 0 )
1542 append
= (info
.prop
.ra_flags
!= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
1543 && ((double)info
.dts
/ in_track
->media
.param
.timescale
<= frag_base_dts
);
1550 output_track_t
*out_track
= &out_movie
->track
[ out_movie
->current_track_number
- 1 ];
1551 uint64_t sample_size
= sample
->length
; /* sample might be deleted internally after appending. */
1552 uint64_t last_sample_dts
= sample
->dts
; /* same as above */
1553 uint32_t sample_index
= sample
->index
; /* same as above */
1554 /* Append a sample into output movie. */
1555 if( lsmash_append_sample( output
->root
, out_track
->track_ID
, sample
) < 0 )
1557 lsmash_delete_sample( sample
);
1558 return ERROR_MSG( "failed to append a sample.\n" );
1560 largest_dts
= LSMASH_MAX( largest_dts
, in_track
->dts
);
1561 in_track
->sample
= NULL
;
1562 in_track
->current_sample_number
+= 1;
1563 in_track
->current_sample_index
= sample_index
;
1564 out_track
->current_sample_number
+= 1;
1565 out_track
->last_sample_dts
= last_sample_dts
;
1566 num_consecutive_sample_skip
= 0;
1567 total_media_size
+= sample_size
;
1568 /* Print, per 256 samples, total size of imported media. */
1569 if( ++sample_count
== 0 )
1570 eprintf( "Importing: %"PRIu64
" bytes\r", total_media_size
);
1574 lsmash_delete_sample( sample
);
1575 in_track
->sample
= NULL
;
1576 in_track
->current_sample_number
+= 1;
1580 ++num_consecutive_sample_skip
; /* Skip appendig sample. */
1583 /* Move the next track. */
1584 if( ++ in_movie
->current_track_number
> in_movie
->num_tracks
)
1586 /* Move the next input movie. */
1587 in_movie
->current_track_number
= 1;
1588 ++input_movie_number
;
1590 if( input_movie_number
> remuxer
->num_input
)
1591 input_movie_number
= 1; /* Back the first input movie. */
1592 if( ++ out_movie
->current_track_number
> out_movie
->num_tracks
)
1593 out_movie
->current_track_number
= 1; /* Back the first track in the output movie. */
1595 for( uint32_t i
= 0; i
< out_movie
->num_tracks
; i
++ )
1596 if( lsmash_flush_pooled_samples( output
->root
, out_movie
->track
[i
].track_ID
, out_movie
->track
[i
].last_sample_delta
) )
1597 return ERROR_MSG( "failed to flush samples.\n" );
1602 static int construct_timeline_maps( remuxer_t
*remuxer
)
1604 input_t
*input
= remuxer
->input
;
1605 output_t
*output
= remuxer
->output
;
1606 output_movie_t
*out_movie
= &output
->file
.movie
;
1607 track_media_option
**track_option
= remuxer
->track_option
;
1608 out_movie
->current_track_number
= 1;
1609 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
1610 for( uint32_t j
= 0; j
< input
[i
].file
.movie
.num_tracks
; j
++ )
1612 input_track_t
*in_track
= &input
[i
].file
.movie
.track
[j
];
1613 if( !in_track
->active
)
1615 output_track_t
*out_track
= &out_movie
->track
[ out_movie
->current_track_number
++ - 1 ];
1616 if( track_option
[i
][j
].seek
)
1618 /* Reconstruct timeline maps. */
1619 if( lsmash_delete_explicit_timeline_map( output
->root
, out_track
->track_ID
) )
1620 return ERROR_MSG( "failed to delete explicit timeline maps.\n" );
1621 uint32_t movie_timescale
= lsmash_get_movie_timescale( output
->root
);
1622 uint32_t media_timescale
= lsmash_get_media_timescale( output
->root
, out_track
->track_ID
);
1623 if( !media_timescale
)
1624 return ERROR_MSG( "media timescale is broken.\n" );
1625 double timescale_convert_multiplier
= (double)movie_timescale
/ media_timescale
;
1627 edit
.start_time
= in_track
->composition_delay
+ in_track
->skip_duration
;
1628 if( edit
.start_time
)
1630 uint64_t empty_duration
= edit
.start_time
+ lsmash_get_composition_to_decode_shift( output
->root
, out_track
->track_ID
);
1631 lsmash_edit_t empty_edit
;
1632 empty_edit
.duration
= empty_duration
* timescale_convert_multiplier
+ 0.5;
1633 empty_edit
.start_time
= ISOM_EDIT_MODE_EMPTY
;
1634 empty_edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
1635 if( lsmash_create_explicit_timeline_map( output
->root
, out_track
->track_ID
, empty_edit
) )
1636 return ERROR_MSG( "failed to create a empty duration.\n" );
1638 if( remuxer
->frag_base_track
== 0 )
1639 edit
.duration
= (out_track
->last_sample_dts
+ out_track
->last_sample_delta
- in_track
->skip_duration
) * timescale_convert_multiplier
;
1641 edit
.duration
= ISOM_EDIT_DURATION_IMPLICIT
;
1642 edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
1643 if( lsmash_create_explicit_timeline_map( output
->root
, out_track
->track_ID
, edit
) )
1644 return ERROR_MSG( "failed to create a explicit timeline map.\n" );
1646 else if( lsmash_copy_timeline_map( output
->root
, out_track
->track_ID
, input
[i
].root
, in_track
->track_ID
) )
1647 return ERROR_MSG( "failed to copy timeline maps.\n" );
1649 out_movie
->current_track_number
= 1;
1653 static int finish_movie( remuxer_t
*remuxer
)
1655 output_t
*output
= remuxer
->output
;
1656 /* Set chapter list */
1657 if( remuxer
->chap_file
)
1658 lsmash_set_tyrant_chapter( output
->root
, remuxer
->chap_file
, remuxer
->add_bom_to_chpl
);
1659 /* Finish muxing. */
1661 if( lsmash_finish_movie( output
->root
, &moov_to_front
) )
1663 return remuxer
->frag_base_track
? 0 : lsmash_write_lsmash_indicator( output
->root
);
1666 int main( int argc
, char *argv
[] )
1673 else if( !strcasecmp( argv
[1], "-h" ) || !strcasecmp( argv
[1], "--help" ) )
1678 else if( !strcasecmp( argv
[1], "-v" ) || !strcasecmp( argv
[1], "--version" ) )
1689 lsmash_get_mainargs( &argc
, &argv
);
1691 for( int i
= 1 ; i
< argc
; i
++ )
1692 if( !strcasecmp( argv
[i
], "-i" ) || !strcasecmp( argv
[i
], "--input" ) )
1695 return ERROR_MSG( "no input file specified.\n" );
1696 output_t output
= { 0 };
1697 input_t
*input
= lsmash_malloc_zero( num_input
* sizeof(input_t
) );
1699 return ERROR_MSG( "failed to allocate the input handler.\n" );
1700 track_media_option
**track_option
= lsmash_malloc_zero( num_input
* sizeof(track_media_option
*) );
1703 lsmash_free( input
);
1704 return ERROR_MSG( "failed to allocate the track option handler.\n" );
1710 .track_option
= track_option
,
1711 .num_input
= num_input
,
1712 .add_bom_to_chpl
= 0,
1713 .ref_chap_available
= 0,
1716 .default_language
= 0,
1717 .frag_base_track
= 0,
1718 .subseg_per_seg
= 0,
1721 if( parse_cli_option( argc
, argv
, &remuxer
) )
1722 return REMUXER_ERR( "failed to parse command line options.\n" );
1723 if( prepare_output( &remuxer
) )
1724 return REMUXER_ERR( "failed to set up preparation for output.\n" );
1725 if( remuxer
.frag_base_track
&& construct_timeline_maps( &remuxer
) )
1726 return REMUXER_ERR( "failed to construct timeline maps.\n" );
1727 if( do_remux( &remuxer
) )
1728 return REMUXER_ERR( "failed to remux movies.\n" );
1729 if( remuxer
.frag_base_track
== 0 && construct_timeline_maps( &remuxer
) )
1730 return REMUXER_ERR( "failed to construct timeline maps.\n" );
1731 if( finish_movie( &remuxer
) )
1732 return REMUXER_ERR( "failed to finish output movie.\n" );
1734 eprintf( "%s completed!\n", !remuxer
.dash
|| remuxer
.subseg_per_seg
== 0 ? "Remuxing" : "Segmentation" );
1735 cleanup_remuxer( &remuxer
);