Skip to content

fix(#91): a VOD stream plays out to its end, then reports done+ended - #113

Merged
fishloa merged 1 commit into
mainfrom
fix/vod-tail-91
Aug 4, 2026
Merged

fishloa merged 1 commit into
mainfrom
fix/vod-tail-91

Conversation

@fishloa

@fishloa fishloa commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Closes #91. Delegated to deepseek-v4-flash via crush; $0.04 for the session. I verified every claim independently — details below.

Root cause — two compounding faults, both measured

  1. The feed burst overflowed the worklet's 16 s ring. _pumpAudioInner drained all decoded PCM and posted it to the worklet in one tick. A clip decodes far faster than it plays, so the ring overflowed and silently dropped audio. Measured: audioSec froze at 16.00 with ahead=2.87 — about the ring's capacity, i.e. 2.87 s of fed audio physically unplayable.
  2. done was never reached while buffered audio was still queued. The end-of-stream path flushed and emitted ended while roughly a segment remained posted-but-unplayed. And done was a transient on the emitted stat object, so a later stats emission cleared it — the first observed run froze at audioSec=14.88, done=false, permanently.

That second half is the worse one: a host could not distinguish "stream finished" from "player froze".

Fix

  • Pace posting — PCM is drained into a JS-side queue and reposted incrementally, capped at _AUDIO_LEAD_S of play-ahead, so the ring cannot overflow.
  • Drain to completion — a new end-of-stream drain keeps posting queued PCM and waits for play-ahead to reach ~0 before reporting the end.
  • Persistent done — set as a field on _stats so a later emission cannot clear it; ended fires exactly once.

Verification — I re-ran all of it, and negative-controlled the tests

gate result
cargo fmt --all --check clean
clippy --workspace --all-targets -- -D warnings zero
cargo nextest run --workspace 157/157
bun test (packages/player) 60/60
tsc --noEmit -p packages/player exit 0
bun run test:streams 21/21 (14 existing + 7 new)

The new tests genuinely bite. I reverted only skyfire-player.js to its pre-fix state, kept the tests, and re-ran:

unfixed:  7 failed   (1.8–2.1s each)
fixed:    7 passed   (19–20s each)

The timing difference is the tell: unfixed, the tests short-circuit in ~2 s because the old code declared the stream over almost immediately. Fixed, each takes the clip's full ~19 s because it now actually waits for playout. A test that passed by accident could not show that asymmetry.

Measured on arte (advertised 18.76 s): before 14.88–16.01 s played with done=false; after 18.85 s, done=true, ended once, ahead→0.03, queued=0. Within 0.1 s of advertised, against a 1 s criterion.

Scope

Additive only — packages/player/skyfire-player.js and 70 new lines in web/tests/streams.spec.mjs. No existing test was modified, which I checked by diffing rather than trusting the report.

Note on the oracle

The assertion is against the playlist's own advertised total (sum of EXTINF), computed per-stream at test time, not a hardcoded number. That was chosen deliberately so it cannot be satisfied by tuning a constant, and it covers all seven fixtures rather than the two I originally measured.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Qsz3AufTr5z6MPZJzkoSCT

The player reported `done`/emitted `ended` while roughly a segment of
audio was still queued-but-unplayed, and it fed the worklet in one burst
that overflowed the fixed 16-second ring and silently dropped audio — so
the last ~4 s of a VOD clip never played and the host could not tell a
finished stream from a freeze.

Pace PCM posting to the worklet by the play-ahead so the ring never
overflows, then drain to completion (play-ahead ~0) before setting the
persistent `done` flag and emitting `ended` exactly once. New browser
tests assert each fixture's audio plays out within 1 s of the advertised
EXTINF total and that `done`/`ended` fire; all said tests fail on the
unfixed player.

Browser gate: 21 passed (14 pre-existing + 7 new), Rust gate 157/157.
@fishloa
fishloa merged commit 02dbad2 into main Aug 4, 2026
4 checks passed
@fishloa
fishloa deleted the fix/vod-tail-91 branch August 4, 2026 22:35
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.

Player: stream tail never plays and no ended/done at end of a VOD playlist

1 participant