setMediaSession method

Future<void> setMediaSession(
  1. MediaSession? session
)
inherited

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;
  }
}