Type Alias: MediaPlaybackStatus
type MediaPlaybackStatus = "playing" | "paused" | "buffering" | "stopped" | "error";
Defined in: packages/media-session/src/specs/media-session.nitro.ts:38
Coarse playback status, broadcast by the app.
These are the five states from the spec doc. Deliberately not media3's
Player.STATE_* nor AVPlayer.timeControlStatus — this is the union the app
thinks in; each platform maps it to its own vocabulary
(see BroadcastPlayer.getState / HybridRnMediaMediaSession.publishNowPlayingInfo).
Platform: iOS collapses three of these into one
Android distinguishes all five — media3 gets STATE_READY+playWhenReady,
STATE_BUFFERING, STATE_IDLE, and for error a real PlaybackException.
iOS has no playback-state key at all for an app to write: the only lever
MPNowPlayingInfoCenter gives is MPNowPlayingInfoPropertyPlaybackRate, and
paused, stopped and error all publish a rate of 0, so every iOS remote
surface renders them identically. MPNowPlayingInfoCenter.playbackState looks
like the missing lever and is not: it exists on iOS 13+ but Apple documents it
as "This property only applies to macOS"
(developer.apple.com/documentation/mediaplayer/mpnowplayinginfocenter/playbackstate,
read 2026-08-16), so writing it on iOS is not something the platform promises
to honour and it is deliberately not written.
buffering does survive on iOS, because it is expressed the same way it is on
Android — as a rate of 0 with the transport still advertised — but it too is
indistinguishable from paused on the lock screen.
NOTE: every member of a string union becomes a native enumerator whose name
is the member, upper-cased with separators stripped (skipToNext →
SKIPTONEXT, Swift .skiptonext). Members must therefore stay distinct
case-insensitively, and must not collide with a C macro or a Swift keyword —
the same constraint that forced defaultMode in @afkcodes/timbre-audio-session.