print: Avoid Variable Length Arrays.
[L-SMASH.git] / cli / timelineeditor.c
blobdf68474c21017e44e4c16ef9fdec8014b8a5474d
1 /*****************************************************************************
2 * timelineeditor.c:
3 *****************************************************************************
4 * Copyright (C) 2011-2014 L-SMASH project
6 * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *****************************************************************************/
21 /* This file is available under an ISC license. */
23 #include "cli.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <inttypes.h>
30 #include <math.h>
31 #include <stdarg.h>
33 #include "config.h"
35 #define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b))
37 #define eprintf( ... ) fprintf( stderr, __VA_ARGS__ )
38 #define REFRESH_CONSOLE eprintf( " \r" )
40 typedef struct
42 int active;
43 lsmash_summary_t *summary;
44 } summary_t;
46 typedef struct
48 int active;
49 uint32_t track_ID;
50 uint32_t last_sample_delta;
51 uint32_t current_sample_number;
52 int reach_end_of_media_timeline;
53 uint32_t *summary_remap;
54 uint32_t num_summaries;
55 summary_t *summaries;
56 lsmash_track_parameters_t track_param;
57 lsmash_media_parameters_t media_param;
58 } track_t;
60 typedef struct
62 lsmash_itunes_metadata_t *itunes_metadata;
63 track_t *track;
64 lsmash_movie_parameters_t param;
65 uint32_t num_tracks;
66 uint32_t num_itunes_metadata;
67 uint32_t current_track_number;
68 } movie_t;
70 typedef struct
72 lsmash_file_t *fh;
73 lsmash_file_parameters_t param;
74 movie_t movie;
75 } file_t;
77 typedef struct
79 lsmash_root_t *root;
80 file_t file;
81 } root_t;
83 typedef struct
85 FILE *file;
86 uint64_t *ts;
87 uint32_t sample_count;
88 int auto_media_timescale;
89 int auto_media_timebase;
90 uint64_t media_timescale;
91 uint64_t media_timebase;
92 uint64_t duration;
93 uint64_t composition_delay;
94 uint64_t empty_delay;
95 } timecode_t;
97 typedef struct
99 root_t *output;
100 root_t *input;
101 timecode_t *timecode;
102 } movie_io_t;
104 typedef struct
106 uint32_t track_number;
107 uint32_t media_timescale;
108 uint32_t media_timebase;
109 uint32_t skip_duration;
110 uint32_t empty_delay;
111 int dts_compression;
112 } opt_t;
114 static void cleanup_root( root_t *h )
116 if( !h )
117 return;
118 movie_t *movie = &h->file.movie;
119 if( movie->itunes_metadata )
121 for( uint32_t i = 0; i < movie->num_itunes_metadata; i++ )
123 lsmash_itunes_metadata_t *metadata = &movie->itunes_metadata[i];
124 if( metadata->type == ITUNES_METADATA_TYPE_STRING )
126 if( metadata->value.string )
127 lsmash_free( metadata->value.string );
129 else if( metadata->type == ITUNES_METADATA_TYPE_BINARY )
130 if( metadata->value.binary.data )
131 lsmash_free( metadata->value.binary.data );
132 if( metadata->meaning )
133 lsmash_free( metadata->meaning );
134 if( metadata->name )
135 lsmash_free( metadata->name );
137 lsmash_freep( &movie->itunes_metadata );
139 if( movie->track )
140 lsmash_freep( &movie->track );
141 lsmash_close_file( &h->file.param );
142 lsmash_destroy_root( h->root );
143 h->root = NULL;
146 static void cleanup_timecode( timecode_t *timecode )
148 if( !timecode )
149 return;
150 if( timecode->file )
152 fclose( timecode->file );
153 timecode->file = NULL;
155 if( timecode->ts )
156 lsmash_freep( &timecode->ts );
159 static int error_message( const char* message, ... )
161 REFRESH_CONSOLE;
162 eprintf( "Error: " );
163 va_list args;
164 va_start( args, message );
165 vfprintf( stderr, message, args );
166 va_end( args );
167 return -1;
170 static int warning_message( const char* message, ... )
172 REFRESH_CONSOLE;
173 eprintf( "Warning: " );
174 va_list args;
175 va_start( args, message );
176 vfprintf( stderr, message, args );
177 va_end( args );
178 return -1;
181 static int timelineeditor_error( movie_io_t *io, const char *message, ... )
183 cleanup_root( io->input );
184 cleanup_root( io->output );
185 cleanup_timecode( io->timecode );
186 va_list args;
187 va_start( args, message );
188 error_message( message, args );
189 va_end( args );
190 return -1;
193 #define TIMELINEEDITOR_ERR( ... ) timelineeditor_error( &io, __VA_ARGS__ )
194 #define ERROR_MSG( ... ) error_message( __VA_ARGS__ )
195 #define WARNING_MSG( ... ) warning_message( __VA_ARGS__ )
197 static char *duplicate_string( char *src )
199 if( !src )
200 return NULL;
201 int dst_size = strlen( src ) + 1;
202 char *dst = lsmash_malloc( dst_size );
203 if( !dst )
204 return NULL;
205 memcpy( dst, src, dst_size );
206 return dst;
209 static int get_itunes_metadata( lsmash_root_t *root, uint32_t metadata_number, lsmash_itunes_metadata_t *metadata )
211 memset( metadata, 0, sizeof(lsmash_itunes_metadata_t) );
212 if( lsmash_get_itunes_metadata( root, metadata_number, metadata ) )
213 return -1;
214 lsmash_itunes_metadata_t shadow = *metadata;
215 metadata->meaning = NULL;
216 metadata->name = NULL;
217 memset( &metadata->value, 0, sizeof(lsmash_itunes_metadata_value_t) );
218 if( shadow.meaning )
220 metadata->meaning = duplicate_string( shadow.meaning );
221 if( !metadata->meaning )
222 return -1;
224 if( shadow.name )
226 metadata->name = duplicate_string( shadow.name );
227 if( !metadata->name )
228 goto fail;
230 if( shadow.type == ITUNES_METADATA_TYPE_STRING )
232 metadata->value.string = duplicate_string( shadow.value.string );
233 if( !metadata->value.string )
234 goto fail;
236 else if( shadow.type == ITUNES_METADATA_TYPE_BINARY )
238 metadata->value.binary.data = lsmash_malloc( shadow.value.binary.size );
239 if( !metadata->value.binary.data )
240 goto fail;
241 memcpy( metadata->value.binary.data, shadow.value.binary.data, shadow.value.binary.size );
243 return 0;
244 fail:
245 if( metadata->meaning )
246 lsmash_free( metadata->meaning );
247 if( metadata->name )
248 lsmash_free( metadata->name );
249 return -1;
252 static int get_summaries( root_t *input, track_t *track )
254 track->num_summaries = lsmash_count_summary( input->root, track->track_ID );
255 if( track->num_summaries == 0 )
256 return ERROR_MSG( "Failed to get find valid summaries.\n" );
257 track->summaries = lsmash_malloc( track->num_summaries * sizeof(summary_t) );
258 if( !track->summaries )
259 return ERROR_MSG( "failed to alloc input summaries.\n" );
260 memset( track->summaries, 0, track->num_summaries * sizeof(summary_t) );
261 for( uint32_t j = 0; j < track->num_summaries; j++ )
263 lsmash_summary_t *summary = lsmash_get_summary( input->root, track->track_ID, j + 1 );
264 if( !summary )
266 WARNING_MSG( "failed to get a summary.\n" );
267 continue;
269 track->summaries[j].summary = summary;
270 track->summaries[j].active = 1;
272 return 0;
275 static int get_movie( root_t *input, char *input_name )
277 if( !strcmp( input_name, "-" ) )
278 return ERROR_MSG( "Standard input not supported.\n" );
279 input->root = lsmash_create_root();
280 if( !input->root )
281 return ERROR_MSG( "failed to create a ROOT for an input file.\n" );
282 file_t *in_file = &input->file;
283 if( lsmash_open_file( input_name, 1, &in_file->param ) < 0 )
284 return ERROR_MSG( "failed to open an input file.\n" );
285 in_file->fh = lsmash_set_file( input->root, &in_file->param );
286 if( !in_file->fh )
287 return ERROR_MSG( "failed to add an input file into a ROOT.\n" );
288 if( lsmash_read_file( in_file->fh, &in_file->param ) < 0 )
289 return ERROR_MSG( "failed to read an input file\n" );
290 movie_t *movie = &in_file->movie;
291 movie->num_itunes_metadata = lsmash_count_itunes_metadata( input->root );
292 if( movie->num_itunes_metadata )
294 movie->itunes_metadata = lsmash_malloc( movie->num_itunes_metadata * sizeof(lsmash_itunes_metadata_t) );
295 if( !movie->itunes_metadata )
296 return ERROR_MSG( "failed to alloc iTunes metadata.\n" );
297 uint32_t itunes_metadata_count = 0;
298 for( uint32_t i = 1; i <= movie->num_itunes_metadata; i++ )
300 if( get_itunes_metadata( input->root, i, &movie->itunes_metadata[itunes_metadata_count] ) )
302 WARNING_MSG( "failed to get an iTunes metadata.\n" );
303 continue;
305 ++itunes_metadata_count;
307 movie->num_itunes_metadata = itunes_metadata_count;
309 lsmash_initialize_movie_parameters( &movie->param );
310 lsmash_get_movie_parameters( input->root, &movie->param );
311 movie->num_tracks = movie->param.number_of_tracks;
312 movie->current_track_number = 1;
313 /* Create tracks. */
314 track_t *track = movie->track = lsmash_malloc( movie->num_tracks * sizeof(track_t) );
315 if( !track )
316 return ERROR_MSG( "Failed to alloc input tracks.\n" );
317 memset( track, 0, movie->num_tracks * sizeof(track_t) );
318 for( uint32_t i = 0; i < movie->num_tracks; i++ )
320 track[i].track_ID = lsmash_get_track_ID( input->root, i + 1 );
321 if( !track[i].track_ID )
322 return ERROR_MSG( "Failed to get track_ID.\n" );
324 for( uint32_t i = 0; i < movie->num_tracks; i++ )
326 lsmash_initialize_track_parameters( &track[i].track_param );
327 if( lsmash_get_track_parameters( input->root, track[i].track_ID, &track[i].track_param ) )
329 WARNING_MSG( "failed to get track parameters.\n" );
330 continue;
332 lsmash_initialize_media_parameters( &track[i].media_param );
333 if( lsmash_get_media_parameters( input->root, track[i].track_ID, &track[i].media_param ) )
335 WARNING_MSG( "failed to get media parameters.\n" );
336 continue;
338 if( lsmash_construct_timeline( input->root, track[i].track_ID ) )
340 WARNING_MSG( "failed to construct timeline.\n" );
341 continue;
343 if( lsmash_get_last_sample_delta_from_media_timeline( input->root, track[i].track_ID, &track[i].last_sample_delta ) )
345 WARNING_MSG( "failed to get the last sample delta.\n" );
346 continue;
348 if( get_summaries( input, &track[i] ) )
350 WARNING_MSG( "failed to get valid summaries.\n" );
351 continue;
353 track[i].active = 1;
354 track[i].current_sample_number = 1;
356 lsmash_destroy_children( lsmash_file_as_box( in_file->fh ) );
357 return 0;
360 static inline uint64_t get_gcd( uint64_t a, uint64_t b )
362 if( !b )
363 return a;
364 while( 1 )
366 uint64_t c = a % b;
367 if( !c )
368 return b;
369 a = b;
370 b = c;
374 static inline uint64_t get_lcm( uint64_t a, uint64_t b )
376 if( !a )
377 return 0;
378 return (a / get_gcd( a, b )) * b;
381 static uint64_t get_media_timebase( lsmash_media_ts_list_t *ts_list )
383 uint64_t timebase = ts_list->timestamp[0].cts;
384 for( uint32_t i = 1; i < ts_list->sample_count; i++ )
385 timebase = get_gcd( timebase, ts_list->timestamp[i].cts );
386 for( uint32_t i = 0; i < ts_list->sample_count; i++ )
387 timebase = get_gcd( timebase, ts_list->timestamp[i].dts );
388 return timebase;
391 static inline double sigexp10( double value, double *exponent )
393 /* This function separates significand and exp10 from double floating point. */
394 *exponent = 1;
395 while( value < 1 )
397 value *= 10;
398 *exponent /= 10;
400 while( value >= 10 )
402 value /= 10;
403 *exponent *= 10;
405 return value;
408 #define DOUBLE_EPSILON 5e-6
409 #define MATROSKA_TIMESCALE 1000000000
410 #define SKIP_LINE_CHARACTER( x ) ((x) == '#' || (x) == '\n' || (x) == '\r')
412 static double correct_fps( double fps, timecode_t *timecode )
414 int i = 1;
415 uint64_t fps_num, fps_den;
416 double exponent;
417 double fps_sig = sigexp10( fps, &exponent );
418 while( 1 )
420 fps_den = i * timecode->media_timebase;
421 fps_num = round( fps_den * fps_sig ) * exponent;
422 if( fps_num > UINT32_MAX )
423 return ERROR_MSG( "framerate correction failed.\n"
424 "Specify an appropriate timebase manually or remake timecode file.\n" );
425 if( fabs( ((double)fps_num / fps_den) / exponent - fps_sig ) < DOUBLE_EPSILON )
426 break;
427 ++i;
429 if( timecode->auto_media_timescale )
431 timecode->media_timescale = timecode->media_timescale
432 ? get_lcm( timecode->media_timescale, fps_num )
433 : fps_num;
434 if( timecode->media_timescale > UINT32_MAX )
435 timecode->auto_media_timescale = 0;
437 return (double)fps_num / fps_den;
440 static int try_matroska_timescale( double *fps_array, timecode_t *timecode, uint32_t num_loops )
442 timecode->media_timebase = 0;
443 timecode->media_timescale = MATROSKA_TIMESCALE;
444 for( uint32_t i = 0; i < num_loops; i++ )
446 uint64_t fps_den;
447 double exponent;
448 double fps_sig = sigexp10( fps_array[i], &exponent );
449 fps_den = round( MATROSKA_TIMESCALE / fps_sig ) / exponent;
450 timecode->media_timebase = fps_den && timecode->media_timebase
451 ? get_gcd( timecode->media_timebase, fps_den )
452 : fps_den;
453 if( timecode->media_timebase > UINT32_MAX || !timecode->media_timebase )
454 return ERROR_MSG( "Automatic media timescale generation failed.\n"
455 "Specify media timescale manually.\n" );
457 return 0;
460 static int parse_timecode( timecode_t *timecode, uint32_t sample_count )
462 #define FAILED_PARSE_TIMECODE( ... ) \
463 do \
465 lsmash_free( fps_array ); \
466 lsmash_free( timecode_array ); \
467 return ERROR_MSG( __VA_ARGS__ ); \
469 while( 0 )
470 int tcfv;
471 int ret = fscanf( timecode->file, "# timecode format v%d", &tcfv );
472 if( ret != 1 || (tcfv != 1 && tcfv != 2) )
473 return ERROR_MSG( "Unsupported timecode format\n" );
474 char buff[256];
475 double *timecode_array = NULL;
476 if( tcfv == 1 )
478 double assume_fps = 0;
479 /* Get assumed framerate. */
480 while( fgets( buff, sizeof(buff), timecode->file ) )
482 if( SKIP_LINE_CHARACTER( buff[0] ) )
483 continue;
484 if( sscanf( buff, "assume %lf", &assume_fps ) != 1
485 && sscanf( buff, "Assume %lf", &assume_fps ) != 1 )
486 return ERROR_MSG( "Assumed fps not found\n" );
487 break;
489 if( assume_fps <= 0 )
490 return ERROR_MSG( "Invalid assumed fps\n" );
491 int64_t file_pos = lsmash_ftell( timecode->file );
492 if( file_pos < 0 )
493 return ERROR_MSG( "Failed to tell the postion of input timecode file.\n" );
494 /* Check whether valid or not and count number of sequences. */
495 uint32_t num_sequences = 0;
496 int64_t start, end;
497 int64_t prev_start = -1, prev_end = -1;
498 double sequence_fps;
499 while( fgets( buff, sizeof(buff), timecode->file ) )
501 if( SKIP_LINE_CHARACTER( buff[0] ) )
502 continue;
503 ret = sscanf( buff, "%"SCNd64",%"SCNd64",%lf", &start, &end, &sequence_fps );
504 if( ret != 3 && ret != EOF )
505 return ERROR_MSG( "Invalid input timecode file\n" );
506 if( start > end || start <= prev_start || end <= prev_end || sequence_fps <= 0 )
507 return ERROR_MSG( "Invalid input timecode file\n" );
508 prev_start = start;
509 prev_end = end;
510 if( timecode->auto_media_timescale || timecode->auto_media_timebase )
511 ++num_sequences;
513 if( lsmash_fseek( timecode->file, file_pos, SEEK_SET ) != 0 )
514 return ERROR_MSG( "Failed to seek input timecode file.\n" );
515 /* Preparation storing timecodes. */
516 double *fps_array = lsmash_malloc( ((timecode->auto_media_timescale || timecode->auto_media_timebase) * num_sequences + 1) * sizeof(double) );
517 return ERROR_MSG( "Failed to allocate fps array\n" );
518 double corrected_assume_fps = correct_fps( assume_fps, timecode );
519 if( corrected_assume_fps < 0 )
520 FAILED_PARSE_TIMECODE( "Failed to correct the assumed framerate\n" );
521 timecode_array = lsmash_malloc( sample_count * sizeof(double) );
522 if( !timecode_array )
523 FAILED_PARSE_TIMECODE( "Failed to alloc timecodes\n" );
524 timecode_array[0] = 0;
525 num_sequences = 0;
526 uint32_t i = 0;
527 while( i < sample_count - 1 && fgets( buff, sizeof(buff), timecode->file ) )
529 if( SKIP_LINE_CHARACTER( buff[0] ) )
530 continue;
531 ret = sscanf( buff, "%"SCNd64",%"SCNd64",%lf", &start, &end, &sequence_fps );
532 if( ret != 3 )
533 start = end = sample_count - 1;
534 for( ; i < start && i < sample_count - 1; i++ )
535 timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps;
536 if( i < sample_count - 1 )
538 if( timecode->auto_media_timescale || timecode->auto_media_timebase )
539 fps_array[num_sequences++] = sequence_fps;
540 sequence_fps = correct_fps( sequence_fps, timecode );
541 if( sequence_fps < 0 )
542 FAILED_PARSE_TIMECODE( "Failed to correct the framerate of a sequence.\n" );
543 for( i = start; i <= end && i < sample_count - 1; i++ )
544 timecode_array[i + 1] = timecode_array[i] + 1 / sequence_fps;
547 for( ; i < sample_count - 1; i++ )
548 timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps;
549 if( timecode->auto_media_timescale || timecode->auto_media_timebase )
550 fps_array[num_sequences] = assume_fps;
551 /* Assume matroska timebase if automatic timescale generation isn't done yet. */
552 if( timecode->auto_media_timebase && !timecode->auto_media_timescale )
554 double exponent;
555 double assume_fps_sig, sequence_fps_sig;
556 if( try_matroska_timescale( fps_array, timecode, num_sequences + 1 ) < 0 )
557 FAILED_PARSE_TIMECODE( "Failed to try matroska timescale.\n" );
558 if( lsmash_fseek( timecode->file, file_pos, SEEK_SET ) != 0 )
559 FAILED_PARSE_TIMECODE( "Failed to seek input timecode file.\n" );
560 assume_fps_sig = sigexp10( assume_fps, &exponent );
561 corrected_assume_fps = MATROSKA_TIMESCALE / ( round( MATROSKA_TIMESCALE / assume_fps_sig ) / exponent );
562 for( i = 0; i < sample_count - 1 && fgets( buff, sizeof(buff), timecode->file ); )
564 if( SKIP_LINE_CHARACTER( buff[0] ) )
565 continue;
566 ret = sscanf( buff, "%"SCNd64",%"SCNd64",%lf", &start, &end, &sequence_fps );
567 if( ret != 3 )
568 start = end = sample_count - 1;
569 sequence_fps_sig = sigexp10( sequence_fps, &exponent );
570 sequence_fps = MATROSKA_TIMESCALE / ( round( MATROSKA_TIMESCALE / sequence_fps_sig ) / exponent );
571 for( ; i < start && i < sample_count - 1; i++ )
572 timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps;
573 for( i = start; i <= end && i < sample_count - 1; i++ )
574 timecode_array[i + 1] = timecode_array[i] + 1 / sequence_fps;
576 for( ; i < sample_count - 1; i++ )
577 timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps;
579 lsmash_free( fps_array );
581 else /* tcfv == 2 */
583 uint32_t num_timecodes = 0;
584 int64_t file_pos = lsmash_ftell( timecode->file );
585 if( file_pos < 0 )
586 return ERROR_MSG( "Failed to tell the postion of input timecode file.\n" );
587 while( fgets( buff, sizeof(buff), timecode->file ) )
589 if( SKIP_LINE_CHARACTER( buff[0] ) )
591 if( !num_timecodes )
593 file_pos = lsmash_ftell( timecode->file );
594 if( file_pos < 0 )
595 return ERROR_MSG( "Failed to tell the postion of input timecode file.\n" );
597 continue;
599 ++num_timecodes;
601 if( !num_timecodes )
602 return ERROR_MSG( "No timecodes!\n" );
603 if( sample_count > num_timecodes )
604 return ERROR_MSG( "Lack number of timecodes.\n" );
605 if( lsmash_fseek( timecode->file, file_pos, SEEK_SET ) != 0 )
606 return ERROR_MSG( "Failed to seek input timecode file.\n" );
607 timecode_array = lsmash_malloc( sample_count * sizeof(uint64_t) );
608 if( !timecode_array )
609 return ERROR_MSG( "Failed to alloc timecodes.\n" );
610 uint32_t i = 0;
611 if( fgets( buff, sizeof(buff), timecode->file ) )
613 ret = sscanf( buff, "%lf", &timecode_array[0] );
614 if( ret != 1 )
616 lsmash_free( timecode_array );
617 return ERROR_MSG( "Invalid timecode number: 0\n" );
619 timecode_array[i++] *= 1e-3; /* Timescale of timecode format v2 is 1000. */
620 while( i < sample_count && fgets( buff, sizeof(buff), timecode->file ) )
622 if( SKIP_LINE_CHARACTER( buff[0] ) )
623 continue;
624 ret = sscanf( buff, "%lf", &timecode_array[i] );
625 timecode_array[i] *= 1e-3; /* Timescale of timecode format v2 is 1000. */
626 if( ret != 1 || timecode_array[i] <= timecode_array[i - 1] )
628 lsmash_free( timecode_array );
629 return ERROR_MSG( "Invalid input timecode.\n" );
631 ++i;
634 if( i < sample_count )
636 lsmash_free( timecode_array );
637 return ERROR_MSG( "Failed to get timecodes.\n" );
639 /* Generate media timescale automatically if needed. */
640 if( sample_count != 1 && timecode->auto_media_timescale )
642 double *fps_array = lsmash_malloc( (sample_count - 1) * sizeof(double) );
643 if( !fps_array )
644 FAILED_PARSE_TIMECODE( "Failed to allocate fps array\n" );
645 for( i = 0; i < sample_count - 1; i++ )
647 fps_array[i] = 1 / (timecode_array[i + 1] - timecode_array[i]);
648 if( timecode->auto_media_timescale )
650 int j = 1;
651 uint64_t fps_num, fps_den;
652 double exponent;
653 double fps_sig = sigexp10( fps_array[i], &exponent );
654 while( 1 )
656 fps_den = j * timecode->media_timebase;
657 fps_num = round( fps_den * fps_sig ) * exponent;
658 if( fps_num > UINT32_MAX
659 || fabs( ((double)fps_num / fps_den) / exponent - fps_sig ) < DOUBLE_EPSILON )
660 break;
661 ++j;
663 timecode->media_timescale = fps_num && timecode->media_timescale
664 ? get_lcm( timecode->media_timescale, fps_num )
665 : fps_num;
666 if( timecode->media_timescale > UINT32_MAX )
668 timecode->auto_media_timescale = 0;
669 continue; /* Don't break because all framerate is needed for try_matroska_timescale. */
673 if( timecode->auto_media_timebase && !timecode->auto_media_timescale
674 && try_matroska_timescale( fps_array, timecode, sample_count - 1 ) < 0 )
675 FAILED_PARSE_TIMECODE( "Failed to try matroska timescale.\n" );
676 lsmash_free( fps_array );
679 if( timecode->auto_media_timescale || timecode->auto_media_timebase )
681 uint64_t reduce = get_gcd( timecode->media_timebase, timecode->media_timescale );
682 timecode->media_timebase /= reduce;
683 timecode->media_timescale /= reduce;
685 else if( timecode->media_timescale > UINT32_MAX || !timecode->media_timescale )
687 lsmash_free( timecode_array );
688 return ERROR_MSG( "Failed to generate media timescale automatically.\n"
689 "Specify an appropriate media timescale manually.\n" );
691 uint32_t timescale = timecode->media_timescale;
692 uint32_t timebase = timecode->media_timebase;
693 double delay_tc = timecode_array[0];
694 timecode->empty_delay = ((uint64_t)(delay_tc * ((double)timescale / timebase) + 0.5)) * timebase;
695 timecode->ts = lsmash_malloc( sample_count * sizeof(uint64_t) );
696 if( !timecode->ts )
698 lsmash_free( timecode_array );
699 return ERROR_MSG( "Failed to allocate timestamps.\n" );
701 timecode->ts[0] = 0;
702 for( uint32_t i = 1; i < sample_count; i++ )
704 timecode->ts[i] = ((uint64_t)((timecode_array[i] - delay_tc) * ((double)timescale / timebase) + 0.5)) * timebase;
705 if( timecode->ts[i] <= timecode->ts[i - 1] )
707 lsmash_free( timecode_array );
708 lsmash_free( timecode->ts );
709 timecode->ts = NULL;
710 return ERROR_MSG( "Invalid timecode.\n" );
713 lsmash_free( timecode_array );
714 return 0;
715 #undef FAILED_PARSE_TIMECODE
718 #undef DOUBLE_EPSILON
719 #undef MATROSKA_TIMESCALE
720 #undef SKIP_LINE_CHARACTER
722 static int edit_media_timeline( root_t *input, timecode_t *timecode, opt_t *opt )
724 if( !timecode->file && !opt->media_timescale && !opt->media_timebase && !opt->dts_compression )
725 return 0;
726 track_t *in_track = &input->file.movie.track[opt->track_number - 1];
727 uint32_t track_ID = in_track->track_ID;
728 lsmash_media_ts_list_t ts_list;
729 if( lsmash_get_media_timestamps( input->root, track_ID, &ts_list ) )
730 return ERROR_MSG( "Failed to get media timestamps.\n" );
731 uint64_t timebase = get_media_timebase( &ts_list );
732 if( !timebase )
733 return ERROR_MSG( "Failed to get media timebase.\n" );
734 lsmash_media_ts_t *timestamp = ts_list.timestamp;
735 uint32_t sample_count = ts_list.sample_count;
736 uint32_t orig_timebase = timebase;
737 uint32_t timescale;
738 double timebase_convert_multiplier;
739 if( opt->media_timescale || opt->media_timebase )
741 uint32_t orig_timescale = in_track->media_param.timescale;
742 timescale = opt->media_timescale ? opt->media_timescale : orig_timescale;
743 timebase = opt->media_timebase ? opt->media_timebase : orig_timebase;
744 if( !opt->media_timescale && opt->media_timebase && (timebase > orig_timebase) )
745 timescale = timescale * ((double)timebase / orig_timebase) + 0.5;
746 timebase_convert_multiplier = ((double)timescale / orig_timescale) * ((double)orig_timebase / timebase);
748 else
750 /* Reduce timescale and timebase. */
751 timescale = in_track->media_param.timescale;
752 uint64_t reduce = get_gcd( timescale, timebase );
753 timescale /= reduce;
754 timebase /= reduce;
755 timebase_convert_multiplier = 1;
757 /* Parse timecode file. */
758 if( timecode->file )
760 timecode->auto_media_timescale = !opt->media_timescale;
761 timecode->auto_media_timebase = !opt->media_timebase;
762 timecode->media_timescale = timecode->auto_media_timescale ? 0 : timescale;
763 timecode->media_timebase = timebase;
764 if( parse_timecode( timecode, sample_count ) )
765 return ERROR_MSG( "Failed to parse timecode file.\n" );
766 timescale = timecode->media_timescale;
767 timebase = timecode->media_timebase;
769 /* Get maximum composition sample delay for DTS generation. */
770 uint32_t sample_delay;
771 if( lsmash_get_max_sample_delay( &ts_list, &sample_delay ) )
772 return ERROR_MSG( "Failed to get maximum composition sample delay.\n" );
773 if( sample_delay ) /* Reorder composition order. */
774 lsmash_sort_timestamps_composition_order( &ts_list );
775 if( !timecode->file )
777 /* Genarate timestamps timescale converted. */
778 timecode->ts = lsmash_malloc( sample_count * sizeof(uint64_t) );
779 if( !timecode->ts )
780 return ERROR_MSG( "Failed to alloc timestamps\n" );
781 for( uint32_t i = 0; i < sample_count; i++ )
783 timecode->ts[i] = (timestamp[i].cts - timestamp[0].cts) / orig_timebase;
784 timecode->ts[i] = ((uint64_t)(timecode->ts[i] * timebase_convert_multiplier + 0.5)) * timebase;
785 if( i && (timecode->ts[i] <= timecode->ts[i - 1]) )
786 return ERROR_MSG( "Invalid timescale conversion.\n" );
789 if( sample_delay )
791 /* If media timescale is specified, disable DTS compression multiplier. */
792 uint32_t dts_compression_multiplier = opt->dts_compression * !opt->media_timescale * sample_delay + 1;
793 uint64_t initial_delta = timecode->ts[1];
794 timescale *= dts_compression_multiplier;
795 if( dts_compression_multiplier > 1 )
796 for( uint32_t i = 0; i < sample_count; i++ )
797 timecode->ts[i] *= dts_compression_multiplier;
798 /* Generate CTS. */
799 uint64_t sample_delay_time = timecode->composition_delay = opt->dts_compression ? 0 : timecode->ts[sample_delay];
800 for( uint32_t i = 0; i < sample_count; i++ )
801 timestamp[i].cts = timecode->ts[i] + sample_delay_time;
802 /* Reorder decode order and generate new DTS from CTS. */
803 lsmash_sort_timestamps_decoding_order( &ts_list );
804 uint64_t *prev_reordered_cts = lsmash_malloc( sample_delay * sizeof(uint64_t) );
805 if( !prev_reordered_cts )
806 return ERROR_MSG( "Failed to allocate the previous reordered CTS array.\n" );
807 for( uint32_t i = 0; i <= sample_delay; i++ )
809 if( !opt->dts_compression )
810 timestamp[i].dts = timecode->ts[i];
811 else
813 timestamp[i].dts = (i * initial_delta) / (!!opt->media_timescale * sample_delay + 1);
814 if( i && (timestamp[i].dts <= timestamp[i - 1].dts) )
816 lsmash_free( prev_reordered_cts );
817 return ERROR_MSG( "Failed to do DTS compression.\n" );
820 prev_reordered_cts[ i % sample_delay ] = timecode->ts[i] + sample_delay_time;
822 for( uint32_t i = sample_delay + 1; i < sample_count; i++ )
824 timestamp[i].dts = prev_reordered_cts[ (i - sample_delay) % sample_delay ];
825 prev_reordered_cts[ i % sample_delay ] = timecode->ts[i] + sample_delay_time;
827 lsmash_free( prev_reordered_cts );
829 else
830 for( uint32_t i = 0; i < sample_count; i++ )
831 timestamp[i].cts = timestamp[i].dts = timecode->ts[i];
832 if( sample_count > 1 )
834 in_track->last_sample_delta = timecode->ts[sample_count - 1] - timecode->ts[sample_count - 2];
835 timecode->duration = timecode->ts[sample_count - 1] + in_track->last_sample_delta;
837 else /* still image */
838 timecode->duration = in_track->last_sample_delta = UINT32_MAX;
839 in_track->media_param.timescale = timescale;
840 if( lsmash_set_media_timestamps( input->root, track_ID, &ts_list ) )
841 return ERROR_MSG( "Failed to set media timestamps.\n" );
842 lsmash_delete_media_timestamps( &ts_list );
843 return 0;
846 static int check_white_brand( lsmash_brand_type brand )
848 static const lsmash_brand_type brand_white_list[] =
850 ISOM_BRAND_TYPE_3G2A,
851 ISOM_BRAND_TYPE_3GG6,
852 ISOM_BRAND_TYPE_3GG9,
853 ISOM_BRAND_TYPE_3GP4,
854 ISOM_BRAND_TYPE_3GP5,
855 ISOM_BRAND_TYPE_3GP6,
856 ISOM_BRAND_TYPE_3GP7,
857 ISOM_BRAND_TYPE_3GP8,
858 ISOM_BRAND_TYPE_3GP9,
859 ISOM_BRAND_TYPE_3GR6,
860 ISOM_BRAND_TYPE_3GR9,
861 ISOM_BRAND_TYPE_M4A ,
862 ISOM_BRAND_TYPE_M4B ,
863 ISOM_BRAND_TYPE_M4V ,
864 ISOM_BRAND_TYPE_AVC1,
865 ISOM_BRAND_TYPE_DBY1,
866 ISOM_BRAND_TYPE_ISO2,
867 ISOM_BRAND_TYPE_ISO3,
868 ISOM_BRAND_TYPE_ISO4,
869 ISOM_BRAND_TYPE_ISO5,
870 ISOM_BRAND_TYPE_ISO6,
871 ISOM_BRAND_TYPE_ISOM,
872 ISOM_BRAND_TYPE_MP41,
873 ISOM_BRAND_TYPE_MP42,
874 ISOM_BRAND_TYPE_QT ,
877 for( int i = 0; brand_white_list[i]; i++ )
878 if( brand == brand_white_list[i] )
879 return 1;
880 return 0;
883 static int moov_to_front_callback( void *param, uint64_t written_movie_size, uint64_t total_movie_size )
885 eprintf( "Finalizing: [%5.2lf%%]\r", ((double)written_movie_size / total_movie_size) * 100.0 );
886 return 0;
889 static void display_version( void )
891 eprintf( "\n"
892 "L-SMASH isom/mov timeline editor rev%s %s\n"
893 "Built on %s %s\n"
894 "Copyright (C) 2011-2014 L-SMASH project\n",
895 LSMASH_REV, LSMASH_GIT_HASH, __DATE__, __TIME__ );
898 static void display_help( void )
900 display_version();
901 eprintf( "\n"
902 "Usage: timelineeditor [options] input output\n"
903 " options:\n"
904 " --help Display help\n"
905 " --version Display version information\n"
906 " --track <integer> Specify track number to edit [1]\n"
907 " --timecode <string> Specify timecode file to edit timeline\n"
908 " --media-timescale <integer> Specify media timescale to convert\n"
909 " --media-timebase <integer> Specify media timebase to convert\n"
910 " --skip <integer> Skip start of media presentation in milliseconds\n"
911 " --delay <integer> Insert blank clip before actual media presentation in milliseconds\n"
912 " --dts-compression Eliminate composition delay with DTS hack\n"
913 " Multiply media timescale and timebase automatically\n" );
916 int main( int argc, char *argv[] )
918 if ( argc < 2 )
920 display_help();
921 return -1;
923 else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) )
925 display_help();
926 return 0;
928 else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) )
930 display_version();
931 return 0;
933 else if( argc < 3 )
935 display_help();
936 return -1;
938 root_t output = { 0 };
939 root_t input = { 0 };
940 timecode_t timecode = { 0 };
941 movie_io_t io = { &output, &input, &timecode };
942 opt_t opt = { 1, 0, 0, 0, 0, 0 };
943 /* Parse options. */
944 lsmash_get_mainargs( &argc, &argv );
945 int argn = 1;
946 while( argn < argc - 2 )
948 if( !strcasecmp( argv[argn], "--track" ) )
950 opt.track_number = atoi( argv[++argn] );
951 if( !opt.track_number )
952 return TIMELINEEDITOR_ERR( "Invalid track number.\n" );
953 ++argn;
955 else if( !strcasecmp( argv[argn], "--timecode" ) )
957 timecode.file = lsmash_fopen( argv[++argn], "rb" );
958 if( !timecode.file )
959 return TIMELINEEDITOR_ERR( "Failed to open timecode file.\n" );
960 ++argn;
962 else if( !strcasecmp( argv[argn], "--media-timescale" ) )
964 opt.media_timescale = atoi( argv[++argn] );
965 if( !opt.media_timescale )
966 return TIMELINEEDITOR_ERR( "Invalid media timescale.\n" );
967 ++argn;
969 else if( !strcasecmp( argv[argn], "--media-timebase" ) )
971 opt.media_timebase = atoi( argv[++argn] );
972 if( !opt.media_timebase )
973 return TIMELINEEDITOR_ERR( "Invalid media timebase.\n" );
974 ++argn;
976 else if( !strcasecmp( argv[argn], "--skip" ) )
978 opt.skip_duration = atoi( argv[++argn] );
979 if( !opt.skip_duration )
980 return TIMELINEEDITOR_ERR( "Invalid skip duration.\n" );
981 ++argn;
983 else if( !strcasecmp( argv[argn], "--delay" ) )
985 opt.empty_delay = atoi( argv[++argn] );
986 if( !opt.empty_delay )
987 return TIMELINEEDITOR_ERR( "Invalid delay time.\n" );
988 ++argn;
990 else if( !strcasecmp( argv[argn], "--dts-compression" ) )
992 opt.dts_compression = 1;
993 ++argn;
995 else
996 return TIMELINEEDITOR_ERR( "Invalid option.\n" );
998 if( argn > argc - 2 )
999 return TIMELINEEDITOR_ERR( "Invalid arguments.\n" );
1000 /* Get input movies. */
1001 if( get_movie( &input, argv[argn++] ) )
1002 return TIMELINEEDITOR_ERR( "Failed to get input movie.\n" );
1003 movie_t *in_movie = &input.file.movie;
1004 if( opt.track_number && (opt.track_number > in_movie->num_tracks) )
1005 return TIMELINEEDITOR_ERR( "Invalid track number.\n" );
1006 /* Create output movie. */
1007 file_t *out_file = &output.file;
1008 output.root = lsmash_create_root();
1009 if( !output.root )
1010 return TIMELINEEDITOR_ERR( "failed to create a ROOT for an output file.\n" );
1011 if( lsmash_open_file( argv[argn], 0, &out_file->param ) < 0 )
1012 return TIMELINEEDITOR_ERR( "failed to open an output file.\n" );
1013 file_t *in_file = &input.file;
1014 out_file->param.major_brand = in_file->param.major_brand;
1015 out_file->param.minor_version = in_file->param.minor_version;
1016 out_file->param.brands = in_file->param.brands;
1017 out_file->param.brand_count = in_file->param.brand_count;
1018 out_file->param.max_chunk_duration = 0.5;
1019 out_file->param.max_async_tolerance = 2.0;
1020 out_file->param.max_chunk_size = 4*1024*1024;
1021 if( !check_white_brand( out_file->param.major_brand ) )
1023 /* Replace with whitelisted brand 'mp42'. */
1024 out_file->param.major_brand = ISOM_BRAND_TYPE_MP42;
1025 out_file->param.minor_version = 0;
1026 uint32_t i;
1027 for( i = 0; i < out_file->param.brand_count; i++ )
1028 if( out_file->param.brands[i] == ISOM_BRAND_TYPE_MP42 )
1029 break;
1030 if( i == out_file->param.brand_count )
1032 /* Add 'mp42' into the list of compatible brands. */
1033 out_file->param.brands = lsmash_malloc( (i + 1) * sizeof(lsmash_brand_type) );
1034 if( out_file->param.brands )
1036 memcpy( out_file->param.brands, in_file->param.brands, i * sizeof(lsmash_brand_type) );
1037 out_file->param.brands[i] = ISOM_BRAND_TYPE_MP42;
1041 out_file->fh = lsmash_set_file( output.root, &out_file->param );
1042 if( !out_file->fh )
1043 return TIMELINEEDITOR_ERR( "failed to add an output file into a ROOT.\n" );
1044 if( out_file->param.brands != in_file->param.brands )
1045 lsmash_freep( &out_file->param.brands );
1046 /* Set movie parameters. */
1047 movie_t *out_movie = &out_file->movie;
1048 out_movie->param = in_movie->param; /* Copy movie parameters. */
1049 if( in_movie->num_tracks == 1 )
1050 out_movie->param.timescale = in_movie->track[0].media_param.timescale;
1051 if( lsmash_set_movie_parameters( output.root, &out_movie->param ) )
1052 return TIMELINEEDITOR_ERR( "Failed to set output movie parameters.\n" );
1053 /* Set iTunes metadata. */
1054 for( uint32_t i = 0; i < in_movie->num_itunes_metadata; i++ )
1055 if( lsmash_set_itunes_metadata( output.root, in_movie->itunes_metadata[i] ) )
1057 WARNING_MSG( "failed to set an iTunes metadata.\n" );
1058 continue;
1060 /* Create tracks of the output movie. */
1061 out_movie->track = lsmash_malloc( in_movie->num_tracks * sizeof(track_t) );
1062 if( !out_movie->track )
1063 return TIMELINEEDITOR_ERR( "Failed to alloc output tracks.\n" );
1064 /* Edit timeline. */
1065 if( edit_media_timeline( &input, &timecode, &opt ) )
1066 return TIMELINEEDITOR_ERR( "Failed to edit timeline.\n" );
1067 out_movie->num_tracks = in_movie->num_tracks;
1068 out_movie->current_track_number = 1;
1069 for( uint32_t i = 0; i < in_movie->num_tracks; i++ )
1071 track_t *in_track = &in_movie->track[i];
1072 if( !in_track->active )
1074 -- out_movie->num_tracks;
1075 continue;
1077 track_t *out_track = &out_movie->track[i];
1078 out_track->summary_remap = lsmash_malloc( in_track->num_summaries * sizeof(uint32_t) );
1079 if( !out_track->summary_remap )
1080 return TIMELINEEDITOR_ERR( "failed to create summary mapping for a track.\n" );
1081 memset( out_track->summary_remap, 0, in_track->num_summaries * sizeof(uint32_t) );
1082 out_track->track_ID = lsmash_create_track( output.root, in_track->media_param.handler_type );
1083 if( !out_track->track_ID )
1084 return TIMELINEEDITOR_ERR( "Failed to create a track.\n" );
1085 /* Copy track and media parameters except for track_ID. */
1086 out_track->track_param = in_track->track_param;
1087 out_track->media_param = in_track->media_param;
1088 out_track->track_param.track_ID = out_track->track_ID;
1089 if( lsmash_set_track_parameters( output.root, out_track->track_ID, &out_track->track_param ) )
1090 return TIMELINEEDITOR_ERR( "Failed to set track parameters.\n" );
1091 if( lsmash_set_media_parameters( output.root, out_track->track_ID, &out_track->media_param ) )
1092 return TIMELINEEDITOR_ERR( "Failed to set media parameters.\n" );
1093 uint32_t valid_summary_count = 0;
1094 for( uint32_t k = 0; k < in_track->num_summaries; k++ )
1096 if( !in_track->summaries[k].active )
1098 out_track->summary_remap[k] = 0;
1099 continue;
1101 lsmash_summary_t *summary = in_track->summaries[k].summary;
1102 if( lsmash_add_sample_entry( output.root, out_track->track_ID, summary ) == 0 )
1104 WARNING_MSG( "failed to append a summary.\n" );
1105 lsmash_cleanup_summary( summary );
1106 in_track->summaries[k].summary = NULL;
1107 in_track->summaries[k].active = 0;
1108 out_track->summary_remap[k] = 0;
1109 continue;
1111 out_track->summary_remap[k] = ++valid_summary_count;
1113 if( valid_summary_count == 0 )
1114 return TIMELINEEDITOR_ERR( "failed to append all summaries.\n" );
1115 out_track->last_sample_delta = in_track->last_sample_delta;
1116 out_track->current_sample_number = 1;
1117 out_track->reach_end_of_media_timeline = 0;
1119 /* Start muxing. */
1120 double largest_dts = 0;
1121 uint32_t num_consecutive_sample_skip = 0;
1122 uint32_t num_active_input_tracks = out_movie->num_tracks;
1123 uint64_t total_media_size = 0;
1124 uint8_t sample_count = 0;
1125 while( 1 )
1127 track_t *in_track = &in_movie->track[ in_movie->current_track_number - 1 ];
1128 /* Try append a sample in an input track where we didn't reach the end of media timeline. */
1129 if( !in_track->reach_end_of_media_timeline )
1131 track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ];
1132 uint32_t in_track_ID = in_track->track_ID;
1133 uint32_t out_track_ID = out_track->track_ID;
1134 uint32_t input_media_timescale = in_track->media_param.timescale;
1135 /* Get a DTS from a track in an input movie. */
1136 uint64_t dts;
1137 if( lsmash_get_dts_from_media_timeline( input.root, in_track_ID, in_track->current_sample_number, &dts ) )
1139 if( lsmash_check_sample_existence_in_media_timeline( input.root, in_track_ID, in_track->current_sample_number ) )
1140 return TIMELINEEDITOR_ERR( "Failed to get the DTS.\n" );
1141 else
1143 in_track->reach_end_of_media_timeline = 1;
1144 if( --num_active_input_tracks == 0 )
1145 break; /* end of muxing */
1148 /* Get and append a sample if it's good time. */
1149 else if( ((double)dts / input_media_timescale) <= largest_dts
1150 || num_consecutive_sample_skip == num_active_input_tracks )
1152 /* Get an actual sample data from a track in an input movie. */
1153 lsmash_sample_t *sample = lsmash_get_sample_from_media_timeline( input.root, in_track_ID, in_track->current_sample_number );
1154 if( !sample )
1155 return TIMELINEEDITOR_ERR( "Failed to get sample.\n" );
1156 sample->index = sample->index > in_track->num_summaries ? in_track->num_summaries
1157 : sample->index == 0 ? 1
1158 : sample->index;
1159 sample->index = out_track->summary_remap[ sample->index - 1 ];
1160 if( sample->index )
1162 /* Append sample into output movie. */
1163 uint64_t sample_size = sample->length; /* sample will be deleted internally after appending. */
1164 if( lsmash_append_sample( output.root, out_track_ID, sample ) )
1166 lsmash_delete_sample( sample );
1167 return TIMELINEEDITOR_ERR( "Failed to append a sample.\n" );
1169 largest_dts = LSMASH_MAX( largest_dts, (double)dts / input_media_timescale );
1170 total_media_size += sample_size;
1171 ++ in_track->current_sample_number;
1172 num_consecutive_sample_skip = 0;
1173 /* Print, per 256 samples, total size of imported media. */
1174 if( ++sample_count == 0 )
1175 eprintf( "Importing: %"PRIu64" bytes\r", total_media_size );
1178 else
1179 ++num_consecutive_sample_skip; /* Skip appendig sample. */
1181 /* Move the next track. */
1182 if( ++ in_movie->current_track_number > in_movie->num_tracks )
1183 in_movie->current_track_number = 1; /* Back the first track. */
1184 if( ++ out_movie->current_track_number > out_movie->num_tracks )
1185 out_movie->current_track_number = 1; /* Back the first track in the output movie. */
1187 for( uint32_t i = 0; i < out_movie->num_tracks; i++ )
1188 if( lsmash_flush_pooled_samples( output.root, out_movie->track[i].track_ID, out_movie->track[i].last_sample_delta ) )
1189 return TIMELINEEDITOR_ERR( "Failed to flush samples.\n" );
1190 /* Copy timeline maps. */
1191 for( uint32_t i = 0; i < out_movie->num_tracks; i++ )
1192 if( lsmash_copy_timeline_map( output.root, out_movie->track[i].track_ID, input.root, in_movie->track[i].track_ID ) )
1193 return TIMELINEEDITOR_ERR( "Failed to copy a timeline map.\n" );
1194 /* Edit timeline map. */
1195 if( argc > 3 )
1197 track_t *out_track = &out_movie->track[ opt.track_number - 1 ];
1198 uint32_t track_ID = out_track->track_ID;
1199 uint32_t movie_timescale = lsmash_get_movie_timescale( output.root );
1200 uint32_t media_timescale = lsmash_get_media_timescale( output.root, track_ID );
1201 uint64_t empty_delay = timecode.empty_delay + (uint64_t)(opt.empty_delay * (1e-3 * media_timescale) + 0.5);
1202 uint64_t duration = timecode.duration + empty_delay;
1203 if( lsmash_delete_explicit_timeline_map( output.root, track_ID ) )
1204 return TIMELINEEDITOR_ERR( "Failed to delete explicit timeline maps.\n" );
1205 if( timecode.empty_delay )
1207 lsmash_edit_t empty_edit;
1208 empty_edit.duration = ((double)timecode.empty_delay / media_timescale) * movie_timescale;
1209 empty_edit.start_time = ISOM_EDIT_MODE_EMPTY;
1210 empty_edit.rate = ISOM_EDIT_MODE_NORMAL;
1211 if( lsmash_create_explicit_timeline_map( output.root, track_ID, empty_edit ) )
1212 return TIMELINEEDITOR_ERR( "Failed to create a empty duration.\n" );
1213 duration = ((double)duration / media_timescale) * movie_timescale;
1214 duration -= empty_edit.duration;
1216 else
1217 duration = ((double)duration / media_timescale) * movie_timescale;
1218 lsmash_edit_t edit;
1219 edit.duration = duration;
1220 edit.start_time = timecode.composition_delay + (uint64_t)(opt.skip_duration * (1e-3 * media_timescale) + 0.5);
1221 edit.rate = ISOM_EDIT_MODE_NORMAL;
1222 if( lsmash_create_explicit_timeline_map( output.root, track_ID, edit ) )
1223 return TIMELINEEDITOR_ERR( "Failed to create a explicit timeline map.\n" );
1225 /* Finish muxing. */
1226 lsmash_adhoc_remux_t moov_to_front;
1227 moov_to_front.func = moov_to_front_callback;
1228 moov_to_front.buffer_size = 4*1024*1024;
1229 moov_to_front.param = NULL;
1230 eprintf( " \r" );
1231 if( lsmash_finish_movie( output.root, &moov_to_front )
1232 || lsmash_write_lsmash_indicator( output.root ) )
1233 return TIMELINEEDITOR_ERR( "Failed to finish output movie.\n" );
1234 cleanup_root( io.input );
1235 cleanup_root( io.output );
1236 cleanup_timecode( io.timecode );
1237 eprintf( "Timeline editing completed! \n" );
1238 return 0;