Skip to content

fix: prevent buffer overruns from destroying stream start position#17

Merged
chrisuthe merged 1 commit intomasterfrom
fix/buffer-overrun-sync
Mar 4, 2026
Merged

fix: prevent buffer overruns from destroying stream start position#17
chrisuthe merged 1 commit intomasterfrom
fix/buffer-overrun-sync

Conversation

@chrisuthe
Copy link
Owner

Summary

  • Pre-playback overrun safety net: When the buffer fills before playback starts (server's initial burst), discard incoming audio instead of dropping oldest. Preserves the stream's starting position so all players begin at the same point.
  • SkipStaleAudio on late start: When a player's pipeline initializes late and the scheduled start time is in the past, skip forward through stale audio to the correct position instead of playing from the beginning of the buffer.
  • Buffer capacity: Increase default from 30s to 120s (~44MB) with 60s minimum enforcement, preventing stale user configs (e.g., old 8s value) from causing undersized buffers.
  • Dynamic BufferCapacity: Derive buffer_capacity in client/hello from actual PCM buffer duration × worst-case codec bitrate, instead of hardcoding 32MB.

Root cause

OPUS frames are tiny (~640 bytes/20ms) so the server blasts 40+ seconds of audio in seconds on LAN. With an 8s buffer (stale user config), 1746 overruns destroyed the beginning of the song. The player's scheduled start anchored to audio from 12.8s into the song, waited 12 seconds of silence, then started from ~35s — while the FLAC player started from 0s. The 22-second gap matched the buffer depth difference.

Test plan

  • Two Windows clients (FLAC + OPUS) in same group start from the same position
  • Player joining late (slow pipeline init) skips to correct position via SkipStaleAudio
  • Stale user config with small CapacityMs is overridden by 60s minimum
  • Logs show Buffer: Xms PCM → Y.ZMB advertised to server on startup
  • No regressions in single-player playback or track changes

🤖 Generated with Claude Code

The server's initial audio burst can far exceed the client's PCM buffer
capacity, especially for compact codecs like OPUS (~43s of audio in
seconds on LAN). The old "drop oldest" overrun behavior destroyed the
beginning of the stream, causing the player to start from the wrong
position — up to 22+ seconds into the song while other players started
from the beginning.

Three-layer fix:
1. Pre-playback overrun safety net: discard incoming audio instead of
   dropping oldest when the buffer fills before playback starts. This
   preserves the stream's starting position.
2. SkipStaleAudio: when playback starts and the first segment's
   timestamp is in the past (late pipeline init), skip forward to
   near-current audio so all players start at the same position.
3. Buffer capacity: increase default from 30s to 120s (~44MB) with
   60s minimum enforcement to prevent stale user configs from causing
   undersized buffers.

Also derives BufferCapacity (compressed bytes advertised to server in
client/hello) from the actual PCM buffer duration and worst-case codec
bitrate, instead of hardcoding 32MB.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@chrisuthe chrisuthe merged commit 583547f into master Mar 4, 2026
2 checks passed
chrisuthe added a commit that referenced this pull request Mar 6, 2026
fix: prevent buffer overruns from destroying stream start position
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.

1 participant