setMediaSession method
Publishes the Player to the OS media session, or removes it when
session is null. Only one Player per process can have an
active session: a non-null session on a second Player throws
StateError, also from another Flutter engine of the process
(add-to-app, a second window): the native side, one per process,
refuses that enable and the state goes back to null.
The session needs the root isolate, the one the OS sends its commands
to: a non-null session on a Player of another isolate throws
StateError.
Implementation
Future<void> setMediaSession(MediaSession? session) async {
_checkNotDisposed();
// 1. Root isolate only: platform messages reach no other isolate, and
// the channels of a background isolate fail. `null` is allowed
// anywhere, as there is nothing to undo. A web page runs one
// isolate, and has no root isolate token to ask.
if (session != null && !kIsWeb && RootIsolateToken.instance == null) {
throw StateError(
'setMediaSession needs the root isolate: the OS media session '
'sends its commands only there. Publish from a Player created in '
'the root isolate.',
);
}
// 2. Single-instance guard.
if (session != null) {
final owner = _sessionOwner;
if (owner != null && !identical(owner, this)) {
throw StateError(
'Another Player instance already owns the OS media session. '
'SMTC (Windows) and MPNowPlayingInfoCenter (Apple) are '
'single-instance per process, so only one Player can publish '
'to the lockscreen at a time. Disable the existing session '
'with `setMediaSession(null)` on the other Player before '
'enabling this one.',
);
}
_sessionOwner = this as Player;
} else if (identical(_sessionOwner, this)) {
_sessionOwner = null;
}
// 3. Update state synchronously (optimistic). The controller's
// own subscription to `stream.mediaSession` will pick up the
// config change and push it to native via `updateConfig`.
_updateField(
(s) => s.copyWith(mediaSession: session),
_mediaSession,
session,
);
// 4. Bring the controller in line with the state, in turn with the
// other calls: see [_queueMediaSessionSync].
try {
await _queueMediaSessionSync();
} catch (_) {
// The controller could not be created: undo the owner and the state
// this call wrote, unless a later call has replaced them.
if (session != null &&
identical(_state.settings.mediaSession, session) &&
_mediaSessionController == null) {
if (identical(_sessionOwner, this)) {
_sessionOwner = null;
}
_updateField(
(s) => s.copyWith(mediaSession: null),
_mediaSession,
null,
);
}
rethrow;
}
}