From 02ed1b15d7329769b3f615bd425f886b4e7cdac6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 9 Feb 2026 17:38:14 -0500 Subject: [PATCH] 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 --- src/zm_ffmpeg_input.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index 4bc3bea1b..d0b469c89 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -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) {