Skip to content

Clarify buffer handling on stream/start messages during an active stream - #283

Merged
chrisuthe merged 1 commit into
mainfrom
clarify-in-place-format-change
Sep 16, 2026
Merged

chrisuthe merged 1 commit into
mainfrom
clarify-in-place-format-change

Conversation

@kahrendt

Copy link
Copy Markdown
Contributor

stream/start on an active stream let the client clear buffers "unless its
implementation requires it", leaving the transition timeline ambiguous when
the player format changes. The player role already assumes the timeline
continues, so state that explicitly.

  • messaging.md: an in-place stream/start updates the configuration without
    ending the stream. Each role defines how previously received data is
    handled, since artwork discards its pending image while the player keeps
    its chunks.
  • roles/player/v1.md: the format changes at a chunk boundary. Servers MUST
    continue the timeline without resending audio. Clients MUST decode each
    buffered chunk in the format in effect when it arrived. A player that
    cannot switch formats gaplessly SHOULD list a single sample_rate and
    channels and let the server resample.

Fixes #276

Breaking changes

  • aiosendspin server (since #336), sendspin-python-cli, SendspinDroid: flush on an in-place stream/start. Must continue the timeline instead.
  • sendspin-go, sendspin-rs example, aiosendspin client: swap the decoder immediately, misdecoding queued old-format chunks. Must apply the new format per chunk.

sendspin-js, SendspinKit, and sendspin-cpp already conform. The conformance suite needs a scenario for a format change with chunks in flight.

…at change

`stream/start` on an active stream let the client clear buffers "unless its
implementation requires it", leaving the transition timeline ambiguous when
the player format changes. The player role already assumes the timeline
continues, so state it explicitly and drop the escape hatch.

- messaging.md: an in-place `stream/start` updates the configuration without
  ending the stream. Each role defines how previously received data is
  handled, since artwork discards its pending image while the player keeps
  its chunks.
- roles/player/v1.md: the format changes at a chunk boundary. Servers MUST
  continue the timeline without resending audio. Clients MUST decode each
  buffered chunk in the format in effect when it arrived. A player that
  cannot switch formats gaplessly SHOULD list a single `sample_rate` and
  `channels` and let the server resample.

Fixes #276
@chrisuthe

Copy link
Copy Markdown
Member

Perfect, this is a good clarification.

@chrisuthe
chrisuthe merged commit f7960f5 into main Sep 16, 2026
1 check passed
@chrisuthe
chrisuthe deleted the clarify-in-place-format-change branch September 16, 2026 12:27
maximmaxim345 added a commit to Sendspin/aiosendspin that referenced this pull request Oct 1, 2026
# What does this implement/fix?

<!-- Quick description and explanation of changes. -->

A mid-stream player format change restarted the stream near the
playhead, resending up to a buffer's worth of audio the player already
had. That only works for players that clear their buffer on
`stream/start`, but most keep it (`sendspin-js`, `sendspin-cpp`,
`sendspin-cli` and the `aiosendspin` client).

Now the new format starts where the last chunk in the old format ended,
as the spec requires. Old-format audio still queued on the connection is
sent first instead of dropped, and an in-place `stream/start` for any
role now queues behind that role's queued messages.

When a player switches to a format another group member already uses,
the shared encoder's chunks don't line up with the old end, so it
resumes at the next chunk boundary. That leaves a gap of less than one
chunk.

> [!NOTE]
> This can break clients that don't follow the spec and clear their
buffer on an in-place `stream/start`, like SendSpinDroid. They get a gap
at a format change until playback reaches the new audio.
> Switching formats on a live stream is rare enough that we accept this
in that edge case, since playback recovers on its own.

**Related issue or spec PR (if applicable):**

- Sendspin/spec#283
- Sendspin/spec#294

## Types of changes

<!--
Tick exactly one box. CI (.github/workflows/pr-labels.yaml) applies the
matching label, and the release notes use it to sort this change.
-->

- [x] Bugfix (non-breaking change which fixes an issue): `bugfix`
- [ ] New feature (non-breaking change which adds functionality):
`new-feature`
- [ ] Enhancement to an existing feature: `enhancement`
- [ ] Breaking change (changes the public API or the protocol in a way
that is not backwards compatible): `breaking-change`
- [ ] Refactor (no behavior change): `refactor`
- [ ] Documentation only: `documentation`
- [ ] Maintenance / chore: `maintenance`
- [ ] CI / workflow change: `ci`
- [ ] Dependencies bump: `dependencies`
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.

Clarify buffer handling when an in-place stream/start changes the player codec or sample rate

2 participants