From 2b14e9044e2d93ea320146b27e09aaa1db0a9684 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Feb 2026 13:16:07 -0500 Subject: [PATCH] fix: improve montage review playback smoothness and fix video seek overshoot Fix FFmpeg initial seek overshooting to a future keyframe when the requested timestamp falls between keyframes. After the initial AVSEEK_FLAG_FRAME seek, detect if the returned frame is past the target and re-seek backward to get the correct keyframe before it. On the JS side, preserve fractional seconds throughout the playback pipeline: remove Math.floor() truncation in mmove() and setSpeed(), and use parseFloat instead of parseInt for currentTimeSecs initialization. Reduce initial display interval from 1000ms to 100ms for ~10fps refresh rate during review playback. Co-Authored-By: Claude Opus 4.6 --- src/zm_ffmpeg_input.cpp | 12 ++++++++++++ web/skins/classic/views/js/montagereview.js | 7 +++---- web/skins/classic/views/js/montagereview.js.php | 4 ++-- web/skins/classic/views/montagereview.php | 2 +- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index d0b469c89..130e54d32 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -283,6 +283,18 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) { Warning("Unable to get frame."); return nullptr; } + + // If the initial seek landed past the target (on a future keyframe), + // re-seek backward to get the keyframe before the target instead. + if (frame->pts > seek_target) { + Debug(1, "Initial seek overshot: frame pts %" PRId64 " > seek_target %" PRId64 ", seeking backward", + frame->pts, seek_target); + ret = av_seek_frame(input_format_context, stream_id, seek_target, + AVSEEK_FLAG_BACKWARD); + if (ret >= 0) { + get_frame(stream_id); + } + } } // end if ! frame if ( diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index 0bb4f6412..fc6a80bdb 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -825,8 +825,8 @@ function mmove(event) { if ( mouseisdown ) { // only do anything if the mouse is depressed while on the sheet const relx = event.target.relMouseCoords(event).x; - const sec = Math.floor(minTimeSecs + rangeTimeSecs / event.target.width * relx); - if (parseInt(sec)) outputUpdate(sec); + const sec = minTimeSecs + rangeTimeSecs / event.target.width * relx; + if (sec) outputUpdate(sec); } } @@ -890,7 +890,7 @@ function setSpeed(speed_index) { } currentSpeed = parseFloat(speeds[speed_index]); speedIndex = speed_index; - playSecsPerInterval = Math.floor( 1000 * currentSpeed * currentDisplayInterval ) / 1000000; + playSecsPerInterval = currentSpeed * currentDisplayInterval / 1000; setCookie('speed', speedIndex); showSpeed(speed_index); timerFire(); @@ -1475,7 +1475,6 @@ function loadFrames(zm_events) { if (last_frame && (frame.EventId != last_frame.EventId)) { last_frame = null; } - //console.log(date, frame.TimeStamp, frame.Delta, frame.TimeStampSecs); if (last_frame) { frame.PrevFrameId = last_frame.Id; last_frame.NextFrameId = frame.Id; diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index a62ae54d8..601f645f4 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -177,9 +177,9 @@ var maxTime='$maxTime'; "; echo 'var rangeTimeSecs='.($maxTimeSecs - $minTimeSecs + 1).";\n"; if ( isset($defaultCurrentTimeSecs) ) { - echo 'var currentTimeSecs=parseInt('.$defaultCurrentTimeSecs.");\n"; + echo 'var currentTimeSecs=parseFloat('.$defaultCurrentTimeSecs.");\n"; } else { - echo 'var currentTimeSecs=parseInt('.$minTimeSecs.");\n"; + echo 'var currentTimeSecs=parseFloat('.$minTimeSecs.");\n"; } echo 'var speeds=['; diff --git a/web/skins/classic/views/montagereview.php b/web/skins/classic/views/montagereview.php index 9fdaa658a..db9b91baf 100644 --- a/web/skins/classic/views/montagereview.php +++ b/web/skins/classic/views/montagereview.php @@ -243,7 +243,7 @@ for ( $i = 0; $i < count($speeds); $i++ ) { } } -$initialDisplayInterval = 1000; +$initialDisplayInterval = 100; if (isset($_REQUEST['displayinterval'])) $initialDisplayInterval = validCardinal($_REQUEST['displayinterval']);