importer: Somewhat clarify and separate its role.
[L-SMASH.git] / cli / muxer.c
blob55c473129d1495d0f0ae8dc32651d15c39264194
1 /*****************************************************************************
2 * muxer.c
3 *****************************************************************************
4 * Copyright (C) 2010-2017 L-SMASH project
6 * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
7 * Takashi Hirata <silverfilain@gmail.com>
9 * Permission to use, copy, modify, and/or distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 *****************************************************************************/
22 /* This file is available under an ISC license. */
24 #include "cli.h"
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <inttypes.h>
32 #include "importer/importer.h"
34 #define MAX_NUM_OF_BRANDS 50
35 #define MAX_NUM_OF_INPUTS 10
36 #define MAX_NUM_OF_TRACKS 1
38 typedef struct
40 char *album_name;
41 char *artist;
42 char *comment;
43 char *release_date;
44 char *encoder;
45 char *genre;
46 char *lyrics;
47 char *title;
48 char *composer;
49 char *album_artist;
50 char *copyright;
51 char *description;
52 char *grouping;
53 uint32_t beats_per_minute;
54 } itunes_metadata_t;
56 typedef struct
58 int help;
59 int version;
60 int isom;
61 int isom_version;
62 int itunes_movie;
63 int qtff;
64 int brand_3gx;
65 int optimize_pd;
66 int timeline_shift;
67 int compact_size_table;
68 uint32_t interleave;
69 uint32_t movie_timescale;
70 uint32_t num_of_brands;
71 uint32_t brands[MAX_NUM_OF_BRANDS];
72 uint32_t major_brand;
73 uint32_t minor_version;
74 uint32_t num_of_inputs;
75 uint32_t chap_track;
76 char *chap_file;
77 int add_bom_to_chpl;
78 char *copyright_notice;
79 uint16_t copyright_language;
80 itunes_metadata_t itunes_metadata;
81 uint16_t default_language;
82 } option_t;
84 typedef struct
86 char *raws;
87 int disable;
88 int sbr;
89 int user_fps;
90 uint32_t fps_num;
91 uint32_t fps_den;
92 uint32_t encoder_delay;
93 int16_t alternate_group;
94 uint16_t ISO_language;
95 uint16_t copyright_language;
96 char *copyright_notice;
97 char *handler_name;
98 uint32_t par_h;
99 uint32_t par_v;
100 } input_track_option_t;
102 typedef struct
104 lsmash_summary_t *summary;
105 input_track_option_t opt;
106 int active;
107 int lpcm;
108 } input_track_t;
110 typedef struct
112 char *whole_track_option;
113 int num_of_track_delimiters;
114 } input_option_t;
116 typedef struct
118 input_option_t opt;
119 lsmash_root_t *root;
120 char *file_name;
121 importer_t *importer;
122 input_track_t track[MAX_NUM_OF_TRACKS];
123 uint32_t num_of_tracks;
124 uint32_t num_of_active_tracks;
125 uint32_t current_track_number;
126 } input_t;
128 typedef struct
130 lsmash_summary_t *summary;
131 lsmash_sample_t *sample;
132 int active;
133 uint32_t track_ID;
134 uint32_t timescale;
135 uint32_t timebase;
136 uint32_t sample_entry;
137 uint32_t current_sample_number;
138 uint32_t ctd_shift;
139 uint32_t priming_samples;
140 uint32_t last_delta;
141 uint64_t prev_dts;
142 int64_t start_offset;
143 double dts;
144 int lpcm;
145 } output_track_t;
147 typedef struct
149 output_track_t *track;
150 uint32_t num_of_tracks;
151 uint32_t current_track_number;
152 } output_movie_t;
154 typedef struct
156 char *name;
157 lsmash_file_t *fh;
158 lsmash_file_parameters_t param;
159 output_movie_t movie;
160 } output_file_t;
162 typedef struct
164 lsmash_root_t *root;
165 output_file_t file;
166 } output_t;
168 typedef struct
170 option_t opt;
171 output_t output;
172 input_t input[MAX_NUM_OF_INPUTS];
173 uint32_t num_of_inputs;
174 } muxer_t;
176 static void cleanup_muxer( muxer_t *muxer )
178 if( !muxer )
179 return;
180 output_t *output = &muxer->output;
181 lsmash_close_file( &output->file.param );
182 lsmash_destroy_root( output->root );
183 if( output->file.movie.track )
185 for( uint32_t i = 0; i < output->file.movie.num_of_tracks; i++ )
187 output_track_t *out_track = &output->file.movie.track[i];
188 lsmash_delete_sample( out_track->sample );
190 lsmash_free( output->file.movie.track );
192 for( uint32_t i = 0; i < muxer->num_of_inputs; i++ )
194 input_t *input = &muxer->input[i];
195 lsmash_importer_close( input->importer );
196 for( uint32_t j = 0; j < input->num_of_tracks; j++ )
197 lsmash_cleanup_summary( input->track[j].summary );
198 lsmash_destroy_root( input->root );
202 #define eprintf( ... ) fprintf( stderr, __VA_ARGS__ )
203 #define REFRESH_CONSOLE eprintf( " \r" )
205 static int muxer_error( muxer_t *muxer, const char *message, ... )
207 cleanup_muxer( muxer );
208 REFRESH_CONSOLE;
209 eprintf( "Error: " );
210 va_list args;
211 va_start( args, message );
212 vfprintf( stderr, message, args );
213 va_end( args );
214 return -1;
217 static int error_message( const char *message, ... )
219 REFRESH_CONSOLE;
220 eprintf( "Error: " );
221 va_list args;
222 va_start( args, message );
223 vfprintf( stderr, message, args );
224 va_end( args );
225 return -1;
228 static void display_version( void )
230 eprintf( "\n"
231 "L-SMASH isom/mov multiplexer rev%s %s\n"
232 "Built on %s %s\n"
233 "Copyright (C) 2010-2017 L-SMASH project\n",
234 LSMASH_REV, LSMASH_GIT_HASH, __DATE__, __TIME__ );
237 static void display_help( void )
239 display_version();
240 eprintf( "\n"
241 "Usage: muxer [global_options] -i input1 [-i input2 -i input3 ...] -o output\n"
242 "Global options:\n"
243 " --help Display help\n"
244 " --version Display version information\n"
245 " --optimize-pd Optimize for progressive download\n"
246 " --interleave <integer> Specify time interval for media interleaving in milliseconds\n"
247 " --file-format <string> Specify output file format\n"
248 " Multiple file format can be specified by comma separators\n"
249 " The first is applied as the best used one\n"
250 " --isom-version <integer> Specify maximum compatible ISO Base Media version\n"
251 " --shift-timeline Enable composition to decode timeline shift\n"
252 " --chapter <string> Set chapters from the file.\n"
253 " --chpl-with-bom Add UTF-8 BOM to the chapter strings\n"
254 " in the chapter list. (experimental)\n"
255 " --chapter-track <integer> Set which track the chapter applies to.\n"
256 " This option takes effect only when reference\n"
257 " chapter is available.\n"
258 " If this option is not used, it defaults to 1.\n"
259 " --copyright-notice <arg> Specify copyright notice with or without language (latter string)\n"
260 " <arg> is <string> or <string>/<string>\n"
261 " --language <string> Specify the default language for all the output tracks.\n"
262 " This option is overridden by the track options.\n"
263 " --compact-size-table Compress sample size tables if possible.\n"
264 " --movie-timescale <integer> Specify movie timescale.\n"
265 "Output file formats:\n"
266 " mp4, mov, 3gp, 3g2, m4a, m4v\n"
267 "\n"
268 "Track options:\n"
269 " disable Disable this track\n"
270 " fps=<arg> Specify video framerate\n"
271 " <arg> is <integer> or <integer>/<integer>\n"
272 " language=<string> Specify media language\n"
273 " alternate-group=<integer> Specify alternate group\n"
274 " encoder-delay=<integer> Represent audio encoder delay (priming samples) explicitly\n"
275 " copyright=<arg> Specify copyright notice with or without language (latter string)\n"
276 " <arg> is <string> or <string>/<string>\n"
277 " handler=<string> Set media handler name\n"
278 " sbr Enable backward-compatible SBR explicit signaling mode\n"
279 " par=<integer>:<integer> Specify pixel aspect ratio of the first sequence\n"
280 " And never change ones in the media stream.\n"
281 "How to use track options:\n"
282 " -i input?[track_option1],[track_option2]...\n"
283 "\n"
284 "iTunes Metadata:\n"
285 " --album-name <string> Album name\n"
286 " --artist <string> Artist\n"
287 " --comment <string> User comment\n"
288 " --release-date <string> Release date (YYYY-MM-DD)\n"
289 " --encoder <string> Person or company that encoded the recording\n"
290 " --genre <string> Genre\n"
291 " --lyrics <string> Lyrics\n"
292 " --title <string> Title or song name\n"
293 " --composer <string> Composer\n"
294 " --album-artist <string> Artist for the whole album (if different than the individual tracks)\n"
295 " --copyright <string> Copyright\n"
296 " --description <string> Description\n"
297 " --grouping <string> Grouping\n"
298 " --tempo <integer> Beats per minute\n" );
301 static int muxer_usage_error( void )
303 display_help();
304 return -1;
307 #define MUXER_ERR( ... ) muxer_error( &muxer, __VA_ARGS__ )
308 #define ERROR_MSG( ... ) error_message( __VA_ARGS__ )
309 #define MUXER_USAGE_ERR() muxer_usage_error();
311 static int add_brand( option_t *opt, uint32_t brand )
313 if( opt->num_of_brands >= MAX_NUM_OF_BRANDS )
314 return -1;
315 /* Avoid duplication. */
316 for( uint32_t i = 0; i < opt->num_of_brands; i++ )
317 if( opt->brands[i] == brand )
318 return -2;
319 opt->brands[opt->num_of_brands ++] = brand;
320 return 0;
323 static int setup_isom_version( option_t *opt )
325 add_brand( opt, ISOM_BRAND_TYPE_ISOM );
326 if( opt->isom_version > 6 )
327 return ERROR_MSG( "unknown ISO Base Media version.\n" );
328 #define SET_ISOM_VERSION( version ) \
329 if( opt->isom_version >= version ) \
330 add_brand( opt, ISOM_BRAND_TYPE_ISO##version )
331 SET_ISOM_VERSION( 2 );
332 SET_ISOM_VERSION( 3 );
333 SET_ISOM_VERSION( 4 );
334 SET_ISOM_VERSION( 5 );
335 SET_ISOM_VERSION( 6 );
336 #undef SET_ISOM_VERSION
337 return 0;
340 static int decide_brands( option_t *opt )
342 if( opt->num_of_brands == 0 )
344 /* default file format */
345 opt->major_brand = ISOM_BRAND_TYPE_MP42;
346 opt->minor_version = 0x00000000;
347 add_brand( opt, ISOM_BRAND_TYPE_MP42 );
348 add_brand( opt, ISOM_BRAND_TYPE_MP41 );
349 add_brand( opt, ISOM_BRAND_TYPE_ISOM );
350 opt->isom = 1;
351 eprintf( "MP4 muxing mode\n" );
352 return setup_isom_version( opt );
354 opt->major_brand = opt->brands[0]; /* Pick the first brand as major brand. */
355 for( uint32_t i = 0; i < opt->num_of_brands; i++ )
357 switch( opt->brands[i] )
359 case ISOM_BRAND_TYPE_3GP6 :
360 /* When being compatible with 3gp6, also compatible with 3g2a. */
361 add_brand( opt, ISOM_BRAND_TYPE_3G2A );
362 opt->brand_3gx = 1;
363 break;
364 case ISOM_BRAND_TYPE_3G2A :
365 opt->brand_3gx = 2;
366 break;
367 case ISOM_BRAND_TYPE_QT :
368 opt->qtff = 1;
369 break;
370 case ISOM_BRAND_TYPE_M4A :
371 case ISOM_BRAND_TYPE_M4V :
372 opt->itunes_movie = 1;
373 /* fall-through */
374 case ISOM_BRAND_TYPE_MP42 :
375 add_brand( opt, ISOM_BRAND_TYPE_MP42 );
376 add_brand( opt, ISOM_BRAND_TYPE_MP41 );
377 break;
378 default :
379 break;
381 if( opt->brands[i] != ISOM_BRAND_TYPE_QT )
382 opt->isom = 1;
384 switch( opt->major_brand )
386 case ISOM_BRAND_TYPE_MP42 :
387 opt->minor_version = 0x00000000;
388 eprintf( "MP4 muxing mode\n" );
389 break;
390 case ISOM_BRAND_TYPE_M4A :
391 case ISOM_BRAND_TYPE_M4V :
392 opt->minor_version = 0x00000000;
393 eprintf( "iTunes MP4 muxing mode\n" );
394 break;
395 case ISOM_BRAND_TYPE_3GP6 :
396 opt->minor_version = 0x00000000; /* means, 3gp(3gp6) 6.0.0 : "6" is not included in minor_version. */
397 eprintf( "3GPP muxing mode\n" );
398 break;
399 case ISOM_BRAND_TYPE_3G2A :
400 opt->minor_version = 0x00010000; /* means, 3g2(3g2a) 1.0.0 : a == 1 */
401 eprintf( "3GPP2 muxing mode\n" );
402 break;
403 case ISOM_BRAND_TYPE_QT :
404 opt->minor_version = 0x00000000; /* We don't know exact version of the spec to use QTFF features. */
405 eprintf( "QuickTime file format muxing mode\n" );
406 break;
407 default :
408 break;
410 /* Set up ISO Base Media version. */
411 if( opt->isom )
412 setup_isom_version( opt );
413 if( opt->num_of_brands > MAX_NUM_OF_BRANDS )
414 return ERROR_MSG( "exceed the maximum number of brands we can deal with.\n" );
415 return 0;
418 static int parse_global_options( int argc, char **argv, muxer_t *muxer )
420 if ( argc < 2 )
421 return -1;
422 else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) )
424 muxer->opt.help = 1;
425 return 0;
427 else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) )
429 muxer->opt.version = 1;
430 return 0;
432 else if( argc < 5 )
433 return -1;
434 uint32_t i = 1;
435 option_t *opt = &muxer->opt;
436 opt->chap_track = 1;
437 opt->add_bom_to_chpl = 0;
438 while( argc > i && *argv[i] == '-' )
440 #define CHECK_NEXT_ARG if( argc == ++i ) return -1
441 if( !strcasecmp( argv[i], "-i" ) || !strcasecmp( argv[i], "--input" ) )
443 CHECK_NEXT_ARG;
444 if( opt->num_of_inputs + 1 > MAX_NUM_OF_INPUTS )
445 return ERROR_MSG( "exceed the maximum number of input files.\n" );
446 input_t *input = &muxer->input[opt->num_of_inputs];
447 input_option_t *input_movie_opt = &input->opt;
448 char *p = argv[i];
449 while( *p )
450 input_movie_opt->num_of_track_delimiters += (*p++ == '?');
451 if( input_movie_opt->num_of_track_delimiters > MAX_NUM_OF_TRACKS )
452 return ERROR_MSG( "you specified options to exceed the maximum number of tracks per input files.\n" );
453 input->file_name = strtok( argv[i], "?" );
454 input_movie_opt->whole_track_option = strtok( NULL, "" );
455 if( input_movie_opt->num_of_track_delimiters )
457 input_track_option_t *track_opt = &input->track[0].opt;
458 track_opt->raws = strtok( input_movie_opt->whole_track_option, "?" );
459 #if (MAX_NUM_OF_TRACKS - 1)
460 for( uint32_t j = 1; j < input_movie_opt->num_of_track_delimiters; j++ )
462 track_opt = &input->track[j].opt;
463 track_opt->raws = strtok( NULL, "?" );
465 #endif
467 ++ opt->num_of_inputs;
469 else if( !strcasecmp( argv[i], "-o" ) || !strcasecmp( argv[i], "--output" ) )
471 CHECK_NEXT_ARG;
472 muxer->output.file.name = argv[i];
474 else if( !strcasecmp( argv[i], "--optimize-pd" ) )
475 opt->optimize_pd = 1;
476 else if( !strcasecmp( argv[i], "--interleave" ) )
478 CHECK_NEXT_ARG;
479 if( opt->interleave )
480 return ERROR_MSG( "you specified --interleave twice.\n" );
481 opt->interleave = atoi( argv[i] );
483 else if( !strcasecmp( argv[i], "--file-format" ) )
485 CHECK_NEXT_ARG;
486 static const struct
488 uint32_t brand_4cc;
489 char *file_format;
490 } file_format_list[]
492 { ISOM_BRAND_TYPE_MP42, "mp4" },
493 { ISOM_BRAND_TYPE_QT, "mov" },
494 { ISOM_BRAND_TYPE_3GP6, "3gp" },
495 { ISOM_BRAND_TYPE_3G2A, "3g2" },
496 { ISOM_BRAND_TYPE_M4A, "m4a" },
497 { ISOM_BRAND_TYPE_M4V, "m4v" },
498 { 0, NULL }
500 char *file_format = NULL;
501 while( (file_format = strtok( file_format ? NULL : argv[i], "," )) != NULL )
503 int j;
504 for( j = 0; file_format_list[j].file_format; j++ )
505 if( !strcmp( file_format, file_format_list[j].file_format ) )
507 int ret = add_brand( opt, file_format_list[j].brand_4cc );
508 if( ret == -2 )
509 return ERROR_MSG( "you specified same output file format twice.\n" );
510 else if( ret == -1 )
511 return ERROR_MSG( "exceed the maximum number of brands we can deal with.\n" );
512 break;
514 if( !file_format_list[j].file_format )
515 return MUXER_USAGE_ERR();
518 else if( !strcasecmp( argv[i], "--isom-version" ) )
520 CHECK_NEXT_ARG;
521 if( opt->isom_version )
522 return ERROR_MSG( "you specified --isom-version twice.\n" );
523 opt->isom_version = atoi( argv[i] );
525 else if( !strcasecmp( argv[i], "--shift-timeline" ) )
526 opt->timeline_shift = 1;
527 else if( !strcasecmp( argv[i], "compact-size-table" ) )
528 opt->compact_size_table = 1;
529 else if( !strcasecmp( argv[i], "--chapter" ) )
531 CHECK_NEXT_ARG;
532 opt->chap_file = argv[i];
534 else if( !strcasecmp( argv[i], "--chapter-track" ) )
536 CHECK_NEXT_ARG;
537 opt->chap_track = atoi( argv[i] );
538 if( !opt->chap_track )
539 return ERROR_MSG( "%s is an invalid track number.\n", argv[i] );
541 else if( !strcasecmp( argv[i], "--chpl-with-bom" ) )
542 opt->add_bom_to_chpl = 1;
543 else if( !strcasecmp( argv[i], "--copyright-notice" ) )
545 CHECK_NEXT_ARG;
546 if( opt->copyright_notice )
547 return ERROR_MSG( "you specified --copyright-notice twice.\n" );
548 opt->copyright_notice = argv[i];
549 char *language = opt->copyright_notice;
550 while( *language )
552 if( *language == '/' )
554 *language++ = '\0';
555 break;
557 ++language;
559 opt->copyright_language = language ? lsmash_pack_iso_language( language ) : ISOM_LANGUAGE_CODE_UNDEFINED;
561 else if( !strcasecmp( argv[i], "--movie-timescale" ) )
563 CHECK_NEXT_ARG;
564 if( opt->movie_timescale )
565 return ERROR_MSG( "you specified --movie-timescale twice.\n" );
566 opt->movie_timescale = atoi( argv[i] );
568 /* iTunes metadata */
569 #define CHECK_ITUNES_METADATA_ARG_STRING( argument, value ) \
570 else if( !strcasecmp( argv[i], "--"#argument ) ) \
572 CHECK_NEXT_ARG; \
573 if( opt->itunes_metadata.value ) \
574 return ERROR_MSG( "you specified --"#argument" twice.\n" ); \
575 opt->itunes_metadata.value = argv[i]; \
577 CHECK_ITUNES_METADATA_ARG_STRING( album-name, album_name )
578 CHECK_ITUNES_METADATA_ARG_STRING( artist, artist )
579 CHECK_ITUNES_METADATA_ARG_STRING( comment, comment )
580 CHECK_ITUNES_METADATA_ARG_STRING( release-date, release_date )
581 CHECK_ITUNES_METADATA_ARG_STRING( encoder, encoder )
582 CHECK_ITUNES_METADATA_ARG_STRING( genre, genre )
583 CHECK_ITUNES_METADATA_ARG_STRING( lyrics, lyrics )
584 CHECK_ITUNES_METADATA_ARG_STRING( title, title )
585 CHECK_ITUNES_METADATA_ARG_STRING( composer, composer )
586 CHECK_ITUNES_METADATA_ARG_STRING( album-artist, album_artist )
587 CHECK_ITUNES_METADATA_ARG_STRING( copyright, copyright )
588 CHECK_ITUNES_METADATA_ARG_STRING( description, description )
589 CHECK_ITUNES_METADATA_ARG_STRING( grouping, grouping )
590 #undef CHECK_ITUNES_METADATA_ARG_STRING
591 else if( !strcasecmp( argv[i], "--tempo" ) )
593 CHECK_NEXT_ARG;
594 if( opt->itunes_metadata.beats_per_minute )
595 return ERROR_MSG( "you specified --tempo twice.\n" );
596 opt->itunes_metadata.beats_per_minute = atoi( argv[i] );
598 else if( !strcasecmp( argv[i], "--language" ) )
600 CHECK_NEXT_ARG;
601 opt->default_language = lsmash_pack_iso_language( argv[i] );
603 #undef CHECK_NEXT_ARG
604 else
605 return ERROR_MSG( "you specified invalid option: %s.\n", argv[i] );
606 ++i;
608 if( !muxer->output.file.name )
609 return ERROR_MSG( "output file name is not specified.\n" );
610 if( decide_brands( opt ) )
611 return ERROR_MSG( "failed to set up output file format.\n" );
612 if( opt->timeline_shift && !opt->qtff && opt->isom_version < 4 )
613 return ERROR_MSG( "timeline shift requires --file-format mov, or --isom-version 4 or later.\n" );
614 muxer->num_of_inputs = opt->num_of_inputs;
615 return 0;
618 static int parse_track_options( input_t *input )
620 for( input->current_track_number = 1;
621 input->current_track_number <= input->num_of_tracks;
622 input->current_track_number ++ )
624 input_track_t *in_track = &input->track[input->current_track_number - 1];
625 input_track_option_t *track_opt = &in_track->opt;
626 if( track_opt->raws == NULL )
627 break;
628 #if 0
629 if( !strchr( track_opt->raws, ':' )
630 || strchr( track_opt->raws, ':' ) == track_opt->raws )
631 return ERROR_MSG( "track number is not specified in %s\n", track_opt->raws );
632 if( strchr( track_opt->raws, ':' ) != strrchr( track_opt->raws, ':' ) )
633 return ERROR_MSG( "multiple colons inside one track option in %s.\n", track_opt->raws );
634 uint32_t track_number = atoi( strtok( track_opt->raws, ":" ) );
635 if( track_number == 0 || track_number > MAX_NUM_OF_TRACKS )
636 return ERROR_MSG( "%s is an invalid track number %"PRIu32".\n", strtok( track_opt->raws, ":" ), track_number );
637 in_track = &input->track[track_number - 1];
638 track_opt = &in_track->opt;
639 char *track_option;
640 while( (track_option = strtok( NULL, "," )) != NULL )
641 #else
642 char *track_option = NULL;
643 while( (track_option = strtok( track_option ? NULL : track_opt->raws, "," )) != NULL )
644 #endif
646 if( strchr( track_option, '=' ) != strrchr( track_option, '=' ) )
647 return ERROR_MSG( "multiple equal signs inside one track option in %s\n", track_option );
648 if( strstr( track_option, "disable" ) )
649 track_opt->disable = 1;
650 else if( strstr( track_option, "alternate-group=" ) )
652 char *track_parameter = strchr( track_option, '=' ) + 1;
653 track_opt->alternate_group = atoi( track_parameter );
655 else if( strstr( track_option, "encoder-delay=" ) )
657 char *track_parameter = strchr( track_option, '=' ) + 1;
658 track_opt->encoder_delay = atoi( track_parameter );
660 else if( strstr( track_option, "language=" ) )
662 char *track_parameter = strchr( track_option, '=' ) + 1;
663 track_opt->ISO_language = lsmash_pack_iso_language( track_parameter );
665 else if( strstr( track_option, "fps=" ) )
667 char *track_parameter = strchr( track_option, '=' ) + 1;
668 if( sscanf( track_parameter, "%"SCNu32"/%"SCNu32, &track_opt->fps_num, &track_opt->fps_den ) == 1 )
670 track_opt->fps_num = atoi( track_parameter );
671 track_opt->fps_den = 1;
673 track_opt->user_fps = 1;
675 else if( strstr( track_option, "copyright=" ) )
677 char *track_parameter = strchr( track_option, '=' ) + 1;
678 track_opt->copyright_notice = track_parameter;
679 while( *track_parameter )
681 if( *track_parameter == '/' )
683 *track_parameter++ = '\0';
684 break;
686 ++track_parameter;
688 track_opt->copyright_language = lsmash_pack_iso_language( track_parameter );
690 else if( strstr( track_option, "handler=" ) )
692 char *track_parameter = strchr( track_option, '=' ) + 1;
693 track_opt->handler_name = track_parameter;
695 else if( strstr( track_option, "sbr" ) )
696 track_opt->sbr = 1;
697 else if( strstr( track_option, "par=" ) )
699 char *track_parameter = strchr( track_option, '=' ) + 1;
700 if( sscanf( track_parameter, "%"SCNu32":%"SCNu32, &track_opt->par_h, &track_opt->par_v ) != 2 )
702 track_opt->par_h = 0;
703 track_opt->par_v = 0;
706 else
707 return ERROR_MSG( "unknown track option %s\n", track_option );
710 return 0;
713 static void display_codec_name( lsmash_codec_type_t codec_type, uint32_t track_number )
715 #define DISPLAY_CODEC_NAME( codec, codec_name ) \
716 else if( lsmash_check_codec_type_identical( codec_type, codec ) ) \
717 eprintf( "Track %"PRIu32": "#codec_name"\n", track_number )
718 if( 0 );
719 DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_AVC1_VIDEO, H.264 Advanced Video Coding );
720 DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_HVC1_VIDEO, H.265 High Efficiency Video Coding );
721 DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_VC_1_VIDEO, SMPTE VC-1 Advanced Profile );
722 DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_MP4A_AUDIO, MPEG-4 Audio );
723 DISPLAY_CODEC_NAME( QT_CODEC_TYPE_MP4A_AUDIO, MPEG-4 Audio );
724 DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_AC_3_AUDIO, AC-3 );
725 DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_EC_3_AUDIO, Enhanced AC-3 );
726 DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_DTSC_AUDIO, DTS );
727 DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_DTSE_AUDIO, DTS LBR );
728 DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_DTSH_AUDIO, DTS-HD );
729 DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_DTSL_AUDIO, DTS-HD Lossless );
730 DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_DTSX_AUDIO, DTS:X );
731 DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_SAWB_AUDIO, Wideband AMR voice );
732 DISPLAY_CODEC_NAME( ISOM_CODEC_TYPE_SAMR_AUDIO, Narrowband AMR voice );
733 DISPLAY_CODEC_NAME( QT_CODEC_TYPE_LPCM_AUDIO, Uncompressed Audio );
734 #undef DISPLAY_CODEC_NAME
737 static int open_input_files( muxer_t *muxer )
739 output_movie_t *out_movie = &muxer->output.file.movie;
740 option_t *opt = &muxer->opt;
741 for( uint32_t current_input_number = 1; current_input_number <= muxer->num_of_inputs; current_input_number++ )
743 input_t *input = &muxer->input[current_input_number - 1];
744 /* Initialize importer framework. */
745 lsmash_root_t *root = lsmash_create_root();
746 if( !root )
747 return ERROR_MSG( "failed to create a ROOT for input file.\n" );
748 input->root = root;
749 input->importer = lsmash_importer_open( root, input->file_name, "auto" );
750 if( !input->importer )
751 return ERROR_MSG( "failed to open input file.\n" );
752 input->num_of_tracks = lsmash_importer_get_track_count( input->importer );
753 if( input->num_of_tracks == 0 )
754 return ERROR_MSG( "there is no valid track in input file.\n" );
755 if( opt->default_language )
756 for( int i = 0; i < input->num_of_tracks; i ++ )
757 input->track[i].opt.ISO_language = opt->default_language;
758 /* Parse track options */
759 if( parse_track_options( input ) )
760 return ERROR_MSG( "failed to parse track options.\n" );
761 /* Activate tracks by CODEC type. */
762 for( input->current_track_number = 1;
763 input->current_track_number <= input->num_of_tracks;
764 input->current_track_number ++ )
766 input_track_t *in_track = &input->track[input->current_track_number - 1];
767 int err = lsmash_importer_construct_timeline( input->importer, input->current_track_number );
768 if( err < 0 && err != LSMASH_ERR_PATCH_WELCOME )
770 in_track->active = 0;
771 continue;
773 in_track->summary = lsmash_duplicate_summary( input->importer, input->current_track_number );
774 if( !in_track->summary )
775 return ERROR_MSG( "failed to get input summary.\n" );
776 /* Check codec type. */
777 lsmash_codec_type_t codec_type = in_track->summary->sample_type;
778 in_track->active = 1;
779 if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_AVC1_VIDEO ) )
781 if( opt->isom )
782 add_brand( opt, ISOM_BRAND_TYPE_AVC1 );
784 else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_HVC1_VIDEO ) )
786 if( !opt->isom && opt->qtff )
787 return ERROR_MSG( "the input seems HEVC, at present available only for ISO Base Media file format.\n" );
789 else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_VC_1_VIDEO ) )
791 if( !opt->isom && opt->qtff )
792 return ERROR_MSG( "the input seems VC-1, at present available only for ISO Base Media file format.\n" );
794 else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_MP4A_AUDIO )
795 || lsmash_check_codec_type_identical( codec_type, QT_CODEC_TYPE_MP4A_AUDIO ) )
796 /* Do nothing. */;
797 else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_AC_3_AUDIO )
798 || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_EC_3_AUDIO ) )
800 if( !opt->isom && opt->qtff )
801 return ERROR_MSG( "the input seems (Enhanced) AC-3, at present available only for ISO Base Media file format.\n" );
802 add_brand( opt, ISOM_BRAND_TYPE_DBY1 );
804 else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_DTSC_AUDIO )
805 || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_DTSE_AUDIO )
806 || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_DTSH_AUDIO )
807 || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_DTSL_AUDIO )
808 || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_DTSX_AUDIO ) )
810 if( !opt->isom && opt->qtff )
811 return ERROR_MSG( "the input seems DTS(-HD) Audio, at present available only for ISO Base Media file format.\n" );
813 else if( lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_SAWB_AUDIO )
814 || lsmash_check_codec_type_identical( codec_type, ISOM_CODEC_TYPE_SAMR_AUDIO ) )
816 if( !opt->brand_3gx )
817 return ERROR_MSG( "the input seems AMR-NB/WB, available for 3GPP(2) file format.\n" );
819 else if( lsmash_check_codec_type_identical( codec_type, QT_CODEC_TYPE_LPCM_AUDIO ) )
821 if( opt->isom && !opt->qtff )
822 return ERROR_MSG( "the input seems Uncompressed Audio, at present available only for QuickTime file format.\n" );
823 in_track->lpcm = 1;
825 else
827 lsmash_cleanup_summary( in_track->summary );
828 in_track->summary = NULL;
829 in_track->active = 0;
831 if( in_track->active )
833 ++ input->num_of_active_tracks;
834 display_codec_name( codec_type, out_movie->num_of_tracks + input->num_of_active_tracks );
837 out_movie->num_of_tracks += input->num_of_active_tracks;
839 if( out_movie->num_of_tracks == 0 )
840 return ERROR_MSG( "there is no media that can be stored in output movie.\n" );
841 return 0;
844 static int set_itunes_metadata( output_t *output, option_t *opt )
846 if( !opt->itunes_movie )
847 return 0;
848 itunes_metadata_t *metadata = &opt->itunes_metadata;
849 #define SET_ITUNES_METADATA( item, type, value ) \
850 if( value \
851 && lsmash_set_itunes_metadata( output->root, (lsmash_itunes_metadata_t){ item, ITUNES_METADATA_TYPE_NONE, { .type = value }, NULL, NULL } ) ) \
852 return -1
853 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ENCODING_TOOL, string, "L-SMASH" );
854 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ALBUM_NAME, string, metadata->album_name );
855 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ARTIST, string, metadata->artist );
856 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_USER_COMMENT, string, metadata->comment );
857 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_RELEASE_DATE, string, metadata->release_date );
858 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ENCODED_BY, string, metadata->encoder );
859 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_USER_GENRE, string, metadata->genre );
860 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_LYRICS, string, metadata->lyrics );
861 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_TITLE, string, metadata->title );
862 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_COMPOSER, string, metadata->composer );
863 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_ALBUM_ARTIST, string, metadata->album_artist );
864 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_COPYRIGHT, string, metadata->copyright );
865 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_DESCRIPTION, string, metadata->description );
866 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_GROUPING, string, metadata->grouping );
867 SET_ITUNES_METADATA( ITUNES_METADATA_ITEM_BEATS_PER_MINUTE, integer, metadata->beats_per_minute );
868 #undef SET_ITUNES_METADATA
869 return 0;
872 static int prepare_output( muxer_t *muxer )
874 option_t *opt = &muxer->opt;
875 output_t *output = &muxer->output;
876 output_file_t *out_file = &output->file;
877 output_movie_t *out_movie = &out_file->movie;
878 /* Allocate output tracks. */
879 out_movie->track = lsmash_malloc( out_movie->num_of_tracks * sizeof(output_track_t) );
880 if( !out_movie->track )
881 return ERROR_MSG( "failed to allocate output tracks.\n" );
882 memset( out_movie->track, 0, out_movie->num_of_tracks * sizeof(output_track_t) );
883 /* Initialize L-SMASH muxer */
884 output->root = lsmash_create_root();
885 if( !output->root )
886 return ERROR_MSG( "failed to create a ROOT for output file.\n" );
887 lsmash_file_parameters_t *file_param = &out_file->param;
888 if( lsmash_open_file( out_file->name, 0, file_param ) < 0 )
889 return ERROR_MSG( "failed to open an output file.\n" );
890 file_param->major_brand = opt->major_brand;
891 file_param->brands = opt->brands;
892 file_param->brand_count = opt->num_of_brands;
893 file_param->minor_version = opt->minor_version;
894 if( opt->interleave )
895 file_param->max_chunk_duration = opt->interleave * 1e-3;
896 out_file->fh = lsmash_set_file( output->root, file_param );
897 if( !out_file->fh )
898 return ERROR_MSG( "failed to add an output file into a ROOT.\n" );
899 /* Initialize movie */
900 lsmash_movie_parameters_t movie_param;
901 lsmash_initialize_movie_parameters( &movie_param );
902 if( opt->movie_timescale )
903 movie_param.timescale = opt->movie_timescale;
904 if( lsmash_set_movie_parameters( output->root, &movie_param ) )
905 return ERROR_MSG( "failed to set movie parameters.\n" );
906 if( opt->copyright_notice
907 && lsmash_set_copyright( output->root, 0, opt->copyright_language, opt->copyright_notice ) )
908 return ERROR_MSG( "failed to set a copyright notice for the entire movie.\n" );
909 if( set_itunes_metadata( output, opt ) )
910 return ERROR_MSG( "failed to set iTunes metadata.\n" );
911 out_movie->current_track_number = 1;
912 for( uint32_t current_input_number = 1; current_input_number <= muxer->num_of_inputs; current_input_number++ )
914 input_t *input = &muxer->input[current_input_number - 1];
915 for( input->current_track_number = 1;
916 input->current_track_number <= input->num_of_tracks;
917 input->current_track_number ++ )
919 input_track_t *in_track = &input->track[ input->current_track_number - 1 ];
920 if( !in_track->active )
921 continue;
922 input_track_option_t *track_opt = &in_track->opt;
923 output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ];
924 /* Set up track parameters. */
925 lsmash_track_parameters_t track_param;
926 lsmash_initialize_track_parameters( &track_param );
927 track_param.mode = ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW;
928 if( !track_opt->disable )
929 track_param.mode |= ISOM_TRACK_ENABLED;
930 if( opt->qtff )
931 track_param.mode |= QT_TRACK_IN_POSTER;
932 track_param.alternate_group = track_opt->alternate_group;
933 lsmash_media_parameters_t media_param;
934 lsmash_initialize_media_parameters( &media_param );
935 media_param.ISO_language = track_opt->ISO_language;
936 media_param.compact_sample_size_table = opt->compact_size_table;
937 switch( in_track->summary->summary_type )
939 case LSMASH_SUMMARY_TYPE_VIDEO :
941 out_track->track_ID = lsmash_create_track( output->root, ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK );
942 if( !out_track->track_ID )
943 return ERROR_MSG( "failed to create a track.\n" );
944 lsmash_video_summary_t *summary = (lsmash_video_summary_t *)in_track->summary;
945 uint64_t display_width = (uint64_t)summary->width << 16;
946 uint64_t display_height = (uint64_t)summary->height << 16;
947 if( track_opt->par_h && track_opt-> par_v )
949 summary->par_h = track_opt->par_h;
950 summary->par_v = track_opt->par_v;
952 if( summary->par_h && summary->par_v )
954 double sar = (double)summary->par_h / summary->par_v;
955 if( sar > 1.0 )
956 display_width *= sar;
957 else
958 display_height /= sar;
960 track_param.display_width = display_width <= UINT32_MAX ? display_width : UINT32_MAX;
961 track_param.display_height = display_height <= UINT32_MAX ? display_height : UINT32_MAX;
962 /* Initialize media */
963 uint32_t timescale = 25; /* default value */
964 uint32_t timebase = 1; /* default value */
965 if( track_opt->user_fps )
967 timescale = track_opt->fps_num << (!!summary->sample_per_field);
968 timebase = track_opt->fps_den;
970 else if( !summary->vfr )
972 if( lsmash_check_codec_type_identical( summary->sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO )
973 || lsmash_check_codec_type_identical( summary->sample_type, ISOM_CODEC_TYPE_HVC1_VIDEO ) )
975 uint32_t compare_timebase = summary->timebase;
976 uint32_t compare_timescale = summary->timescale;
977 static const struct
979 uint32_t timescale;
980 uint32_t timebase;
981 } well_known_fps[]
983 { 24000, 1001 }, { 30000, 1001 }, { 60000, 1001 }, { 120000, 1001 }, { 72000, 1001 },
984 { 25, 1 }, { 50, 1 }, { 24, 1 }, { 30, 1 }, { 60, 1 }, { 120, 1 }, { 72, 1 }, { 0, 0 }
986 for( int i = 0; well_known_fps[i].timescale; i++ )
987 if( well_known_fps[i].timescale == compare_timescale
988 && well_known_fps[i].timebase == compare_timebase )
990 timescale = well_known_fps[i].timescale;
991 timebase = well_known_fps[i].timebase;
992 break;
994 lsmash_codec_specific_t *bitrate = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE,
995 LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED );
996 if( bitrate )
997 lsmash_add_codec_specific_data( in_track->summary, bitrate );
998 lsmash_destroy_codec_specific_data( bitrate );
1000 else
1002 timescale = summary->timescale;
1003 timebase = summary->timebase;
1006 media_param.timescale = timescale;
1007 media_param.media_handler_name = track_opt->handler_name ? track_opt->handler_name : "L-SMASH Video Handler";
1008 media_param.roll_grouping = 1;
1009 media_param.rap_grouping = opt->isom_version >= 6;
1010 out_track->timescale = timescale;
1011 out_track->timebase = timebase;
1012 break;
1014 case LSMASH_SUMMARY_TYPE_AUDIO :
1016 out_track->track_ID = lsmash_create_track( output->root, ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK );
1017 if( !out_track->track_ID )
1018 return ERROR_MSG( "failed to create a track.\n" );
1019 lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)in_track->summary;
1020 if( track_opt->sbr )
1022 /* Check if explicit SBR is valid or not. */
1023 if( lsmash_mp4sys_get_object_type_indication( (lsmash_summary_t *)summary ) != MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3 )
1024 return ERROR_MSG( "--sbr is only valid with MPEG-4 Audio.\n" );
1025 summary->sbr_mode = MP4A_AAC_SBR_BACKWARD_COMPATIBLE;
1026 if( lsmash_setup_AudioSpecificConfig( summary ) )
1027 return ERROR_MSG( "failed to set SBR mode.\n" );
1029 media_param.timescale = summary->frequency;
1030 media_param.media_handler_name = track_opt->handler_name ? track_opt->handler_name : "L-SMASH Audio Handler";
1031 media_param.roll_grouping = (opt->isom_version >= 2 || (opt->qtff && !in_track->lpcm));
1032 out_track->priming_samples = track_opt->encoder_delay;
1033 out_track->timescale = summary->frequency;
1034 out_track->timebase = 1;
1035 out_track->lpcm = in_track->lpcm;
1036 break;
1038 default :
1039 return ERROR_MSG( "not supported stream type.\n" );
1041 /* Reset the movie timescale in order to match the media timescale if only one track is there. */
1042 if( muxer->num_of_inputs == 1
1043 && current_input_number == 1
1044 && input->current_track_number == 1 )
1046 movie_param.timescale = media_param.timescale;
1047 if( lsmash_set_movie_parameters( output->root, &movie_param ) )
1048 return ERROR_MSG( "failed to set movie parameters.\n" );
1050 /* Set copyright information. */
1051 if( track_opt->copyright_notice
1052 && lsmash_set_copyright( output->root, out_track->track_ID, track_opt->copyright_language, track_opt->copyright_notice ) )
1053 return ERROR_MSG( "failed to set a copyright notice.\n" );
1054 /* Set track parameters. */
1055 if( lsmash_set_track_parameters( output->root, out_track->track_ID, &track_param ) )
1056 return ERROR_MSG( "failed to set track parameters.\n" );
1057 /* Set media parameters. */
1058 if( lsmash_set_media_parameters( output->root, out_track->track_ID, &media_param ) )
1059 return ERROR_MSG( "failed to set media parameters.\n" );
1060 out_track->summary = in_track->summary;
1061 out_track->sample_entry = lsmash_add_sample_entry( output->root, out_track->track_ID, out_track->summary );
1062 if( !out_track->sample_entry )
1063 return ERROR_MSG( "failed to add sample description entry.\n" );
1064 out_track->active = 1;
1065 ++ out_movie->current_track_number;
1067 input->current_track_number = 1;
1069 out_movie->current_track_number = 1;
1070 return 0;
1073 static void set_reference_chapter_track( output_t *output, option_t *opt )
1075 if( !opt->chap_file || (!opt->qtff && !opt->itunes_movie) || (opt->brand_3gx == 1) )
1076 return;
1077 lsmash_create_reference_chapter_track( output->root, opt->chap_track, opt->chap_file );
1080 static int do_mux( muxer_t *muxer )
1082 #define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b))
1083 option_t *opt = &muxer->opt;
1084 output_t *output = &muxer->output;
1085 output_movie_t *out_movie = &output->file.movie;
1086 set_reference_chapter_track( output, opt );
1087 double largest_dts = 0;
1088 uint32_t current_input_number = 1;
1089 uint32_t num_consecutive_sample_skip = 0;
1090 uint32_t num_active_input_tracks = out_movie->num_of_tracks;
1091 uint64_t total_media_size = 0;
1092 uint32_t progress_pos = 0;
1093 while( 1 )
1095 input_t *input = &muxer->input[current_input_number - 1];
1096 output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ];
1097 if( out_track->active )
1099 lsmash_sample_t *sample = out_track->sample;
1100 /* Get a new sample data if the track doesn't hold any one. */
1101 if( !sample )
1103 /* lsmash_importer_get_access_unit() returns 1 if there're any changes in stream's properties. */
1104 int ret = lsmash_importer_get_access_unit( input->importer, input->current_track_number, &sample );
1105 if( ret == LSMASH_ERR_MEMORY_ALLOC )
1106 return ERROR_MSG( "failed to alloc memory for buffer.\n" );
1107 else if( ret <= -1 )
1109 lsmash_delete_sample( sample );
1110 ERROR_MSG( "failed to get a frame from input file. Maybe corrupted.\n"
1111 "Aborting muxing operation and trying to let output be valid file.\n" );
1112 break;
1114 else if( ret == 1 ) /* a change of stream's properties */
1116 input_track_t *in_track = &input->track[input->current_track_number - 1];
1117 lsmash_cleanup_summary( in_track->summary );
1118 in_track->summary = lsmash_duplicate_summary( input->importer, input->current_track_number );
1119 out_track->summary = in_track->summary;
1120 out_track->sample_entry = lsmash_add_sample_entry( output->root, out_track->track_ID, out_track->summary );
1121 if( !out_track->sample_entry )
1123 ERROR_MSG( "failed to add sample description entry.\n" );
1124 break;
1127 else if( ret == 2 ) /* EOF */
1129 /* No more appendable samples in this track. */
1130 lsmash_delete_sample( sample );
1131 sample = NULL;
1132 out_track->active = 0;
1133 out_track->last_delta = lsmash_importer_get_last_delta( input->importer, input->current_track_number );
1134 if( out_track->last_delta == 0 )
1135 ERROR_MSG( "failed to get the last sample delta.\n" );
1136 out_track->last_delta *= out_track->timebase;
1137 if( --num_active_input_tracks == 0 )
1138 break; /* Reached the end of whole tracks. */
1140 if( sample )
1142 sample->index = out_track->sample_entry;
1143 sample->dts *= out_track->timebase;
1144 sample->cts *= out_track->timebase;
1145 if( opt->timeline_shift )
1147 if( out_track->current_sample_number == 0 )
1148 out_track->ctd_shift = sample->cts;
1149 sample->cts -= out_track->ctd_shift;
1151 out_track->dts = (double)sample->dts / out_track->timescale;
1152 out_track->sample = sample;
1155 if( sample )
1157 /* Append a sample if meeting a condition. */
1158 if( out_track->dts <= largest_dts || num_consecutive_sample_skip == num_active_input_tracks )
1160 uint64_t sample_size = sample->length; /* sample might be deleted internally after appending. */
1161 uint64_t sample_dts = sample->dts; /* same as above */
1162 uint64_t sample_cts = sample->cts; /* same as above */
1163 if( lsmash_append_sample( output->root, out_track->track_ID, sample ) )
1164 return ERROR_MSG( "failed to append a sample.\n" );
1165 if( out_track->current_sample_number == 0 )
1166 out_track->start_offset = sample_cts;
1167 else
1168 out_track->last_delta = sample_dts - out_track->prev_dts; /* for any changes in stream's properties */
1169 out_track->prev_dts = sample_dts;
1170 out_track->sample = NULL;
1171 largest_dts = LSMASH_MAX( largest_dts, out_track->dts );
1172 total_media_size += sample_size;
1173 ++ out_track->current_sample_number;
1174 num_consecutive_sample_skip = 0;
1175 /* Print, per 4 megabytes, total size of imported media. */
1176 if( (total_media_size >> 22) > progress_pos )
1178 progress_pos = total_media_size >> 22;
1179 eprintf( "Importing: %"PRIu64" bytes\r", total_media_size );
1182 else
1183 ++num_consecutive_sample_skip; /* Skip appendig sample. */
1186 if( ++ out_movie->current_track_number > out_movie->num_of_tracks )
1187 out_movie->current_track_number = 1; /* Back the first output track. */
1188 /* Move the next track. */
1189 if( ++ input->current_track_number > input->num_of_tracks )
1191 /* Move the next input movie. */
1192 input->current_track_number = 1;
1193 if( ++ current_input_number > muxer->num_of_inputs )
1194 current_input_number = 1; /* Back the first input movie. */
1197 for( out_movie->current_track_number = 1;
1198 out_movie->current_track_number <= out_movie->num_of_tracks;
1199 out_movie->current_track_number ++ )
1201 /* Close track. */
1202 output_track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ];
1203 uint32_t last_sample_delta = out_track->lpcm ? 1 : out_track->last_delta;
1204 if( lsmash_flush_pooled_samples( output->root, out_track->track_ID, last_sample_delta ) )
1205 ERROR_MSG( "failed to flush the rest of samples.\n" );
1206 /* Create edit list.
1207 * Don't trust media duration basically. It's just duration of media, not duration of track presentation. */
1208 uint64_t actual_duration = out_track->lpcm
1209 ? lsmash_get_media_duration( output->root, out_track->track_ID )
1210 : out_track->prev_dts + last_sample_delta;
1211 actual_duration -= out_track->priming_samples;
1212 lsmash_edit_t edit;
1213 edit.duration = actual_duration * ((double)lsmash_get_movie_timescale( output->root ) / out_track->timescale);
1214 edit.start_time = out_track->priming_samples + out_track->start_offset;
1215 edit.rate = ISOM_EDIT_MODE_NORMAL;
1216 if( lsmash_create_explicit_timeline_map( output->root, out_track->track_ID, edit ) )
1217 ERROR_MSG( "failed to set timeline map.\n" );
1219 return 0;
1220 #undef LSMASH_MAX
1223 static int moov_to_front_callback( void *param, uint64_t written_movie_size, uint64_t total_movie_size )
1225 static uint32_t progress_pos = 0;
1226 if ( (written_movie_size >> 24) <= progress_pos )
1227 return 0;
1228 REFRESH_CONSOLE;
1229 eprintf( "Finalizing: [%5.2lf%%]\r", total_movie_size ? ((double)written_movie_size / total_movie_size) * 100.0 : 0 );
1230 /* Print, per 16 megabytes */
1231 progress_pos = written_movie_size >> 24;
1232 return 0;
1235 static int finish_movie( output_t *output, option_t *opt )
1237 /* Set chapter list. */
1238 if( opt->chap_file )
1239 lsmash_set_tyrant_chapter( output->root, opt->chap_file, opt->add_bom_to_chpl );
1240 /* Close movie. */
1241 REFRESH_CONSOLE;
1242 if( opt->optimize_pd )
1244 lsmash_adhoc_remux_t moov_to_front;
1245 moov_to_front.func = moov_to_front_callback;
1246 moov_to_front.buffer_size = 4*1024*1024; /* 4MiB */
1247 moov_to_front.param = NULL;
1248 return lsmash_finish_movie( output->root, &moov_to_front );
1250 if( lsmash_finish_movie( output->root, NULL ) )
1251 return -1;
1252 return lsmash_write_lsmash_indicator( output->root );
1255 int main( int argc, char *argv[] )
1257 muxer_t muxer = { { 0 } };
1258 lsmash_get_mainargs( &argc, &argv );
1259 if( parse_global_options( argc, argv, &muxer ) )
1260 return MUXER_USAGE_ERR();
1261 if( muxer.opt.help )
1263 display_help();
1264 cleanup_muxer( &muxer );
1265 return 0;
1267 else if( muxer.opt.version )
1269 display_version();
1270 cleanup_muxer( &muxer );
1271 return 0;
1273 if( open_input_files( &muxer ) )
1274 return MUXER_ERR( "failed to open input files.\n" );
1275 if( prepare_output( &muxer ) )
1276 return MUXER_ERR( "failed to set up preparation for output.\n" );
1277 if( do_mux( &muxer ) )
1278 return MUXER_ERR( "failed to do muxing.\n" );
1279 if( finish_movie( &muxer.output, &muxer.opt ) )
1280 return MUXER_ERR( "failed to finish movie.\n" );
1281 REFRESH_CONSOLE;
1282 eprintf( "Muxing completed!\n" );
1283 cleanup_muxer( &muxer ); /* including lsmash_destroy_root() */
1284 return 0;