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. */
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
;
49 output_track_t
*track
;
50 lsmash_movie_parameters_t param
;
52 uint32_t current_track_number
;
59 lsmash_file_parameters_t param
;
60 lsmash_file_parameters_t seg_param
;
62 uint32_t current_subseg_number
;
69 uint32_t current_seg_number
;
75 lsmash_summary_t
*summary
;
81 lsmash_file_parameters_t param
;
86 lsmash_media_parameters_t param
;
87 uint32_t num_data_refs
;
88 input_data_ref_t
*data_refs
;
94 lsmash_sample_t
*sample
;
96 uint64_t composition_delay
;
97 uint64_t skip_duration
;
98 int reach_end_of_media_timeline
;
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
;
111 input_track_t
*track
;
112 lsmash_itunes_metadata_t
*itunes_metadata
;
113 lsmash_movie_parameters_t param
;
116 uint32_t num_itunes_metadata
;
117 uint32_t current_track_number
;
123 lsmash_file_parameters_t param
;
135 char *raw_track_option
;
138 int16_t alternate_group
;
139 uint16_t ISO_language
;
143 } track_media_option
;
149 track_media_option
**track_option
;
152 int ref_chap_available
;
155 uint16_t default_language
;
156 uint32_t fragment_base_track
;
157 uint32_t subseg_per_seg
;
163 char *whole_track_option
;
164 int num_track_delimiter
;
167 static void cleanup_input_movie( input_t
*input
)
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
);
202 static void cleanup_output_movie( output_t
*output
)
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
);
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
);
242 eprintf( "[Error] " );
244 va_start( args
, message
);
245 vfprintf( stderr
, message
, args
);
250 static int error_message( const char *message
, ... )
253 eprintf( "[Error] " );
255 va_start( args
, message
);
256 vfprintf( stderr
, message
, args
);
261 static int warning_message( const char *message
, ... )
264 eprintf( "[Warning] " );
266 va_start( args
, message
);
267 vfprintf( stderr
, message
, args
);
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 )
279 "L-SMASH isom/mov re-muliplexer rev%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 )
289 "Usage: remuxer -i input1 [-i input2 -i input3 ...] -o output\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"
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"
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
)
327 int dst_size
= strlen( src
) + 1;
328 char *dst
= lsmash_malloc( dst_size
);
331 memcpy( dst
, src
, dst_size
);
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
) )
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
) );
346 metadata
->meaning
= duplicate_string( shadow
.meaning
);
347 if( !metadata
->meaning
)
352 metadata
->name
= duplicate_string( shadow
.name
);
353 if( !metadata
->name
)
356 if( shadow
.type
== ITUNES_METADATA_TYPE_STRING
)
358 metadata
->value
.string
= duplicate_string( shadow
.value
.string
);
359 if( !metadata
->value
.string
)
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
)
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
;
372 metadata
->value
= shadow
.value
;
375 lsmash_freep( &metadata
->meaning
);
376 lsmash_freep( &metadata
->name
);
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
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" );
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" );
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" );
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();
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
);
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" );
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
;
455 input_track_t
*in_track
= in_movie
->track
= lsmash_malloc_zero( num_tracks
* sizeof(input_track_t
) );
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" );
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" );
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" );
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" );
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" );
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
!= '\\' )
509 int relative_path_length
= p
== input_name
? 2 : p
- input_name
;
510 char *location
= lsmash_malloc( relative_path_length
+ location_length
+ 2 );
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
);
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" );
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" );
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" );
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 );
556 WARNING_MSG( "failed to get a summary.\n" );
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" );
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
;
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
) );
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
)
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
);
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. */
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
);
639 return ERROR_MSG( "unknown track option %s\n", track_option
);
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 */
657 return ERROR_MSG( "-i requires an argument.\n" );
658 input_file_option
[input_movie_number
].num_track_delimiter
= 0;
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 */
676 return ERROR_MSG( "-o requires an argument.\n" );
677 output_t
*output
= remuxer
->output
;
678 output
->root
= lsmash_create_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 */
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 */
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" ) )
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" ) )
710 return ERROR_MSG( "--fragment requires an argument.\n" );
711 remuxer
->fragment_base_track
= atoi( argv
[i
] );
712 if( remuxer
->fragment_base_track
== 0 )
713 return ERROR_MSG( "%s is an invalid track number.\n", argv
[i
] );
715 else if( !strcasecmp( argv
[i
], "--dash" ) )
718 return ERROR_MSG( "--dash requires an argument.\n" );
719 remuxer
->subseg_per_seg
= atoi( argv
[i
] );
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
)
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" );
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
,
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
++ )
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
->fragment_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 )
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
;
846 *version
= 0x00000700;
849 *brand
= LSMASH_4CC( '3', 'g', 'g', *brand
& 0xFF );
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
;
864 /* Replace with the 'mp42' brand. */
865 *brand
= ISOM_BRAND_TYPE_MP42
;
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
->fragment_base_track
)
879 out_file
->param
.mode
|= LSMASH_FILE_MODE_FRAGMENTED
;
880 int self_containd_segment
= (remuxer
->dash
&& remuxer
->subseg_per_seg
== 0);
883 if( remuxer
->fragment_base_track
)
885 if( self_containd_segment
)
886 out_file
->param
.mode
|= LSMASH_FILE_MODE_INDEX
;
889 out_file
->param
.mode
&= ~LSMASH_FILE_MODE_MEDIA
;
890 out_file
->param
.mode
|= LSMASH_FILE_MODE_SEGMENT
;
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;
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
) )
919 ++major_brand_count
[num_major_brand
];
922 /* This major_brand already exists. Skip this. */
923 major_brand_count
[num_major_brand
] = 0;
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
) );
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. */
970 out_file
->param
.brand_count
= num_output_brands
;
971 out_file
->param
.brands
= output_brands
;
973 out_file
->fh
= lsmash_set_file( output
->root
, &out_file
->param
);
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' )
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;
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
;
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" );
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 )
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
) )
1035 return ERROR_MSG( "failed to get the first random accessible point.\n" );
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
;
1044 /* Get composition delay. */
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" );
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" );
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
;
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
, ... )
1083 eprintf( "[Warning] in %"PRIu32
"/%"PRIu32
" -> out %"PRIu32
": ", in_movie
->movie_ID
, in_track
->track_ID
, out_track
->track_ID
);
1085 va_start( args
, message
);
1086 vfprintf( stderr
, message
, 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
)
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" );
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" );
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;
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;
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" );
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" );
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;
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
)
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" );
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
)
1246 static int moov_to_front_callback( void *param
, uint64_t written_movie_size
, uint64_t total_movie_size
)
1249 eprintf( "Finalizing: [%5.2lf%%]\r", ((double)written_movie_size
/ total_movie_size
) * 100.0 );
1253 static lsmash_adhoc_remux_t moov_to_front
=
1255 .func
= moov_to_front_callback
,
1256 .buffer_size
= 4 * 1024 * 1024, /* 4MiB */
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.
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
!= '\\' )
1277 if( p
< out_file
->name
)
1281 int suffix_length
= 1;
1282 for( uint32_t i
= output
->current_seg_number
; i
; i
/= 10 )
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
);
1291 memcpy( seg_name
+ suffixless_length
+ suffix_length
, p
, end
- p
);
1292 int ret
= lsmash_open_file( seg_name
, 0, seg_param
);
1294 eprintf( "[Segment] out: %s\n", seg_name
);
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
) );
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
;
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
);
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
;
1343 static int handle_segmentation( remuxer_t
*remuxer
)
1345 if( remuxer
->subseg_per_seg
== 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" );
1356 output
->file
.current_subseg_number
= 1;
1357 ++ output
->current_seg_number
;
1360 ++ output
->file
.current_subseg_number
;
1364 static int do_remux( remuxer_t
*remuxer
)
1366 #define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b))
1367 input_t
*inputs
= remuxer
->input
;
1368 output_t
*output
= remuxer
->output
;
1369 output_movie_t
*out_movie
= &output
->file
.movie
;
1370 set_reference_chapter_track( remuxer
);
1371 double largest_dts
= 0;
1372 uint32_t input_movie_number
= 1;
1373 uint32_t num_consecutive_sample_skip
= 0;
1374 uint32_t num_active_input_tracks
= out_movie
->num_tracks
;
1375 uint64_t total_media_size
= 0;
1376 uint8_t sample_count
= 0;
1377 uint8_t pending_flush_fragments
= (remuxer
->fragment_base_track
!= 0);
1380 input_t
*in
= &inputs
[input_movie_number
- 1];
1381 input_movie_t
*in_movie
= &in
->file
.movie
;
1382 input_track_t
*in_track
= &in_movie
->track
[ in_movie
->current_track_number
- 1 ];
1383 if( !in_track
->active
)
1385 /* Move the next track. */
1386 if( ++ in_movie
->current_track_number
> in_movie
->num_tracks
)
1388 /* Move the next input movie. */
1389 in_movie
->current_track_number
= 1;
1390 ++input_movie_number
;
1392 if( input_movie_number
> remuxer
->num_input
)
1393 input_movie_number
= 1; /* Back the first input movie. */
1396 /* Try append a sample in an input track where we didn't reach the end of media timeline. */
1397 if( !in_track
->reach_end_of_media_timeline
)
1399 lsmash_sample_t
*sample
= in_track
->sample
;
1400 /* Get a new sample data if the track doesn't hold any one. */
1403 sample
= lsmash_get_sample_from_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
);
1406 in_track
->sample
= sample
;
1407 in_track
->dts
= (double)sample
->dts
/ in_track
->media
.param
.timescale
;
1408 /* Adapt the sample description index. */
1409 output_track_t
*out_track
= &out_movie
->track
[ out_movie
->current_track_number
- 1 ];
1410 sample
->index
= sample
->index
> in_track
->num_summaries
? in_track
->num_summaries
1411 : sample
->index
== 0 ? 1
1413 sample
->index
= out_track
->summary_remap
[ sample
->index
- 1 ];
1414 if( in_track
->current_sample_index
== 0 )
1415 in_track
->current_sample_index
= sample
->index
;
1419 if( lsmash_check_sample_existence_in_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
) )
1421 ERROR_MSG( "failed to get a sample.\n" );
1424 lsmash_sample_t sample_info
= { 0 };
1425 if( lsmash_get_sample_info_from_media_timeline( in
->root
, in_track
->track_ID
, in_track
->current_sample_number
, &sample_info
) < 0 )
1427 /* No more appendable samples in this track. */
1428 in_track
->sample
= NULL
;
1429 in_track
->reach_end_of_media_timeline
= 1;
1430 if( --num_active_input_tracks
== 0 )
1431 break; /* end of muxing */
1435 ERROR_MSG( "failed to get a sample.\n" );
1442 /* Flushing the active movie fragment is pending until random accessible point sample within all active tracks are ready. */
1443 if( remuxer
->fragment_base_track
)
1445 if( pending_flush_fragments
== 0 )
1446 pending_flush_fragments
= (remuxer
->fragment_base_track
== out_movie
->current_track_number
1447 && sample
->prop
.ra_flags
!= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
);
1448 else if( num_consecutive_sample_skip
== num_active_input_tracks
|| total_media_size
== 0 )
1450 if( flush_movie_fragment( remuxer
) < 0 )
1452 ERROR_MSG( "failed to flush a movie fragment.\n" );
1455 if( handle_segmentation( remuxer
) < 0 )
1457 if( lsmash_create_fragment_movie( output
->root
) < 0 )
1459 ERROR_MSG( "failed to create a movie fragment.\n" );
1462 pending_flush_fragments
= 0;
1465 /* Append a sample if meeting a condition. */
1466 if( pending_flush_fragments
== 0
1467 ? in_track
->dts
<= largest_dts
|| num_consecutive_sample_skip
== num_active_input_tracks
1468 : sample
->prop
.ra_flags
== ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
&& sample
->index
== in_track
->current_sample_index
)
1472 output_track_t
*out_track
= &out_movie
->track
[ out_movie
->current_track_number
- 1 ];
1473 /* The first DTS must be 0. */
1474 if( out_track
->current_sample_number
== 1 )
1475 out_track
->skip_dt_interval
= sample
->dts
;
1476 if( out_track
->skip_dt_interval
)
1478 sample
->dts
-= out_track
->skip_dt_interval
;
1479 sample
->cts
-= out_track
->skip_dt_interval
;
1481 uint64_t sample_size
= sample
->length
; /* sample might be deleted internally after appending. */
1482 uint64_t last_sample_dts
= sample
->dts
; /* same as above */
1483 uint32_t sample_index
= sample
->index
; /* same as above */
1484 /* Append a sample into output movie. */
1485 if( lsmash_append_sample( output
->root
, out_track
->track_ID
, sample
) )
1487 lsmash_delete_sample( sample
);
1488 return ERROR_MSG( "failed to append a sample.\n" );
1490 largest_dts
= LSMASH_MAX( largest_dts
, in_track
->dts
);
1491 in_track
->sample
= NULL
;
1492 in_track
->current_sample_number
+= 1;
1493 in_track
->current_sample_index
= sample_index
;
1494 out_track
->current_sample_number
+= 1;
1495 out_track
->last_sample_dts
= last_sample_dts
;
1496 num_consecutive_sample_skip
= 0;
1497 total_media_size
+= sample_size
;
1498 /* Print, per 256 samples, total size of imported media. */
1499 if( ++sample_count
== 0 )
1500 eprintf( "Importing: %"PRIu64
" bytes\r", total_media_size
);
1504 lsmash_delete_sample( sample
);
1505 in_track
->sample
= NULL
;
1506 in_track
->current_sample_number
+= 1;
1510 ++num_consecutive_sample_skip
; /* Skip appendig sample. */
1513 /* Move the next track. */
1514 if( ++ in_movie
->current_track_number
> in_movie
->num_tracks
)
1516 /* Move the next input movie. */
1517 in_movie
->current_track_number
= 1;
1518 ++input_movie_number
;
1520 if( input_movie_number
> remuxer
->num_input
)
1521 input_movie_number
= 1; /* Back the first input movie. */
1522 if( ++ out_movie
->current_track_number
> out_movie
->num_tracks
)
1523 out_movie
->current_track_number
= 1; /* Back the first track in the output movie. */
1525 for( uint32_t i
= 0; i
< out_movie
->num_tracks
; i
++ )
1526 if( lsmash_flush_pooled_samples( output
->root
, out_movie
->track
[i
].track_ID
, out_movie
->track
[i
].last_sample_delta
) )
1527 return ERROR_MSG( "failed to flush samples.\n" );
1532 static int construct_timeline_maps( remuxer_t
*remuxer
)
1534 input_t
*input
= remuxer
->input
;
1535 output_t
*output
= remuxer
->output
;
1536 output_movie_t
*out_movie
= &output
->file
.movie
;
1537 track_media_option
**track_option
= remuxer
->track_option
;
1538 out_movie
->current_track_number
= 1;
1539 for( int i
= 0; i
< remuxer
->num_input
; i
++ )
1540 for( uint32_t j
= 0; j
< input
[i
].file
.movie
.num_tracks
; j
++ )
1542 input_track_t
*in_track
= &input
[i
].file
.movie
.track
[j
];
1543 if( !in_track
->active
)
1545 output_track_t
*out_track
= &out_movie
->track
[ out_movie
->current_track_number
++ - 1 ];
1546 if( track_option
[i
][j
].seek
)
1548 /* Reconstruct timeline maps. */
1549 if( lsmash_delete_explicit_timeline_map( output
->root
, out_track
->track_ID
) )
1550 return ERROR_MSG( "failed to delete explicit timeline maps.\n" );
1551 uint32_t movie_timescale
= lsmash_get_movie_timescale( output
->root
);
1552 uint32_t media_timescale
= lsmash_get_media_timescale( output
->root
, out_track
->track_ID
);
1553 if( !media_timescale
)
1554 return ERROR_MSG( "media timescale is broken.\n" );
1555 double timescale_convert_multiplier
= (double)movie_timescale
/ media_timescale
;
1557 edit
.start_time
= in_track
->composition_delay
+ in_track
->skip_duration
;
1558 if( edit
.start_time
)
1560 uint64_t empty_duration
= edit
.start_time
+ lsmash_get_composition_to_decode_shift( output
->root
, out_track
->track_ID
);
1561 lsmash_edit_t empty_edit
;
1562 empty_edit
.duration
= empty_duration
* timescale_convert_multiplier
+ 0.5;
1563 empty_edit
.start_time
= ISOM_EDIT_MODE_EMPTY
;
1564 empty_edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
1565 if( lsmash_create_explicit_timeline_map( output
->root
, out_track
->track_ID
, empty_edit
) )
1566 return ERROR_MSG( "failed to create a empty duration.\n" );
1568 if( remuxer
->fragment_base_track
== 0 )
1569 edit
.duration
= (out_track
->last_sample_dts
+ out_track
->last_sample_delta
- in_track
->skip_duration
) * timescale_convert_multiplier
;
1571 edit
.duration
= ISOM_EDIT_DURATION_IMPLICIT
;
1572 edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
1573 if( lsmash_create_explicit_timeline_map( output
->root
, out_track
->track_ID
, edit
) )
1574 return ERROR_MSG( "failed to create a explicit timeline map.\n" );
1576 else if( lsmash_copy_timeline_map( output
->root
, out_track
->track_ID
, input
[i
].root
, in_track
->track_ID
) )
1577 return ERROR_MSG( "failed to copy timeline maps.\n" );
1579 out_movie
->current_track_number
= 1;
1583 static int finish_movie( remuxer_t
*remuxer
)
1585 output_t
*output
= remuxer
->output
;
1586 /* Set chapter list */
1587 if( remuxer
->chap_file
)
1588 lsmash_set_tyrant_chapter( output
->root
, remuxer
->chap_file
, remuxer
->add_bom_to_chpl
);
1589 /* Finish muxing. */
1591 if( lsmash_finish_movie( output
->root
, &moov_to_front
) )
1593 return remuxer
->fragment_base_track
? 0 : lsmash_write_lsmash_indicator( output
->root
);
1596 int main( int argc
, char *argv
[] )
1603 else if( !strcasecmp( argv
[1], "-h" ) || !strcasecmp( argv
[1], "--help" ) )
1608 else if( !strcasecmp( argv
[1], "-v" ) || !strcasecmp( argv
[1], "--version" ) )
1619 lsmash_get_mainargs( &argc
, &argv
);
1621 for( int i
= 1 ; i
< argc
; i
++ )
1622 if( !strcasecmp( argv
[i
], "-i" ) || !strcasecmp( argv
[i
], "--input" ) )
1625 return ERROR_MSG( "no input file specified.\n" );
1626 output_t output
= { 0 };
1627 input_t input
[ num_input
];
1628 track_media_option
*track_option
[ num_input
];
1633 .track_option
= track_option
,
1634 .num_input
= num_input
,
1635 .add_bom_to_chpl
= 0,
1636 .ref_chap_available
= 0,
1639 .default_language
= 0,
1640 .fragment_base_track
= 0,
1641 .subseg_per_seg
= 0,
1644 memset( input
, 0, num_input
* sizeof(input_t
) );
1645 memset( track_option
, 0, num_input
* sizeof(track_media_option
*) );
1646 if( parse_cli_option( argc
, argv
, &remuxer
) )
1647 return REMUXER_ERR( "failed to parse command line options.\n" );
1648 if( prepare_output( &remuxer
) )
1649 return REMUXER_ERR( "failed to set up preparation for output.\n" );
1650 if( remuxer
.fragment_base_track
&& construct_timeline_maps( &remuxer
) )
1651 return REMUXER_ERR( "failed to construct timeline maps.\n" );
1652 if( do_remux( &remuxer
) )
1653 return REMUXER_ERR( "failed to remux movies.\n" );
1654 if( remuxer
.fragment_base_track
== 0 && construct_timeline_maps( &remuxer
) )
1655 return REMUXER_ERR( "failed to construct timeline maps.\n" );
1656 if( finish_movie( &remuxer
) )
1657 return REMUXER_ERR( "failed to finish output movie.\n" );
1659 eprintf( "%s completed!\n", !remuxer
.dash
|| remuxer
.subseg_per_seg
== 0 ? "Remuxing" : "Segmentation" );
1660 cleanup_remuxer( &remuxer
);