Skip to content

OCP Pipeline

Maturity — Stable ⬤⬤⬤⬤◯

Established and production-ready, actively maintained. Rated by repository health, not version.

In a nutshell

When you say "play some jazz" or "next song", the assistant first has to realise you are talking about media and not, say, the weather. This is the part that does that: it spots that an utterance is a playback request, figures out what kind of media you want, asks the installed music/podcast/video skills to search for it, and hands the best result off to be played. Think of it as the dispatcher that turns "play X" into actual playback. See the Intent Pipeline overview or the Glossary for related terms.

What OCP means here

"OCP" names three different things in OVOS. Know which one a page is about:

  • The OCP pipeline plugin: matches utterances like "play some jazz" to a media request. See OCP Pipeline.
  • The OCP skill base class: OVOSCommonPlaybackSkill; skills built on it provide or embody media for the pipeline to find. See OCP Skills.
  • The legacy OCP audio plugin: ovos-plugin-common-play, the current default playback engine running inside ovos-audio. See The OCP Audio Plugin.

Shipped in the default pipeline.

The rest of this page is for people deploying or customizing OVOS. If you only wanted to know what this stage does, you are done.

📐 Formal specification

The media player this pipeline drives is specified by OVOS-OCP-1 — OVOS Common Playback: the per-session Virtual Media Player and its ovos.common_play.* control surface. The matching/classification side is a pipeline plugin under OVOS-PIPELINE-1 (OCP-1 explicitly leaves the NLU to PIPELINE-1). Media stop is one subscriber to the OVOS-STOP-1 cascade. See the spec index.

The OCP (OVOS Common Play) Pipeline Plugin handles media playback commands: "play some jazz", "pause", "next song". It recognises that an utterance is about media, works out what kind of media is wanted, asks OCP-enabled skills to search for it, filters the results, and hands the best one to the active player to play. That player is ovos-audio's legacy OCP backend by default, or the ovos-media daemon if enabled.

Skills act purely as catalogs: they return search results, they do not play anything themselves. OCP centralises selection and playback.


Quick start

pip install ovos-ocp-pipeline-plugin

It exposes confidence-tiered matchers (ovos-ocp-pipeline-plugin-high, ovos-ocp-pipeline-plugin-medium, ovos-ocp-pipeline-plugin-low) plus a legacy matcher (ovos-ocp-pipeline-plugin-legacy). A typical pipeline puts the high tier early and the low tier last:

{
  "intents": {
    "pipeline": [
      "ovos-ocp-pipeline-plugin-high",
      "ovos-padatious-pipeline-plugin-high",
      "ovos-adapt-pipeline-plugin-high",
      "ovos-ocp-pipeline-plugin-medium",
      "ovos-fallback-pipeline-plugin-medium",
      "ovos-ocp-pipeline-plugin-low"
    ]
  }
}

The older short names ocp_high / ocp_medium / ocp_low / ocp_legacy still work as deprecated aliases (ovos-core rewrites them to the canonical IDs above), but new configs should use the canonical names.

Then install OCP-enabled media skills and ask to play something.


Pipeline matchers

The matcher class is OCPPipelineMatcher, registered under the single OPM entry point ovos-ocp-pipeline-plugin. Because it is a ConfidenceMatcherPipeline, OVOS derives three tier matchers from it at runtime via match_high / match_medium / match_low — you reference them in the pipeline by the IDs below (the short ocp_* aliases are deprecated):

Pipeline ID Legacy alias Tier When to use
ovos-ocp-pipeline-plugin-high ocp_high high Explicit OCP intents ("play.intent", "pause", "next") — primary media commands.
ovos-ocp-pipeline-plugin-medium ocp_medium medium Utterance classified as a media query by keyword matching.
ovos-ocp-pipeline-plugin-low ocp_low low Broad keyword hits — only on devices used mainly for media playback.

A separate class, MycroftCPSLegacyPipeline, is registered as its own entry point ovos-ocp-pipeline-plugin-legacy (alias ocp_legacy). It bridges to deprecated Mycroft CommonPlaySkill (CPS) skills and is off by default. It is only useful if you still run legacy CPS skills.

ocp_low keys off skill-registered media keywords, so it can fire on phrases that merely contain a known artist or show name even when no playback was intended. Place it last and only enable it on media-focused devices.


Playback vs. control, and why ordering matters

OCP-1 §2 splits media commands into two classes the player must distinguish: playback requests ("play X", "open X") that acquire new media, and control requests ("pause", "resume", "next", "previous", "stop", seek) that act on whatever is already playing. This includes media OVOS did not start, when the MPRIS bridge (OCP-1 §6) is enabled. There is exactly one Virtual Media Player per session (OCP-1 §2, §5). A request names the player, not a backend, and the player routes.

This is why a high-tier OCP stage belongs early in session.pipeline: as a selective pipeline plugin it claims a control utterance like "resume" or "next" only while it holds paused media for that session. First-match-wins (PIPELINE-1 §6.2) lets it intercept those bare words before a general intent engine does. This is exactly the conservative, state-aware claiming pattern the spec describes.

Seek on the wire (ovos.common_play.seek) carries a relative seconds delta. The player, not the pipeline matcher, converts it to milliseconds and adds it to its own live playback position, so the result never depends on stale matcher-side state (a separate GUI-only seekValue field carries an absolute position). The ovos.common_play.* bus surface in the spec is the formal counterpart of the ovos.common_play.query / …status / …track.state topics used below.

flowchart TD
    U[Utterance] --> Q{"Playback or control?"}
    Q -- "play X / open X" --> NEW["Acquire new media\novos.common_play.query"]
    Q -- "pause/resume/next/\nprevious/stop/seek" --> CTRL{"Player holding\npaused media?"}
    CTRL -- yes --> ACT["Claim as control request"]
    CTRL -- no --> PASS["Pass to next pipeline stage"]

Diagram: an utterance is classified as either a playback request, which acquires new media via a common_play query, or a control word, which OCP claims only if it is already holding paused media, otherwise passing it to the next pipeline stage.

How a media intent is recognized

OCP combines several signals:

  • Explicit intents (ocp_high): localized .intent files for play, pause, resume, stop, next, previous, shuffle, etc.
  • Keyword matching (ocp_medium / ocp_low): classify_media() delegates to a ContextAwareClassifier backed by the ovos-media-classifier package (a hard dependency), which feeds player status and registered NER entities into the classification. The classifier backend is pluggable — the default is a keyword/vocab matcher, but other opm.media.classifier entry points can be selected via config. is_ocp_query() treats any non-GENERIC media type as a playback query.
  • Skill-registered keywords: skills announce entities (artist names, show titles) over ovos.common_play.register_keyword. These feed the AhocorasickNER entity matcher and bias media-type classification.

If a query maps to exactly one media type that a skill can serve, that type is used with full confidence.

MediaProvider plugins

In-process MediaProvider plugins (entry-point group opm.media.provider) run alongside the legacy bus @ocp_search window rather than replacing it — results from both are merged, bounded by the same timeout. A provider that declares no media types is demoted (confidence capped at 50); each provider's results are capped at 50. With no MediaProvider plugins installed, behavior is unchanged from the legacy bus-only flow. Settings live under intents.ovos-ocp-pipeline-plugin.media_providers in mycroft.conf; set media_providers.enabled: false to turn the mechanism off entirely.

Supported media types include music, podcast, movie, radio, audiobook, news, and many more from ovos_utils.ocp.MediaType.


Search and result filtering

After classifying, OCP emits ovos.common_play.query and gathers ovos.common_play.query.response results from skills, then filters them in filter_results():

  • Confidence: drops results whose match_confidence is below min_score.
  • Media-type consistency: when a non-GENERIC type was classified, results of other types are removed (filter_media).
  • Stream-extractor availability: results needing a Stream Extractor plugin (SEI) that is not installed are removed (filter_SEI). Available extractors come from the opm.ocp.extractor plugin group, queried via ovos.common_play.SEI.get.
  • Playback mode: audio-only / video-only preferences drop incompatible results (playback_mode).

OCP tracks player state per Session over the bus (ovos.common_play.status, ovos.common_play.track.state), so context-dependent commands behave correctly. For example, "next song" does nothing when no player is active.


Configuration

{
  "intents": {
    "ovos-ocp-pipeline-plugin": {
      "legacy": false,
      "min_score": 50,
      "filter_media": true,
      "filter_SEI": true,
      "playback_mode": 0,
      "search_fallback": true,
      "entity_csvs": []
    }
  }
}
Key Type Default Description
legacy bool false Use the classic (Mycroft) audio service API instead of OCP. Not recommended.
min_score int 50 Minimum match_confidence to keep a skill result (0–100).
filter_media bool true Drop results whose media type differs from the classified one.
filter_SEI bool true Drop results needing an unavailable Stream Extractor plugin.
playback_mode int 0 0 = auto, 10 = audio-only, 20 = video-only (PlaybackMode).
search_fallback bool true Run a generic search when no type-specific results are found.
entity_csvs list [] User-supplied keyword CSVs feeding the entity matcher.
legacy_cps bool true Only read by the ovos-ocp-pipeline-plugin-legacy matcher: set false to disable the legacy Mycroft CommonPlay stage entirely.

The config block is read from intents.ovos-ocp-pipeline-plugin, the plugin's entry-point ID.

Key your config by the plugin ID, not OCP

The plugin-ID key wins: the plugin resolves its config as intents["ovos-ocp-pipeline-plugin"] or intents["OCP"], so anything you set under intents.OCP is shadowed the moment the plugin-ID key exists — and the bundled mycroft.conf always ships that key (including min_score: 40), so in practice intents.OCP entries change nothing on a stock install. intents.OCP survives only as a back-compat fallback for configs that define nothing under the plugin ID. Put yours under the plugin ID:

{"intents": {"ovos-ocp-pipeline-plugin": {"min_score": 40}}}

Gotcha: legacy vs. OCP playback

ocp_legacy and legacy: true are two different things. ocp_legacy is a pipeline matcher that routes to deprecated Mycroft CPS skills. legacy: true forces OCP to drive playback through the classic audio service API instead of OCP itself. Leave both off unless you specifically need to support pre-OCP skills.


Source code: OpenVoiceOS/ovos-ocp-pipeline-plugin.


Read next: Common Query Pipeline Related: Media Skills (OCP) · ovos-media OCP Pipeline Plugin · OCP Audio Plugin · Stop Pipeline