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; }