Music Assistant Player Groups and Sync Groups Management: Architecture and Implementation
Music Assistant server implements two distinct group player architectures—SyncGroupPlayer for synchronized multi-room playback and UniversalGroupPlayer for virtual multicast streaming—both inheriting from the core Player model and managed through a centralized controller with support for dynamic or static membership, idle-grace lifecycle handling, and configurable power controls.
The Music Assistant server repository provides sophisticated group player capabilities that allow multiple physical audio devices to function as unified logical targets. Understanding the architecture behind Music Assistant player groups and sync groups management is essential for developers integrating with the API or configuring complex multi-room audio setups. The implementation distinguishes between perfectly synchronized playback groups that maintain sample-accurate alignment and universal groups that stream a single encoded audio flow to heterogeneous devices.
Understanding Group Player Types
Music Assistant defines two primary group implementations, each serving different synchronization and protocol requirements.
SyncGroupPlayer for Multi-Room Synchronization
The SyncGroupPlayer class, defined in music_assistant/providers/sync_group/player.py, enables perfectly synchronized playback across multiple physical devices. This implementation ensures that audio output remains sample-accurate across all group members, making it ideal for multi-room scenarios where timing alignment is critical.
Key properties include:
sync_leader– Designates the current leader player that drives playback for the entire groupsupported_features– Dynamically aggregates capabilities from the leader, ensuring volume, mute, and DSP controls appear in the UIrequires_flow_mode– Mirrors the leader's flow-mode setting, forcing the group to use identical streaming parameters
UniversalGroupPlayer for Virtual Streaming
The UniversalGroupPlayer class, located in music_assistant/providers/universal_group/player.py, creates a virtual streaming endpoint that serves a single encoded audio flow (FLAC or MP3) to any number of members. This approach benefits protocols that only accept a single source connection but need to distribute audio to multiple physical endpoints.
Key characteristics include:
- Virtual streaming endpoint – Exposes URLs at
/ugp/<player_id>.flacor/ugp/<player_id>.mp3for client connections UGPStreaminstance – Tracks multicast stream state through thestreamattribute, withstream.doneindicating active statussupported_sample_rates– Derived fromCONF_UGP_OUTPUT_FORMATconfiguration, limiting the group to a single encoded bitrate
Group Lifecycle and Session Management
Both group types share a common lifecycle implemented in the base Player model (music_assistant/models/player.py).
Formation and Activation
Groups follow a three-phase lifecycle:
- Formation – The group automatically forms when playback begins via
cmd_play_media, establishing the initial member set and selecting a sync leader (for sync groups) or initializing the virtual stream (for universal groups) - Active session – The
is_active_sessionproperty returnsTruewhile the leader remains active or the virtual multicast stream is alive - Idle-grace handling – When playback stops, a timer (
IDLE_GRACE_SECONDS) maintains the group state to absorb rapid resume actions
Dissolution
If the idle-grace timer expires, the group dissolves and members revert to their prior independent states. For sync groups, dissolution also clears the sync_leader attribute.
Configuring Sync Group Players
Dynamic vs Static Members
Sync groups support two membership modes controlled by the CONF_DYNAMIC_GROUP_MEMBERS configuration entry:
- Dynamic groups – When enabled, configured members act as a preset list; the actual member set is built only when the group powers on via
on_config_updated - Static groups – Member lists from
CONF_GROUP_MEMBERSare stored in_attr_static_group_membersand used directly without runtime modification
Power Control Features
By default, groups lack power control capabilities. Users can enable a virtual power toggle by setting CONF_POWER_CONTROL to PLAYER_CONTROL_FAKE. When enabled, supported_features adds PlayerFeature.POWER, allowing UI-based power management even when underlying hardware lacks explicit power commands.
Configuring Universal Group Players
Output Format and Sample Rates
Universal groups derive their audio capabilities from the CONF_UGP_OUTPUT_FORMAT configuration entry. Unlike sync groups that pass through raw audio, universal groups transcode to a specific format, limiting supported_sample_rates to the configured output parameters.
Dynamic Member Handling
The is_dynamic property controls runtime membership changes. When CONF_DYNAMIC_GROUP_MEMBERS is enabled, the group advertises PlayerFeature.SET_MEMBERS, allowing clients to add or remove members via cmd_set_group_members while the stream is active.
Centralized Group Control Architecture
All group operations are orchestrated by the players controller in music_assistant/controllers/players/controller.py. This controller handles configuration updates, persists changes to member lists, and triggers formation or dissolution events.
Key controller responsibilities include:
- Watching for configuration changes via
on_config_updatedcallbacks - Processing group volume commands through
cmd_group_volume - Managing member notifications via
iter_group_members
Practical API Usage Examples
The following examples demonstrate how to interact with Music Assistant player groups programmatically:
# Retrieve a group player by its ID
group = mass.players.get_player("syncgroup_myroom")
assert group is not None
# Inspect aggregated supported features
print("Features:", group.supported_features)
# Dynamically modify group members (requires dynamic configuration)
await mass.players.cmd_set_group_members(
group.player_id,
["player1", "player2"]
)
# Initiate playback - triggers automatic group formation
await mass.players.cmd_play_media(
player_id=group.player_id,
media=PlayerMedia(url="https://example.com/song.mp3"),
)
# Stop playback - group remains alive for IDLE_GRACE_SECONDS
await mass.players.cmd_stop(group.player_id)
Summary
- Two distinct architectures –
SyncGroupPlayerprovides sample-accurate synchronization via a leader-follower model, whileUniversalGroupPlayercreates virtual multicast streams for protocol-constrained devices - Shared lifecycle – Both types use formation on playback start, active session tracking via
is_active_session, and idle-grace dissolution controlled byIDLE_GRACE_SECONDS - Flexible membership – Support for both static member lists (
CONF_GROUP_MEMBERS) and dynamic runtime configuration (CONF_DYNAMIC_GROUP_MEMBERS) - Centralized control – The players controller in
music_assistant/controllers/players/controller.pymanages all group state transitions and configuration persistence - Optional power control – Virtual power toggles can be exposed via
CONF_POWER_CONTROLwhen hardware lacks native power management
Frequently Asked Questions
What is the difference between SyncGroupPlayer and UniversalGroupPlayer?
SyncGroupPlayer maintains perfect synchronization across members by designating a sync_leader that drives playback timing, making it ideal for multi-room audio where alignment matters. UniversalGroupPlayer creates a virtual streaming endpoint that serves a single encoded audio stream (FLAC/MP3) to multiple clients, which is necessary when target devices only accept individual connections or require specific encoded formats.
How does dynamic member configuration work in Music Assistant groups?
When CONF_DYNAMIC_GROUP_MEMBERS is enabled in the player configuration, the group exposes PlayerFeature.SET_MEMBERS and allows runtime modification of the member list via cmd_set_group_members. For static configurations, members are defined in CONF_GROUP_MEMBERS and stored in _attr_static_group_members, remaining fixed during the group lifecycle.
What triggers the dissolution of a group player?
A group dissolves when the IDLE_GRACE_SECONDS timer expires after playback stops, or when the sync leader disappears (for sync groups). During the idle-grace period, the group remains active to accommodate rapid resume actions without requiring re-formation overhead.
How does the idle-grace period affect group playback?
The idle-grace period maintains the group structure temporarily after cmd_stop is called, preventing unnecessary formation overhead during brief pauses. If playback resumes within this window, the group continues with existing members; if the timer expires, the group dissolves and members revert to independent states, requiring full re-formation on the next play command.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →