1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2011-2015 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. */
32 #define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b))
34 #define eprintf( ... ) fprintf( stderr, __VA_ARGS__ )
35 #define REFRESH_CONSOLE eprintf( " \r" )
40 lsmash_summary_t
*summary
;
47 uint32_t last_sample_delta
;
48 uint32_t current_sample_number
;
49 int reach_end_of_media_timeline
;
50 uint32_t *summary_remap
;
51 uint32_t num_summaries
;
53 lsmash_track_parameters_t track_param
;
54 lsmash_media_parameters_t media_param
;
59 lsmash_itunes_metadata_t
*itunes_metadata
;
61 lsmash_movie_parameters_t param
;
63 uint32_t num_itunes_metadata
;
64 uint32_t current_track_number
;
70 lsmash_file_parameters_t param
;
84 uint32_t sample_count
;
85 int auto_media_timescale
;
86 int auto_media_timebase
;
87 uint64_t media_timescale
;
88 uint64_t media_timebase
;
90 uint64_t composition_delay
;
103 uint32_t track_number
;
104 uint32_t media_timescale
;
105 uint32_t media_timebase
;
106 uint32_t skip_duration
;
107 uint32_t empty_delay
;
111 static void cleanup_root( root_t
*h
)
115 movie_t
*movie
= &h
->file
.movie
;
116 if( movie
->itunes_metadata
)
118 for( uint32_t i
= 0; i
< movie
->num_itunes_metadata
; i
++ )
120 lsmash_itunes_metadata_t
*metadata
= &movie
->itunes_metadata
[i
];
121 if( metadata
->type
== ITUNES_METADATA_TYPE_STRING
)
123 if( metadata
->value
.string
)
124 lsmash_free( metadata
->value
.string
);
126 else if( metadata
->type
== ITUNES_METADATA_TYPE_BINARY
)
127 if( metadata
->value
.binary
.data
)
128 lsmash_free( metadata
->value
.binary
.data
);
129 if( metadata
->meaning
)
130 lsmash_free( metadata
->meaning
);
132 lsmash_free( metadata
->name
);
134 lsmash_freep( &movie
->itunes_metadata
);
137 lsmash_freep( &movie
->track
);
138 lsmash_close_file( &h
->file
.param
);
139 lsmash_destroy_root( h
->root
);
143 static void cleanup_timecode( timecode_t
*timecode
)
149 fclose( timecode
->file
);
150 timecode
->file
= NULL
;
153 lsmash_freep( &timecode
->ts
);
156 static int error_message( const char* message
, ... )
159 eprintf( "Error: " );
161 va_start( args
, message
);
162 vfprintf( stderr
, message
, args
);
167 static int warning_message( const char* message
, ... )
170 eprintf( "Warning: " );
172 va_start( args
, message
);
173 vfprintf( stderr
, message
, args
);
178 static int timelineeditor_error( movie_io_t
*io
, const char *message
, ... )
180 cleanup_root( io
->input
);
181 cleanup_root( io
->output
);
182 cleanup_timecode( io
->timecode
);
184 va_start( args
, message
);
185 error_message( message
, args
);
190 #define TIMELINEEDITOR_ERR( ... ) timelineeditor_error( &io, __VA_ARGS__ )
191 #define ERROR_MSG( ... ) error_message( __VA_ARGS__ )
192 #define WARNING_MSG( ... ) warning_message( __VA_ARGS__ )
194 static char *duplicate_string( char *src
)
198 int dst_size
= strlen( src
) + 1;
199 char *dst
= lsmash_malloc( dst_size
);
202 memcpy( dst
, src
, dst_size
);
206 static int get_itunes_metadata( lsmash_root_t
*root
, uint32_t metadata_number
, lsmash_itunes_metadata_t
*metadata
)
208 memset( metadata
, 0, sizeof(lsmash_itunes_metadata_t
) );
209 if( lsmash_get_itunes_metadata( root
, metadata_number
, metadata
) )
211 lsmash_itunes_metadata_t shadow
= *metadata
;
212 metadata
->meaning
= NULL
;
213 metadata
->name
= NULL
;
214 memset( &metadata
->value
, 0, sizeof(lsmash_itunes_metadata_value_t
) );
217 metadata
->meaning
= duplicate_string( shadow
.meaning
);
218 if( !metadata
->meaning
)
223 metadata
->name
= duplicate_string( shadow
.name
);
224 if( !metadata
->name
)
227 if( shadow
.type
== ITUNES_METADATA_TYPE_STRING
)
229 metadata
->value
.string
= duplicate_string( shadow
.value
.string
);
230 if( !metadata
->value
.string
)
233 else if( shadow
.type
== ITUNES_METADATA_TYPE_BINARY
)
235 metadata
->value
.binary
.data
= lsmash_malloc( shadow
.value
.binary
.size
);
236 if( !metadata
->value
.binary
.data
)
238 memcpy( metadata
->value
.binary
.data
, shadow
.value
.binary
.data
, shadow
.value
.binary
.size
);
242 if( metadata
->meaning
)
243 lsmash_free( metadata
->meaning
);
245 lsmash_free( metadata
->name
);
249 static int get_summaries( root_t
*input
, track_t
*track
)
251 track
->num_summaries
= lsmash_count_summary( input
->root
, track
->track_ID
);
252 if( track
->num_summaries
== 0 )
253 return ERROR_MSG( "Failed to get find valid summaries.\n" );
254 track
->summaries
= lsmash_malloc( track
->num_summaries
* sizeof(summary_t
) );
255 if( !track
->summaries
)
256 return ERROR_MSG( "failed to alloc input summaries.\n" );
257 memset( track
->summaries
, 0, track
->num_summaries
* sizeof(summary_t
) );
258 for( uint32_t j
= 0; j
< track
->num_summaries
; j
++ )
260 lsmash_summary_t
*summary
= lsmash_get_summary( input
->root
, track
->track_ID
, j
+ 1 );
263 WARNING_MSG( "failed to get a summary.\n" );
266 track
->summaries
[j
].summary
= summary
;
267 track
->summaries
[j
].active
= 1;
272 static int get_movie( root_t
*input
, char *input_name
)
274 if( !strcmp( input_name
, "-" ) )
275 return ERROR_MSG( "Standard input not supported.\n" );
276 input
->root
= lsmash_create_root();
278 return ERROR_MSG( "failed to create a ROOT for an input file.\n" );
279 file_t
*in_file
= &input
->file
;
280 if( lsmash_open_file( input_name
, 1, &in_file
->param
) < 0 )
281 return ERROR_MSG( "failed to open an input file.\n" );
282 in_file
->fh
= lsmash_set_file( input
->root
, &in_file
->param
);
284 return ERROR_MSG( "failed to add an input file into a ROOT.\n" );
285 if( lsmash_read_file( in_file
->fh
, &in_file
->param
) < 0 )
286 return ERROR_MSG( "failed to read an input file\n" );
287 movie_t
*movie
= &in_file
->movie
;
288 movie
->num_itunes_metadata
= lsmash_count_itunes_metadata( input
->root
);
289 if( movie
->num_itunes_metadata
)
291 movie
->itunes_metadata
= lsmash_malloc( movie
->num_itunes_metadata
* sizeof(lsmash_itunes_metadata_t
) );
292 if( !movie
->itunes_metadata
)
293 return ERROR_MSG( "failed to alloc iTunes metadata.\n" );
294 uint32_t itunes_metadata_count
= 0;
295 for( uint32_t i
= 1; i
<= movie
->num_itunes_metadata
; i
++ )
297 if( get_itunes_metadata( input
->root
, i
, &movie
->itunes_metadata
[itunes_metadata_count
] ) )
299 WARNING_MSG( "failed to get an iTunes metadata.\n" );
302 ++itunes_metadata_count
;
304 movie
->num_itunes_metadata
= itunes_metadata_count
;
306 lsmash_initialize_movie_parameters( &movie
->param
);
307 lsmash_get_movie_parameters( input
->root
, &movie
->param
);
308 movie
->num_tracks
= movie
->param
.number_of_tracks
;
309 movie
->current_track_number
= 1;
311 track_t
*track
= movie
->track
= lsmash_malloc( movie
->num_tracks
* sizeof(track_t
) );
313 return ERROR_MSG( "Failed to alloc input tracks.\n" );
314 memset( track
, 0, movie
->num_tracks
* sizeof(track_t
) );
315 for( uint32_t i
= 0; i
< movie
->num_tracks
; i
++ )
317 track
[i
].track_ID
= lsmash_get_track_ID( input
->root
, i
+ 1 );
318 if( !track
[i
].track_ID
)
319 return ERROR_MSG( "Failed to get track_ID.\n" );
321 for( uint32_t i
= 0; i
< movie
->num_tracks
; i
++ )
323 lsmash_initialize_track_parameters( &track
[i
].track_param
);
324 if( lsmash_get_track_parameters( input
->root
, track
[i
].track_ID
, &track
[i
].track_param
) )
326 WARNING_MSG( "failed to get track parameters.\n" );
329 lsmash_initialize_media_parameters( &track
[i
].media_param
);
330 if( lsmash_get_media_parameters( input
->root
, track
[i
].track_ID
, &track
[i
].media_param
) )
332 WARNING_MSG( "failed to get media parameters.\n" );
335 if( lsmash_construct_timeline( input
->root
, track
[i
].track_ID
) )
337 WARNING_MSG( "failed to construct timeline.\n" );
340 if( lsmash_get_last_sample_delta_from_media_timeline( input
->root
, track
[i
].track_ID
, &track
[i
].last_sample_delta
) )
342 WARNING_MSG( "failed to get the last sample delta.\n" );
345 if( get_summaries( input
, &track
[i
] ) )
347 WARNING_MSG( "failed to get valid summaries.\n" );
351 track
[i
].current_sample_number
= 1;
353 lsmash_destroy_children( lsmash_file_as_box( in_file
->fh
) );
357 static inline uint64_t get_gcd( uint64_t a
, uint64_t b
)
371 static inline uint64_t get_lcm( uint64_t a
, uint64_t b
)
375 return (a
/ get_gcd( a
, b
)) * b
;
378 static uint64_t get_media_timebase( lsmash_media_ts_list_t
*ts_list
)
380 uint64_t timebase
= ts_list
->timestamp
[0].cts
;
381 for( uint32_t i
= 1; i
< ts_list
->sample_count
; i
++ )
382 timebase
= get_gcd( timebase
, ts_list
->timestamp
[i
].cts
);
383 for( uint32_t i
= 0; i
< ts_list
->sample_count
; i
++ )
384 timebase
= get_gcd( timebase
, ts_list
->timestamp
[i
].dts
);
388 static inline double sigexp10( double value
, double *exponent
)
390 /* This function separates significand and exp10 from double floating point. */
405 #define DOUBLE_EPSILON 5e-6
406 #define MATROSKA_TIMESCALE 1000000000
407 #define SKIP_LINE_CHARACTER( x ) ((x) == '#' || (x) == '\n' || (x) == '\r')
409 static double correct_fps( double fps
, timecode_t
*timecode
)
412 uint64_t fps_num
, fps_den
;
414 double fps_sig
= sigexp10( fps
, &exponent
);
417 fps_den
= i
* timecode
->media_timebase
;
418 fps_num
= round( fps_den
* fps_sig
) * exponent
;
419 if( fps_num
> UINT32_MAX
)
420 return ERROR_MSG( "framerate correction failed.\n"
421 "Specify an appropriate timebase manually or remake timecode file.\n" );
422 if( fabs( ((double)fps_num
/ fps_den
) / exponent
- fps_sig
) < DOUBLE_EPSILON
)
426 if( timecode
->auto_media_timescale
)
428 timecode
->media_timescale
= timecode
->media_timescale
429 ? get_lcm( timecode
->media_timescale
, fps_num
)
431 if( timecode
->media_timescale
> UINT32_MAX
)
432 timecode
->auto_media_timescale
= 0;
434 return (double)fps_num
/ fps_den
;
437 static int try_matroska_timescale( double *fps_array
, timecode_t
*timecode
, uint32_t num_loops
)
439 timecode
->media_timebase
= 0;
440 timecode
->media_timescale
= MATROSKA_TIMESCALE
;
441 for( uint32_t i
= 0; i
< num_loops
; i
++ )
445 double fps_sig
= sigexp10( fps_array
[i
], &exponent
);
446 fps_den
= round( MATROSKA_TIMESCALE
/ fps_sig
) / exponent
;
447 timecode
->media_timebase
= fps_den
&& timecode
->media_timebase
448 ? get_gcd( timecode
->media_timebase
, fps_den
)
450 if( timecode
->media_timebase
> UINT32_MAX
|| !timecode
->media_timebase
)
451 return ERROR_MSG( "Automatic media timescale generation failed.\n"
452 "Specify media timescale manually.\n" );
457 static int parse_timecode( timecode_t
*timecode
, uint32_t sample_count
)
459 #define FAILED_PARSE_TIMECODE( ... ) \
462 lsmash_free( fps_array ); \
463 lsmash_free( timecode_array ); \
464 return ERROR_MSG( __VA_ARGS__ ); \
468 int ret
= fscanf( timecode
->file
, "# timecode format v%d", &tcfv
);
469 if( ret
!= 1 || (tcfv
!= 1 && tcfv
!= 2) )
470 return ERROR_MSG( "Unsupported timecode format\n" );
472 double *timecode_array
= NULL
;
475 double assume_fps
= 0;
476 /* Get assumed framerate. */
477 while( fgets( buff
, sizeof(buff
), timecode
->file
) )
479 if( SKIP_LINE_CHARACTER( buff
[0] ) )
481 if( sscanf( buff
, "assume %lf", &assume_fps
) != 1
482 && sscanf( buff
, "Assume %lf", &assume_fps
) != 1 )
483 return ERROR_MSG( "Assumed fps not found\n" );
486 if( assume_fps
<= 0 )
487 return ERROR_MSG( "Invalid assumed fps\n" );
488 int64_t file_pos
= lsmash_ftell( timecode
->file
);
490 return ERROR_MSG( "Failed to tell the postion of input timecode file.\n" );
491 /* Check whether valid or not and count number of sequences. */
492 uint32_t num_sequences
= 0;
494 int64_t prev_start
= -1, prev_end
= -1;
496 while( fgets( buff
, sizeof(buff
), timecode
->file
) )
498 if( SKIP_LINE_CHARACTER( buff
[0] ) )
500 ret
= sscanf( buff
, "%"SCNd64
",%"SCNd64
",%lf", &start
, &end
, &sequence_fps
);
501 if( ret
!= 3 && ret
!= EOF
)
502 return ERROR_MSG( "Invalid input timecode file\n" );
503 if( start
> end
|| start
<= prev_start
|| end
<= prev_end
|| sequence_fps
<= 0 )
504 return ERROR_MSG( "Invalid input timecode file\n" );
507 if( timecode
->auto_media_timescale
|| timecode
->auto_media_timebase
)
510 if( lsmash_fseek( timecode
->file
, file_pos
, SEEK_SET
) != 0 )
511 return ERROR_MSG( "Failed to seek input timecode file.\n" );
512 /* Preparation storing timecodes. */
513 double *fps_array
= lsmash_malloc( ((timecode
->auto_media_timescale
|| timecode
->auto_media_timebase
) * num_sequences
+ 1) * sizeof(double) );
515 return ERROR_MSG( "Failed to allocate fps array\n" );
516 double corrected_assume_fps
= correct_fps( assume_fps
, timecode
);
517 if( corrected_assume_fps
< 0 )
518 FAILED_PARSE_TIMECODE( "Failed to correct the assumed framerate\n" );
519 timecode_array
= lsmash_malloc( sample_count
* sizeof(double) );
520 if( !timecode_array
)
521 FAILED_PARSE_TIMECODE( "Failed to alloc timecodes\n" );
522 timecode_array
[0] = 0;
525 while( i
< sample_count
- 1 && fgets( buff
, sizeof(buff
), timecode
->file
) )
527 if( SKIP_LINE_CHARACTER( buff
[0] ) )
529 ret
= sscanf( buff
, "%"SCNd64
",%"SCNd64
",%lf", &start
, &end
, &sequence_fps
);
531 start
= end
= sample_count
- 1;
532 for( ; i
< start
&& i
< sample_count
- 1; i
++ )
533 timecode_array
[i
+ 1] = timecode_array
[i
] + 1 / corrected_assume_fps
;
534 if( i
< sample_count
- 1 )
536 if( timecode
->auto_media_timescale
|| timecode
->auto_media_timebase
)
537 fps_array
[num_sequences
++] = sequence_fps
;
538 sequence_fps
= correct_fps( sequence_fps
, timecode
);
539 if( sequence_fps
< 0 )
540 FAILED_PARSE_TIMECODE( "Failed to correct the framerate of a sequence.\n" );
541 for( i
= start
; i
<= end
&& i
< sample_count
- 1; i
++ )
542 timecode_array
[i
+ 1] = timecode_array
[i
] + 1 / sequence_fps
;
545 for( ; i
< sample_count
- 1; i
++ )
546 timecode_array
[i
+ 1] = timecode_array
[i
] + 1 / corrected_assume_fps
;
547 if( timecode
->auto_media_timescale
|| timecode
->auto_media_timebase
)
548 fps_array
[num_sequences
] = assume_fps
;
549 /* Assume matroska timebase if automatic timescale generation isn't done yet. */
550 if( timecode
->auto_media_timebase
&& !timecode
->auto_media_timescale
)
553 double assume_fps_sig
, sequence_fps_sig
;
554 if( try_matroska_timescale( fps_array
, timecode
, num_sequences
+ 1 ) < 0 )
555 FAILED_PARSE_TIMECODE( "Failed to try matroska timescale.\n" );
556 if( lsmash_fseek( timecode
->file
, file_pos
, SEEK_SET
) != 0 )
557 FAILED_PARSE_TIMECODE( "Failed to seek input timecode file.\n" );
558 assume_fps_sig
= sigexp10( assume_fps
, &exponent
);
559 corrected_assume_fps
= MATROSKA_TIMESCALE
/ ( round( MATROSKA_TIMESCALE
/ assume_fps_sig
) / exponent
);
560 for( i
= 0; i
< sample_count
- 1 && fgets( buff
, sizeof(buff
), timecode
->file
); )
562 if( SKIP_LINE_CHARACTER( buff
[0] ) )
564 ret
= sscanf( buff
, "%"SCNd64
",%"SCNd64
",%lf", &start
, &end
, &sequence_fps
);
566 start
= end
= sample_count
- 1;
567 sequence_fps_sig
= sigexp10( sequence_fps
, &exponent
);
568 sequence_fps
= MATROSKA_TIMESCALE
/ ( round( MATROSKA_TIMESCALE
/ sequence_fps_sig
) / exponent
);
569 for( ; i
< start
&& i
< sample_count
- 1; i
++ )
570 timecode_array
[i
+ 1] = timecode_array
[i
] + 1 / corrected_assume_fps
;
571 for( i
= start
; i
<= end
&& i
< sample_count
- 1; i
++ )
572 timecode_array
[i
+ 1] = timecode_array
[i
] + 1 / sequence_fps
;
574 for( ; i
< sample_count
- 1; i
++ )
575 timecode_array
[i
+ 1] = timecode_array
[i
] + 1 / corrected_assume_fps
;
577 lsmash_free( fps_array
);
581 uint32_t num_timecodes
= 0;
582 int64_t file_pos
= lsmash_ftell( timecode
->file
);
584 return ERROR_MSG( "Failed to tell the postion of input timecode file.\n" );
585 while( fgets( buff
, sizeof(buff
), timecode
->file
) )
587 if( SKIP_LINE_CHARACTER( buff
[0] ) )
591 file_pos
= lsmash_ftell( timecode
->file
);
593 return ERROR_MSG( "Failed to tell the postion of input timecode file.\n" );
600 return ERROR_MSG( "No timecodes!\n" );
601 if( sample_count
> num_timecodes
)
602 return ERROR_MSG( "Lack number of timecodes.\n" );
603 if( lsmash_fseek( timecode
->file
, file_pos
, SEEK_SET
) != 0 )
604 return ERROR_MSG( "Failed to seek input timecode file.\n" );
605 timecode_array
= lsmash_malloc( sample_count
* sizeof(uint64_t) );
606 if( !timecode_array
)
607 return ERROR_MSG( "Failed to alloc timecodes.\n" );
609 if( fgets( buff
, sizeof(buff
), timecode
->file
) )
611 ret
= sscanf( buff
, "%lf", &timecode_array
[0] );
614 lsmash_free( timecode_array
);
615 return ERROR_MSG( "Invalid timecode number: 0\n" );
617 timecode_array
[i
++] *= 1e-3; /* Timescale of timecode format v2 is 1000. */
618 while( i
< sample_count
&& fgets( buff
, sizeof(buff
), timecode
->file
) )
620 if( SKIP_LINE_CHARACTER( buff
[0] ) )
622 ret
= sscanf( buff
, "%lf", &timecode_array
[i
] );
623 timecode_array
[i
] *= 1e-3; /* Timescale of timecode format v2 is 1000. */
624 if( ret
!= 1 || timecode_array
[i
] <= timecode_array
[i
- 1] )
626 lsmash_free( timecode_array
);
627 return ERROR_MSG( "Invalid input timecode.\n" );
632 if( i
< sample_count
)
634 lsmash_free( timecode_array
);
635 return ERROR_MSG( "Failed to get timecodes.\n" );
637 /* Generate media timescale automatically if needed. */
638 if( sample_count
!= 1 && timecode
->auto_media_timescale
)
640 double *fps_array
= lsmash_malloc( (sample_count
- 1) * sizeof(double) );
642 FAILED_PARSE_TIMECODE( "Failed to allocate fps array\n" );
643 for( i
= 0; i
< sample_count
- 1; i
++ )
645 fps_array
[i
] = 1 / (timecode_array
[i
+ 1] - timecode_array
[i
]);
646 if( timecode
->auto_media_timescale
)
649 uint64_t fps_num
, fps_den
;
651 double fps_sig
= sigexp10( fps_array
[i
], &exponent
);
654 fps_den
= j
* timecode
->media_timebase
;
655 fps_num
= round( fps_den
* fps_sig
) * exponent
;
656 if( fps_num
> UINT32_MAX
657 || fabs( ((double)fps_num
/ fps_den
) / exponent
- fps_sig
) < DOUBLE_EPSILON
)
661 timecode
->media_timescale
= fps_num
&& timecode
->media_timescale
662 ? get_lcm( timecode
->media_timescale
, fps_num
)
664 if( timecode
->media_timescale
> UINT32_MAX
)
666 timecode
->auto_media_timescale
= 0;
667 continue; /* Don't break because all framerate is needed for try_matroska_timescale. */
671 if( timecode
->auto_media_timebase
&& !timecode
->auto_media_timescale
672 && try_matroska_timescale( fps_array
, timecode
, sample_count
- 1 ) < 0 )
673 FAILED_PARSE_TIMECODE( "Failed to try matroska timescale.\n" );
674 lsmash_free( fps_array
);
677 if( timecode
->auto_media_timescale
|| timecode
->auto_media_timebase
)
679 uint64_t reduce
= get_gcd( timecode
->media_timebase
, timecode
->media_timescale
);
680 timecode
->media_timebase
/= reduce
;
681 timecode
->media_timescale
/= reduce
;
683 else if( timecode
->media_timescale
> UINT32_MAX
|| !timecode
->media_timescale
)
685 lsmash_free( timecode_array
);
686 return ERROR_MSG( "Failed to generate media timescale automatically.\n"
687 "Specify an appropriate media timescale manually.\n" );
689 uint32_t timescale
= timecode
->media_timescale
;
690 uint32_t timebase
= timecode
->media_timebase
;
691 double delay_tc
= timecode_array
[0];
692 timecode
->empty_delay
= ((uint64_t)(delay_tc
* ((double)timescale
/ timebase
) + 0.5)) * timebase
;
693 timecode
->ts
= lsmash_malloc( sample_count
* sizeof(uint64_t) );
696 lsmash_free( timecode_array
);
697 return ERROR_MSG( "Failed to allocate timestamps.\n" );
700 for( uint32_t i
= 1; i
< sample_count
; i
++ )
702 timecode
->ts
[i
] = ((uint64_t)((timecode_array
[i
] - delay_tc
) * ((double)timescale
/ timebase
) + 0.5)) * timebase
;
703 if( timecode
->ts
[i
] <= timecode
->ts
[i
- 1] )
705 lsmash_free( timecode_array
);
706 lsmash_free( timecode
->ts
);
708 return ERROR_MSG( "Invalid timecode.\n" );
711 lsmash_free( timecode_array
);
713 #undef FAILED_PARSE_TIMECODE
716 #undef DOUBLE_EPSILON
717 #undef MATROSKA_TIMESCALE
718 #undef SKIP_LINE_CHARACTER
720 static int edit_media_timeline( root_t
*input
, timecode_t
*timecode
, opt_t
*opt
)
722 if( !timecode
->file
&& !opt
->media_timescale
&& !opt
->media_timebase
&& !opt
->dts_compression
)
724 track_t
*in_track
= &input
->file
.movie
.track
[opt
->track_number
- 1];
725 uint32_t track_ID
= in_track
->track_ID
;
726 lsmash_media_ts_list_t ts_list
;
727 if( lsmash_get_media_timestamps( input
->root
, track_ID
, &ts_list
) )
728 return ERROR_MSG( "Failed to get media timestamps.\n" );
729 uint64_t timebase
= get_media_timebase( &ts_list
);
731 return ERROR_MSG( "Failed to get media timebase.\n" );
732 lsmash_media_ts_t
*timestamp
= ts_list
.timestamp
;
733 uint32_t sample_count
= ts_list
.sample_count
;
734 uint32_t orig_timebase
= timebase
;
736 double timebase_convert_multiplier
;
737 if( opt
->media_timescale
|| opt
->media_timebase
)
739 uint32_t orig_timescale
= in_track
->media_param
.timescale
;
740 timescale
= opt
->media_timescale
? opt
->media_timescale
: orig_timescale
;
741 timebase
= opt
->media_timebase
? opt
->media_timebase
: orig_timebase
;
742 if( !opt
->media_timescale
&& opt
->media_timebase
&& (timebase
> orig_timebase
) )
743 timescale
= timescale
* ((double)timebase
/ orig_timebase
) + 0.5;
744 timebase_convert_multiplier
= ((double)timescale
/ orig_timescale
) * ((double)orig_timebase
/ timebase
);
748 /* Reduce timescale and timebase. */
749 timescale
= in_track
->media_param
.timescale
;
750 uint64_t reduce
= get_gcd( timescale
, timebase
);
753 timebase_convert_multiplier
= 1;
755 /* Parse timecode file. */
758 timecode
->auto_media_timescale
= !opt
->media_timescale
;
759 timecode
->auto_media_timebase
= !opt
->media_timebase
;
760 timecode
->media_timescale
= timecode
->auto_media_timescale
? 0 : timescale
;
761 timecode
->media_timebase
= timebase
;
762 if( parse_timecode( timecode
, sample_count
) )
763 return ERROR_MSG( "Failed to parse timecode file.\n" );
764 timescale
= timecode
->media_timescale
;
765 timebase
= timecode
->media_timebase
;
767 /* Get maximum composition sample delay for DTS generation. */
768 uint32_t sample_delay
;
769 if( lsmash_get_max_sample_delay( &ts_list
, &sample_delay
) )
770 return ERROR_MSG( "Failed to get maximum composition sample delay.\n" );
771 if( sample_delay
) /* Reorder composition order. */
772 lsmash_sort_timestamps_composition_order( &ts_list
);
773 if( !timecode
->file
)
775 /* Genarate timestamps timescale converted. */
776 timecode
->ts
= lsmash_malloc( sample_count
* sizeof(uint64_t) );
778 return ERROR_MSG( "Failed to alloc timestamps\n" );
779 for( uint32_t i
= 0; i
< sample_count
; i
++ )
781 timecode
->ts
[i
] = (timestamp
[i
].cts
- timestamp
[0].cts
) / orig_timebase
;
782 timecode
->ts
[i
] = ((uint64_t)(timecode
->ts
[i
] * timebase_convert_multiplier
+ 0.5)) * timebase
;
783 if( i
&& (timecode
->ts
[i
] <= timecode
->ts
[i
- 1]) )
784 return ERROR_MSG( "Invalid timescale conversion.\n" );
789 /* If media timescale is specified, disable DTS compression multiplier. */
790 uint32_t dts_compression_multiplier
= opt
->dts_compression
* !opt
->media_timescale
* sample_delay
+ 1;
791 uint64_t initial_delta
= timecode
->ts
[1];
792 timescale
*= dts_compression_multiplier
;
793 if( dts_compression_multiplier
> 1 )
794 for( uint32_t i
= 0; i
< sample_count
; i
++ )
795 timecode
->ts
[i
] *= dts_compression_multiplier
;
797 uint64_t sample_delay_time
= timecode
->composition_delay
= opt
->dts_compression
? 0 : timecode
->ts
[sample_delay
];
798 for( uint32_t i
= 0; i
< sample_count
; i
++ )
799 timestamp
[i
].cts
= timecode
->ts
[i
] + sample_delay_time
;
800 /* Reorder decode order and generate new DTS from CTS. */
801 lsmash_sort_timestamps_decoding_order( &ts_list
);
802 uint64_t *prev_reordered_cts
= lsmash_malloc( sample_delay
* sizeof(uint64_t) );
803 if( !prev_reordered_cts
)
804 return ERROR_MSG( "Failed to allocate the previous reordered CTS array.\n" );
805 for( uint32_t i
= 0; i
<= sample_delay
; i
++ )
807 if( !opt
->dts_compression
)
808 timestamp
[i
].dts
= timecode
->ts
[i
];
811 timestamp
[i
].dts
= (i
* initial_delta
) / (!!opt
->media_timescale
* sample_delay
+ 1);
812 if( i
&& (timestamp
[i
].dts
<= timestamp
[i
- 1].dts
) )
814 lsmash_free( prev_reordered_cts
);
815 return ERROR_MSG( "Failed to do DTS compression.\n" );
818 prev_reordered_cts
[ i
% sample_delay
] = timecode
->ts
[i
] + sample_delay_time
;
820 for( uint32_t i
= sample_delay
+ 1; i
< sample_count
; i
++ )
822 timestamp
[i
].dts
= prev_reordered_cts
[ (i
- sample_delay
) % sample_delay
];
823 prev_reordered_cts
[ i
% sample_delay
] = timecode
->ts
[i
] + sample_delay_time
;
825 lsmash_free( prev_reordered_cts
);
828 for( uint32_t i
= 0; i
< sample_count
; i
++ )
829 timestamp
[i
].cts
= timestamp
[i
].dts
= timecode
->ts
[i
];
830 if( sample_count
> 1 )
832 in_track
->last_sample_delta
= timecode
->ts
[sample_count
- 1] - timecode
->ts
[sample_count
- 2];
833 timecode
->duration
= timecode
->ts
[sample_count
- 1] + in_track
->last_sample_delta
;
835 else /* still image */
836 timecode
->duration
= in_track
->last_sample_delta
= UINT32_MAX
;
837 in_track
->media_param
.timescale
= timescale
;
838 if( lsmash_set_media_timestamps( input
->root
, track_ID
, &ts_list
) )
839 return ERROR_MSG( "Failed to set media timestamps.\n" );
840 lsmash_delete_media_timestamps( &ts_list
);
844 static int check_white_brand( lsmash_brand_type brand
)
846 static const lsmash_brand_type brand_white_list
[] =
848 ISOM_BRAND_TYPE_3G2A
,
849 ISOM_BRAND_TYPE_3GG6
,
850 ISOM_BRAND_TYPE_3GG9
,
851 ISOM_BRAND_TYPE_3GP4
,
852 ISOM_BRAND_TYPE_3GP5
,
853 ISOM_BRAND_TYPE_3GP6
,
854 ISOM_BRAND_TYPE_3GP7
,
855 ISOM_BRAND_TYPE_3GP8
,
856 ISOM_BRAND_TYPE_3GP9
,
857 ISOM_BRAND_TYPE_3GR6
,
858 ISOM_BRAND_TYPE_3GR9
,
859 ISOM_BRAND_TYPE_M4A
,
860 ISOM_BRAND_TYPE_M4B
,
861 ISOM_BRAND_TYPE_M4V
,
862 ISOM_BRAND_TYPE_AVC1
,
863 ISOM_BRAND_TYPE_DBY1
,
864 ISOM_BRAND_TYPE_ISO2
,
865 ISOM_BRAND_TYPE_ISO3
,
866 ISOM_BRAND_TYPE_ISO4
,
867 ISOM_BRAND_TYPE_ISO5
,
868 ISOM_BRAND_TYPE_ISO6
,
869 ISOM_BRAND_TYPE_ISOM
,
870 ISOM_BRAND_TYPE_MP41
,
871 ISOM_BRAND_TYPE_MP42
,
875 for( int i
= 0; brand_white_list
[i
]; i
++ )
876 if( brand
== brand_white_list
[i
] )
881 static int moov_to_front_callback( void *param
, uint64_t written_movie_size
, uint64_t total_movie_size
)
883 eprintf( "Finalizing: [%5.2lf%%]\r", ((double)written_movie_size
/ total_movie_size
) * 100.0 );
887 static void display_version( void )
890 "L-SMASH isom/mov timeline editor rev%s %s\n"
892 "Copyright (C) 2011-2015 L-SMASH project\n",
893 LSMASH_REV
, LSMASH_GIT_HASH
, __DATE__
, __TIME__
);
896 static void display_help( void )
900 "Usage: timelineeditor [options] input output\n"
902 " --help Display help\n"
903 " --version Display version information\n"
904 " --track <integer> Specify track number to edit [1]\n"
905 " --timecode <string> Specify timecode file to edit timeline\n"
906 " --media-timescale <integer> Specify media timescale to convert\n"
907 " --media-timebase <integer> Specify media timebase to convert\n"
908 " --skip <integer> Skip start of media presentation in milliseconds\n"
909 " --delay <integer> Insert blank clip before actual media presentation in milliseconds\n"
910 " --dts-compression Eliminate composition delay with DTS hack\n"
911 " Multiply media timescale and timebase automatically\n" );
914 int main( int argc
, char *argv
[] )
921 else if( !strcasecmp( argv
[1], "-h" ) || !strcasecmp( argv
[1], "--help" ) )
926 else if( !strcasecmp( argv
[1], "-v" ) || !strcasecmp( argv
[1], "--version" ) )
936 root_t output
= { 0 };
937 root_t input
= { 0 };
938 timecode_t timecode
= { 0 };
939 movie_io_t io
= { &output
, &input
, &timecode
};
940 opt_t opt
= { 1, 0, 0, 0, 0, 0 };
942 lsmash_get_mainargs( &argc
, &argv
);
944 while( argn
< argc
- 2 )
946 if( !strcasecmp( argv
[argn
], "--track" ) )
948 opt
.track_number
= atoi( argv
[++argn
] );
949 if( !opt
.track_number
)
950 return TIMELINEEDITOR_ERR( "Invalid track number.\n" );
953 else if( !strcasecmp( argv
[argn
], "--timecode" ) )
955 timecode
.file
= lsmash_fopen( argv
[++argn
], "rb" );
957 return TIMELINEEDITOR_ERR( "Failed to open timecode file.\n" );
960 else if( !strcasecmp( argv
[argn
], "--media-timescale" ) )
962 opt
.media_timescale
= atoi( argv
[++argn
] );
963 if( !opt
.media_timescale
)
964 return TIMELINEEDITOR_ERR( "Invalid media timescale.\n" );
967 else if( !strcasecmp( argv
[argn
], "--media-timebase" ) )
969 opt
.media_timebase
= atoi( argv
[++argn
] );
970 if( !opt
.media_timebase
)
971 return TIMELINEEDITOR_ERR( "Invalid media timebase.\n" );
974 else if( !strcasecmp( argv
[argn
], "--skip" ) )
976 opt
.skip_duration
= atoi( argv
[++argn
] );
977 if( !opt
.skip_duration
)
978 return TIMELINEEDITOR_ERR( "Invalid skip duration.\n" );
981 else if( !strcasecmp( argv
[argn
], "--delay" ) )
983 opt
.empty_delay
= atoi( argv
[++argn
] );
984 if( !opt
.empty_delay
)
985 return TIMELINEEDITOR_ERR( "Invalid delay time.\n" );
988 else if( !strcasecmp( argv
[argn
], "--dts-compression" ) )
990 opt
.dts_compression
= 1;
994 return TIMELINEEDITOR_ERR( "Invalid option.\n" );
996 if( argn
> argc
- 2 )
997 return TIMELINEEDITOR_ERR( "Invalid arguments.\n" );
998 /* Get input movies. */
999 if( get_movie( &input
, argv
[argn
++] ) )
1000 return TIMELINEEDITOR_ERR( "Failed to get input movie.\n" );
1001 movie_t
*in_movie
= &input
.file
.movie
;
1002 if( opt
.track_number
&& (opt
.track_number
> in_movie
->num_tracks
) )
1003 return TIMELINEEDITOR_ERR( "Invalid track number.\n" );
1004 /* Create output movie. */
1005 file_t
*out_file
= &output
.file
;
1006 output
.root
= lsmash_create_root();
1008 return TIMELINEEDITOR_ERR( "failed to create a ROOT for an output file.\n" );
1009 if( lsmash_open_file( argv
[argn
], 0, &out_file
->param
) < 0 )
1010 return TIMELINEEDITOR_ERR( "failed to open an output file.\n" );
1011 file_t
*in_file
= &input
.file
;
1012 out_file
->param
.major_brand
= in_file
->param
.major_brand
;
1013 out_file
->param
.minor_version
= in_file
->param
.minor_version
;
1014 out_file
->param
.brands
= in_file
->param
.brands
;
1015 out_file
->param
.brand_count
= in_file
->param
.brand_count
;
1016 out_file
->param
.max_chunk_duration
= 0.5;
1017 out_file
->param
.max_async_tolerance
= 2.0;
1018 out_file
->param
.max_chunk_size
= 4*1024*1024;
1019 if( !check_white_brand( out_file
->param
.major_brand
) )
1021 /* Replace with whitelisted brand 'mp42'. */
1022 out_file
->param
.major_brand
= ISOM_BRAND_TYPE_MP42
;
1023 out_file
->param
.minor_version
= 0;
1025 for( i
= 0; i
< out_file
->param
.brand_count
; i
++ )
1026 if( out_file
->param
.brands
[i
] == ISOM_BRAND_TYPE_MP42
)
1028 if( i
== out_file
->param
.brand_count
)
1030 /* Add 'mp42' into the list of compatible brands. */
1031 out_file
->param
.brands
= lsmash_malloc( (i
+ 1) * sizeof(lsmash_brand_type
) );
1032 if( out_file
->param
.brands
)
1034 memcpy( out_file
->param
.brands
, in_file
->param
.brands
, i
* sizeof(lsmash_brand_type
) );
1035 out_file
->param
.brands
[i
] = ISOM_BRAND_TYPE_MP42
;
1039 out_file
->fh
= lsmash_set_file( output
.root
, &out_file
->param
);
1041 return TIMELINEEDITOR_ERR( "failed to add an output file into a ROOT.\n" );
1042 if( out_file
->param
.brands
!= in_file
->param
.brands
)
1043 lsmash_freep( &out_file
->param
.brands
);
1044 /* Set movie parameters. */
1045 movie_t
*out_movie
= &out_file
->movie
;
1046 out_movie
->param
= in_movie
->param
; /* Copy movie parameters. */
1047 if( in_movie
->num_tracks
== 1 )
1048 out_movie
->param
.timescale
= in_movie
->track
[0].media_param
.timescale
;
1049 if( lsmash_set_movie_parameters( output
.root
, &out_movie
->param
) )
1050 return TIMELINEEDITOR_ERR( "Failed to set output movie parameters.\n" );
1051 /* Set iTunes metadata. */
1052 for( uint32_t i
= 0; i
< in_movie
->num_itunes_metadata
; i
++ )
1053 if( lsmash_set_itunes_metadata( output
.root
, in_movie
->itunes_metadata
[i
] ) )
1055 WARNING_MSG( "failed to set an iTunes metadata.\n" );
1058 /* Create tracks of the output movie. */
1059 out_movie
->track
= lsmash_malloc( in_movie
->num_tracks
* sizeof(track_t
) );
1060 if( !out_movie
->track
)
1061 return TIMELINEEDITOR_ERR( "Failed to alloc output tracks.\n" );
1062 /* Edit timeline. */
1063 if( edit_media_timeline( &input
, &timecode
, &opt
) )
1064 return TIMELINEEDITOR_ERR( "Failed to edit timeline.\n" );
1065 out_movie
->num_tracks
= in_movie
->num_tracks
;
1066 out_movie
->current_track_number
= 1;
1067 for( uint32_t i
= 0; i
< in_movie
->num_tracks
; i
++ )
1069 track_t
*in_track
= &in_movie
->track
[i
];
1070 if( !in_track
->active
)
1072 -- out_movie
->num_tracks
;
1075 track_t
*out_track
= &out_movie
->track
[i
];
1076 out_track
->summary_remap
= lsmash_malloc( in_track
->num_summaries
* sizeof(uint32_t) );
1077 if( !out_track
->summary_remap
)
1078 return TIMELINEEDITOR_ERR( "failed to create summary mapping for a track.\n" );
1079 memset( out_track
->summary_remap
, 0, in_track
->num_summaries
* sizeof(uint32_t) );
1080 out_track
->track_ID
= lsmash_create_track( output
.root
, in_track
->media_param
.handler_type
);
1081 if( !out_track
->track_ID
)
1082 return TIMELINEEDITOR_ERR( "Failed to create a track.\n" );
1083 /* Copy track and media parameters except for track_ID. */
1084 out_track
->track_param
= in_track
->track_param
;
1085 out_track
->media_param
= in_track
->media_param
;
1086 out_track
->track_param
.track_ID
= out_track
->track_ID
;
1087 if( lsmash_set_track_parameters( output
.root
, out_track
->track_ID
, &out_track
->track_param
) )
1088 return TIMELINEEDITOR_ERR( "Failed to set track parameters.\n" );
1089 if( lsmash_set_media_parameters( output
.root
, out_track
->track_ID
, &out_track
->media_param
) )
1090 return TIMELINEEDITOR_ERR( "Failed to set media parameters.\n" );
1091 uint32_t valid_summary_count
= 0;
1092 for( uint32_t k
= 0; k
< in_track
->num_summaries
; k
++ )
1094 if( !in_track
->summaries
[k
].active
)
1096 out_track
->summary_remap
[k
] = 0;
1099 lsmash_summary_t
*summary
= in_track
->summaries
[k
].summary
;
1100 if( lsmash_add_sample_entry( output
.root
, out_track
->track_ID
, summary
) == 0 )
1102 WARNING_MSG( "failed to append a summary.\n" );
1103 lsmash_cleanup_summary( summary
);
1104 in_track
->summaries
[k
].summary
= NULL
;
1105 in_track
->summaries
[k
].active
= 0;
1106 out_track
->summary_remap
[k
] = 0;
1109 out_track
->summary_remap
[k
] = ++valid_summary_count
;
1111 if( valid_summary_count
== 0 )
1112 return TIMELINEEDITOR_ERR( "failed to append all summaries.\n" );
1113 out_track
->last_sample_delta
= in_track
->last_sample_delta
;
1114 out_track
->current_sample_number
= 1;
1115 out_track
->reach_end_of_media_timeline
= 0;
1118 double largest_dts
= 0;
1119 uint32_t num_consecutive_sample_skip
= 0;
1120 uint32_t num_active_input_tracks
= out_movie
->num_tracks
;
1121 uint64_t total_media_size
= 0;
1122 uint8_t sample_count
= 0;
1125 track_t
*in_track
= &in_movie
->track
[ in_movie
->current_track_number
- 1 ];
1126 /* Try append a sample in an input track where we didn't reach the end of media timeline. */
1127 if( !in_track
->reach_end_of_media_timeline
)
1129 track_t
*out_track
= &out_movie
->track
[ out_movie
->current_track_number
- 1 ];
1130 uint32_t in_track_ID
= in_track
->track_ID
;
1131 uint32_t out_track_ID
= out_track
->track_ID
;
1132 uint32_t input_media_timescale
= in_track
->media_param
.timescale
;
1133 /* Get a DTS from a track in an input movie. */
1135 if( lsmash_get_dts_from_media_timeline( input
.root
, in_track_ID
, in_track
->current_sample_number
, &dts
) )
1137 if( lsmash_check_sample_existence_in_media_timeline( input
.root
, in_track_ID
, in_track
->current_sample_number
) )
1138 return TIMELINEEDITOR_ERR( "Failed to get the DTS.\n" );
1141 in_track
->reach_end_of_media_timeline
= 1;
1142 if( --num_active_input_tracks
== 0 )
1143 break; /* end of muxing */
1146 /* Get and append a sample if it's good time. */
1147 else if( ((double)dts
/ input_media_timescale
) <= largest_dts
1148 || num_consecutive_sample_skip
== num_active_input_tracks
)
1150 /* Get an actual sample data from a track in an input movie. */
1151 lsmash_sample_t
*sample
= lsmash_get_sample_from_media_timeline( input
.root
, in_track_ID
, in_track
->current_sample_number
);
1153 return TIMELINEEDITOR_ERR( "Failed to get sample.\n" );
1154 sample
->index
= sample
->index
> in_track
->num_summaries
? in_track
->num_summaries
1155 : sample
->index
== 0 ? 1
1157 sample
->index
= out_track
->summary_remap
[ sample
->index
- 1 ];
1160 /* Append sample into output movie. */
1161 uint64_t sample_size
= sample
->length
; /* sample will be deleted internally after appending. */
1162 if( lsmash_append_sample( output
.root
, out_track_ID
, sample
) )
1164 lsmash_delete_sample( sample
);
1165 return TIMELINEEDITOR_ERR( "Failed to append a sample.\n" );
1167 largest_dts
= LSMASH_MAX( largest_dts
, (double)dts
/ input_media_timescale
);
1168 total_media_size
+= sample_size
;
1169 ++ in_track
->current_sample_number
;
1170 num_consecutive_sample_skip
= 0;
1171 /* Print, per 256 samples, total size of imported media. */
1172 if( ++sample_count
== 0 )
1173 eprintf( "Importing: %"PRIu64
" bytes\r", total_media_size
);
1177 ++num_consecutive_sample_skip
; /* Skip appendig sample. */
1179 /* Move the next track. */
1180 if( ++ in_movie
->current_track_number
> in_movie
->num_tracks
)
1181 in_movie
->current_track_number
= 1; /* Back the first track. */
1182 if( ++ out_movie
->current_track_number
> out_movie
->num_tracks
)
1183 out_movie
->current_track_number
= 1; /* Back the first track in the output movie. */
1185 for( uint32_t i
= 0; i
< out_movie
->num_tracks
; i
++ )
1186 if( lsmash_flush_pooled_samples( output
.root
, out_movie
->track
[i
].track_ID
, out_movie
->track
[i
].last_sample_delta
) )
1187 return TIMELINEEDITOR_ERR( "Failed to flush samples.\n" );
1188 /* Copy timeline maps. */
1189 for( uint32_t i
= 0; i
< out_movie
->num_tracks
; i
++ )
1190 if( lsmash_copy_timeline_map( output
.root
, out_movie
->track
[i
].track_ID
, input
.root
, in_movie
->track
[i
].track_ID
) )
1191 return TIMELINEEDITOR_ERR( "Failed to copy a timeline map.\n" );
1192 /* Edit timeline map. */
1195 track_t
*out_track
= &out_movie
->track
[ opt
.track_number
- 1 ];
1196 uint32_t track_ID
= out_track
->track_ID
;
1197 uint32_t movie_timescale
= lsmash_get_movie_timescale( output
.root
);
1198 uint32_t media_timescale
= lsmash_get_media_timescale( output
.root
, track_ID
);
1199 uint64_t empty_delay
= timecode
.empty_delay
+ (uint64_t)(opt
.empty_delay
* (1e-3 * media_timescale
) + 0.5);
1200 uint64_t duration
= timecode
.duration
+ empty_delay
;
1201 if( lsmash_delete_explicit_timeline_map( output
.root
, track_ID
) )
1202 return TIMELINEEDITOR_ERR( "Failed to delete explicit timeline maps.\n" );
1203 if( timecode
.empty_delay
)
1205 lsmash_edit_t empty_edit
;
1206 empty_edit
.duration
= ((double)timecode
.empty_delay
/ media_timescale
) * movie_timescale
;
1207 empty_edit
.start_time
= ISOM_EDIT_MODE_EMPTY
;
1208 empty_edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
1209 if( lsmash_create_explicit_timeline_map( output
.root
, track_ID
, empty_edit
) )
1210 return TIMELINEEDITOR_ERR( "Failed to create a empty duration.\n" );
1211 duration
= ((double)duration
/ media_timescale
) * movie_timescale
;
1212 duration
-= empty_edit
.duration
;
1215 duration
= ((double)duration
/ media_timescale
) * movie_timescale
;
1217 edit
.duration
= duration
;
1218 edit
.start_time
= timecode
.composition_delay
+ (uint64_t)(opt
.skip_duration
* (1e-3 * media_timescale
) + 0.5);
1219 edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
1220 if( lsmash_create_explicit_timeline_map( output
.root
, track_ID
, edit
) )
1221 return TIMELINEEDITOR_ERR( "Failed to create a explicit timeline map.\n" );
1223 /* Finish muxing. */
1224 lsmash_adhoc_remux_t moov_to_front
;
1225 moov_to_front
.func
= moov_to_front_callback
;
1226 moov_to_front
.buffer_size
= 4*1024*1024;
1227 moov_to_front
.param
= NULL
;
1229 if( lsmash_finish_movie( output
.root
, &moov_to_front
)
1230 || lsmash_write_lsmash_indicator( output
.root
) )
1231 return TIMELINEEDITOR_ERR( "Failed to finish output movie.\n" );
1232 cleanup_root( io
.input
);
1233 cleanup_root( io
.output
);
1234 cleanup_timecode( io
.timecode
);
1235 eprintf( "Timeline editing completed! \n" );