1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2011 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. */
30 #define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b))
32 #define eprintf( ... ) fprintf( stderr, __VA_ARGS__ )
37 uint32_t last_sample_delta
;
38 uint32_t current_sample_number
;
39 int reach_end_of_media_timeline
;
40 lsmash_track_parameters_t track_param
;
41 lsmash_media_parameters_t media_param
;
47 lsmash_itunes_metadata_list_t
*itunes_meta_list
;
49 lsmash_movie_parameters_t movie_param
;
56 uint32_t sample_count
;
58 uint64_t composition_delay
;
71 uint32_t track_number
;
72 uint32_t media_timescale
;
73 uint32_t media_timebase
;
74 uint32_t skip_duration
;
79 static void cleanup_movie( movie_t
*movie
)
83 if( movie
->itunes_meta_list
)
84 lsmash_destroy_itunes_metadata( movie
->itunes_meta_list
);
87 lsmash_destroy_root( movie
->root
);
89 movie
->itunes_meta_list
= NULL
;
93 static void cleanup_timecode( timecode_t
*timecode
)
98 fclose( timecode
->file
);
100 free( timecode
->ts
);
101 timecode
->file
= NULL
;
105 static int timelineeditor_error( movie_io_t
*io
, char *message
)
107 cleanup_movie( io
->input
);
108 cleanup_movie( io
->output
);
109 cleanup_timecode( io
->timecode
);
114 static int error_message( char *message
)
120 #define TIMELINEEDITOR_ERR( message ) timelineeditor_error( &io, message )
121 #define ERROR_MSG( message ) error_message( message )
123 static int get_movie( movie_t
*input
, char *input_name
, uint32_t *num_tracks
)
125 if( !strcmp( input_name
, "-" ) )
126 return ERROR_MSG( "Standard input not supported.\n" );
127 input
->root
= lsmash_open_movie( input_name
, LSMASH_FILE_MODE_READ
);
129 return ERROR_MSG( "Failed to open input file.\n" );
130 lsmash_movie_parameters_t
*movie_param
= &input
->movie_param
;
131 lsmash_initialize_movie_parameters( movie_param
);
132 lsmash_get_movie_parameters( input
->root
, movie_param
);
133 *num_tracks
= movie_param
->number_of_tracks
;
135 track_t
*track
= input
->track
= malloc( *num_tracks
* sizeof(track_t
) );
137 return ERROR_MSG( "Failed to alloc input tracks.\n" );
138 memset( track
, 0, *num_tracks
* sizeof(track_t
) );
139 for( uint32_t i
= 0; i
< *num_tracks
; i
++ )
141 track
[i
].track_ID
= lsmash_get_track_ID( input
->root
, i
+ 1 );
142 if( !track
[i
].track_ID
)
143 return ERROR_MSG( "Failed to get track_ID.\n" );
145 for( uint32_t i
= 0; i
< *num_tracks
; i
++ )
147 input
->itunes_meta_list
= lsmash_export_itunes_metadata( input
->root
);
148 if( !input
->itunes_meta_list
)
149 return ERROR_MSG( "Failed to get iTunes metadata.\n" );
150 lsmash_initialize_track_parameters( &track
[i
].track_param
);
151 if( lsmash_get_track_parameters( input
->root
, track
[i
].track_ID
, &track
[i
].track_param
) )
152 return ERROR_MSG( "Failed to get track parameters.\n" );
153 lsmash_initialize_media_parameters( &track
[i
].media_param
);
154 if( lsmash_get_media_parameters( input
->root
, track
[i
].track_ID
, &track
[i
].media_param
) )
155 return ERROR_MSG( "Failed to get media parameters.\n" );
156 if( lsmash_construct_timeline( input
->root
, track
[i
].track_ID
) )
157 return ERROR_MSG( "Failed to construct timeline.\n" );
158 if( lsmash_get_last_sample_delta_from_media_timeline( input
->root
, track
[i
].track_ID
, &track
[i
].last_sample_delta
) )
159 return ERROR_MSG( "Failed to get the last sample delta.\n" );
160 track
[i
].current_sample_number
= 1;
162 lsmash_discard_boxes( input
->root
);
166 static uint64_t get_gcd( uint64_t a
, uint64_t b
)
180 static uint64_t get_media_timebase( lsmash_media_ts_list_t
*ts_list
)
182 uint64_t timebase
= ts_list
->timestamp
[0].cts
;
183 for( uint32_t i
= 1; i
< ts_list
->sample_count
; i
++ )
184 timebase
= get_gcd( timebase
, ts_list
->timestamp
[i
].cts
);
185 for( uint32_t i
= 0; i
< ts_list
->sample_count
; i
++ )
186 timebase
= get_gcd( timebase
, ts_list
->timestamp
[i
].dts
);
190 static int parse_timecode( movie_io_t
*io
, lsmash_media_ts_list_t
*ts_list
, uint32_t timescale
, uint32_t timebase
)
193 timecode_t
*timecode
= io
->timecode
;
194 int ret
= fscanf( timecode
->file
, "# timecode format v%d", &tcfv
);
195 if( ret
!= 1 || tcfv
!= 2 )
196 return ERROR_MSG( "Unsupported timecode format\n" );
198 uint32_t num_timecodes
= 0;
199 uint64_t file_pos
= ftell( timecode
->file
);
200 while( fgets( buff
, sizeof(buff
), timecode
->file
) )
202 if( buff
[0] == '#' || buff
[0] == '\n' || buff
[0] == '\r' )
205 file_pos
= ftell( timecode
->file
);
211 return ERROR_MSG( "No timecodes!\n" );
212 if( ts_list
->sample_count
> num_timecodes
)
213 return ERROR_MSG( "Lack number of timecodes!\n" );
214 fseek( timecode
->file
, file_pos
, SEEK_SET
);
215 timecode
->ts
= malloc( ts_list
->sample_count
* sizeof(uint64_t) );
217 return ERROR_MSG( "Failed to alloc timestamps.\n" );
218 fgets( buff
, sizeof(buff
), timecode
->file
);
220 ret
= sscanf( buff
, "%lf", &tc
);
222 return ERROR_MSG( "Invalid timecode number: 0\n" );
223 double delay_tc
= tc
* 1e-3; /* Timecode format v2 is expressed in milliseconds. */
224 timecode
->empty_delay
= ((uint64_t)(delay_tc
* ((double)timescale
/ timebase
) + 0.5)) * timebase
;
226 for( uint32_t i
= 1; i
< ts_list
->sample_count
; )
228 fgets( buff
, sizeof(buff
), timecode
->file
);
229 if( buff
[0] == '#' || buff
[0] == '\n' || buff
[0] == '\r' )
231 ret
= sscanf( buff
, "%lf", &tc
);
233 return ERROR_MSG( "Invalid timecode\n" );
234 tc
*= 1e-3; /* Timecode format v2 is expressed in milliseconds. */
235 timecode
->ts
[i
] = ((uint64_t)((tc
- delay_tc
) * ((double)timescale
/ timebase
) + 0.5)) * timebase
;
236 if( timecode
->ts
[i
] <= timecode
->ts
[i
- 1] )
237 return ERROR_MSG( "Invalid timecode.\n" );
243 static uint32_t get_max_sample_delay( lsmash_media_ts_list_t
*ts_list
)
245 uint32_t sample_delay
= 0;
246 uint32_t max_sample_delay
= 0;
247 lsmash_media_ts_t
*ts
= ts_list
->timestamp
;
248 for( uint32_t i
= 1; i
< ts_list
->sample_count
; i
++ )
250 if( ts
[i
].cts
< ts
[i
- 1].cts
)
253 max_sample_delay
= LSMASH_MAX( max_sample_delay
, sample_delay
);
258 return max_sample_delay
;
261 static int compare_cts( const lsmash_media_ts_t
*a
, const lsmash_media_ts_t
*b
)
263 int64_t diff
= (int64_t)(a
->cts
- b
->cts
);
264 return diff
> 0 ? 1 : (diff
== 0 ? 0 : -1);
267 static int compare_dts( const lsmash_media_ts_t
*a
, const lsmash_media_ts_t
*b
)
269 int64_t diff
= (int64_t)(a
->dts
- b
->dts
);
270 return diff
> 0 ? 1 : (diff
== 0 ? 0 : -1);
273 static int edit_media_timeline( movie_io_t
*io
, opt_t
*opt
)
275 timecode_t
*timecode
= io
->timecode
;
276 if( !timecode
->file
&& !opt
->media_timescale
&& !opt
->media_timebase
&& !opt
->dts_compression
)
278 movie_t
*input
= io
->input
;
279 track_t
*in_track
= &input
->track
[opt
->track_number
- 1];
280 uint32_t track_ID
= in_track
->track_ID
;
281 lsmash_media_ts_list_t ts_list
;
282 if( lsmash_get_media_timestamps( input
->root
, track_ID
, &ts_list
) )
283 return ERROR_MSG( "Failed to get media timestamps.\n" );
284 uint64_t timebase
= get_media_timebase( &ts_list
);
286 return ERROR_MSG( "Failed to get media timebase.\n" );
287 lsmash_media_ts_t
*timestamp
= ts_list
.timestamp
;
288 uint32_t sample_count
= ts_list
.sample_count
;
289 uint32_t orig_timebase
= timebase
;
291 double timebase_convert_multiplier
;
292 if( opt
->media_timescale
|| opt
->media_timebase
)
294 uint32_t orig_timescale
= in_track
->media_param
.timescale
;
295 timescale
= opt
->media_timescale
? opt
->media_timescale
: orig_timescale
;
296 timebase
= opt
->media_timebase
? opt
->media_timebase
: orig_timebase
;
297 if( !opt
->media_timescale
&& opt
->media_timebase
&& (timebase
> orig_timebase
) )
298 timescale
= timescale
* ((double)timebase
/ orig_timebase
) + 0.5;
299 timebase_convert_multiplier
= ((double)timescale
/ orig_timescale
) * ((double)orig_timebase
/ timebase
);
303 /* Reduce timescale and timebase. */
304 timescale
= in_track
->media_param
.timescale
;
305 uint64_t reduce
= get_gcd( timescale
, timebase
);
308 timebase_convert_multiplier
= 1;
310 in_track
->media_param
.timescale
= timescale
;
311 /* Parse timecode file. */
312 if( timecode
->file
&& parse_timecode( io
, &ts_list
, timescale
, timebase
) )
313 return ERROR_MSG( "Failed to parse timecode file.\n" );
314 /* Get maximum composition sample delay for DTS generation. */
315 uint32_t sample_delay
= get_max_sample_delay( &ts_list
);
316 if( sample_delay
) /* Reorder composition order. */
317 qsort( timestamp
, sample_count
, sizeof(lsmash_media_ts_t
), (int(*)( const void *, const void * ))compare_cts
);
318 if( !timecode
->file
)
320 /* Genarate timestamps timescale converted. */
321 timecode
->ts
= malloc( sample_count
* sizeof(uint64_t) );
323 return ERROR_MSG( "Failed to alloc timestamps\n" );
324 for( uint32_t i
= 0; i
< sample_count
; i
++ )
326 timecode
->ts
[i
] = (timestamp
[i
].cts
- timestamp
[0].cts
) / orig_timebase
;
327 timecode
->ts
[i
] = ((uint64_t)(timecode
->ts
[i
] * timebase_convert_multiplier
+ 0.5)) * timebase
;
328 if( i
&& (timecode
->ts
[i
] <= timecode
->ts
[i
- 1]) )
329 return ERROR_MSG( "Invalid timescale conversion.\n" );
334 /* If media timescale is specified, disable DTS compression multiplier. */
335 uint32_t dts_compression_multiplier
= opt
->dts_compression
* !opt
->media_timescale
* sample_delay
+ 1;
336 uint64_t initial_delta
= timecode
->ts
[1];
337 in_track
->media_param
.timescale
*= dts_compression_multiplier
;
338 if( dts_compression_multiplier
> 1 )
339 for( uint32_t i
= 0; i
< sample_count
; i
++ )
340 timecode
->ts
[i
] *= dts_compression_multiplier
;
342 uint64_t sample_delay_time
= timecode
->composition_delay
= opt
->dts_compression
? 0 : timecode
->ts
[sample_delay
];
343 for( uint32_t i
= 0; i
< sample_count
; i
++ )
344 timestamp
[i
].cts
= timecode
->ts
[i
] + sample_delay_time
;
345 /* Reorder decode order and generate new DTS from CTS. */
346 qsort( timestamp
, sample_count
, sizeof(lsmash_media_ts_t
), (int(*)( const void *, const void * ))compare_dts
);
347 uint64_t prev_reordered_cts
[sample_delay
];
348 for( uint32_t i
= 0; i
<= sample_delay
; i
++ )
350 if( !opt
->dts_compression
)
351 timestamp
[i
].dts
= timecode
->ts
[i
];
354 timestamp
[i
].dts
= (i
* initial_delta
) / (!!opt
->media_timescale
* sample_delay
+ 1);
355 if( i
&& (timestamp
[i
].dts
<= timestamp
[i
- 1].dts
) )
356 return ERROR_MSG( "Failed to do DTS compression.\n" );
358 prev_reordered_cts
[ i
% sample_delay
] = timecode
->ts
[i
] + sample_delay_time
;
360 for( uint32_t i
= sample_delay
+ 1; i
< sample_count
; i
++ )
362 timestamp
[i
].dts
= prev_reordered_cts
[ (i
- sample_delay
) % sample_delay
];
363 prev_reordered_cts
[ i
% sample_delay
] = timecode
->ts
[i
] + sample_delay_time
;
367 for( uint32_t i
= 0; i
< sample_count
; i
++ )
368 timestamp
[i
].cts
= timestamp
[i
].dts
= timecode
->ts
[i
];
369 if( sample_count
> 1 )
371 in_track
->last_sample_delta
= timecode
->ts
[sample_count
- 1] - timecode
->ts
[sample_count
- 2];
372 timecode
->duration
= timecode
->ts
[sample_count
- 1] + in_track
->last_sample_delta
;
374 else /* still image */
375 timecode
->duration
= in_track
->last_sample_delta
= UINT32_MAX
;
376 return lsmash_set_media_timestamps( input
->root
, track_ID
, &ts_list
);
379 static int moov_to_front_callback( void *param
, uint64_t written_movie_size
, uint64_t total_movie_size
)
381 eprintf( "Finalizing: [%5.2lf%%]\r", ((double)written_movie_size
/ total_movie_size
) * 100.0 );
385 int main( int argc
, char *argv
[] )
387 if( argc
< 3 || !strcasecmp( argv
[1], "-h" ) || !strcasecmp( argv
[1], "--help" ) )
389 fprintf( stderr
, "Usage: timelineeditor [options] input output\n"
391 " --track <integer> Specify track number to edit [1]\n"
392 " --timecode <string> Specify timecode file to edit timeline\n"
393 " --media-timescale <integer> Specify media timescale to convert\n"
394 " --media-timebase <integer> Specify media timebase to convert\n"
395 " --skip <integer> Skip start of media presentation in milliseconds\n"
396 " --delay <integer> Insert blank clip before actual media presentation in milliseconds\n"
397 " --dts-compression Eliminate composition delay with DTS hack\n"
398 " Multiply media timescale and timebase automatically\n" );
401 movie_t output
= { 0 };
402 movie_t input
= { 0 };
403 timecode_t timecode
= { 0 };
404 movie_io_t io
= { &output
, &input
, &timecode
};
405 opt_t opt
= { 1, 0, 0, 0, 0, 0 };
408 while( argn
< argc
- 2 )
410 if( !strcasecmp( argv
[argn
], "--track" ) )
412 opt
.track_number
= atoi( argv
[++argn
] );
413 if( !opt
.track_number
)
414 return TIMELINEEDITOR_ERR( "Invalid track number.\n" );
417 else if( !strcasecmp( argv
[argn
], "--timecode" ) )
419 timecode
.file
= fopen( argv
[++argn
], "rb" );
421 return TIMELINEEDITOR_ERR( "Failed to open timecode file.\n" );
424 else if( !strcasecmp( argv
[argn
], "--media-timescale" ) )
426 opt
.media_timescale
= atoi( argv
[++argn
] );
427 if( !opt
.media_timescale
)
428 return TIMELINEEDITOR_ERR( "Invalid media timescale.\n" );
431 else if( !strcasecmp( argv
[argn
], "--media-timebase" ) )
433 opt
.media_timebase
= atoi( argv
[++argn
] );
434 if( !opt
.media_timebase
)
435 return TIMELINEEDITOR_ERR( "Invalid media timebase.\n" );
438 else if( !strcasecmp( argv
[argn
], "--skip" ) )
440 opt
.skip_duration
= atoi( argv
[++argn
] );
441 if( !opt
.skip_duration
)
442 return TIMELINEEDITOR_ERR( "Invalid skip duration.\n" );
445 else if( !strcasecmp( argv
[argn
], "--delay" ) )
447 opt
.empty_delay
= atoi( argv
[++argn
] );
448 if( !opt
.empty_delay
)
449 return TIMELINEEDITOR_ERR( "Invalid delay time.\n" );
452 else if( !strcasecmp( argv
[argn
], "--dts-compression" ) )
454 opt
.dts_compression
= 1;
458 return TIMELINEEDITOR_ERR( "Invalid option.\n" );
460 if( argn
> argc
- 2 )
461 return TIMELINEEDITOR_ERR( "Invalid arguments.\n" );
463 /* Get input movies. */
464 if( get_movie( &input
, argv
[argn
++], &num_tracks
) )
465 return TIMELINEEDITOR_ERR( "Failed to get input movie.\n" );
466 if( opt
.track_number
&& (opt
.track_number
> num_tracks
) )
467 return TIMELINEEDITOR_ERR( "Invalid track number.\n" );
468 /* Create output movie. */
469 output
.root
= lsmash_open_movie( argv
[argn
], LSMASH_FILE_MODE_WRITE
);
471 return TIMELINEEDITOR_ERR( "Failed to open output movie.\n" );
472 output
.movie_param
= input
.movie_param
;
473 output
.movie_param
.max_chunk_duration
= 0.5;
474 output
.movie_param
.max_async_tolerance
= 2.0;
475 output
.movie_param
.max_chunk_size
= 4*1024*1024;
476 output
.movie_param
.max_read_size
= 4*1024*1024;
477 if( lsmash_set_movie_parameters( output
.root
, &output
.movie_param
) )
478 return TIMELINEEDITOR_ERR( "Failed to set output movie parameters.\n" );
479 /* Set iTunes metadata. */
480 if( lsmash_import_itunes_metadata( output
.root
, input
.itunes_meta_list
) )
481 return TIMELINEEDITOR_ERR( "Failed to set iTunes metadata.\n" );
482 /* Create tracks of the output movie. */
483 output
.track
= malloc( num_tracks
* sizeof(track_t
) );
485 return TIMELINEEDITOR_ERR( "Failed to alloc output tracks.\n" );
487 if( edit_media_timeline( &io
, &opt
) )
488 return TIMELINEEDITOR_ERR( "Failed to edit timeline.\n" );
489 for( uint32_t i
= 0; i
< num_tracks
; i
++ )
491 track_t
*in_track
= &input
.track
[i
];
492 track_t
*out_track
= &output
.track
[i
];
493 uint32_t handler_type
= in_track
->media_param
.handler_type
;
494 out_track
->track_ID
= lsmash_create_track( output
.root
, handler_type
);
495 if( !out_track
->track_ID
)
496 return TIMELINEEDITOR_ERR( "Failed to create a track.\n" );
497 /* Copy track and media parameters except for track_ID. */
498 out_track
->track_param
= in_track
->track_param
;
499 out_track
->media_param
= in_track
->media_param
;
500 out_track
->track_param
.track_ID
= out_track
->track_ID
;
501 if( lsmash_set_track_parameters( output
.root
, out_track
->track_ID
, &out_track
->track_param
) )
502 return TIMELINEEDITOR_ERR( "Failed to set track parameters.\n" );
503 if( lsmash_set_media_parameters( output
.root
, out_track
->track_ID
, &out_track
->media_param
) )
504 return TIMELINEEDITOR_ERR( "Failed to set media parameters.\n" );
505 if( lsmash_copy_decoder_specific_info( output
.root
, out_track
->track_ID
, input
.root
, in_track
->track_ID
) )
506 return TIMELINEEDITOR_ERR( "Failed to copy a Decoder Specific Info.\n" );
507 out_track
->last_sample_delta
= in_track
->last_sample_delta
;
508 out_track
->current_sample_number
= 1;
509 out_track
->reach_end_of_media_timeline
= 0;
512 double largest_dts
= 0;
513 uint32_t current_track_number
= 1;
514 uint32_t num_consecutive_sample_skip
= 0;
515 uint32_t num_active_input_tracks
= num_tracks
;
516 uint64_t total_media_size
= 0;
517 uint8_t sample_count
= 0;
520 track_t
*in_track
= &input
.track
[current_track_number
- 1];
521 /* Try append a sample in an input track where we didn't reach the end of media timeline. */
522 if( !in_track
->reach_end_of_media_timeline
)
524 track_t
*out_track
= &output
.track
[current_track_number
- 1];
525 uint32_t in_track_ID
= in_track
->track_ID
;
526 uint32_t out_track_ID
= out_track
->track_ID
;
527 uint32_t input_media_timescale
= in_track
->media_param
.timescale
;
528 /* Get a DTS from a track in an input movie. */
530 if( lsmash_get_dts_from_media_timeline( input
.root
, in_track_ID
, in_track
->current_sample_number
, &dts
) )
532 if( lsmash_check_sample_existence_in_media_timeline( input
.root
, in_track_ID
, in_track
->current_sample_number
) )
533 return TIMELINEEDITOR_ERR( "Failed to get the DTS.\n" );
536 in_track
->reach_end_of_media_timeline
= 1;
537 if( --num_active_input_tracks
== 0 )
538 break; /* end of muxing */
541 /* Get and append a sample if it's good time. */
542 else if( ((double)dts
/ input_media_timescale
) <= largest_dts
543 || num_consecutive_sample_skip
== num_active_input_tracks
)
545 /* Get an actual sample data from a track in an input movie. */
546 lsmash_sample_t
*sample
= lsmash_get_sample_from_media_timeline( input
.root
, in_track_ID
, in_track
->current_sample_number
);
548 return TIMELINEEDITOR_ERR( "Failed to get sample.\n" );
549 /* Append sample into output movie. */
550 uint64_t sample_size
= sample
->length
; /* sample will be deleted internally after appending. */
551 if( lsmash_append_sample( output
.root
, out_track_ID
, sample
) )
553 lsmash_delete_sample( sample
);
554 return TIMELINEEDITOR_ERR( "Failed to append a sample.\n" );
556 largest_dts
= LSMASH_MAX( largest_dts
, (double)dts
/ input_media_timescale
);
557 total_media_size
+= sample_size
;
558 ++ in_track
->current_sample_number
;
559 num_consecutive_sample_skip
= 0;
560 /* Print, per 256 samples, total size of imported media. */
561 if( ++sample_count
== 0 )
562 eprintf( "Importing: %"PRIu64
" bytes\r", total_media_size
);
565 ++num_consecutive_sample_skip
; /* Skip appendig sample. */
567 /* Move the next track. */
568 ++current_track_number
;
569 if( current_track_number
> num_tracks
)
570 current_track_number
= 1; /* Back the first track. */
572 for( uint32_t i
= 0; i
< num_tracks
; i
++ )
573 if( lsmash_flush_pooled_samples( output
.root
, output
.track
[i
].track_ID
, output
.track
[i
].last_sample_delta
) )
574 return TIMELINEEDITOR_ERR( "Failed to flush samples.\n" );
575 /* Copy timeline maps. */
576 for( uint32_t i
= 0; i
< num_tracks
; i
++ )
577 if( lsmash_copy_timeline_map( output
.root
, output
.track
[i
].track_ID
, input
.root
, input
.track
[i
].track_ID
) )
578 return TIMELINEEDITOR_ERR( "Failed to copy a timeline map.\n" );
579 /* Edit timeline map. */
582 track_t
*out_track
= &output
.track
[opt
.track_number
- 1];
583 uint32_t track_ID
= out_track
->track_ID
;
584 uint32_t movie_timescale
= lsmash_get_movie_timescale( output
.root
);
585 uint32_t media_timescale
= lsmash_get_media_timescale( output
.root
, track_ID
);
586 uint32_t empty_delay
= timecode
.empty_delay
+ (uint64_t)(opt
.empty_delay
* (1e-3 * media_timescale
) + 0.5);
587 uint32_t duration
= timecode
.duration
+ empty_delay
;
588 if( lsmash_delete_explicit_timeline_map( output
.root
, track_ID
) )
589 return TIMELINEEDITOR_ERR( "Failed to delete explicit timeline maps.\n" );
590 if( timecode
.empty_delay
)
592 uint32_t empty_duration
= ((double)timecode
.empty_delay
/ media_timescale
) * movie_timescale
;
593 if( lsmash_create_explicit_timeline_map( output
.root
, track_ID
, empty_duration
, ISOM_EDIT_MODE_EMPTY
, ISOM_EDIT_MODE_NORMAL
) )
594 return TIMELINEEDITOR_ERR( "Failed to create a empty duration.\n" );
595 duration
= ((double)duration
/ media_timescale
) * movie_timescale
;
596 duration
-= empty_duration
;
599 duration
= ((double)duration
/ media_timescale
) * movie_timescale
;
600 uint64_t start_time
= timecode
.composition_delay
+ (uint64_t)(opt
.skip_duration
* (1e-3 * media_timescale
) + 0.5);
601 if( lsmash_create_explicit_timeline_map( output
.root
, track_ID
, duration
, start_time
, ISOM_EDIT_MODE_NORMAL
) )
602 return TIMELINEEDITOR_ERR( "Failed to create a explicit timeline map.\n" );
605 lsmash_adhoc_remux_t moov_to_front
;
606 moov_to_front
.func
= moov_to_front_callback
;
607 moov_to_front
.buffer_size
= 4*1024*1024;
608 moov_to_front
.param
= NULL
;
610 if( lsmash_finish_movie( output
.root
, &moov_to_front
) )
611 return TIMELINEEDITOR_ERR( "Failed to finish output movie.\n" );
612 cleanup_movie( io
.input
);
613 cleanup_movie( io
.output
);
614 eprintf( "Timeline editing completed! \n" );