int libavsmash_video_setup_timestamp_info ( libavsmash_video_decode_handler_t *vdhp, libavsmash_video_output_handler_t *vohp, int64_t *framerate_num, int64_t *framerate_den ) { int err = -1; uint64_t media_timescale = lsmash_get_media_timescale( vdhp->root, vdhp->track_id ); uint64_t media_duration = lsmash_get_media_duration_from_media_timeline( vdhp->root, vdhp->track_id ); if( media_duration == 0 ) media_duration = INT32_MAX; if( vdhp->sample_count == 1 ) { /* Calculate average framerate. */ reduce_fraction( &media_timescale, &media_duration ); *framerate_num = (int64_t)media_timescale; *framerate_den = (int64_t)media_duration; err = 0; goto setup_finish; } lw_log_handler_t *lhp = &vdhp->config.lh; lsmash_media_ts_list_t ts_list; if( lsmash_get_media_timestamps( vdhp->root, vdhp->track_id, &ts_list ) < 0 ) { lw_log_show( lhp, LW_LOG_ERROR, "Failed to get timestamps." ); goto setup_finish; } if( ts_list.sample_count != vdhp->sample_count ) { lw_log_show( lhp, LW_LOG_ERROR, "Failed to count number of video samples." ); goto setup_finish; } uint32_t composition_sample_delay; if( lsmash_get_max_sample_delay( &ts_list, &composition_sample_delay ) < 0 ) { lsmash_delete_media_timestamps( &ts_list ); lw_log_show( lhp, LW_LOG_ERROR, "Failed to get composition delay." ); goto setup_finish; } if( composition_sample_delay ) { /* Consider composition order for keyframe detection. * Note: sample number for L-SMASH is 1-origin. */ vdhp->order_converter = (order_converter_t *)lw_malloc_zero( (ts_list.sample_count + 1) * sizeof(order_converter_t) ); if( !vdhp->order_converter ) { lsmash_delete_media_timestamps( &ts_list ); lw_log_show( lhp, LW_LOG_ERROR, "Failed to allocate memory." ); goto setup_finish; } for( uint32_t i = 0; i < ts_list.sample_count; i++ ) ts_list.timestamp[i].dts = i + 1; lsmash_sort_timestamps_composition_order( &ts_list ); for( uint32_t i = 0; i < ts_list.sample_count; i++ ) vdhp->order_converter[i + 1].composition_to_decoding = (uint32_t)ts_list.timestamp[i].dts; } /* Calculate average framerate. */ uint64_t largest_cts = ts_list.timestamp[0].cts; uint64_t second_largest_cts = 0; uint64_t first_duration = ts_list.timestamp[1].cts - ts_list.timestamp[0].cts; uint64_t composition_timebase = first_duration; int strict_cfr = 1; for( uint32_t i = 1; i < ts_list.sample_count; i++ ) { uint64_t duration = ts_list.timestamp[i].cts - ts_list.timestamp[i - 1].cts; if( duration == 0 ) { lsmash_delete_media_timestamps( &ts_list ); lw_log_show( lhp, LW_LOG_WARNING, "Detected CTS duplication at frame %" PRIu32, i ); err = 0; goto setup_finish; } if( strict_cfr && duration != first_duration ) strict_cfr = 0; composition_timebase = get_gcd( composition_timebase, duration ); second_largest_cts = largest_cts; largest_cts = ts_list.timestamp[i].cts; } uint64_t reduce = reduce_fraction( &media_timescale, &composition_timebase ); uint64_t composition_duration = ((largest_cts - ts_list.timestamp[0].cts) + (largest_cts - second_largest_cts)) / reduce; lsmash_delete_media_timestamps( &ts_list ); double avg_frame_rate = (vdhp->sample_count * ((double)media_timescale / composition_duration)); if( strict_cfr || !lw_try_rational_framerate( avg_frame_rate, framerate_num, framerate_den, composition_timebase ) ) { uint64_t num = (uint64_t)(avg_frame_rate * composition_timebase + 0.5); uint64_t den = composition_timebase; if( num && den ) reduce_fraction( &num, &den ); else { num = 1; den = 1; } *framerate_num = (int64_t)num; *framerate_den = (int64_t)den; } err = 0; setup_finish:; if( vohp->vfr2cfr ) { /* Override average framerate by specified output constant framerate. */ *framerate_num = (int64_t)vohp->cfr_num; *framerate_den = (int64_t)vohp->cfr_den; vohp->frame_count = ((double)vohp->cfr_num / vohp->cfr_den) * ((double)media_duration / media_timescale) + 0.5; } else vohp->frame_count = libavsmash_video_get_sample_count( vdhp ); uint32_t min_cts_sample_number = get_decoding_sample_number( vdhp->order_converter, 1 ); vdhp->config.error = lsmash_get_cts_from_media_timeline( vdhp->root, vdhp->track_id, min_cts_sample_number, &vdhp->min_cts ); return err; }
static void setup_timestamp_info( libavsmash_video_decode_handler_t *hp, VideoInfo *vi, uint64_t media_timescale, IScriptEnvironment *env ) { if( vi->num_frames == 1 ) { /* Calculate average framerate. */ uint64_t media_duration = lsmash_get_media_duration_from_media_timeline( hp->root, hp->track_ID ); if( media_duration == 0 ) media_duration = INT32_MAX; reduce_fraction( &media_timescale, &media_duration ); vi->fps_numerator = (unsigned int)media_timescale; vi->fps_denominator = (unsigned int)media_duration; return; } lsmash_media_ts_list_t ts_list; if( lsmash_get_media_timestamps( hp->root, hp->track_ID, &ts_list ) ) env->ThrowError( "LSMASHVideoSource: failed to get timestamps." ); if( ts_list.sample_count != vi->num_frames ) env->ThrowError( "LSMASHVideoSource: failed to count number of video samples." ); uint32_t composition_sample_delay; if( lsmash_get_max_sample_delay( &ts_list, &composition_sample_delay ) ) { lsmash_delete_media_timestamps( &ts_list ); env->ThrowError( "LSMASHVideoSource: failed to get composition delay." ); } if( composition_sample_delay ) { /* Consider composition order for keyframe detection. * Note: sample number for L-SMASH is 1-origin. */ hp->order_converter = (order_converter_t *)malloc( (ts_list.sample_count + 1) * sizeof(order_converter_t) ); if( !hp->order_converter ) { lsmash_delete_media_timestamps( &ts_list ); env->ThrowError( "LSMASHVideoSource: failed to allocate memory." ); } for( uint32_t i = 0; i < ts_list.sample_count; i++ ) ts_list.timestamp[i].dts = i + 1; lsmash_sort_timestamps_composition_order( &ts_list ); for( uint32_t i = 0; i < ts_list.sample_count; i++ ) hp->order_converter[i + 1].composition_to_decoding = (uint32_t)ts_list.timestamp[i].dts; } /* Calculate average framerate. */ uint64_t largest_cts = ts_list.timestamp[1].cts; uint64_t second_largest_cts = ts_list.timestamp[0].cts; uint64_t composition_timebase = ts_list.timestamp[1].cts - ts_list.timestamp[0].cts; for( uint32_t i = 2; i < ts_list.sample_count; i++ ) { if( ts_list.timestamp[i].cts == ts_list.timestamp[i - 1].cts ) { lsmash_delete_media_timestamps( &ts_list ); return; } composition_timebase = get_gcd( composition_timebase, ts_list.timestamp[i].cts - ts_list.timestamp[i - 1].cts ); second_largest_cts = largest_cts; largest_cts = ts_list.timestamp[i].cts; } uint64_t reduce = reduce_fraction( &media_timescale, &composition_timebase ); uint64_t composition_duration = ((largest_cts - ts_list.timestamp[0].cts) + (largest_cts - second_largest_cts)) / reduce; lsmash_delete_media_timestamps( &ts_list ); vi->fps_numerator = (unsigned int)((vi->num_frames * ((double)media_timescale / composition_duration)) * composition_timebase + 0.5); vi->fps_denominator = (unsigned int)composition_timebase; }
int main( int argc, char *argv[] ) { if ( argc < 2 ) { display_help(); return -1; } else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) ) { display_help(); return 0; } else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) ) { display_version(); return 0; } int dump_box = 1; int chapter = 0; char *filename; lsmash_get_mainargs( &argc, &argv ); if( argc > 2 ) { if( !strcasecmp( argv[1], "--box" ) ) DO_NOTHING; else if( !strcasecmp( argv[1], "--chapter" ) ) chapter = 1; else if( !strcasecmp( argv[1], "--timestamp" ) ) dump_box = 0; else { display_help(); return -1; } filename = argv[2]; } else { filename = argv[1]; } /* Open the input file. */ lsmash_root_t *root = lsmash_create_root(); if( !root ) { fprintf( stderr, "Failed to create a ROOT.\n" ); return -1; } lsmash_file_parameters_t file_param = { 0 }; if( lsmash_open_file( filename, 1, &file_param ) < 0 ) return BOXDUMPER_ERR( "Failed to open an input file.\n" ); if( dump_box ) file_param.mode |= LSMASH_FILE_MODE_DUMP; lsmash_file_t *file = lsmash_set_file( root, &file_param ); if( !file ) return BOXDUMPER_ERR( "Failed to add a file into a ROOT.\n" ); if( lsmash_read_file( file, &file_param ) < 0 ) return BOXDUMPER_ERR( "Failed to read a file\n" ); /* Dump the input file. */ if( chapter ) { if( lsmash_print_chapter_list( root ) ) return BOXDUMPER_ERR( "Failed to extract chapter.\n" ); } else if( dump_box ) { if( lsmash_print_movie( root, "-" ) ) return BOXDUMPER_ERR( "Failed to dump box structure.\n" ); } else { lsmash_movie_parameters_t movie_param; lsmash_initialize_movie_parameters( &movie_param ); lsmash_get_movie_parameters( root, &movie_param ); uint32_t num_tracks = movie_param.number_of_tracks; for( uint32_t track_number = 1; track_number <= num_tracks; track_number++ ) { uint32_t track_ID = lsmash_get_track_ID( root, track_number ); if( !track_ID ) return BOXDUMPER_ERR( "Failed to get track_ID.\n" ); lsmash_media_parameters_t media_param; lsmash_initialize_media_parameters( &media_param ); if( lsmash_get_media_parameters( root, track_ID, &media_param ) ) return BOXDUMPER_ERR( "Failed to get media parameters.\n" ); if( lsmash_construct_timeline( root, track_ID ) ) return BOXDUMPER_ERR( "Failed to construct timeline.\n" ); uint32_t timeline_shift; if( lsmash_get_composition_to_decode_shift_from_media_timeline( root, track_ID, &timeline_shift ) ) return BOXDUMPER_ERR( "Failed to get timestamps.\n" ); lsmash_media_ts_list_t ts_list; if( lsmash_get_media_timestamps( root, track_ID, &ts_list ) ) return BOXDUMPER_ERR( "Failed to get timestamps.\n" ); fprintf( stdout, "track_ID: %"PRIu32"\n", track_ID ); fprintf( stdout, "Media timescale: %"PRIu32"\n", media_param.timescale ); lsmash_media_ts_t *ts_array = ts_list.timestamp; if( !ts_array ) { fprintf( stdout, "\n" ); continue; } for( uint32_t i = 0; i < ts_list.sample_count; i++ ) fprintf( stdout, "DTS = %"PRIu64", CTS = %"PRIu64"\n", ts_array[i].dts, ts_array[i].cts + timeline_shift ); lsmash_free( ts_array ); fprintf( stdout, "\n" ); } } lsmash_destroy_root( root ); return 0; }
int main( int argc, char *argv[] ) { if( argc < 2 ) return print_help( -1 ); int dump_box = 1; int chapter = 0; char *filename; if( argc > 2 ) { if( !strcasecmp( argv[1], "--box" ) ) DO_NOTHING; else if( !strcasecmp( argv[1], "--chapter" ) ) chapter = 1; else if( !strcasecmp( argv[1], "--timestamp" ) ) dump_box = 0; else return print_help( -1 ); filename = argv[2]; } else { if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) ) return print_help( 0 ); filename = argv[1]; } #ifdef _WIN32 _setmode( _fileno(stdin), _O_BINARY ); #endif lsmash_file_mode mode = LSMASH_FILE_MODE_READ; if( dump_box ) mode |= LSMASH_FILE_MODE_DUMP; lsmash_root_t *root = lsmash_open_movie( filename, mode ); if( !root ) { fprintf( stderr, "Failed to open input file.\n" ); return -1; } if( chapter ) { if( lsmash_print_chapter_list( root ) ) return BOXDUMPER_ERR( "Failed to extract chapter.\n" ); } else if( dump_box ) { if( lsmash_print_movie( root, "-" ) ) return BOXDUMPER_ERR( "Failed to dump box structure.\n" ); } else { lsmash_movie_parameters_t movie_param; lsmash_initialize_movie_parameters( &movie_param ); lsmash_get_movie_parameters( root, &movie_param ); uint32_t num_tracks = movie_param.number_of_tracks; for( uint32_t track_number = 1; track_number <= num_tracks; track_number++ ) { uint32_t track_ID = lsmash_get_track_ID( root, track_number ); if( !track_ID ) return BOXDUMPER_ERR( "Failed to get track_ID.\n" ); lsmash_media_parameters_t media_param; lsmash_initialize_media_parameters( &media_param ); if( lsmash_get_media_parameters( root, track_ID, &media_param ) ) return BOXDUMPER_ERR( "Failed to get media parameters.\n" ); if( lsmash_construct_timeline( root, track_ID ) ) return BOXDUMPER_ERR( "Failed to construct timeline.\n" ); uint32_t timeline_shift; if( lsmash_get_composition_to_decode_shift_from_media_timeline( root, track_ID, &timeline_shift ) ) return BOXDUMPER_ERR( "Failed to get timestamps.\n" ); lsmash_media_ts_list_t ts_list; if( lsmash_get_media_timestamps( root, track_ID, &ts_list ) ) return BOXDUMPER_ERR( "Failed to get timestamps.\n" ); fprintf( stdout, "track_ID: %"PRIu32"\n", track_ID ); fprintf( stdout, "Media timescale: %"PRIu32"\n", media_param.timescale ); lsmash_media_ts_t *ts_array = ts_list.timestamp; if( !ts_array ) { fprintf( stdout, "\n" ); continue; } for( uint32_t i = 0; i < ts_list.sample_count; i++ ) fprintf( stdout, "DTS = %"PRIu64", CTS = %"PRIu64"\n", ts_array[i].dts, ts_array[i].cts + timeline_shift ); free( ts_array ); fprintf( stdout, "\n" ); } } lsmash_destroy_root( root ); return 0; }