Fix FFmpeg EOF handling for unknown durations - #116
Draft
Blackspirits wants to merge 1 commit into
Draft
Blackspirits wants to merge 1 commit into
Blackspirits wants to merge 1 commit into
Conversation
Track observed video/audio end positions, drain libswresample before audio EOF, and finish unknown-duration video/audio playback without jumping back to zero or staying stuck in playing state.
Blackspirits
force-pushed
the
audit/ffmpeg-unknown-duration-eof-3e4d
branch
from
September 15, 2026 07:32
f221094 to
d09bda7
Compare
Blackspirits
commented
Sep 15, 2026
Blackspirits
left a comment
Owner
Author
There was a problem hiding this comment.
Final adversarial review on the frozen HEAD after CI SubtitleEdit#246. Rechecked the EOF marker semantics, serial/request guards, unknown-duration end-position fallback, audio tail accounting, and libswresample drain path. No blocking defect found in this tranche. CI SubtitleEdit#246 passed build + full suite on this exact HEAD; retry was not used. This remains an audit draft only: no merge/promotion intended.
This was referenced Sep 15, 2026
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.
Purpose
Second independent follow-up to merged upstream PR SubtitleEdit#14878 ("FFmpeg player: fix eight teardown, seek and clock defects") on current upstream main
3e4d052adc78464e71bbd5154880d935fd592960.SubtitleEdit#14878 correctly stopped clamping seeks to zero when a stream reports no duration. The wider audit found that EOF handling still assumes a known duration in several places, so the same raw/transport streams remain incorrect at the other end of playback.
This PR is deliberately separate from #115 (session lifetime/concurrency).
Findings fixed
1. Unknown-duration video jumped back to 0 at EOF
ReachEnd()unconditionally assigned:_pausedPosition = Durationand the Position getter returned
Durationwhile_endReached.For raw/unknown-duration media
Duration == 0, so reaching the real end rewound the reported playhead to 0.Video decode now carries the observed media end into its EOF marker. Known Duration remains authoritative; when duration is unknown, the observed end is retained instead of zero.
2. Video + audio with unknown duration did not wait for trailing audio
The video-EOS path used:
now >= Duration - 0.05With Duration 0 this is true immediately, so a video stream ending before its audio caused playback to stop without waiting for the remaining audio.
Audio now publishes EOF for the active seek serial together with the media position represented by all PCM actually queued. The presenter waits until that serial's audio EOF exists and the sink clock has reached the queued tail.
3. Audio-only unknown-duration streams never ended
PresentAudioOnlyTick()only called ReachEnd whenDuration > 0.Audio-only streams whose container/stream duration is unavailable therefore remained
IsPlaying=trueindefinitely after the decoder and device queue were finished.Audio-only playback now also completes from the serial-scoped audio EOF/drain state.
4. libswresample tail was never flushed
FFmpeg's libswresample API can buffer delayed output during sample-rate conversion. At end of conversion it must be drained with
swr_convert(..., NULL, 0).The old loop freed
SwrContextat EOF without draining it, so the final samples could be lost.This PR drains
swrafter the decoder reachesAVERROR_EOF, writes all remaining PCM to the sink, and only then publishes audio EOF.5. Timestamp-less streams had no usable observed end
Both video and audio converted missing timestamps to literal 0. Repeated timestamp-less frames therefore never advanced the observed media end, and seeks into such streams could keep treating later frames as if they started at zero.
Missing video timestamps are now inferred from the prior decoded end (or seek target for the first frame).
Missing audio timestamps advance from an inferred per-serial media position.
Serial / seek safety
Audio EOF is published only when:
Audio-only presentation also requires current == requested serial before accepting EOF, so a pending newer seek cannot be overwritten by an old EOF.
Regression coverage
Pure tests pin:
Existing seek/planar-format/queue/sink tests remain in place.
FFmpeg API contract used
The implementation follows FFmpeg's documented drain model:
AVERROR_EOF;swr_convertwith NULL input / zero input count until no delayed output remains.Branch state
3e4d052adc78464e71bbd5154880d935fd5929600252d55fd2f95752425c2e30b70363e6af7a93e8d09bda70cd79ea8a0aedce7a736e925fccd9a789Final CI
Authoritative run: SubtitleEdit#246 on
d09bda70cd79ea8a0aedce7a736e925fccd9a789Compiler warnings are confined to pre-existing accessibility/VoiceManager files outside this PR's changed set.
AI assistance: ChatGPT was used for adversarial EOF/timestamp/drain review of the merged FFmpeg-player hardening and to prepare focused regressions.