v1.5.6 — receiver MSE+SourceBuffer (live-edge seek)
PC Nest Speaker 1.5.6
The receiver is the change. Desktop bytes are unchanged from 1.5.5 except the version string — the work all lives in receiver-audio.html on GitHub Pages, which the Cast SDK reloads on every cast (force-relaunch from 1.5.0 onwards).
What changed — receiver-side
MSE + SourceBuffer path (preferred, with <audio> fallback)
Problem. Plain <audio src=stream> on Cast Audio runs an opaque internal jitter buffer we can't read or write. Steady-state latency floored at ~1–1.5 s and currentTime writes against a live HTTP-chunked source are likely silent no-ops (per MDN, and confirmed by absence of a seek-probe in v1.5.4/1.5.5 logs because we never had a chance to fire one).
Fix. New connectHttpPcmMse() is now the preferred path:
MediaSource+addSourceBuffer('audio/webm; codecs="opus"')withmode='sequence'.fetch().body.getReader()pumps WebM clusters into the SourceBuffer.- A 250 ms drift loop calls
sb.remove(0, currentTime - 0.5s)to trim history andaudio.fastSeek(buffered.end - 0.2s)to push the playhead toward the live edge when we lag. - One-shot
mse-seek-probereports back whether the seek inside the MSE buffer actually moves the playhead.
Watchdog + automatic fallback
Cast Audio Chromium has historically had fetch().body.getReader() stalls (the existing CLAUDE.md notes this). 1.5.6's MSE path arms a 3.5 s first-byte watchdog. If no bytes arrive — or any of MediaSource.isTypeSupported, sourceopen, addSourceBuffer, fetch, or appendBuffer fail — the receiver tears down MSE and falls through to the plain <audio src> path that's been working since 1.5.0. So this is strictly additive: best case we cut 300–700 ms off latency, worst case we land on the same path as 1.5.5.
Why this approach (research summary)
Inspired by:
- shaka-player #9872 — production live
SourceBuffer.remove()+ seek-to-live pattern - w3c/media-source #291 — documents the
seekablewindow afterremove() - owntone-server cast.c — the only open-source path proven to hit ~400 ms total latency on Cast Audio. They use Google's built-in mirroring receiver
85CDB22Fwith Cast RTP / Opus /targetDelay=400. If MSE doesn't crack it, that's where 1.5.7 goes — but RTP is a 1–2 week rewrite, so we test cheaper options first.
What you'll see in the desktop log
Where 1.5.5 only emitted audio-src-set / audio-loadedmetadata / audio-canplay / audio-playing, 1.5.6 will additionally emit (when MSE works):
mse-attempt
mse-src-set
mse-sourceopen
mse-sb-added
mse-fetch-headers
mse-first-byte
mse-canplay
mse-playing
mse-seek-probe { seek_works: true|false, ... } ← the answer we've been waiting for
mse-drift-resync / mse-drift-catchup ← drift management firing
If MSE fails for any reason you'll see mse-fallback-to-audio reason=... followed by the same audio-* events as 1.5.5.
Files changed
pcnestspeaker-receiver/receiver-audio.html—connectHttpPcmMse()+ dispatcher + capabilities probe extended withmse_isTypeSupported_webm_opuspackage.json— version bump
Desktop binaries are unchanged from 1.5.5 functionally; rebuilding only so the version string and "About" dialog match the receiver.