Skip to content

Report playback from the null and stdout sinks so the player releases chunks - #89

Merged
chrisuthe merged 1 commit into
mainfrom
chrisuthe/fix/null-sink-playback-reports
Oct 15, 2026
Merged

chrisuthe merged 1 commit into
mainfrom
chrisuthe/fix/null-sink-playback-reports

Conversation

@chrisuthe

Copy link
Copy Markdown
Member

The null and stdout sinks never reported playback, so the player's sync task stayed in initial-sync priming: the codec header stayed held, every chunk queued behind it in the inbound ring, the player's quota overflowed, the library closed the connection, and the server reopened it in a loop. Both sinks now report every write as played against a virtual clock, so each chunk is released as it arrives.

Fixes #82

What changes

  • NullAudioSink::write() reports the frames it consumed, in the discard, muted and stdout branches alike, with a finish timestamp on a playhead that is never behind real time: max(now, playhead) + frames / rate. The clock runs ahead of real time by whatever the server sends ahead, which is what lets the sync task release each chunk on arrival; it is the pattern the library's own VirtualSinkListener uses. A short stdout write reports what it consumed, so the sync task's frame accounting does not drift.
  • The audio_sink.h note that said reporting was optional for instant sinks is corrected: a sink that never reports stalls the player before its first chunk.
  • tests/null_sink_test.cpp: every write is reported with its frame count on a clock that keeps advancing; nothing is reported before configure(), for a zero-length write, or after stop().

Verification

Against Music Assistant 2.11.0.dev with a 44.1 kHz / 24-bit FLAC track, the same player identity on the same server, -o null -d debug.

main before this change, three stream starts in a row:

I player: Stream started
I sendspin.sync_task: Processed new codec header: flac, 44100 Hz, 2 ch, 24-bit
W sendspin.inbound: Player over its buffer; dropping items until it drains
W sendspin.connection: No inbound ring space for a 17911-byte message within 100 ms: ring pinned behind held items (986344 bytes held); closing the connection
D sendspin.conn_mgr: Admitted connection closed by the client
W sendspin.inbound: Dropped 13 player items
I player: Stream ended

With this change, the player restarted on the same state directory while the server kept playing:

I sendspin.conn_mgr: Connection admitted: server_id=OraobU4leb48EaZTGPbLhNug8XjEZsKXv8qiczhEGTU
I player: Stream started
I sendspin.sync_task: Processed new codec header: flac, 44100 Hz, 2 ch, 24-bit

One stream start, no warning of any kind, 0.2% CPU over the following minute.

ctest 521/521 apart from ConfigMerge.AConfiguredControlSocketIsAbsolutizedUnderDaemonize, which fails only from a 105-byte worktree socket path; scripts/format.sh --check, scripts/smoke_test.sh and a -DSENDSPIN_CLI_WERROR=ON build are clean. Device-backed sinks already report and are unaffected.

… chunks

A sink that never calls on_frames_played leaves the player's sync task in
initial-sync priming: the codec header stays held, every chunk queues behind
it in the inbound ring, the player's quota overflows and the library closes
the connection, which the server then reopens in a loop (#82).

The null and stdout sinks now report every write as played against a virtual
clock that runs ahead of real time by the server's lead, so each chunk is
released on arrival. The audio_sink.h note that said reporting was optional
for instant sinks is corrected.
@chrisuthe
chrisuthe marked this pull request as ready for review October 15, 2026 23:31
@chrisuthe
chrisuthe merged commit 6285bf5 into main Oct 15, 2026
19 checks passed
@chrisuthe chrisuthe mentioned this pull request Oct 15, 2026
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.

Inbound ring overflows at FLAC stream start and the connection closes, in a reconnect loop

1 participant