fix: correct seek threshold in FFmpeg_Input::get_frame to avoid sequential scan

The jump-ahead heuristic used 10*time_base.den*frame->duration which for
a typical stream (time_base=1/90000, 30fps duration=3000) produced a
threshold of ~30000 seconds, effectively never triggering. This caused
large seek jumps to decode every intermediate packet sequentially.

Replace with av_rescale_q-based 5-second threshold that correctly
converts to stream time_base units regardless of the time_base format.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Isaac Connor
2026-02-09 17:38:14 -05:00
parent b62285b90b
commit 02ed1b15d7

View File

@@ -314,12 +314,13 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) {
last_seek_request = seek_target;
// Normally it is likely just the next packet. Need a heuristic for seeking, something like duration * keyframe interval
#if LIBAVCODEC_VERSION_CHECK(60, 3, 0, 3, 0)
if (frame->pts + 10*input_format_context->streams[stream_id]->time_base.den * frame->duration < seek_target)
#else
if (frame->pts + 10*input_format_context->streams[stream_id]->time_base.den * frame->pkt_duration < seek_target)
#endif
// If more than 5 seconds behind seek target, use av_seek_frame to jump
// ahead instead of decoding packets sequentially (which is very slow for
// large gaps). 5 seconds is well beyond a typical GOP, making seek
// worthwhile even after decoding forward from the nearest keyframe.
int64_t seek_threshold = av_rescale_q(5 * AV_TIME_BASE, AV_TIME_BASE_Q,
input_format_context->streams[stream_id]->time_base);
if (frame->pts + seek_threshold < seek_target)
{
Debug(1, "Jumping ahead to %" PRId64, seek_target);
if (( ret = av_seek_frame(input_format_context, stream_id, seek_target, AVSEEK_FLAG_FRAME) ) < 0) {