fragment: Utilize meaningful error values.
[L-SMASH.git] / cli / timelineeditor.c
blob5a62832dd13c940e3bc34237267ef40472d8114d
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 "common/internal.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 "lsmash.h"
34 #include "cli.h"
36 #include "config.h"
38 #define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b))
40 #define eprintf( ... ) fprintf( stderr, __VA_ARGS__ )
41 #define REFRESH_CONSOLE eprintf( " \r" )
43 typedef struct
45 int active;
46 lsmash_summary_t *summary;
47 } summary_t;
49 typedef struct
51 int active;
52 uint32_t track_ID;
53 uint32_t last_sample_delta;
54 uint32_t current_sample_number;
55 int reach_end_of_media_timeline;
56 uint32_t *summary_remap;
57 uint32_t num_summaries;
58 summary_t *summaries;
59 lsmash_track_parameters_t track_param;
60 lsmash_media_parameters_t media_param;
61 } track_t;
63 typedef struct
65 lsmash_itunes_metadata_t *itunes_metadata;
66 track_t *track;
67 lsmash_movie_parameters_t param;
68 uint32_t num_tracks;
69 uint32_t num_itunes_metadata;
70 uint32_t current_track_number;
71 } movie_t;
73 typedef struct
75 lsmash_file_t *fh;
76 lsmash_file_parameters_t param;
77 movie_t movie;
78 } file_t;
80 typedef struct
82 lsmash_root_t *root;
83 file_t file;
84 } root_t;
86 typedef struct
88 FILE *file;
89 uint64_t *ts;
90 uint32_t sample_count;
91 int auto_media_timescale;
92 int auto_media_timebase;
93 uint64_t media_timescale;
94 uint64_t media_timebase;
95 uint64_t duration;
96 uint64_t composition_delay;
97 uint64_t empty_delay;
98 } timecode_t;
100 typedef struct
102 root_t *output;
103 root_t *input;
104 timecode_t *timecode;
105 } movie_io_t;
107 typedef struct
109 uint32_t track_number;
110 uint32_t media_timescale;
111 uint32_t media_timebase;
112 uint32_t skip_duration;
113 uint32_t empty_delay;
114 int dts_compression;
115 } opt_t;
117 static void cleanup_root( root_t *h )
119 if( !h )
120 return;
121 movie_t *movie = &h->file.movie;
122 if( movie->itunes_metadata )
124 for( uint32_t i = 0; i < movie->num_itunes_metadata; i++ )
126 lsmash_itunes_metadata_t *metadata = &movie->itunes_metadata[i];
127 if( metadata->type == ITUNES_METADATA_TYPE_STRING )
129 if( metadata->value.string )
130 lsmash_free( metadata->value.string );
132 else if( metadata->type == ITUNES_METADATA_TYPE_BINARY )
133 if( metadata->value.binary.data )
134 lsmash_free( metadata->value.binary.data );
135 if( metadata->meaning )
136 lsmash_free( metadata->meaning );
137 if( metadata->name )
138 lsmash_free( metadata->name );
140 lsmash_freep( &movie->itunes_metadata );
142 if( movie->track )
143 lsmash_freep( &movie->track );
144 lsmash_close_file( &h->file.param );
145 lsmash_destroy_root( h->root );
146 h->root = NULL;
149 static void cleanup_timecode( timecode_t *timecode )
151 if( !timecode )
152 return;
153 if( timecode->file )
155 fclose( timecode->file );
156 timecode->file = NULL;
158 if( timecode->ts )
159 lsmash_freep( &timecode->ts );
162 static int error_message( const char* message, ... )
164 REFRESH_CONSOLE;
165 eprintf( "Error: " );
166 va_list args;
167 va_start( args, message );
168 vfprintf( stderr, message, args );
169 va_end( args );
170 return -1;
173 static int warning_message( const char* message, ... )
175 REFRESH_CONSOLE;
176 eprintf( "Warning: " );
177 va_list args;
178 va_start( args, message );
179 vfprintf( stderr, message, args );
180 va_end( args );
181 return -1;
184 static int timelineeditor_error( movie_io_t *io, const char *message, ... )
186 cleanup_root( io->input );
187 cleanup_root( io->output );
188 cleanup_timecode( io->timecode );
189 va_list args;
190 va_start( args, message );
191 error_message( message, args );
192 va_end( args );
193 return -1;
196 #define TIMELINEEDITOR_ERR( ... ) timelineeditor_error( &io, __VA_ARGS__ )
197 #define ERROR_MSG( ... ) error_message( __VA_ARGS__ )
198 #define WARNING_MSG( ... ) warning_message( __VA_ARGS__ )
200 static char *duplicate_string( char *src )
202 if( !src )
203 return NULL;
204 int dst_size = strlen( src ) + 1;
205 char *dst = lsmash_malloc( dst_size );
206 if( !dst )
207 return NULL;
208 memcpy( dst, src, dst_size );
209 return dst;
212 static int get_itunes_metadata( lsmash_root_t *root, uint32_t metadata_number, lsmash_itunes_metadata_t *metadata )
214 memset( metadata, 0, sizeof(lsmash_itunes_metadata_t) );
215 if( lsmash_get_itunes_metadata( root, metadata_number, metadata ) )
216 return -1;
217 lsmash_itunes_metadata_t shadow = *metadata;
218 metadata->meaning = NULL;
219 metadata->name = NULL;
220 memset( &metadata->value, 0, sizeof(lsmash_itunes_metadata_value_t) );
221 if( shadow.meaning )
223 metadata->meaning = duplicate_string( shadow.meaning );
224 if( !metadata->meaning )
225 return -1;
227 if( shadow.name )
229 metadata->name = duplicate_string( shadow.name );
230 if( !metadata->name )
231 goto fail;
233 if( shadow.type == ITUNES_METADATA_TYPE_STRING )
235 metadata->value.string = duplicate_string( shadow.value.string );
236 if( !metadata->value.string )
237 goto fail;
239 else if( shadow.type == ITUNES_METADATA_TYPE_BINARY )
241 metadata->value.binary.data = lsmash_malloc( shadow.value.binary.size );
242 if( !metadata->value.binary.data )
243 goto fail;
244 memcpy( metadata->value.binary.data, shadow.value.binary.data, shadow.value.binary.size );
246 return 0;
247 fail:
248 if( metadata->meaning )
249 lsmash_free( metadata->meaning );
250 if( metadata->name )
251 lsmash_free( metadata->name );
252 return -1;
255 static int get_summaries( root_t *input, track_t *track )
257 track->num_summaries = lsmash_count_summary( input->root, track->track_ID );
258 if( track->num_summaries == 0 )
259 return ERROR_MSG( "Failed to get find valid summaries.\n" );
260 track->summaries = lsmash_malloc( track->num_summaries * sizeof(summary_t) );
261 if( !track->summaries )
262 return ERROR_MSG( "failed to alloc input summaries.\n" );
263 memset( track->summaries, 0, track->num_summaries * sizeof(summary_t) );
264 for( uint32_t j = 0; j < track->num_summaries; j++ )
266 lsmash_summary_t *summary = lsmash_get_summary( input->root, track->track_ID, j + 1 );
267 if( !summary )
269 WARNING_MSG( "failed to get a summary.\n" );
270 continue;
272 track->summaries[j].summary = summary;
273 track->summaries[j].active = 1;
275 return 0;
278 static int get_movie( root_t *input, char *input_name )
280 if( !strcmp( input_name, "-" ) )
281 return ERROR_MSG( "Standard input not supported.\n" );
282 input->root = lsmash_create_root();
283 if( !input->root )
284 return ERROR_MSG( "failed to create a ROOT for an input file.\n" );
285 file_t *in_file = &input->file;
286 if( lsmash_open_file( input_name, 1, &in_file->param ) < 0 )
287 return ERROR_MSG( "failed to open an input file.\n" );
288 in_file->fh = lsmash_set_file( input->root, &in_file->param );
289 if( !in_file->fh )
290 return ERROR_MSG( "failed to add an input file into a ROOT.\n" );
291 if( lsmash_read_file( in_file->fh, &in_file->param ) < 0 )
292 return ERROR_MSG( "failed to read an input file\n" );
293 movie_t *movie = &in_file->movie;
294 movie->num_itunes_metadata = lsmash_count_itunes_metadata( input->root );
295 if( movie->num_itunes_metadata )
297 movie->itunes_metadata = lsmash_malloc( movie->num_itunes_metadata * sizeof(lsmash_itunes_metadata_t) );
298 if( !movie->itunes_metadata )
299 return ERROR_MSG( "failed to alloc iTunes metadata.\n" );
300 uint32_t itunes_metadata_count = 0;
301 for( uint32_t i = 1; i <= movie->num_itunes_metadata; i++ )
303 if( get_itunes_metadata( input->root, i, &movie->itunes_metadata[itunes_metadata_count] ) )
305 WARNING_MSG( "failed to get an iTunes metadata.\n" );
306 continue;
308 ++itunes_metadata_count;
310 movie->num_itunes_metadata = itunes_metadata_count;
312 lsmash_initialize_movie_parameters( &movie->param );
313 lsmash_get_movie_parameters( input->root, &movie->param );
314 movie->num_tracks = movie->param.number_of_tracks;
315 movie->current_track_number = 1;
316 /* Create tracks. */
317 track_t *track = movie->track = lsmash_malloc( movie->num_tracks * sizeof(track_t) );
318 if( !track )
319 return ERROR_MSG( "Failed to alloc input tracks.\n" );
320 memset( track, 0, movie->num_tracks * sizeof(track_t) );
321 for( uint32_t i = 0; i < movie->num_tracks; i++ )
323 track[i].track_ID = lsmash_get_track_ID( input->root, i + 1 );
324 if( !track[i].track_ID )
325 return ERROR_MSG( "Failed to get track_ID.\n" );
327 for( uint32_t i = 0; i < movie->num_tracks; i++ )
329 lsmash_initialize_track_parameters( &track[i].track_param );
330 if( lsmash_get_track_parameters( input->root, track[i].track_ID, &track[i].track_param ) )
332 WARNING_MSG( "failed to get track parameters.\n" );
333 continue;
335 lsmash_initialize_media_parameters( &track[i].media_param );
336 if( lsmash_get_media_parameters( input->root, track[i].track_ID, &track[i].media_param ) )
338 WARNING_MSG( "failed to get media parameters.\n" );
339 continue;
341 if( lsmash_construct_timeline( input->root, track[i].track_ID ) )
343 WARNING_MSG( "failed to construct timeline.\n" );
344 continue;
346 if( lsmash_get_last_sample_delta_from_media_timeline( input->root, track[i].track_ID, &track[i].last_sample_delta ) )
348 WARNING_MSG( "failed to get the last sample delta.\n" );
349 continue;
351 if( get_summaries( input, &track[i] ) )
353 WARNING_MSG( "failed to get valid summaries.\n" );
354 continue;
356 track[i].active = 1;
357 track[i].current_sample_number = 1;
359 lsmash_destroy_children( lsmash_file_as_box( in_file->fh ) );
360 return 0;
363 static inline uint64_t get_gcd( uint64_t a, uint64_t b )
365 if( !b )
366 return a;
367 while( 1 )
369 uint64_t c = a % b;
370 if( !c )
371 return b;
372 a = b;
373 b = c;
377 static inline uint64_t get_lcm( uint64_t a, uint64_t b )
379 if( !a )
380 return 0;
381 return (a / get_gcd( a, b )) * b;
384 static uint64_t get_media_timebase( lsmash_media_ts_list_t *ts_list )
386 uint64_t timebase = ts_list->timestamp[0].cts;
387 for( uint32_t i = 1; i < ts_list->sample_count; i++ )
388 timebase = get_gcd( timebase, ts_list->timestamp[i].cts );
389 for( uint32_t i = 0; i < ts_list->sample_count; i++ )
390 timebase = get_gcd( timebase, ts_list->timestamp[i].dts );
391 return timebase;
394 static inline double sigexp10( double value, double *exponent )
396 /* This function separates significand and exp10 from double floating point. */
397 *exponent = 1;
398 while( value < 1 )
400 value *= 10;
401 *exponent /= 10;
403 while( value >= 10 )
405 value /= 10;
406 *exponent *= 10;
408 return value;
411 #define DOUBLE_EPSILON 5e-6
412 #define MATROSKA_TIMESCALE 1000000000
413 #define SKIP_LINE_CHARACTER( x ) ((x) == '#' || (x) == '\n' || (x) == '\r')
415 static double correct_fps( double fps, timecode_t *timecode )
417 int i = 1;
418 uint64_t fps_num, fps_den;
419 double exponent;
420 double fps_sig = sigexp10( fps, &exponent );
421 while( 1 )
423 fps_den = i * timecode->media_timebase;
424 fps_num = round( fps_den * fps_sig ) * exponent;
425 if( fps_num > UINT32_MAX )
426 return ERROR_MSG( "framerate correction failed.\n"
427 "Specify an appropriate timebase manually or remake timecode file.\n" );
428 if( fabs( ((double)fps_num / fps_den) / exponent - fps_sig ) < DOUBLE_EPSILON )
429 break;
430 ++i;
432 if( timecode->auto_media_timescale )
434 timecode->media_timescale = timecode->media_timescale
435 ? get_lcm( timecode->media_timescale, fps_num )
436 : fps_num;
437 if( timecode->media_timescale > UINT32_MAX )
438 timecode->auto_media_timescale = 0;
440 return (double)fps_num / fps_den;
443 static int try_matroska_timescale( double *fps_array, timecode_t *timecode, uint32_t num_loops )
445 timecode->media_timebase = 0;
446 timecode->media_timescale = MATROSKA_TIMESCALE;
447 for( uint32_t i = 0; i < num_loops; i++ )
449 uint64_t fps_den;
450 double exponent;
451 double fps_sig = sigexp10( fps_array[i], &exponent );
452 fps_den = round( MATROSKA_TIMESCALE / fps_sig ) / exponent;
453 timecode->media_timebase = fps_den && timecode->media_timebase
454 ? get_gcd( timecode->media_timebase, fps_den )
455 : fps_den;
456 if( timecode->media_timebase > UINT32_MAX || !timecode->media_timebase )
457 return ERROR_MSG( "Automatic media timescale generation failed.\n"
458 "Specify media timescale manually.\n" );
460 return 0;
463 static int parse_timecode( timecode_t *timecode, uint32_t sample_count )
465 int tcfv;
466 int ret = fscanf( timecode->file, "# timecode format v%d", &tcfv );
467 if( ret != 1 || (tcfv != 1 && tcfv != 2) )
468 return ERROR_MSG( "Unsupported timecode format\n" );
469 char buff[256];
470 double *timecode_array = NULL;
471 if( tcfv == 1 )
473 double assume_fps = 0;
474 /* Get assumed framerate. */
475 while( fgets( buff, sizeof(buff), timecode->file ) )
477 if( SKIP_LINE_CHARACTER( buff[0] ) )
478 continue;
479 if( sscanf( buff, "assume %lf", &assume_fps ) != 1
480 && sscanf( buff, "Assume %lf", &assume_fps ) != 1 )
481 return ERROR_MSG( "Assumed fps not found\n" );
482 break;
484 if( assume_fps <= 0 )
485 return ERROR_MSG( "Invalid assumed fps\n" );
486 uint64_t file_pos = ftell( timecode->file );
487 /* Check whether valid or not and count number of sequences. */
488 uint32_t num_sequences = 0;
489 int64_t start, end;
490 int64_t prev_start = -1, prev_end = -1;
491 double sequence_fps;
492 while( fgets( buff, sizeof(buff), timecode->file ) )
494 if( SKIP_LINE_CHARACTER( buff[0] ) )
495 continue;
496 ret = sscanf( buff, "%"SCNd64",%"SCNd64",%lf", &start, &end, &sequence_fps );
497 if( ret != 3 && ret != EOF )
498 return ERROR_MSG( "Invalid input timecode file\n" );
499 if( start > end || start <= prev_start || end <= prev_end || sequence_fps <= 0 )
500 return ERROR_MSG( "Invalid input timecode file\n" );
501 prev_start = start;
502 prev_end = end;
503 if( timecode->auto_media_timescale || timecode->auto_media_timebase )
504 ++num_sequences;
506 fseek( timecode->file, file_pos, SEEK_SET );
507 /* Preparation storing timecodes. */
508 double fps_array[ (timecode->auto_media_timescale || timecode->auto_media_timebase) * num_sequences + 1 ];
509 double corrected_assume_fps = correct_fps( assume_fps, timecode );
510 if( corrected_assume_fps < 0 )
511 return ERROR_MSG( "Failed to correct the assumed framerate\n" );
512 timecode_array = lsmash_malloc( sample_count * sizeof(double) );
513 if( !timecode_array )
514 return ERROR_MSG( "Failed to alloc timecodes\n" );
515 timecode_array[0] = 0;
516 num_sequences = 0;
517 uint32_t i = 0;
518 while( i < sample_count - 1 && fgets( buff, sizeof(buff), timecode->file ) )
520 if( SKIP_LINE_CHARACTER( buff[0] ) )
521 continue;
522 ret = sscanf( buff, "%"SCNd64",%"SCNd64",%lf", &start, &end, &sequence_fps );
523 if( ret != 3 )
524 start = end = sample_count - 1;
525 for( ; i < start && i < sample_count - 1; i++ )
526 timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps;
527 if( i < sample_count - 1 )
529 if( timecode->auto_media_timescale || timecode->auto_media_timebase )
530 fps_array[num_sequences++] = sequence_fps;
531 sequence_fps = correct_fps( sequence_fps, timecode );
532 if( sequence_fps < 0 )
534 lsmash_free( timecode_array );
535 return ERROR_MSG( "Failed to correct the framerate of a sequence.\n" );
537 for( i = start; i <= end && i < sample_count - 1; i++ )
538 timecode_array[i + 1] = timecode_array[i] + 1 / sequence_fps;
541 for( ; i < sample_count - 1; i++ )
542 timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps;
543 if( timecode->auto_media_timescale || timecode->auto_media_timebase )
544 fps_array[num_sequences] = assume_fps;
545 /* Assume matroska timebase if automatic timescale generation isn't done yet. */
546 if( timecode->auto_media_timebase && !timecode->auto_media_timescale )
548 double exponent;
549 double assume_fps_sig, sequence_fps_sig;
550 if( try_matroska_timescale( fps_array, timecode, num_sequences + 1 ) < 0 )
552 lsmash_free( timecode_array );
553 return ERROR_MSG( "Failed to try matroska timescale.\n" );
555 fseek( timecode->file, file_pos, SEEK_SET );
556 assume_fps_sig = sigexp10( assume_fps, &exponent );
557 corrected_assume_fps = MATROSKA_TIMESCALE / ( round( MATROSKA_TIMESCALE / assume_fps_sig ) / exponent );
558 for( i = 0; i < sample_count - 1 && fgets( buff, sizeof(buff), timecode->file ); )
560 if( SKIP_LINE_CHARACTER( buff[0] ) )
561 continue;
562 ret = sscanf( buff, "%"SCNd64",%"SCNd64",%lf", &start, &end, &sequence_fps );
563 if( ret != 3 )
564 start = end = sample_count - 1;
565 sequence_fps_sig = sigexp10( sequence_fps, &exponent );
566 sequence_fps = MATROSKA_TIMESCALE / ( round( MATROSKA_TIMESCALE / sequence_fps_sig ) / exponent );
567 for( ; i < start && i < sample_count - 1; i++ )
568 timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps;
569 for( i = start; i <= end && i < sample_count - 1; i++ )
570 timecode_array[i + 1] = timecode_array[i] + 1 / sequence_fps;
572 for( ; i < sample_count - 1; i++ )
573 timecode_array[i + 1] = timecode_array[i] + 1 / corrected_assume_fps;
576 else /* tcfv == 2 */
578 uint32_t num_timecodes = 0;
579 uint64_t file_pos = ftell( timecode->file );
580 while( fgets( buff, sizeof(buff), timecode->file ) )
582 if( SKIP_LINE_CHARACTER( buff[0] ) )
584 if( !num_timecodes )
585 file_pos = ftell( timecode->file );
586 continue;
588 ++num_timecodes;
590 if( !num_timecodes )
591 return ERROR_MSG( "No timecodes!\n" );
592 if( sample_count > num_timecodes )
593 return ERROR_MSG( "Lack number of timecodes.\n" );
594 fseek( timecode->file, file_pos, SEEK_SET );
595 timecode_array = lsmash_malloc( sample_count * sizeof(uint64_t) );
596 if( !timecode_array )
597 return ERROR_MSG( "Failed to alloc timecodes.\n" );
598 uint32_t i = 0;
599 if( fgets( buff, sizeof(buff), timecode->file ) )
601 ret = sscanf( buff, "%lf", &timecode_array[0] );
602 if( ret != 1 )
604 lsmash_free( timecode_array );
605 return ERROR_MSG( "Invalid timecode number: 0\n" );
607 timecode_array[i++] *= 1e-3; /* Timescale of timecode format v2 is 1000. */
608 while( i < sample_count && fgets( buff, sizeof(buff), timecode->file ) )
610 if( SKIP_LINE_CHARACTER( buff[0] ) )
611 continue;
612 ret = sscanf( buff, "%lf", &timecode_array[i] );
613 timecode_array[i] *= 1e-3; /* Timescale of timecode format v2 is 1000. */
614 if( ret != 1 || timecode_array[i] <= timecode_array[i - 1] )
616 lsmash_free( timecode_array );
617 return ERROR_MSG( "Invalid input timecode.\n" );
619 ++i;
622 if( i < sample_count )
624 lsmash_free( timecode_array );
625 return ERROR_MSG( "Failed to get timecodes.\n" );
627 /* Generate media timescale automatically if needed. */
628 if( sample_count != 1 && timecode->auto_media_timescale )
630 double fps_array[sample_count - 1];
631 for( i = 0; i < sample_count - 1; i++ )
633 fps_array[i] = 1 / (timecode_array[i + 1] - timecode_array[i]);
634 if( timecode->auto_media_timescale )
636 int j = 1;
637 uint64_t fps_num, fps_den;
638 double exponent;
639 double fps_sig = sigexp10( fps_array[i], &exponent );
640 while( 1 )
642 fps_den = j * timecode->media_timebase;
643 fps_num = round( fps_den * fps_sig ) * exponent;
644 if( fps_num > UINT32_MAX
645 || fabs( ((double)fps_num / fps_den) / exponent - fps_sig ) < DOUBLE_EPSILON )
646 break;
647 ++j;
649 timecode->media_timescale = fps_num && timecode->media_timescale
650 ? get_lcm( timecode->media_timescale, fps_num )
651 : fps_num;
652 if( timecode->media_timescale > UINT32_MAX )
654 timecode->auto_media_timescale = 0;
655 continue; /* Don't break because all framerate is needed for try_matroska_timescale. */
659 if( timecode->auto_media_timebase && !timecode->auto_media_timescale
660 && try_matroska_timescale( fps_array, timecode, sample_count - 1 ) < 0 )
662 lsmash_free( timecode_array );
663 return ERROR_MSG( "Failed to try matroska timescale.\n" );
667 if( timecode->auto_media_timescale || timecode->auto_media_timebase )
669 uint64_t reduce = get_gcd( timecode->media_timebase, timecode->media_timescale );
670 timecode->media_timebase /= reduce;
671 timecode->media_timescale /= reduce;
673 else if( timecode->media_timescale > UINT32_MAX || !timecode->media_timescale )
675 lsmash_free( timecode_array );
676 return ERROR_MSG( "Failed to generate media timescale automatically.\n"
677 "Specify an appropriate media timescale manually.\n" );
679 uint32_t timescale = timecode->media_timescale;
680 uint32_t timebase = timecode->media_timebase;
681 double delay_tc = timecode_array[0];
682 timecode->empty_delay = ((uint64_t)(delay_tc * ((double)timescale / timebase) + 0.5)) * timebase;
683 timecode->ts = lsmash_malloc( sample_count * sizeof(uint64_t) );
684 if( !timecode->ts )
686 lsmash_free( timecode_array );
687 return ERROR_MSG( "Failed to allocate timestamps.\n" );
689 timecode->ts[0] = 0;
690 for( uint32_t i = 1; i < sample_count; i++ )
692 timecode->ts[i] = ((uint64_t)((timecode_array[i] - delay_tc) * ((double)timescale / timebase) + 0.5)) * timebase;
693 if( timecode->ts[i] <= timecode->ts[i - 1] )
695 lsmash_free( timecode_array );
696 lsmash_free( timecode->ts );
697 timecode->ts = NULL;
698 return ERROR_MSG( "Invalid timecode.\n" );
701 lsmash_free( timecode_array );
702 return 0;
705 #undef DOUBLE_EPSILON
706 #undef MATROSKA_TIMESCALE
707 #undef SKIP_LINE_CHARACTER
709 static int edit_media_timeline( root_t *input, timecode_t *timecode, opt_t *opt )
711 if( !timecode->file && !opt->media_timescale && !opt->media_timebase && !opt->dts_compression )
712 return 0;
713 track_t *in_track = &input->file.movie.track[opt->track_number - 1];
714 uint32_t track_ID = in_track->track_ID;
715 lsmash_media_ts_list_t ts_list;
716 if( lsmash_get_media_timestamps( input->root, track_ID, &ts_list ) )
717 return ERROR_MSG( "Failed to get media timestamps.\n" );
718 uint64_t timebase = get_media_timebase( &ts_list );
719 if( !timebase )
720 return ERROR_MSG( "Failed to get media timebase.\n" );
721 lsmash_media_ts_t *timestamp = ts_list.timestamp;
722 uint32_t sample_count = ts_list.sample_count;
723 uint32_t orig_timebase = timebase;
724 uint32_t timescale;
725 double timebase_convert_multiplier;
726 if( opt->media_timescale || opt->media_timebase )
728 uint32_t orig_timescale = in_track->media_param.timescale;
729 timescale = opt->media_timescale ? opt->media_timescale : orig_timescale;
730 timebase = opt->media_timebase ? opt->media_timebase : orig_timebase;
731 if( !opt->media_timescale && opt->media_timebase && (timebase > orig_timebase) )
732 timescale = timescale * ((double)timebase / orig_timebase) + 0.5;
733 timebase_convert_multiplier = ((double)timescale / orig_timescale) * ((double)orig_timebase / timebase);
735 else
737 /* Reduce timescale and timebase. */
738 timescale = in_track->media_param.timescale;
739 uint64_t reduce = get_gcd( timescale, timebase );
740 timescale /= reduce;
741 timebase /= reduce;
742 timebase_convert_multiplier = 1;
744 /* Parse timecode file. */
745 if( timecode->file )
747 timecode->auto_media_timescale = !opt->media_timescale;
748 timecode->auto_media_timebase = !opt->media_timebase;
749 timecode->media_timescale = timecode->auto_media_timescale ? 0 : timescale;
750 timecode->media_timebase = timebase;
751 if( parse_timecode( timecode, sample_count ) )
752 return ERROR_MSG( "Failed to parse timecode file.\n" );
753 timescale = timecode->media_timescale;
754 timebase = timecode->media_timebase;
756 /* Get maximum composition sample delay for DTS generation. */
757 uint32_t sample_delay;
758 if( lsmash_get_max_sample_delay( &ts_list, &sample_delay ) )
759 return ERROR_MSG( "Failed to get maximum composition sample delay.\n" );
760 if( sample_delay ) /* Reorder composition order. */
761 lsmash_sort_timestamps_composition_order( &ts_list );
762 if( !timecode->file )
764 /* Genarate timestamps timescale converted. */
765 timecode->ts = lsmash_malloc( sample_count * sizeof(uint64_t) );
766 if( !timecode->ts )
767 return ERROR_MSG( "Failed to alloc timestamps\n" );
768 for( uint32_t i = 0; i < sample_count; i++ )
770 timecode->ts[i] = (timestamp[i].cts - timestamp[0].cts) / orig_timebase;
771 timecode->ts[i] = ((uint64_t)(timecode->ts[i] * timebase_convert_multiplier + 0.5)) * timebase;
772 if( i && (timecode->ts[i] <= timecode->ts[i - 1]) )
773 return ERROR_MSG( "Invalid timescale conversion.\n" );
776 if( sample_delay )
778 /* If media timescale is specified, disable DTS compression multiplier. */
779 uint32_t dts_compression_multiplier = opt->dts_compression * !opt->media_timescale * sample_delay + 1;
780 uint64_t initial_delta = timecode->ts[1];
781 timescale *= dts_compression_multiplier;
782 if( dts_compression_multiplier > 1 )
783 for( uint32_t i = 0; i < sample_count; i++ )
784 timecode->ts[i] *= dts_compression_multiplier;
785 /* Generate CTS. */
786 uint64_t sample_delay_time = timecode->composition_delay = opt->dts_compression ? 0 : timecode->ts[sample_delay];
787 for( uint32_t i = 0; i < sample_count; i++ )
788 timestamp[i].cts = timecode->ts[i] + sample_delay_time;
789 /* Reorder decode order and generate new DTS from CTS. */
790 lsmash_sort_timestamps_decoding_order( &ts_list );
791 uint64_t prev_reordered_cts[sample_delay];
792 for( uint32_t i = 0; i <= sample_delay; i++ )
794 if( !opt->dts_compression )
795 timestamp[i].dts = timecode->ts[i];
796 else
798 timestamp[i].dts = (i * initial_delta) / (!!opt->media_timescale * sample_delay + 1);
799 if( i && (timestamp[i].dts <= timestamp[i - 1].dts) )
800 return ERROR_MSG( "Failed to do DTS compression.\n" );
802 prev_reordered_cts[ i % sample_delay ] = timecode->ts[i] + sample_delay_time;
804 for( uint32_t i = sample_delay + 1; i < sample_count; i++ )
806 timestamp[i].dts = prev_reordered_cts[ (i - sample_delay) % sample_delay ];
807 prev_reordered_cts[ i % sample_delay ] = timecode->ts[i] + sample_delay_time;
810 else
811 for( uint32_t i = 0; i < sample_count; i++ )
812 timestamp[i].cts = timestamp[i].dts = timecode->ts[i];
813 if( sample_count > 1 )
815 in_track->last_sample_delta = timecode->ts[sample_count - 1] - timecode->ts[sample_count - 2];
816 timecode->duration = timecode->ts[sample_count - 1] + in_track->last_sample_delta;
818 else /* still image */
819 timecode->duration = in_track->last_sample_delta = UINT32_MAX;
820 in_track->media_param.timescale = timescale;
821 if( lsmash_set_media_timestamps( input->root, track_ID, &ts_list ) )
822 return ERROR_MSG( "Failed to set media timestamps.\n" );
823 lsmash_delete_media_timestamps( &ts_list );
824 return 0;
827 static int check_white_brand( lsmash_brand_type brand )
829 static const lsmash_brand_type brand_white_list[] =
831 ISOM_BRAND_TYPE_3G2A,
832 ISOM_BRAND_TYPE_3GG6,
833 ISOM_BRAND_TYPE_3GG9,
834 ISOM_BRAND_TYPE_3GP4,
835 ISOM_BRAND_TYPE_3GP5,
836 ISOM_BRAND_TYPE_3GP6,
837 ISOM_BRAND_TYPE_3GP7,
838 ISOM_BRAND_TYPE_3GP8,
839 ISOM_BRAND_TYPE_3GP9,
840 ISOM_BRAND_TYPE_3GR6,
841 ISOM_BRAND_TYPE_3GR9,
842 ISOM_BRAND_TYPE_M4A ,
843 ISOM_BRAND_TYPE_M4B ,
844 ISOM_BRAND_TYPE_M4V ,
845 ISOM_BRAND_TYPE_AVC1,
846 ISOM_BRAND_TYPE_DBY1,
847 ISOM_BRAND_TYPE_ISO2,
848 ISOM_BRAND_TYPE_ISO3,
849 ISOM_BRAND_TYPE_ISO4,
850 ISOM_BRAND_TYPE_ISO5,
851 ISOM_BRAND_TYPE_ISO6,
852 ISOM_BRAND_TYPE_ISOM,
853 ISOM_BRAND_TYPE_MP41,
854 ISOM_BRAND_TYPE_MP42,
855 ISOM_BRAND_TYPE_QT ,
858 for( int i = 0; brand_white_list[i]; i++ )
859 if( brand == brand_white_list[i] )
860 return 1;
861 return 0;
864 static int moov_to_front_callback( void *param, uint64_t written_movie_size, uint64_t total_movie_size )
866 eprintf( "Finalizing: [%5.2lf%%]\r", ((double)written_movie_size / total_movie_size) * 100.0 );
867 return 0;
870 static void display_version( void )
872 eprintf( "\n"
873 "L-SMASH isom/mov timeline editor rev%s %s\n"
874 "Built on %s %s\n"
875 "Copyright (C) 2011-2014 L-SMASH project\n",
876 LSMASH_REV, LSMASH_GIT_HASH, __DATE__, __TIME__ );
879 static void display_help( void )
881 display_version();
882 eprintf( "\n"
883 "Usage: timelineeditor [options] input output\n"
884 " options:\n"
885 " --help Display help\n"
886 " --version Display version information\n"
887 " --track <integer> Specify track number to edit [1]\n"
888 " --timecode <string> Specify timecode file to edit timeline\n"
889 " --media-timescale <integer> Specify media timescale to convert\n"
890 " --media-timebase <integer> Specify media timebase to convert\n"
891 " --skip <integer> Skip start of media presentation in milliseconds\n"
892 " --delay <integer> Insert blank clip before actual media presentation in milliseconds\n"
893 " --dts-compression Eliminate composition delay with DTS hack\n"
894 " Multiply media timescale and timebase automatically\n" );
897 int main( int argc, char *argv[] )
899 if ( argc < 2 )
901 display_help();
902 return -1;
904 else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) )
906 display_help();
907 return 0;
909 else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) )
911 display_version();
912 return 0;
914 else if( argc < 3 )
916 display_help();
917 return -1;
919 root_t output = { 0 };
920 root_t input = { 0 };
921 timecode_t timecode = { 0 };
922 movie_io_t io = { &output, &input, &timecode };
923 opt_t opt = { 1, 0, 0, 0, 0, 0 };
924 /* Parse options. */
925 lsmash_get_mainargs( &argc, &argv );
926 int argn = 1;
927 while( argn < argc - 2 )
929 if( !strcasecmp( argv[argn], "--track" ) )
931 opt.track_number = atoi( argv[++argn] );
932 if( !opt.track_number )
933 return TIMELINEEDITOR_ERR( "Invalid track number.\n" );
934 ++argn;
936 else if( !strcasecmp( argv[argn], "--timecode" ) )
938 timecode.file = lsmash_fopen( argv[++argn], "rb" );
939 if( !timecode.file )
940 return TIMELINEEDITOR_ERR( "Failed to open timecode file.\n" );
941 ++argn;
943 else if( !strcasecmp( argv[argn], "--media-timescale" ) )
945 opt.media_timescale = atoi( argv[++argn] );
946 if( !opt.media_timescale )
947 return TIMELINEEDITOR_ERR( "Invalid media timescale.\n" );
948 ++argn;
950 else if( !strcasecmp( argv[argn], "--media-timebase" ) )
952 opt.media_timebase = atoi( argv[++argn] );
953 if( !opt.media_timebase )
954 return TIMELINEEDITOR_ERR( "Invalid media timebase.\n" );
955 ++argn;
957 else if( !strcasecmp( argv[argn], "--skip" ) )
959 opt.skip_duration = atoi( argv[++argn] );
960 if( !opt.skip_duration )
961 return TIMELINEEDITOR_ERR( "Invalid skip duration.\n" );
962 ++argn;
964 else if( !strcasecmp( argv[argn], "--delay" ) )
966 opt.empty_delay = atoi( argv[++argn] );
967 if( !opt.empty_delay )
968 return TIMELINEEDITOR_ERR( "Invalid delay time.\n" );
969 ++argn;
971 else if( !strcasecmp( argv[argn], "--dts-compression" ) )
973 opt.dts_compression = 1;
974 ++argn;
976 else
977 return TIMELINEEDITOR_ERR( "Invalid option.\n" );
979 if( argn > argc - 2 )
980 return TIMELINEEDITOR_ERR( "Invalid arguments.\n" );
981 /* Get input movies. */
982 if( get_movie( &input, argv[argn++] ) )
983 return TIMELINEEDITOR_ERR( "Failed to get input movie.\n" );
984 movie_t *in_movie = &input.file.movie;
985 if( opt.track_number && (opt.track_number > in_movie->num_tracks) )
986 return TIMELINEEDITOR_ERR( "Invalid track number.\n" );
987 /* Create output movie. */
988 file_t *out_file = &output.file;
989 output.root = lsmash_create_root();
990 if( !output.root )
991 return TIMELINEEDITOR_ERR( "failed to create a ROOT for an output file.\n" );
992 if( lsmash_open_file( argv[argn], 0, &out_file->param ) < 0 )
993 return TIMELINEEDITOR_ERR( "failed to open an output file.\n" );
994 file_t *in_file = &input.file;
995 out_file->param.major_brand = in_file->param.major_brand;
996 out_file->param.minor_version = in_file->param.minor_version;
997 out_file->param.brands = in_file->param.brands;
998 out_file->param.brand_count = in_file->param.brand_count;
999 out_file->param.max_chunk_duration = 0.5;
1000 out_file->param.max_async_tolerance = 2.0;
1001 out_file->param.max_chunk_size = 4*1024*1024;
1002 if( !check_white_brand( out_file->param.major_brand ) )
1004 /* Replace with whitelisted brand 'mp42'. */
1005 out_file->param.major_brand = ISOM_BRAND_TYPE_MP42;
1006 out_file->param.minor_version = 0;
1007 uint32_t i;
1008 for( i = 0; i < out_file->param.brand_count; i++ )
1009 if( out_file->param.brands[i] == ISOM_BRAND_TYPE_MP42 )
1010 break;
1011 if( i == out_file->param.brand_count )
1013 /* Add 'mp42' into the list of compatible brands. */
1014 out_file->param.brands = lsmash_malloc( (i + 1) * sizeof(lsmash_brand_type) );
1015 if( out_file->param.brands )
1017 memcpy( out_file->param.brands, in_file->param.brands, i * sizeof(lsmash_brand_type) );
1018 out_file->param.brands[i] = ISOM_BRAND_TYPE_MP42;
1022 out_file->fh = lsmash_set_file( output.root, &out_file->param );
1023 if( !out_file->fh )
1024 return TIMELINEEDITOR_ERR( "failed to add an output file into a ROOT.\n" );
1025 if( out_file->param.brands != in_file->param.brands )
1026 lsmash_freep( &out_file->param.brands );
1027 /* Set movie parameters. */
1028 movie_t *out_movie = &out_file->movie;
1029 out_movie->param = in_movie->param; /* Copy movie parameters. */
1030 if( in_movie->num_tracks == 1 )
1031 out_movie->param.timescale = in_movie->track[0].media_param.timescale;
1032 if( lsmash_set_movie_parameters( output.root, &out_movie->param ) )
1033 return TIMELINEEDITOR_ERR( "Failed to set output movie parameters.\n" );
1034 /* Set iTunes metadata. */
1035 for( uint32_t i = 0; i < in_movie->num_itunes_metadata; i++ )
1036 if( lsmash_set_itunes_metadata( output.root, in_movie->itunes_metadata[i] ) )
1038 WARNING_MSG( "failed to set an iTunes metadata.\n" );
1039 continue;
1041 /* Create tracks of the output movie. */
1042 out_movie->track = lsmash_malloc( in_movie->num_tracks * sizeof(track_t) );
1043 if( !out_movie->track )
1044 return TIMELINEEDITOR_ERR( "Failed to alloc output tracks.\n" );
1045 /* Edit timeline. */
1046 if( edit_media_timeline( &input, &timecode, &opt ) )
1047 return TIMELINEEDITOR_ERR( "Failed to edit timeline.\n" );
1048 out_movie->num_tracks = in_movie->num_tracks;
1049 out_movie->current_track_number = 1;
1050 for( uint32_t i = 0; i < in_movie->num_tracks; i++ )
1052 track_t *in_track = &in_movie->track[i];
1053 if( !in_track->active )
1055 -- out_movie->num_tracks;
1056 continue;
1058 track_t *out_track = &out_movie->track[i];
1059 out_track->summary_remap = lsmash_malloc( in_track->num_summaries * sizeof(uint32_t) );
1060 if( !out_track->summary_remap )
1061 return TIMELINEEDITOR_ERR( "failed to create summary mapping for a track.\n" );
1062 memset( out_track->summary_remap, 0, in_track->num_summaries * sizeof(uint32_t) );
1063 out_track->track_ID = lsmash_create_track( output.root, in_track->media_param.handler_type );
1064 if( !out_track->track_ID )
1065 return TIMELINEEDITOR_ERR( "Failed to create a track.\n" );
1066 /* Copy track and media parameters except for track_ID. */
1067 out_track->track_param = in_track->track_param;
1068 out_track->media_param = in_track->media_param;
1069 out_track->track_param.track_ID = out_track->track_ID;
1070 if( lsmash_set_track_parameters( output.root, out_track->track_ID, &out_track->track_param ) )
1071 return TIMELINEEDITOR_ERR( "Failed to set track parameters.\n" );
1072 if( lsmash_set_media_parameters( output.root, out_track->track_ID, &out_track->media_param ) )
1073 return TIMELINEEDITOR_ERR( "Failed to set media parameters.\n" );
1074 uint32_t valid_summary_count = 0;
1075 for( uint32_t k = 0; k < in_track->num_summaries; k++ )
1077 if( !in_track->summaries[k].active )
1079 out_track->summary_remap[k] = 0;
1080 continue;
1082 lsmash_summary_t *summary = in_track->summaries[k].summary;
1083 if( lsmash_add_sample_entry( output.root, out_track->track_ID, summary ) == 0 )
1085 WARNING_MSG( "failed to append a summary.\n" );
1086 lsmash_cleanup_summary( summary );
1087 in_track->summaries[k].summary = NULL;
1088 in_track->summaries[k].active = 0;
1089 out_track->summary_remap[k] = 0;
1090 continue;
1092 out_track->summary_remap[k] = ++valid_summary_count;
1094 if( valid_summary_count == 0 )
1095 return TIMELINEEDITOR_ERR( "failed to append all summaries.\n" );
1096 out_track->last_sample_delta = in_track->last_sample_delta;
1097 out_track->current_sample_number = 1;
1098 out_track->reach_end_of_media_timeline = 0;
1100 /* Start muxing. */
1101 double largest_dts = 0;
1102 uint32_t num_consecutive_sample_skip = 0;
1103 uint32_t num_active_input_tracks = out_movie->num_tracks;
1104 uint64_t total_media_size = 0;
1105 uint8_t sample_count = 0;
1106 while( 1 )
1108 track_t *in_track = &in_movie->track[ in_movie->current_track_number - 1 ];
1109 /* Try append a sample in an input track where we didn't reach the end of media timeline. */
1110 if( !in_track->reach_end_of_media_timeline )
1112 track_t *out_track = &out_movie->track[ out_movie->current_track_number - 1 ];
1113 uint32_t in_track_ID = in_track->track_ID;
1114 uint32_t out_track_ID = out_track->track_ID;
1115 uint32_t input_media_timescale = in_track->media_param.timescale;
1116 /* Get a DTS from a track in an input movie. */
1117 uint64_t dts;
1118 if( lsmash_get_dts_from_media_timeline( input.root, in_track_ID, in_track->current_sample_number, &dts ) )
1120 if( lsmash_check_sample_existence_in_media_timeline( input.root, in_track_ID, in_track->current_sample_number ) )
1121 return TIMELINEEDITOR_ERR( "Failed to get the DTS.\n" );
1122 else
1124 in_track->reach_end_of_media_timeline = 1;
1125 if( --num_active_input_tracks == 0 )
1126 break; /* end of muxing */
1129 /* Get and append a sample if it's good time. */
1130 else if( ((double)dts / input_media_timescale) <= largest_dts
1131 || num_consecutive_sample_skip == num_active_input_tracks )
1133 /* Get an actual sample data from a track in an input movie. */
1134 lsmash_sample_t *sample = lsmash_get_sample_from_media_timeline( input.root, in_track_ID, in_track->current_sample_number );
1135 if( !sample )
1136 return TIMELINEEDITOR_ERR( "Failed to get sample.\n" );
1137 sample->index = sample->index > in_track->num_summaries ? in_track->num_summaries
1138 : sample->index == 0 ? 1
1139 : sample->index;
1140 sample->index = out_track->summary_remap[ sample->index - 1 ];
1141 if( sample->index )
1143 /* Append sample into output movie. */
1144 uint64_t sample_size = sample->length; /* sample will be deleted internally after appending. */
1145 if( lsmash_append_sample( output.root, out_track_ID, sample ) )
1147 lsmash_delete_sample( sample );
1148 return TIMELINEEDITOR_ERR( "Failed to append a sample.\n" );
1150 largest_dts = LSMASH_MAX( largest_dts, (double)dts / input_media_timescale );
1151 total_media_size += sample_size;
1152 ++ in_track->current_sample_number;
1153 num_consecutive_sample_skip = 0;
1154 /* Print, per 256 samples, total size of imported media. */
1155 if( ++sample_count == 0 )
1156 eprintf( "Importing: %"PRIu64" bytes\r", total_media_size );
1159 else
1160 ++num_consecutive_sample_skip; /* Skip appendig sample. */
1162 /* Move the next track. */
1163 if( ++ in_movie->current_track_number > in_movie->num_tracks )
1164 in_movie->current_track_number = 1; /* Back the first track. */
1165 if( ++ out_movie->current_track_number > out_movie->num_tracks )
1166 out_movie->current_track_number = 1; /* Back the first track in the output movie. */
1168 for( uint32_t i = 0; i < out_movie->num_tracks; i++ )
1169 if( lsmash_flush_pooled_samples( output.root, out_movie->track[i].track_ID, out_movie->track[i].last_sample_delta ) )
1170 return TIMELINEEDITOR_ERR( "Failed to flush samples.\n" );
1171 /* Copy timeline maps. */
1172 for( uint32_t i = 0; i < out_movie->num_tracks; i++ )
1173 if( lsmash_copy_timeline_map( output.root, out_movie->track[i].track_ID, input.root, in_movie->track[i].track_ID ) )
1174 return TIMELINEEDITOR_ERR( "Failed to copy a timeline map.\n" );
1175 /* Edit timeline map. */
1176 if( argc > 3 )
1178 track_t *out_track = &out_movie->track[ opt.track_number - 1 ];
1179 uint32_t track_ID = out_track->track_ID;
1180 uint32_t movie_timescale = lsmash_get_movie_timescale( output.root );
1181 uint32_t media_timescale = lsmash_get_media_timescale( output.root, track_ID );
1182 uint64_t empty_delay = timecode.empty_delay + (uint64_t)(opt.empty_delay * (1e-3 * media_timescale) + 0.5);
1183 uint64_t duration = timecode.duration + empty_delay;
1184 if( lsmash_delete_explicit_timeline_map( output.root, track_ID ) )
1185 return TIMELINEEDITOR_ERR( "Failed to delete explicit timeline maps.\n" );
1186 if( timecode.empty_delay )
1188 lsmash_edit_t empty_edit;
1189 empty_edit.duration = ((double)timecode.empty_delay / media_timescale) * movie_timescale;
1190 empty_edit.start_time = ISOM_EDIT_MODE_EMPTY;
1191 empty_edit.rate = ISOM_EDIT_MODE_NORMAL;
1192 if( lsmash_create_explicit_timeline_map( output.root, track_ID, empty_edit ) )
1193 return TIMELINEEDITOR_ERR( "Failed to create a empty duration.\n" );
1194 duration = ((double)duration / media_timescale) * movie_timescale;
1195 duration -= empty_edit.duration;
1197 else
1198 duration = ((double)duration / media_timescale) * movie_timescale;
1199 lsmash_edit_t edit;
1200 edit.duration = duration;
1201 edit.start_time = timecode.composition_delay + (uint64_t)(opt.skip_duration * (1e-3 * media_timescale) + 0.5);
1202 edit.rate = ISOM_EDIT_MODE_NORMAL;
1203 if( lsmash_create_explicit_timeline_map( output.root, track_ID, edit ) )
1204 return TIMELINEEDITOR_ERR( "Failed to create a explicit timeline map.\n" );
1206 /* Finish muxing. */
1207 lsmash_adhoc_remux_t moov_to_front;
1208 moov_to_front.func = moov_to_front_callback;
1209 moov_to_front.buffer_size = 4*1024*1024;
1210 moov_to_front.param = NULL;
1211 eprintf( " \r" );
1212 if( lsmash_finish_movie( output.root, &moov_to_front )
1213 || lsmash_write_lsmash_indicator( output.root ) )
1214 return TIMELINEEDITOR_ERR( "Failed to finish output movie.\n" );
1215 cleanup_root( io.input );
1216 cleanup_root( io.output );
1217 cleanup_timecode( io.timecode );
1218 eprintf( "Timeline editing completed! \n" );
1219 return 0;