Skip to content

Arbitrate every activation and gate audio on playback - #314

Draft
maximmaxim345 wants to merge 8 commits into
mainfrom
fix/activity-based-admission
Draft

maximmaxim345 wants to merge 8 commits into
mainfrom
fix/activity-based-admission

Conversation

@maximmaxim345

@maximmaxim345 maximmaxim345 commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Fixes a gap where a pairing connection held next to a playback connection could add 'playback' without arbitration, so two servers could play on one client.
Instead of adding another special case for this, admission is now based on activities, as suggested in #299.

A client still admits one connection by default. One that admits more follows these rules:

  • At most one admitted connection has 'playback', and at most one has 'pairing'.
  • A connection with empty activities is only admitted alone.

Every server/activate is now arbitrated, not only the first, with the existing ranking and exceptions. 'playback' gates the audio: the player and visualizer streams and the source role's start. Artwork, metadata and controller state don't need it, but a server still declares it while the client's group plays, so it can also take over a client that only shows metadata. The client also ignores the audio streams and start on a connection without 'playback', since with unpaired access a pairing connection can still hold these roles. Player commands are ignored the same way, but only while another connection has 'playback'.

With this, a pairing connection that adds 'playback' displaces the current playback connection, the same way a new connection would.

Alternative that keeps the current model and patches each gap with its own rule:

Closes #299

Breaking change

Clients that admit only one connection keep their admission behavior. Clients that hold a playback and a pairing connection now arbitrate every activation, which changes three outcomes the old text defined:

  • A new playback connection is admitted next to another server's pairing connection, instead of displacing it (or being rejected during a pairing attempt).
  • A new pairing connection is rejected while the playback connection is pairing itself, instead of being held next to it.
  • A new pairing connection displaces a pairing-only connection unless its attempt is in progress, instead of being rejected.

All three follow from the general rules. The second and third match what a client with one connection already does, and the first mirrors the existing case of a pairing connection arriving during playback. The alternative only changes the second one and keeps the other two as they are today.

Servers must not start a player or visualizer stream or send the source start command without 'playback', and must end the player and visualizer streams before dropping it. Clients ignore them without 'playback', and a source client stops its input stream when 'playback' is dropped. Player commands don't need 'playback', so a server can still change the volume of a stopped speaker.

Servers should declare 'playback' while the client's group plays or while they use its source, and drop it only once that ends, not at a track change or a move to another playing group: since every server/activate is now arbitrated, dropping and re-adding 'playback' can hand the client to another server.

Limitations and open questions

  • An idle connection is still only admitted alone, so a client can't hold several servers, for example to show each one's controls and cover. We should be able to drop that limit in a follow-up: since 'playback' now only gates audio, idle connections could coexist, a client that loses 'playback' to another server would send client/leave to the old one instead of closing it, and a client that can only show one server sets its own cap. Should we do that?

  • After a successful pairing, the new server is closed when it goes idle while another server plays, and a server that stops while another server pairs is closed too. Which one should keep the client?

  • A later activation that adds 'pairing' while another server has an attempt in progress closes the connection, so a playback server also loses playback. Keeping it open would need a pair/abort reason that doesn't close the connection.

  • I accept the Contributor License Agreement for this contribution.

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.

Should a held pairing connection that adds the playback activity trigger arbitration?

1 participant