Repository navigation
fix(player): keep picture and sound level when coming back to the app - #32
Merged
Merged
Conversation
Coming back to the app reopened the picture and started the sound process again on every activation, even when the window stayed in sight and the picture had kept pace. Restarting ffplay left a gap and a small jump back in the sound, which showed as a hitch each time. Now activation compares the sound's clock with the picture's playhead and reopens only when the picture trails by 200ms or more, which happens when the window was out of sight. The reopened picture takes over the sound that kept playing instead of opening a new one, and a deactivation during the reopen is carried to the new picture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
This pull request conflicts with |
While the window is out of sight VideoPlayer stops the picture, but the sound plays on. Coming back reopened the picture there, so the frame the window was hidden on stayed up for the 200-400ms ffmpeg needed to seek, and then jumped. Now, while the app is inactive, a goroutine checks whether the picture has fallen behind the sound. Once it has, it opens a decoder where the sound is and keeps it on the frame the sound is in. Coming back hands that frame and decoder to a new VideoPlayer, which takes the running sound over, so the picture shows the right frame at once. Returning before the picture has fallen behind falls back to the drift check and reopen from the previous commit. The background code moves to playback_background.go, and replacing the playing film goes through playReplace, which also stops its background decoder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve the conflict with the volume control (#31). A picture taking over from the background keeps the sound and the volume keep had, and soundAt reads the sound clock through a returning field from the moment the app comes back until the picture takes over or fails, since backgroundAt is cleared then. backgroundResumeAt becomes soundClock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Switching away from Kando and back made playback hitch. There were three causes:
Changes:
playerDidResignActiverecords the sound's clock and startsfollowSound, a goroutine that checks every 100ms whether the picture has fallen at leastbackgroundDrift(200ms) behind the sound. That only happens while the window is out of sight. Once it has, it opens a decoder at the sound's position and keeps it on the frame the sound is in.playerDidBecomeActivehands that frame and decoder to a new VideoPlayer (takeBackgroundPicture), which takes over the running sound and its volume. If the picture never fell behind, nothing happens. If the return comes before the stall was noticed, it falls back to the drift check and reopen, which also keeps the running sound.frameDecoderthen gives the frame the sound is in at that moment, for what is left of it, so from the first moving frame the picture follows the sound clock.soundAtreads the sound clock throughreturningfrom the moment the app comes back until the picture takes over or fails.backgroundResumeAtbecomessoundClock.playReplace, which also stops its background decoder. The background code lives inplayback_background.go.Screenshots
The real app ran with a fake HOME and a test clip that has a seconds counter. The app was hidden for about 4s and then brought back, and each pair below shows the first frame after the window appears and the frame 100ms later, at full size.
Before (main): the frame the window was hidden on (0:11) shows first, then the picture jumps to 0:16, where the sound is.
After: the first frame is already where the sound is (35, handed over at 35.67s), and the gradient strip has moved on 100ms later.
Verification
For each run I recorded the window with
screencapturearound the return, measured per-frame change in the moving gradient strip, read the counter at 30fps, and checked whether the ffplay PID changed. Runs where someone clicked the test window, or where the hide did not take effect, were discarded.Hidden for about 4s, then brought back:
-ssevery timeWith this PR, 5 of 6 runs had no gap over 60ms in the 2s after the return. One run had gaps of 66–78ms, which steady playback with no switching shows too: two to five per 2s, from the screen recording's timing. Timing logs from a local build show the handover finishing 10–20ms after activation, and the decoder joining the sound 15–30ms later, as the window shows.
With the window kept in sight (4 runs), the picture played on, nothing was reopened, and the sound process stayed the same.
Tests:
TestPlaybackResyncsWhenAppReturns: the drift check, clamping at the end, and a user-paused film.TestPlaybackHandsTheSoundToTheReopenedPicture: the reopen fallback keeps the same sound and volume.TestPlaybackFollowsTheSoundBehindTheWindow: a stalled picture is noticed. Just after the return,soundAtfollows the sound. The background picture takes over without reopening the film, keeping the same sound, and the return's clock is cleared.TestHandedOverPictureJoinsTheSound: with real ffmpeg on a 10fps clip, the held frame lasts 1ms. With the sound at 450ms, the next frame given is the one from 400 to 500ms, lasting 50ms, and the frames after it last their full length.make test,go vet,go tool mygo vet, and the player and volume tests with-race.