v2.6.1 — chained Ogg-Opus + transcoder flap survival
Three-layer fix for the long-running auto-transcoder failure on robodj-style sources that rotate their Ogg logical stream between tracks. Pure Go end-to-end (no CGO introduced).
Fixed
- Chained Ogg-Opus decoder. Replaced
kazzmir/opus-go'sPacketReader(which pins the bitstream serial on first page and errors out on every new BOS) with a small pure-Go Ogg page reader that handles RFC 3533 §3 chained logical streams transparently. Each BOS resets the per-stream state (channels, preskip) and re-initialises the decoder; audio decoding is continuous across track-boundary rotations. - Codec swap to libopus-transpiled, lenient with real-world packets. Initial cut used
pion/opusfor the codec. Strict RFC 6716 validation rejected several real-world packets per minute (code-1 even-payload, ≤120 ms duration, VBR overrun, CBR divisibility) — each reject was a silent ~20 ms gap audible as a skip. Switched tokazzmir/opus-go's codec, which is libopus transpiled to pure Go viaccgo(no cgo) and matches reference-C tolerance.pion/opusdropped fromgo.mod. - Transcoded outputs survive source flaps.
HealthMonitor.checkskips auto-remove for streams flaggedIsTranscoded. A brief upstream disconnect (8-36 s is routine for robodj) no longer turns into an end-to-end outage with the player hitting 404 and giving up. - No stale audio burst on source-resume. New
Stream.FlushAtHeadbumps a per-stream flush generation, wakes every listener, and snapsMinListenerOffsetto the current buffer head. Listeners (existing and any auto-reconnecting subscribers) skip the stale buffered MP3 from before the gap and pick up at the live edge. - Stalled-pump watchdog kept as a safety net (extended 8 s → 30 s) — chain rotation no longer triggers it, so it only fires on a genuine decoder hang.
Upgrade notes
Drop-in. No config changes, no public API changes.
Full changelog: https://github.com/DatanoiseTV/tinyice/blob/v2.6.1/CHANGELOG.md