Common Playback: the Virtual Media Player

August 13, 2026 · View on GitHub

Spec ID: OVOS-OCP-1 · Version: 2 · Status: Draft

This specification defines the Virtual Media Player — a single logical media player, scoped to a session, that every media voice command targets. It is the contract by which an orchestrator turns "play jazz", "pause", "next", "louder", and "stop the music" into observable playback state, and by which that state is mirrored to and from the host operating system over open standards (MPRIS).

OCP stands for Common Playback: common because one player arbitrates all media for a session regardless of which application, provider, or output device ultimately serves a track — the same way the intent stack gives one utterance one handler.

Dependencies: OVOS-MSG-1 (envelope and the context.session carrier), OVOS-SESSION-1 (session field registry), OVOS-SESSION-2 (session assignment and mutation boundaries), OVOS-PIPELINE-1 (a pipeline plugin conformant to OVOS-PIPELINE-1 that classifies playback and control utterances and dispatches into this surface), OVOS-STOP-1 (global stop cascade, of which media stop is one subscriber).

The key words MUST, MUST NOT, SHOULD, SHOULD NOT, MAY are used as in RFC 2119.


1. Scope

This specification defines: the Virtual Media Player abstraction and its session scoping; the player and media state model; the bus surface that requests playback and transport control and that reports state; the distinction between playback requests (start something) and control requests (act on what is already playing); and the MPRIS bridge by which the virtual player is exported to the host OS and by which externally-initiated, standards-compliant players become controllable by voice.

It does not define: how candidate media is discovered or ranked (a provider concern), how a URI is turned into bytes on a speaker (a backend concern), stream-extraction formats, the GUI rendering of now-playing, the NLU that classifies an utterance as media (OVOS-PIPELINE-1), or the on-disk/programming-language shape of any implementation. These are implementer concerns; this spec fixes only the observable contract.


2. The Virtual Media Player

A conformant orchestrator MUST present, per session, exactly one Virtual Media Player: a single addressable target that owns the session's now-playing track, its playback queue, and its transport state. "Virtual" because it is not a device and not an application — it is an arbitration point. Behind it, any number of playback backends, remote devices, or external OS players may do the actual work; in front of it, all voice commands and all status consumers see one coherent player.

This mirrors the rest of the platform: as the intent stack gives one utterance one winning handler (OVOS-PIPELINE-1), the media stack gives one session one player. A request never names a backend by necessity; it names the player, and the player routes.

Two request classes target the player and MUST be distinguished:

  • Playback requests — "play X", "open X". The player acquires media (via the pipeline and providers) and begins playback. §4.2.
  • Control requests — "pause", "resume", "next", "previous", "stop", "shuffle", seek, repeat. The player acts on its existing now-playing / queue without acquiring new media. §4.3.

A control request MUST NOT require the player to have been started by the voice OS: if the player is bridged to an external OS source (§6), control requests act on that source.


3. State model

The player exposes five orthogonal state axes: PlayerState (§3.1), MediaState (§3.2), loop/shuffle (§3.3), track state (§3.4), and PlaybackType (§3.5). Each axis has a fixed enumeration; an implementation MUST NOT report a value outside its axis, and SHOULD treat unknown received values as the axis's neutral member.

3.1 Player state

PlayerStateCodeMeaning
STOPPED0No active playback.
PLAYING1A track is advancing.
PAUSED2A track is loaded and held at a position.

Every axis member has a stable numeric code; the code, not the symbolic name, is what travels on the wire (§4.4).

3.2 Media state

MediaState describes the loaded media, independent of whether it is advancing: at minimum NO_MEDIA, LOADING, LOADED, BUFFERING, END_OF_MEDIA, INVALID_MEDIA. It is advisory for consumers (e.g. a GUI spinner) and MUST NOT be conflated with PlayerState.

3.3 Loop / shuffle

The player MUST track a loop mode (at minimum NONE, REPEAT, REPEAT_TRACK) and a boolean shuffle mode. Both are control-request targets (§4.3).

The player is the single writer of its own state. It emits a state event when state changes (§4.4); it MUST NOT derive its authoritative state by subscribing to its own emitted events.

3.4 Track state

Track state reports where the now-playing track is in its per-backend lifecycle. It is reported on ovos.common_play.track.state (§4.4) and recorded on a media entry's status field (§4.5) once numeric codes are assigned (§4.4, Open items).

TrackState memberMeaning
disambiguationA result exists (§4.2.1's disambiguation set) but is not queued to any backend.
queuedThe track is waiting for a backend to start it. Qualified by backend kind (§3.5): skill-internal, audio, video, web view, external OS player.
playingA backend has confirmed playback. Qualified by backend kind (§3.5). A playing-family value implies PlayerState.PLAYING (§3.1).

Pausing is a PlayerState (§3.1) / MediaState (§3.2) concern and MUST NOT be represented as a track-state value.

The lifecycle implied by this axis is disambiguationqueuedplaying. Transitions not listed here are not defined by this version of the spec; an implementation MUST NOT treat them as valid.

3.5 Playback type

PlaybackType names the backend kind a media entry targets or a queued/playing track state is qualified by (§3.4, §4.5 playback field):

PlaybackType memberMeaning
skill-internalThe originating skill renders the media itself (no shared backend).
audioAn audio backend (OVOS-AUDIO-1-adjacent playback service).
videoA video-capable backend / display surface.
web viewAn embedded web view renders the media.
external OS playerAn MPRIS-bridged external player (§6) is the target.

4. Bus surface

All player traffic is namespaced under ovos.common_play. and carried in the OVOS-MSG-1 envelope. Every message MUST carry context.session (OVOS-SESSION-1); the player it addresses is the one owning that session_id (§5).

4.1 Namespace reservation

The ovos.common_play. prefix is reserved for the Virtual Media Player. Components other than the player and its pipeline MUST NOT emit playback-mutating messages under this prefix; they observe state (§4.4) and issue requests (§4.2, §4.3).

4.2 Playback requests

MessageMeaning
ovos.common_play.playBegin playback of a resolved result / queue. Payload in §4.2.1.
ovos.common_play.searchAcquire candidate media for a phrase (the pipeline's discovery step). Bracketed by the two Messages below.
ovos.common_play.search.startThe component orchestrating the search MUST emit this before querying providers.
ovos.common_play.search.endThe component orchestrating the search MUST emit this after result aggregation completes.

A playback request MAY name a preferred output (a backend alias) in the utterance; absent that, the player selects an output by its configured preference order. Output selection is informative here and owned by the implementation.

4.2.1 ovos.common_play.play payload

FieldTypeRequiredMeaning
mediamedia entry (§4.5)yesThe track to play now.
playlistarray of media entriesnoThe playback queue. When absent, the queue is [media].
disambiguationarray of media entriesnoThe full candidate result set the queue was chosen from, kept for "play something else" style follow-ups. When absent, defaults to the playlist.
repeatbooleannoWhen true, the player enters loop mode REPEAT (§3.3).

When playlist is present, media MUST be a member of it: the player locates media's position within playlist by matching uri (§4.5). ovos.common_play.next and ovos.common_play.previous (§4.3) move relative to that located position, not to an externally supplied index.

The ovos.common_play.search payload and the payloads of its bracketing Messages (search.start, search.end) are all implementation-defined in this version.

4.2.2 ovos.common_play.seek payload

FieldTypeRequiredMeaning
positionnumber (ms)yesAbsolute position within now-playing to move to.

Seek is absolute: relative "skip forward N" commands are resolved to an absolute position by the requester (which can read the state reports of §4.4), not by the player — one addressing mode keeps concurrent seekers from compounding each other's offsets.

4.3 Control requests

MessageActs on
ovos.common_play.pausenow-playing → PAUSED
ovos.common_play.resumenow-playing → PLAYING
ovos.common_play.stopnow-playing → STOPPED (an OVOS-STOP-1 subscriber, §7)
ovos.common_play.nextadvance the queue
ovos.common_play.previousretreat the queue
ovos.common_play.seekmove the position within now-playing

Control requests are idempotent with respect to absent media: issuing pause with nothing playing is a no-op, not an error.

4.4 State reports

The player MUST announce state transitions so that GUIs, satellites, MPRIS exporters, and the pipeline's per-session tracking stay coherent:

MessageCarries
ovos.common_play.player.statethe §3.1 value
ovos.common_play.media.statethe §3.2 value
ovos.common_play.track.statenow-playing track transitions

All three share one payload shape:

FieldTypeRequiredMeaning
statenumberyesThe numeric code of the new state on the topic's axis (§3.1 PlayerState, §3.2 MediaState, or the track axis below).

Currently, numeric codes are assigned only for PlayerState (0/1/2); MediaState and track-state values remain symbolic pending future assignment (§4.6 Open items).

ovos.common_play.track.state reports the §3.4 TrackState value.

A consumer MUST NOT assume it can read player state synchronously; the state reports are the contract.

4.6 Open items

This version of the spec leaves the following unresolved; implementers MUST NOT assume a numeric encoding for them beyond what is stated:

  • MediaState numeric codes (§3.2) — members are named but no stable numeric code is assigned yet, unlike PlayerState (§3.1).
  • Track-state numeric codes (§3.4) — the TrackState members (disambiguation, queued, playing, each qualified by PlaybackType, §3.5) are named but not yet numbered.

Until these are assigned, MediaState and track-state values travel symbolically; a future version of this spec fixes their numeric codes the way §3.1 already fixes PlayerState's.

4.5 The media entry

Playback requests and state consumers exchange tracks as media entry objects:

FieldTypeRequiredMeaning
uristringyesWhere the media lives; scheme selects the backend/extractor.
titlestringnoDisplay title.
artiststringnoDisplay artist.
imagestringnoArtwork, delivered per the GUI image rules (OVOS-GUI-1 §3.5).
playbacknumbernoRequested playback kind on the PlaybackType axis (§3.5).
statusreservednoThe entry's current track-state value (§3.4, §4.4). Reserved in this version pending the track-state numeric assignment (§4.6 Open items): producers SHOULD omit this field, and consumers MUST ignore any value present until numeric TrackState codes are assigned.
media_typenumbernoContent classification (music, radio, podcast, video, …) used for result ranking.
lengthnumber (ms)noTrack duration in milliseconds; -1 = unknown/live. One time convention across the media surface: durations and positions count in milliseconds with -1 meaning unknown/live (§4.2.2) — the convention OVOS-GUI-1 v2 adopts uniformly for its media templates (OVOS-GUI-1 §3.4).
match_confidencenumber 0–100noProvider's self-reported relevance for the originating query.
skill_idstringnoThe provider that produced this entry.

Consumers MUST ignore unknown media-entry fields; providers ride extra metadata on entries freely.


5. Session scoping

The Virtual Media Player is per session (OVOS-SESSION-2). A request's context.session.session_id selects the player instance; co-located single-user setups use session_id: "default".

An orchestrator serving multiple concurrent sessions (e.g. a hub serving satellites) MUST keep each session's now-playing, queue, and transport state isolated: a pause for session A MUST NOT affect session B. State reports (§4.4) MUST carry the originating session so consumers can demultiplex.


6. OS integration — the MPRIS bridge

The Virtual Media Player SHOULD integrate with the host operating system through MPRIS (the freedesktop org.mpris.MediaPlayer2 D-Bus interface). The bridge has two independent roles; an implementation MAY provide either or both, and each MUST be separately configurable.

6.1 Role A — export (the player as an MPRIS player)

The virtual player MAY publish itself on the session bus as an MPRIS MediaPlayer2 (e.g. org.mpris.MediaPlayer2.OCP), mapping §3 state and §4 transport onto the MPRIS PlaybackStatus, LoopStatus, Metadata, Position, and the Play/Pause/Next/Previous/Stop/Seek methods. This makes the virtual player's playback visible to and controllable by ordinary desktop media keys and applets, with no knowledge of the voice OS.

A Role A exporter MUST report only MPRIS-valid strings (e.g. PlaybackStatus ∈ {Playing, Paused, Stopped}, LoopStatus ∈ {None, Track, Playlist}) and MUST degrade gracefully — log and continue — when no session D-Bus is available (headless hosts).

6.2 Role B — control external players

The virtual player MAY discover and control other MPRIS players on the host (org.mpris.MediaPlayer2.*). This is the key consequence of the common-playback model: media playback that the virtual player did not initiate is still controllable by voice, provided the source speaks an open standard. "Pause the music" can pause a browser tab or a desktop player; "next" can skip the system's current player.

Role B is off by default and gated by configuration, because it acts on software the virtual player does not own. When enabled, the player MUST maintain an ignore-list (at minimum its own export name from Role A) and SHOULD scope control to the most recently active external player to avoid ambiguous broadcast.

6.3 Arbitration

When both player-initiated playback and external MPRIS sources are present, the virtual player is the single arbiter of "the current media" for control requests (§4.3). The arbitration policy (prefer own playback, prefer most recently active, etc.) is implementation-defined, but the player MUST present one coherent answer per control request — a control request MUST NOT act on two players at once unless the user explicitly requested a global action.


7. Relationship to stop

Media stop is one subscriber to the OVOS-STOP-1 cascade. When the stop pipeline dispatches to the media player's …:stop (the player being an active handler) or broadcasts a global stop, the player MUST transition now-playing to STOPPED (§3.1) and MUST scope the effect to the inbound session_id (OVOS-STOP-1 §6). A global stop MUST NOT stop another session's playback.


8. Conformance

A Virtual Media Player implementation MUST:

  • present exactly one player per session (§2, §5) and keep sessions isolated (§5);
  • distinguish playback requests from control requests, and honour control requests against externally-sourced media when bridged (§2, §4.3, §6.2);
  • be the single writer of its own state and announce transitions on ovos.common_play.player.state / …media.state / …track.state (§3.3, §4.4);
  • treat control requests as no-ops when no media is present (§4.3);
  • scope a stop to the inbound session (§7);
  • reject a ovos.common_play.play request whose media entry lacks a uri before dispatching it to any backend (§4.2.1, §4.5);
  • on acquisition failure (the resolved media cannot be loaded), announce MediaState INVALID_MEDIA (§3.2, §4.4);
  • if the playback backend dies mid-track, announce PlayerState STOPPED (§3.1, §4.4).

A Virtual Media Player implementation SHOULD:

  • integrate with the host OS via MPRIS Role A (§6.1);
  • degrade gracefully without a session D-Bus (§6.1).

A Virtual Media Player implementation MAY:

  • control external MPRIS players via Role B, off by default and gated by configuration (§6.2);
  • accept a user-named output preference in a playback request (§4.2).

See also

  • OVOS-PIPELINE-1 — a pipeline plugin conformant to OVOS-PIPELINE-1 that classifies playback vs. control utterances and dispatches into the §4 surface.
  • OVOS-STOP-1 — global stop cascade; media stop is a subscriber (§7).
  • OVOS-SESSION-1 / OVOS-SESSION-2 — the context.session carrier and per-session ownership that scope the player (§5).
  • OVOS-MSG-1 — envelope and session carrier for all §4 traffic.
  • OVOS-AUDIO-1 — audio output service (TTS/dialog); distinct from media playback, which this spec owns.