Bluetooth Media Control Service (MCS) – Part 1
Understand how BLE lets your headphones, earbuds, and speakers control media playback — step by step, in plain English.
What is the Media Control Service?
When your phone plays music and your TWS earbuds can pause, skip, or rewind that music — some protocol makes that happen. On Bluetooth Classic, that’s AVRCP. On BLE (Bluetooth Low Energy), that job belongs to the Media Control Service (MCS).
MCS is a GATT-based BLE service standardised by the Bluetooth SIG (adopted March 2021). It lets a client device (e.g. earbuds, smartwatch, car head unit) read the current song title, control playback, and get notified when the track changes — all from a server device (e.g. your phone or laptop).
MCS vs GMCS — What’s the Difference?
The spec actually defines two services. Their behaviour is identical — only their scope differs.
- Represents one specific media player app (e.g. Spotify)
- A phone can have multiple MCS instances — one per app
- UUID:
«Media Control»
- Represents the entire device as a single media unit
- Only one GMCS instance per device
- UUID:
«Generic Media Control»
Example: Your Android phone runs Spotify, YouTube Music, and a Podcast app. It will have three MCS instances (one per app) plus one GMCS that reflects whichever app is currently active.
System Architecture at a Glance
|
🎧
CLIENT Device
Earbuds / Smartwatch / Car Head Unit
✅ Reads track title & duration
✅ Sends Play / Pause / Skip ✅ Gets notifications on track change |
GATT Write
GATT Notify / Read
BLE Link
(ATT/GATT) |
📱
SERVER Device
Phone / Laptop / Smart TV
✅ Exposes MCS/GMCS
✅ Hosts media player state ✅ Responds to control opcodes |
Key Concepts You Must Know
A logical entity that plays media. Could be a phone app, a TV, a radio, a podcast player. MCS does not control the audio stream itself — it only controls the playback state and metadata. Think of it as controlling the player, not the speaker.
The song/podcast episode currently playing, paused, or being seeked. Key properties exposed over BLE:
A group is a collection of tracks. It’s equivalent to a playlist or an album. The Current Group defines which pool of tracks the player draws the “next track” from. Groups can also contain other groups (hierarchical, like folders). Only one group is active at a time.
Controls how the next track is chosen. The spec defines 10 modes:
| Value | Name | What Happens |
|---|---|---|
0x01 |
Single Once | Play one track, then stop |
0x02 |
Single Repeat | Loop the same track forever |
0x03 |
In Order Once | Play all tracks in order, then stop |
0x04 |
In Order Repeat | Play all tracks in order, loop |
0x09 |
Shuffle Once | Play all tracks randomly, then stop |
0x0A |
Shuffle Repeat | Play all tracks randomly, loop |
The media player is always in one of four states. This is tracked by the Media State characteristic:
Mandatory GATT Sub-Procedures
Before any characteristic work, a server implementing MCS must support these GATT operations:
| GATT Sub-Procedure | Requirement | Why It Matters in MCS |
|---|---|---|
| Write Characteristic Value | M | Send Play/Pause opcodes; set Track Position |
| Write Without Response | M | Faster control writes (no ATT ACK wait) |
| Notifications | M | Server pushes Track Changed, State changes to client |
| Read Characteristic Descriptors | M | Read CCCD to check if notifications are on |
| Write Characteristic Descriptors | M | Write CCCD to enable/disable notifications |
Complete MCS Characteristics Reference
M = Mandatory · O = Optional · C.n = Conditional (see notes below)
| Characteristic | Req. | Properties | What it tells you |
|---|---|---|---|
| MEDIA PLAYER INFORMATION | |||
| Media Player Name | M | Read, Notify* | Name of the app (e.g. “Spotify”) |
| Media Player Icon Object ID | O | Read | Object ID pointing to player icon in OTS |
| Media Player Icon URL | O | Read | Internet URL to the player icon image |
| CURRENT TRACK INFORMATION | |||
| Track Changed | M | Notify only | Zero-length notification — fires when track changes |
| Track Title | M | Read, Notify | Current song/episode name (UTF-8) |
| Track Duration | M | Read, Notify | Track length in 0.01s steps (sint32); 0xFFFFFFFF = unknown |
| Track Position | M | Read, Write, WwoR, Notify | Current playhead offset (scrub); write to seek |
| Current Track Segments Object ID | C.1 | Read | OTS pointer to track chapter markers |
| Current Track Object ID | O | Read, Write, WwoR, Notify | OTS pointer; write to jump to specific track |
| Next Track Object ID | C.1 | Read, Write, WwoR, Notify | Write to override next track (jukebox mode) |
| PLAYBACK CONTROL | |||
| Playback Speed | O | Read, Write, WwoR, Notify | sint8 p; speed = 2^(p/64). 0=1x, 64=2x, -64=0.5x |
| Seeking Speed | O | Read, Notify | Current FF/RW multiple; 0 when not seeking |
| GROUP & PLAYING ORDER | |||
| Parent Group Object ID | C.1 | Read, Notify | OTS pointer to parent group of current group |
| Current Group Object ID | C.1 | Read, Write, WwoR, Notify | Write to change active group (changes current track) |
| Playing Order | O | Read, Write, WwoR, Notify | Current shuffle/repeat mode (enum 0x01-0x0A) |
| Playing Orders Supported | O | Read | 16-bit bitmask of which playing modes this device supports |
| STATE & CONTROL | |||
| Media State | M | Read, Notify | Current state: Inactive/Playing/Paused/Seeking |
| Media Control Point | O | Write, WwoR, Notify | Send control opcodes (Play, Pause, Next Track…) |
| Media Control Point Opcodes Supported | C.2 | Read, Notify | 32-bit bitmask of supported control opcodes |
| Search Control Point | C.3 | Write, WwoR, Notify | Trigger search by artist/album/genre etc. |
| Search Results Object ID | O | Read, Notify | OTS pointer to search results group object |
| Content Control ID | M | Read | Links this MCS instance to an audio stream |
C.1: Mandatory if Current Track Object ID is supported, otherwise excluded.
C.2: Mandatory if Media Control Point is supported, otherwise excluded.
C.3: Mandatory if Search Results Object ID is supported, otherwise excluded.
WwoR = Write Without Response.
Understanding the Playback Speed Formula
The Playback Speed characteristic is a signed 8-bit integer (p). The actual playback speed multiplier is calculated as:
| Characteristic Value (p) | Calculated Speed | What You’d Hear |
|---|---|---|
| -128 | 0.25× (quarter speed) | Very slow — like a slowed-down tape |
| -64 | 0.5× (half speed) | Half speed — podcasts for review |
| 0 | 1.0× (normal speed) | Normal playback |
| 64 | 2.0× (double speed) | 2x speed — podcast speed-up feature |
| 127 | ≈3.96× (nearly 4x) | Maximum speed |
Track Position — How Scrubbing Works
Writing the Track Position characteristic:
What is OTS and Why Does MCS Need It?
Several MCS characteristics expose an Object ID (a uint48 value) rather than the data directly. These IDs are pointers into the Object Transfer Service (OTS) — a separate BLE service that acts like a simple file system over BLE.
| Object Type | Accessed Via | Contains |
|---|---|---|
| Media Player Icon | Media Player Icon Object ID | PNG image of the player app icon |
| Track Segments | Current Track Segments Object ID | Chapter markers with name + time offset |
| Track Object | Current / Next Track Object ID | ID3v2-format metadata (artwork, tags) |
| Group Object | Current / Parent Group Object ID | Sequence of Track + Group references (like a playlist) |
📌 What’s in Part 2?
Part 1 covered the overview, concepts, and all 22 characteristics. In Part 2 we go deeper into:
- The Media State Machine diagram — every state transition explained
- All 21 Media Control Point opcodes with parameters
- The Search Control Point — how to search by artist, album, genre
- BlueZ C code — how to interact with MCS from Linux
- SDP interoperability for BR/EDR devices
