Skip to content

Fix unreliable small custom seek steps (e.g. 1 ms) on single key press (#12027)#12033

Merged
niksedk merged 1 commit into
SubtitleEdit:mainfrom
muaz978:fix/seek-small-step-accumulate
Jul 1, 2026
Merged

Fix unreliable small custom seek steps (e.g. 1 ms) on single key press (#12027)#12033
niksedk merged 1 commit into
SubtitleEdit:mainfrom
muaz978:fix/seek-small-step-accumulate

Conversation

@muaz978

@muaz978 muaz978 commented Jun 30, 2026

Copy link
Copy Markdown

Fix unreliable small custom seek steps on single key press

Fixes #12027.

Problem

When a custom video seek shortcut is set to a very small value (the reporter used 1 ms on "D" / "A"), a single key press usually does nothing, but holding the key works. It used to work in older 4.x builds.

Root cause is in the relative seek path. MoveVideoPositionMs re-reads vp.Position on every press and adds the delta:

SetVideoPositionSeconds(vp.Position + (ms / 1000.0));

While the player is playing or running in frame mode, the libmpv backend's Position getter returns the live time-pos, which lags an in-flight (async) seek rather than the value just requested. So two quick presses both read the same not-yet-updated base position and each computes base + 1 ms, and the second press is effectively lost. With a tiny step this means single presses "sometimes work, mostly don't", while holding the key (auto-repeat) fires fast enough to accumulate across a frame boundary and appears to work.

(When paused and not in frame mode the backend already caches the last seek target, so that specific case was reliable; playing and frame mode were not.)

Fix

Track the intended position across successive relative seeks so every press moves exactly its delta instead of re-reading a possibly stale position:

  • MoveVideoPositionMs accumulates from a tracked target while playback is paused and the player still agrees with that target (within half a second).
  • The tracker resyncs to the real player position whenever playback is running or the player diverges by more than half a second (the user played, clicked the waveform, or jumped to a cue).
  • SetVideoPositionSeconds, the shared choke point for every position change, clears the tracker, and MoveVideoPositionMs re-arms it right after, so only chained relative steps accumulate.

Behaviour for the normal larger steps (500 ms, 1 s, etc.) is unchanged; they already exceeded a frame, and they still resync correctly.

Files

  • src/ui/Features/Main/MainViewModel.cs

Note on sub-frame steps

A step well below one frame (1 ms versus roughly 33 to 40 ms per frame) still cannot change the displayed video frame on a single press, but the timecode and waveform cursor now advance by exactly the configured amount each press, which is what fine subtitle timing relies on.

SubtitleEdit#12027)

Custom video seek shortcuts re-read vp.Position on every press and add the
delta. While playing or in frame mode the player reports the live time-pos,
which lags an in-flight seek, so consecutive small steps compute from the same
stale base and are lost. A 1 ms custom seek then "sometimes works, mostly
doesn't" on single presses yet works while the key is held (auto-repeat
accumulates enough to land on a new frame).

Track the intended position across successive relative seeks so each press
moves exactly its delta. The tracker resyncs to the real position whenever
playback is running or the player diverges by more than half a second (the user
played, clicked the waveform, or jumped to a cue), and SetVideoPositionSeconds
clears it for any non-relative position change.
@niksedk niksedk merged commit a27e710 into SubtitleEdit:main Jul 1, 2026
2 checks passed
pull Bot pushed a commit to matrixer2306/subtitleedit that referenced this pull request Jul 1, 2026
…llow-up)

The waveform playhead is a smoothed estimate that extrapolates on a wall clock
during playback. On pause it kept gliding until mpv's IsPlaying flipped ~100 ms
later, then snapped to mpv's still-lagging reported position - so the cursor
didn't stop on the keypress and jittered backward.

Freeze the estimate the instant a pause is requested (Pause / TogglePlayPause set
_pauseRequested, which the estimator honors before IsPlaying flips), and hold it
for a short settle window instead of snapping to the stale reported position;
release on a large gap (real seek) or once mpv settles. Play paths clear the
flag, and it auto-clears if the pause doesn't take, so the cursor can't wedge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Problem with customizable millisecond hotkey

2 participants