1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2011-2014 L-SMASH project
6 * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *****************************************************************************/
21 /* This file is available under an ISC license. */
35 #define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b))
37 #define eprintf( ... ) fprintf( stderr, __VA_ARGS__ )
38 #define REFRESH_CONSOLE eprintf( " \r" )
43 lsmash_summary_t
*summary
;
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
;
56 lsmash_track_parameters_t track_param
;
57 lsmash_media_parameters_t media_param
;
62 lsmash_itunes_metadata_t
*itunes_metadata
;
64 lsmash_movie_parameters_t param
;
66 uint32_t num_itunes_metadata
;
67 uint32_t current_track_number
;
73 lsmash_file_parameters_t param
;
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
;
93 uint64_t composition_delay
;
101 timecode_t
*timecode
;
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
;
114 static void cleanup_root( root_t
*h
)
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
);
135 lsmash_free( metadata
->name
);
137 lsmash_freep( &movie
->itunes_metadata
);
140 lsmash_freep( &movie
->track
);
141 lsmash_close_file( &h
->file
.param
);
142 lsmash_destroy_root( h
->root
);
146 static void cleanup_timecode( timecode_t
*timecode
)
152 fclose( timecode
->file
);
153 timecode
->file
= NULL
;
156 lsmash_freep( &timecode
->ts
);
159 static int error_message( const char* message
, ... )
162 eprintf( "Error: " );
164 va_start( args
, message
);
165 vfprintf( stderr
, message
, args
);
170 static int warning_message( const char* message
, ... )
173 eprintf( "Warning: " );
175 va_start( args
, message
);
176 vfprintf( stderr
, message
, args
);
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
);
187 va_start( args
, message
);
188 error_message( message
, args
);
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
)
201 int dst_size
= strlen( src
) + 1;
202 char *dst
= lsmash_malloc( dst_size
);
205 memcpy( dst
, src
, dst_size
);
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
) )
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
) );
220 metadata
->meaning
= duplicate_string( shadow
.meaning
);
221 if( !metadata
->meaning
)
226 metadata
->name
= duplicate_string( shadow
.name
);
227 if( !metadata
->name
)
230 if( shadow
.type
== ITUNES_METADATA_TYPE_STRING
)
232 metadata
->value
.string
= duplicate_string( shadow
.value
.string
);
233 if( !metadata
->value
.string
)
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
)
241 memcpy( metadata
->value
.binary
.data
, shadow
.value
.binary
.data
, shadow
.value
.binary
.size
);
245 if( metadata
->meaning
)
246 lsmash_free( metadata
->meaning
);
248 lsmash_free( metadata
->name
);
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 );
266 WARNING_MSG( "failed to get a summary.\n" );
269 track
->summaries
[j
].summary
= summary
;
270 track
->summaries
[j
].active
= 1;
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();
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
);
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" );
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;
314 track_t
*track
= movie
->track
= lsmash_malloc( movie
->num_tracks
* sizeof(track_t
) );
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" );
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" );
338 if( lsmash_construct_timeline( input
->root
, track
[i
].track_ID
) )
340 WARNING_MSG( "failed to construct timeline.\n" );
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" );
348 if( get_summaries( input
, &track
[i
] ) )
350 WARNING_MSG( "failed to get valid summaries.\n" );
354 track
[i
].current_sample_number
= 1;
356 lsmash_destroy_children( lsmash_file_as_box( in_file
->fh
) );
360 static inline uint64_t get_gcd( uint64_t a
, uint64_t b
)
374 static inline uint64_t get_lcm( uint64_t a
, uint64_t b
)
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
);
391 static inline double sigexp10( double value
, double *exponent
)
393 /* This function separates significand and exp10 from double floating point. */
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
)
415 uint64_t fps_num
, fps_den
;
417 double fps_sig
= sigexp10( fps
, &exponent
);
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
)
429 if( timecode
->auto_media_timescale
)
431 timecode
->media_timescale
= timecode
->media_timescale
432 ? get_lcm( timecode
->media_timescale
, 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
++ )
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
)
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" );
460 static int parse_timecode( timecode_t
*timecode
, uint32_t sample_count
)
462 #define FAILED_PARSE_TIMECODE( ... ) \
465 lsmash_free( fps_array ); \
466 lsmash_free( timecode_array ); \
467 return ERROR_MSG( __VA_ARGS__ ); \
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" );
475 double *timecode_array
= NULL
;
478 double assume_fps
= 0;
479 /* Get assumed framerate. */
480 while( fgets( buff
, sizeof(buff
), timecode
->file
) )
482 if( SKIP_LINE_CHARACTER( buff
[0] ) )
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" );
489 if( assume_fps
<= 0 )
490 return ERROR_MSG( "Invalid assumed fps\n" );
491 int64_t file_pos
= lsmash_ftell( timecode
->file
);
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;
497 int64_t prev_start
= -1, prev_end
= -1;
499 while( fgets( buff
, sizeof(buff
), timecode
->file
) )
501 if( SKIP_LINE_CHARACTER( buff
[0] ) )
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" );
510 if( timecode
->auto_media_timescale
|| timecode
->auto_media_timebase
)
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;
527 while( i
< sample_count
- 1 && fgets( buff
, sizeof(buff
), timecode
->file
) )
529 if( SKIP_LINE_CHARACTER( buff
[0] ) )
531 ret
= sscanf( buff
, "%"SCNd64
",%"SCNd64
",%lf", &start
, &end
, &sequence_fps
);
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
)
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] ) )
566 ret
= sscanf( buff
, "%"SCNd64
",%"SCNd64
",%lf", &start
, &end
, &sequence_fps
);
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
);
583 uint32_t num_timecodes
= 0;
584 int64_t file_pos
= lsmash_ftell( timecode
->file
);
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] ) )
593 file_pos
= lsmash_ftell( timecode
->file
);
595 return ERROR_MSG( "Failed to tell the postion of input timecode file.\n" );
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" );
611 if( fgets( buff
, sizeof(buff
), timecode
->file
) )
613 ret
= sscanf( buff
, "%lf", &timecode_array
[0] );
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] ) )
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" );
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) );
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
)
651 uint64_t fps_num
, fps_den
;
653 double fps_sig
= sigexp10( fps_array
[i
], &exponent
);
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
)
663 timecode
->media_timescale
= fps_num
&& timecode
->media_timescale
664 ? get_lcm( timecode
->media_timescale
, 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) );
698 lsmash_free( timecode_array
);
699 return ERROR_MSG( "Failed to allocate timestamps.\n" );
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
);
710 return ERROR_MSG( "Invalid timecode.\n" );
713 lsmash_free( timecode_array
);
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
)
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
);
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
;
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
);
750 /* Reduce timescale and timebase. */
751 timescale
= in_track
->media_param
.timescale
;
752 uint64_t reduce
= get_gcd( timescale
, timebase
);
755 timebase_convert_multiplier
= 1;
757 /* Parse 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) );
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" );
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
;
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
];
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
);
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
);
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
,
877 for( int i
= 0; brand_white_list
[i
]; i
++ )
878 if( brand
== brand_white_list
[i
] )
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 );
889 static void display_version( void )
892 "L-SMASH isom/mov timeline editor rev%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 )
902 "Usage: timelineeditor [options] input output\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
[] )
923 else if( !strcasecmp( argv
[1], "-h" ) || !strcasecmp( argv
[1], "--help" ) )
928 else if( !strcasecmp( argv
[1], "-v" ) || !strcasecmp( argv
[1], "--version" ) )
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 };
944 lsmash_get_mainargs( &argc
, &argv
);
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" );
955 else if( !strcasecmp( argv
[argn
], "--timecode" ) )
957 timecode
.file
= lsmash_fopen( argv
[++argn
], "rb" );
959 return TIMELINEEDITOR_ERR( "Failed to open timecode file.\n" );
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" );
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" );
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" );
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" );
990 else if( !strcasecmp( argv
[argn
], "--dts-compression" ) )
992 opt
.dts_compression
= 1;
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();
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;
1027 for( i
= 0; i
< out_file
->param
.brand_count
; i
++ )
1028 if( out_file
->param
.brands
[i
] == ISOM_BRAND_TYPE_MP42
)
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
);
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" );
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
;
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;
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;
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;
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;
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. */
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" );
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
);
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
1159 sample
->index
= out_track
->summary_remap
[ sample
->index
- 1 ];
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
);
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. */
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
;
1217 duration
= ((double)duration
/ media_timescale
) * movie_timescale
;
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
;
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" );