mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-03-24 16:51:47 -04:00
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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 (
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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=[';
|
||||
|
||||
@@ -243,7 +243,7 @@ for ( $i = 0; $i < count($speeds); $i++ ) {
|
||||
}
|
||||
}
|
||||
|
||||
$initialDisplayInterval = 1000;
|
||||
$initialDisplayInterval = 100;
|
||||
if (isset($_REQUEST['displayinterval']))
|
||||
$initialDisplayInterval = validCardinal($_REQUEST['displayinterval']);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user