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; }
void lwlibav_setup_timestamp_info ( lwlibav_file_handler_t *lwhp, lwlibav_video_decode_handler_t *vdhp, lwlibav_video_output_handler_t *vohp, int64_t *framerate_num, int64_t *framerate_den ) { AVStream *video_stream = vdhp->format->streams[ vdhp->stream_index ]; if( vdhp->frame_count == 1 || lwhp->raw_demuxer || ((lwhp->format_flags & AVFMT_TS_DISCONT) && !(vdhp->lw_seek_flags & SEEK_DTS_BASED)) || !(vdhp->lw_seek_flags & (SEEK_DTS_BASED | SEEK_PTS_BASED | SEEK_PTS_GENERATED)) ) { *framerate_num = (int64_t)video_stream->avg_frame_rate.num; *framerate_den = (int64_t)video_stream->avg_frame_rate.den; return; } video_frame_info_t *info = vdhp->frame_list; int64_t first_ts; int64_t largest_ts; int64_t second_largest_ts; uint64_t first_duration; uint64_t stream_timebase; int strict_cfr; if( !(lwhp->format_flags & AVFMT_TS_DISCONT) && (vdhp->lw_seek_flags & (SEEK_PTS_BASED | SEEK_PTS_GENERATED)) ) { first_ts = info[1].pts; largest_ts = first_ts; second_largest_ts = first_ts; first_duration = info[2].pts - info[1].pts; stream_timebase = first_duration; strict_cfr = (first_duration != 0); for( uint32_t i = 2; i <= vdhp->frame_count; i++ ) { uint64_t duration = info[i].pts - info[i - 1].pts; if( duration == 0 ) { if( vdhp->lh.show_log ) vdhp->lh.show_log( &vdhp->lh, LW_LOG_WARNING, "Detected PTS %"PRId64" duplication at frame %"PRIu32, info[i].pts, i ); goto fail; } if( strict_cfr && duration != first_duration ) strict_cfr = 0; stream_timebase = get_gcd( stream_timebase, duration ); second_largest_ts = largest_ts; largest_ts = info[i].pts; } } else { uint32_t prev; uint32_t curr; if( vdhp->order_converter ) { prev = vdhp->order_converter[1].decoding_to_presentation; curr = vdhp->order_converter[2].decoding_to_presentation; } else { prev = 1; curr = 2; } first_ts = info[prev].dts; largest_ts = first_ts; second_largest_ts = first_ts; first_duration = info[curr].dts - info[prev].dts; stream_timebase = first_duration; strict_cfr = (first_duration != 0); for( uint32_t i = 2; i <= vdhp->frame_count; i++ ) { if( vdhp->order_converter ) { prev = vdhp->order_converter[i - 1].decoding_to_presentation; curr = vdhp->order_converter[i ].decoding_to_presentation; } else { prev = i - 1; curr = i; } uint64_t duration = info[curr].dts - info[prev].dts; if( duration == 0 ) { if( vdhp->lh.show_log ) vdhp->lh.show_log( &vdhp->lh, LW_LOG_WARNING, "Detected DTS %"PRId64" duplication at frame %"PRIu32, info[curr].dts, curr ); goto fail; } if( strict_cfr && duration != first_duration ) strict_cfr = 0; stream_timebase = get_gcd( stream_timebase, duration ); second_largest_ts = largest_ts; largest_ts = info[curr].dts; } } stream_timebase *= video_stream->time_base.num; uint64_t stream_timescale = video_stream->time_base.den; uint64_t reduce = reduce_fraction( &stream_timescale, &stream_timebase ); uint64_t stream_duration = (((largest_ts - first_ts) + (largest_ts - second_largest_ts)) * video_stream->time_base.num) / reduce; double stream_framerate = (vohp->frame_count - (vohp->repeat_correction_ts ? 1 : 0)) * ((double)stream_timescale / stream_duration); if( strict_cfr || !lw_try_rational_framerate( stream_framerate, framerate_num, framerate_den, stream_timebase ) ) { if( stream_timebase > INT_MAX || (uint64_t)(stream_framerate * stream_timebase + 0.5) > INT_MAX ) goto fail; uint64_t num = (uint64_t)(stream_framerate * stream_timebase + 0.5); uint64_t den = stream_timebase; if( num && den ) reduce_fraction( &num, &den ); else if( video_stream->avg_frame_rate.num == 0 || video_stream->avg_frame_rate.den == 0 ) { num = 1; den = 1; } else goto fail; *framerate_num = (int64_t)num; *framerate_den = (int64_t)den; } return; fail: *framerate_num = (int64_t)video_stream->avg_frame_rate.num; *framerate_den = (int64_t)video_stream->avg_frame_rate.den; return; }