Skip to main content

Interface: NativePlaybackState

Defined in: packages/media-session/src/specs/media-session.nitro.ts:703

Everything a surface needs to render "now", except the metadata.

Broadcast wholesale (never patched): a partial update would need a merge policy on three platforms, and the app already holds the full state.

Properties

bufferedPosition?

optional bufferedPosition?: number;

Defined in: packages/media-session/src/specs/media-session.nitro.ts:719

Buffered position in ms. Omit when unknown.

Android only. media3 renders it as the secondary bar behind the scrubber (SimpleBasePlayer.State.setContentBufferedPositionMs). iOS has no key for it: MPNowPlayingInfoCenter's complete metadata key set was read for one and the nearest thing, MPNowPlayingInfoPropertyPlaybackProgress, is documented as a watched-so-far indicator ("A value of 0.0 indicates the item isn't watched, while a value of 1.0 indicates the item was fully watched"), not a buffer level (developer.apple.com/documentation/mediaplayer/mpnowplayinginfocenter, read 2026-08-16). Broadcasting it on iOS is harmless and renders nothing.


capabilities

capabilities: MediaCapability[];

Defined in: packages/media-session/src/specs/media-session.nitro.ts:723

Commands to accept. See MediaCapability.


compactControlIndices?

optional compactControlIndices?: number[];

Defined in: packages/media-session/src/specs/media-session.nitro.ts:743

Android only: indices into controls that get the ≤3 slots of the collapsed notification. Omit to let the platform take the first three.

Not in the spec doc's sketch, which folded the two concepts together; they have to be separable because Android 13+ derives the expanded layout from the session while the compact layout stays an explicit choice (audio_service calls this androidCompactActionIndices).

iOS has no notion of button layout at all — MPRemoteCommandCenter is a flat set of commands that are either enabled or not, and the system decides what to draw — so controls and capabilities are simply unioned there and this field is ignored.


controls

controls: MediaControl[];

Defined in: packages/media-session/src/specs/media-session.nitro.ts:721

Buttons to offer, in order.


customActions

customActions: MediaCustomAction[];

Defined in: packages/media-session/src/specs/media-session.nitro.ts:728

Extra buttons with app-defined meanings. Android only — see MediaCustomAction for why iOS cannot have them.


errorMessage?

optional errorMessage?: string;

Defined in: packages/media-session/src/specs/media-session.nitro.ts:767

Only meaningful when status === 'error'.

Android only. It becomes the PlaybackException message on the facade player's state, which a MediaController can read (PlaybackException.ERROR_CODE_UNSPECIFIED, because the app's error taxonomy does not map onto media3's codes and guessing one would be worse than admitting we do not know).

iOS drops it, and there is nowhere to put it. MPNowPlayingInfoCenter has no error key anywhere in its metadata set and MPRemoteCommandCenter has no error channel; the whole key space was enumerated (developer.apple.com/documentation/mediaplayer/mpnowplayinginfocenter, read 2026-08-16). Combined with MediaPlaybackStatus's note that iOS cannot distinguish error from paused at all, an errored session simply looks paused on an iOS remote surface. Surface the message in your own UI.


position

position: PositionAnchor;

Defined in: packages/media-session/src/specs/media-session.nitro.ts:705


queueIndex?

optional queueIndex?: number;

Defined in: packages/media-session/src/specs/media-session.nitro.ts:749

Index into the last RnMediaMediaSession.setQueue array that mediaItem corresponds to. Omit (or -1) when playback is not queue-backed; Android then presents a single-item timeline built from the media item.


repeatMode

repeatMode: MediaRepeatMode;

Defined in: packages/media-session/src/specs/media-session.nitro.ts:781

Current repeat mode, as the remote surfaces should draw it.

Additive field: it rides the existing playbackState channel rather than introducing a fourth one, because it is state a surface renders next to the transport controls and it changes on exactly the same discontinuities.

Only drawn when the app also advertises setRepeatMode in capabilities — media3 greys out a control whose command is missing, and iOS never enables a command with no target. Broadcasting a mode without the capability is legal and means "this is my state, but do not offer to change it".


shuffleEnabled

shuffleEnabled: boolean;

Defined in: packages/media-session/src/specs/media-session.nitro.ts:783

Current shuffle state. Same rules as repeatMode.


status

status: MediaPlaybackStatus;

Defined in: packages/media-session/src/specs/media-session.nitro.ts:704