Contrôler et annoncer la lecture avec MediaSession

Les sessions multimédias offrent un moyen universel d'interagir avec un lecteur audio ou vidéo. Dans Media3, le lecteur par défaut est la classe ExoPlayer, qui implémente l'interface Player. La connexion de la session multimédia au lecteur permet à une application d'annoncer la lecture de contenus multimédias en externe et de recevoir des commandes de lecture provenant de sources externes.

Les commandes peuvent provenir de boutons physiques tels que le bouton de lecture sur un casque ou une télécommande de téléviseur. Elles peuvent également provenir d'applications clientes disposant d'un contrôleur multimédia, comme la commande "Mettre en pause" adressée à l'Assistant Google. La session multimédia délègue ces commandes au lecteur de l'application multimédia.

Quand choisir une session multimédia ?

Lorsque vous implémentez MediaSession, vous permettez aux utilisateurs de contrôler la lecture :

  • Dans leurs écouteurs. Les utilisateurs peuvent souvent appuyer sur des boutons ou interagir avec des commandes tactiles sur leurs écouteurs pour lire ou mettre en pause des contenus multimédias, ou passer au titre suivant ou précédent.
  • En parlant à l'Assistant Google Une commande courante consiste à dire "OK Google, mets en pause" pour mettre en pause tout contenu multimédia en cours de lecture sur l'appareil.
  • Sur leur montre Wear OS. Cela permet d'accéder plus facilement aux commandes de lecture les plus courantes lorsqu'ils regardent du contenu sur leur téléphone.
  • Via les commandes multimédias. Ce carrousel affiche les commandes pour chaque session multimédia en cours.
  • Sur Télévision. Permet d'effectuer des actions avec les boutons de lecture physiques, le contrôle de la lecture sur la plate-forme et la gestion de l'alimentation (par exemple, si le téléviseur, la barre de son ou le récepteur audio/vidéo s'éteint ou si l'entrée est modifiée, la lecture doit s'arrêter dans l'application).
  • Via les commandes multimédias Android Auto. Cela permet de contrôler la lecture en toute sécurité pendant la conduite.
  • et tout autre processus externe qui doit influencer la lecture.

Cette approche est idéale pour de nombreux cas d'utilisation. En particulier, vous devriez fortement envisager d'utiliser MediaSession dans les cas suivants :

  • Vous diffusez du contenu vidéo long, comme des films ou des chaînes de télévision en direct.
  • Vous écoutez en streaming des contenus audio longs, comme des podcasts ou des playlists musicales.
  • Vous créez une appli TV.

Cependant, tous les cas d'utilisation ne sont pas adaptés à MediaSession. Vous pouvez utiliser uniquement Player dans les cas suivants :

  • Vous diffusez du contenu court, qui ne nécessite pas de contrôle externe ni de lecture en arrière-plan.
  • Il n'y a pas de vidéo active unique, par exemple lorsque l'utilisateur fait défiler une liste et que plusieurs vidéos sont affichées à l'écran en même temps.
  • Vous diffusez une vidéo d'introduction ou d'explication ponctuelle que vous attendez de votre utilisateur qu'il regarde activement sans avoir besoin de commandes de lecture externes.
  • Votre contenu est sensible en termes de confidentialité et vous ne souhaitez pas que des processus externes accèdent aux métadonnées multimédias (par exemple, le mode incognito d'un navigateur).

Si votre cas d'utilisation ne correspond à aucun de ceux listés ci-dessus, demandez-vous si vous êtes d'accord pour que votre application continue la lecture lorsque l'utilisateur n'interagit pas activement avec le contenu. Si la réponse est "oui", vous devez probablement choisir MediaSession. Si la réponse est non, vous devriez probablement utiliser Player à la place.

Créer une session multimédia

Une session multimédia existe en même temps que le lecteur qu'elle gère. Vous pouvez créer une session multimédia avec un objet Context et un objet Player. Vous devez créer et initialiser une session multimédia lorsque cela est nécessaire, par exemple la méthode de cycle de vie onStart() ou onResume() de Activity ou Fragment, ou la méthode onCreate() de Service qui possède la session multimédia et son lecteur associé.

Pour créer une session multimédia, initialisez un Player et fournissez-le à MediaSession.Builder comme suit :

Kotlin

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

Gestion automatique de l'état

La bibliothèque Media3 met automatiquement à jour la session multimédia en fonction de l'état du lecteur. Vous n'avez donc pas besoin de gérer manuellement le mappage du lecteur à la session.

Cela diffère de la session multimédia de la plate-forme, où vous deviez créer et gérer un PlaybackState indépendamment du lecteur lui-même, par exemple pour indiquer les erreurs.

ID de session unique

Par défaut, MediaSession.Builder crée une session avec une chaîne vide comme ID de session. Cela suffit si une application n'a l'intention de créer qu'une seule instance de session, ce qui est le cas le plus courant.

Si une application souhaite gérer plusieurs instances de session en même temps, elle doit s'assurer que l'ID de session de chaque session est unique. L'ID de session peut être défini lors de la création de la session avec MediaSession.Builder.setId(String id).

Si vous voyez un IllegalStateException planter votre application avec le message d'erreur IllegalStateException: Session ID must be unique. ID=, il est probable qu'une session ait été créée de manière inattendue avant qu'une instance précédemment créée avec le même ID ait été libérée. Pour éviter que des sessions ne soient divulguées en raison d'une erreur de programmation, ces cas sont détectés et signalés par une exception.

Accorder le contrôle à d'autres clients

La session multimédia est essentielle pour contrôler la lecture. Il vous permet de router les commandes provenant de sources externes vers le lecteur qui lit votre contenu multimédia. Ces sources peuvent être des boutons physiques, comme le bouton de lecture sur un casque ou une télécommande de téléviseur, ou des commandes indirectes, comme la commande "pause" donnée à l'Assistant Google. De même, vous pouvez accorder l'accès au système Android pour faciliter les notifications et les commandes de l'écran de verrouillage, ou à une montre Wear OS pour pouvoir contrôler la lecture depuis le cadran. Les clients externes peuvent utiliser un contrôleur multimédia pour envoyer des commandes de lecture à votre application multimédia. Ces commandes sont reçues par votre session multimédia, qui les délègue finalement au lecteur multimédia.

Schéma illustrant l'interaction entre une MediaSession et un MediaController.
Figure 1 : Le contrôleur multimédia facilite la transmission des commandes de sources externes à la session multimédia.
.

Lorsque le contrôleur est sur le point de se connecter à votre session multimédia, la méthode onConnect() est appelée. Vous pouvez utiliser le ControllerInfo fourni pour décider d'accepter ou de refuser la demande. Consultez un exemple d'acceptation d'une demande de connexion dans la section Déclarer des commandes personnalisées.

Une fois connecté, un contrôleur peut envoyer des commandes de lecture à la session. La session délègue ensuite ces commandes au lecteur. Les commandes de lecture et de playlist définies dans l'interface Player sont automatiquement gérées par la session.

D'autres méthodes de rappel vous permettent de gérer, par exemple, les demandes de commandes personnalisées et la modification de la playlist. Ces rappels incluent également un objet ControllerInfo afin que vous puissiez modifier la façon dont vous répondez à chaque requête par contrôleur.

Modifier la playlist

Une session multimédia peut modifier directement la playlist de son lecteur, comme expliqué dans le guide ExoPlayer pour les playlists. Les contrôleurs peuvent également modifier la playlist si COMMAND_SET_MEDIA_ITEM ou COMMAND_CHANGE_MEDIA_ITEMS sont disponibles pour le contrôleur.

Lorsque vous ajoutez des éléments à la playlist, le lecteur nécessite généralement des instances MediaItem avec un URI défini pour qu'elles puissent être lues. Par défaut, les éléments nouvellement ajoutés sont automatiquement transmis aux méthodes du lecteur telles que player.addMediaItem s'ils ont un URI défini.

Si vous souhaitez personnaliser les instances MediaItem ajoutées au lecteur, vous pouvez remplacer onAddMediaItems(). Cette étape est nécessaire lorsque vous souhaitez prendre en charge les contrôleurs qui demandent des contenus multimédias sans URI défini. En revanche, le MediaItem comporte généralement un ou plusieurs des champs suivants définis pour décrire le contenu multimédia demandé :

  • MediaItem.id : ID générique identifiant le contenu multimédia.
  • MediaItem.RequestMetadata.mediaUri : URI de requête pouvant utiliser un schéma personnalisé et qui n'est pas nécessairement directement lisible par le lecteur.
  • MediaItem.RequestMetadata.searchQuery : requête de recherche textuelle, par exemple depuis l'Assistant Google.
  • MediaItem.MediaMetadata : métadonnées structurées telles que "titre" ou "artiste".

Pour plus d'options de personnalisation des playlists entièrement nouvelles, vous pouvez également remplacer onSetMediaItems(), ce qui vous permet de définir l'élément de départ et la position dans la playlist. Par exemple, vous pouvez étendre un seul élément demandé à une playlist entière et demander au lecteur de commencer à l'index de l'élément demandé à l'origine. Vous trouverez un exemple d'implémentation de onSetMediaItems() avec cette fonctionnalité dans l'application de démonstration de session.

Gérer les préférences des boutons multimédias

Chaque contrôleur, par exemple l'interface utilisateur système, Android Auto ou Wear OS, peut décider des boutons à afficher à l'utilisateur. Pour indiquer les commandes de lecture que vous souhaitez exposer à l'utilisateur, vous pouvez spécifier les préférences des boutons multimédias sur MediaSession. Ces préférences se composent d'une liste ordonnée d'instances CommandButton, chacune définissant une préférence pour un bouton de l'interface utilisateur.

Définir des boutons de commande

Les instances CommandButton sont utilisées pour définir les préférences des boutons multimédias. Chaque bouton définit trois aspects de l'élément d'UI souhaité :

  1. L'icône, qui définit l'apparence visuelle. L'icône doit être définie sur l'une des constantes prédéfinies lors de la création d'un CommandButton.Builder. Notez qu'il ne s'agit pas d'une ressource Bitmap ou d'image réelle. Une constante générique aide les contrôleurs à choisir une ressource appropriée pour une apparence cohérente au sein de leur propre UI. Si aucune des constantes d'icône prédéfinies ne correspond à votre cas d'utilisation, vous pouvez utiliser setCustomIconResId.
  2. Command (Commande) : définit l'action déclenchée lorsque l'utilisateur interagit avec le bouton. Vous pouvez utiliser setPlayerCommand pour un Player.Command ou setSessionCommand pour un SessionCommand prédéfini ou personnalisé.
  3. Le slot, qui définit l'emplacement du bouton dans l'UI du contrôleur. Ce champ est facultatif et est automatiquement défini en fonction de l'icône et de la commande. Par exemple, il permet de spécifier qu'un bouton doit être affiché dans la zone de navigation "avant" de l'UI au lieu de la zone de "dépassement" par défaut.

Kotlin

val button =
  CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
    .setSessionCommand(SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
    .setSlots(CommandButton.SLOT_FORWARD)
    .build()

Java

CommandButton button =
    new CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
        .setSessionCommand(new SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
        .setSlots(CommandButton.SLOT_FORWARD)
        .build();

Une fois les préférences des boutons multimédias résolues, l'algorithme suivant est appliqué :

  1. Pour chaque CommandButton dans les préférences des boutons multimédias, placez le bouton dans le premier emplacement disponible et autorisé.
  2. Si l'un des emplacements central, avant ou arrière n'est pas rempli par un bouton, ajoutez des boutons par défaut pour cet emplacement.

Vous pouvez utiliser CommandButton.DisplayConstraints pour générer un aperçu de la façon dont les préférences des boutons multimédias seront résolues en fonction des contraintes d'affichage de l'UI.

Définir les préférences du bouton multimédia

Le moyen le plus simple de définir les préférences des boutons multimédias consiste à définir la liste lors de la création de MediaSession. Vous pouvez également remplacer MediaSession.Callback.onConnect pour personnaliser les préférences des boutons multimédias pour chaque manette connectée.

Kotlin

val mediaSession =
  MediaSession.Builder(context, player)
    .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
    .build()

Java

MediaSession mediaSession =
  new MediaSession.Builder(context, player)
      .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
      .build();

Mettre à jour les préférences du bouton multimédia après une interaction de l'utilisateur

Après avoir géré une interaction avec votre lecteur, vous pouvez mettre à jour les boutons affichés dans l'UI du contrôleur. Un exemple typique est un bouton bascule qui change d'icône et d'action après le déclenchement de l'action associée à ce bouton. Pour mettre à jour les préférences des boutons multimédias, vous pouvez utiliser MediaSession.setMediaButtonPreferences pour mettre à jour les préférences de tous les contrôleurs ou d'un contrôleur spécifique :

Kotlin

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
  ImmutableList.of(likeButton, removeFromFavoritesButton))

Java

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
    ImmutableList.of(likeButton, removeFromFavoritesButton));

Ajouter des commandes personnalisées et personnaliser le comportement par défaut

Les commandes du lecteur disponibles peuvent être étendues par des commandes personnalisées. Il est également possible d'intercepter les commandes du lecteur et les boutons multimédias entrants pour modifier le comportement par défaut.

Déclarer et gérer des commandes personnalisées

Les applications multimédias peuvent définir des commandes personnalisées qui peuvent, par exemple, être utilisées dans les préférences des boutons multimédias. Par exemple, vous pouvez implémenter des boutons qui permettent à l'utilisateur d'enregistrer un élément multimédia dans une liste d'éléments favoris. MediaController envoie des commandes personnalisées et MediaSession.Callback les reçoit.

Pour définir des commandes personnalisées, vous devez remplacer MediaSession.Callback.onConnect() afin de définir les commandes personnalisées disponibles pour chaque manette connectée.

Kotlin

private class CustomMediaSessionCallback: MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  override fun onConnect(
    session: MediaSession,
    controller: MediaSession.ControllerInfo
  ): MediaSession.ConnectionResult {
    val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
        .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY))
        .build()
    return AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build()
  }
}

Java

class CustomMediaSessionCallback implements MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  @Override
  public ConnectionResult onConnect(
    MediaSession session,
    ControllerInfo controller) {
    SessionCommands sessionCommands =
        ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
            .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle()))
            .build();
    return new AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build();
  }
}

Pour recevoir des requêtes de commandes personnalisées d'un MediaController, remplacez la méthode onCustomCommand() dans Callback.

Kotlin

private class CustomMediaSessionCallback: MediaSession.Callback {
  ...
  override fun onCustomCommand(
    session: MediaSession,
    controller: MediaSession.ControllerInfo,
    customCommand: SessionCommand,
    args: Bundle
  ): ListenableFuture<SessionResult> {
    if (customCommand.customAction == SAVE_TO_FAVORITES) {
      // Do custom logic here
      saveToFavorites(session.player.currentMediaItem)
      return Futures.immediateFuture(
        SessionResult(SessionResult.RESULT_SUCCESS)
      )
    }
    ...
  }
}

Java

class CustomMediaSessionCallback implements MediaSession.Callback {
  ...
  @Override
  public ListenableFuture<SessionResult> onCustomCommand(
    MediaSession session, 
    ControllerInfo controller,
    SessionCommand customCommand,
    Bundle args
  ) {
    if(customCommand.customAction.equals(SAVE_TO_FAVORITES)) {
      // Do custom logic here
      saveToFavorites(session.getPlayer().getCurrentMediaItem());
      return Futures.immediateFuture(
        new SessionResult(SessionResult.RESULT_SUCCESS)
      );
    }
    ...
  }
}

Vous pouvez suivre le contrôleur multimédia qui effectue une requête à l'aide de la propriété packageName de l'objet MediaSession.ControllerInfo transmis aux méthodes Callback. Cela vous permet d'adapter le comportement de votre application en réponse à une commande donnée, qu'elle provienne du système, de votre propre application ou d'autres applications clientes.

Personnaliser les commandes du lecteur par défaut

Toutes les commandes par défaut et la gestion de l'état sont déléguées au Player qui se trouve sur le MediaSession. Pour personnaliser le comportement d'une commande définie dans l'interface Player, telle que play() ou seekToNext(), enveloppez votre Player dans un ForwardingSimpleBasePlayer avant de le transmettre à MediaSession :

Kotlin

val player = (logic to build a Player instance)

val forwardingPlayer = object : ForwardingSimpleBasePlayer(player) {
  // Customizations
}

val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()

Java

ExoPlayer player = (logic to build a Player instance)

ForwardingSimpleBasePlayer forwardingPlayer =
    new ForwardingSimpleBasePlayer(player) {
      // Customizations
    };

MediaSession mediaSession =
  new MediaSession.Builder(context, forwardingPlayer).build();

Pour en savoir plus sur ForwardingSimpleBasePlayer, consultez le guide ExoPlayer sur la personnalisation.

Identifier le contrôleur à l'origine d'une commande de lecteur

Lorsqu'un appel à une méthode Player est initié par un MediaController, vous pouvez identifier la source d'origine avec MediaSession.controllerForCurrentRequest et obtenir le ControllerInfo pour la requête actuelle :

Kotlin

class CallerAwarePlayer(player: Player) :
  ForwardingSimpleBasePlayer(player) {

  override fun handleSeek(
    mediaItemIndex: Int,
    positionMs: Long,
    seekCommand: Int,
  ): ListenableFuture<*> {
    Log.d(
      "caller",
      "seek operation from package ${session.controllerForCurrentRequest?.packageName}",
    )
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand)
  }
}

Java

public class CallerAwarePlayer extends ForwardingSimpleBasePlayer {
  public CallerAwarePlayer(Player player) {
    super(player);
  }

  @Override
  protected ListenableFuture<?> handleSeek(
        int mediaItemIndex, long positionMs, int seekCommand) {
    Log.d(
        "caller",
        "seek operation from package: "
            + session.getControllerForCurrentRequest().getPackageName());
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand);
  }
}

Personnaliser la gestion des boutons multimédias

Les boutons multimédias sont des boutons physiques présents sur les appareils Android et d'autres périphériques, comme le bouton de lecture/pause d'un casque Bluetooth. Media3 gère les événements des boutons multimédias pour vous lorsqu'ils arrivent dans la session et appelle la méthode Player appropriée sur le lecteur de session.

Il est recommandé de gérer tous les événements de bouton multimédia entrants dans la méthode Player correspondante. Pour les cas d'utilisation plus avancés, les événements du bouton multimédia peuvent être interceptés dans MediaSession.Callback.onMediaButtonEvent(Intent).

Gestion et signalement des erreurs

Une session peut émettre deux types d'erreurs et les signaler aux contrôleurs. Les erreurs fatales signalent un échec technique de la lecture par le lecteur de session, qui interrompt la lecture. Les erreurs fatales sont signalées automatiquement au contrôleur lorsqu'elles se produisent. Les erreurs non fatales sont des erreurs non techniques ou liées aux règles qui n'interrompent pas la lecture et qui sont envoyées manuellement aux contrôleurs par l'application.

Erreurs de lecture fatales

Une erreur de lecture fatale est signalée à la session par le lecteur, puis aux contrôleurs pour qu'ils appellent Player.Listener.onPlayerError(PlaybackException) et Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException).

Dans ce cas, l'état de lecture passe à STATE_IDLE et MediaController.getPlaybackError() renvoie le PlaybackException qui a provoqué la transition. Un contrôleur peut inspecter PlayerException.errorCode pour obtenir des informations sur la raison de l'erreur.

Pour l'interopérabilité, une erreur fatale est répliquée dans la session de plate-forme en faisant passer son état à STATE_ERROR et en définissant le code et le message d'erreur en fonction de PlaybackException.

Personnalisation des erreurs fatales

Pour fournir des informations localisées et pertinentes à l'utilisateur, le code d'erreur, le message d'erreur et les informations supplémentaires sur l'erreur de lecture fatale peuvent être personnalisés à l'aide d'un ForwardingPlayer lors de la création de la session :

Kotlin

val forwardingPlayer = ErrorForwardingPlayer(player)
val session = MediaSession.Builder(context, forwardingPlayer).build()

Java

Player forwardingPlayer = new ErrorForwardingPlayer(player);
MediaSession session =
    new MediaSession.Builder(context, forwardingPlayer).build();

Le lecteur de transfert peut utiliser ForwardingSimpleBasePlayer pour intercepter l'erreur et personnaliser le code d'erreur, le message ou les extras. De la même manière, vous pouvez également générer de nouvelles erreurs qui n'existent pas dans le lecteur d'origine :

Kotlin

class ErrorForwardingPlayer (private val context: Context, player: Player) :
    ForwardingSimpleBasePlayer(player) {

  override fun getState(): State {
    var state = super.getState()
    if (state.playerError != null) {
      state =
        state.buildUpon()
          .setPlayerError(customizePlaybackException(state.playerError!!))
          .build()
    }
    return state
  }

  fun customizePlaybackException(error: PlaybackException): PlaybackException {
    val buttonLabel: String
    val errorMessage: String
    when (error.errorCode) {
      PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> {
        buttonLabel = context.getString(R.string.err_button_label_restart_stream)
        errorMessage = context.getString(R.string.err_msg_behind_live_window)
      }
      else -> {
        buttonLabel = context.getString(R.string.err_button_label_ok)
        errorMessage = context.getString(R.string.err_message_default)
      }
    }
    val extras = Bundle()
    extras.putString("button_label", buttonLabel)
    return PlaybackException(errorMessage, error.cause, error.errorCode, extras)
  }
}

Java

class ErrorForwardingPlayer extends ForwardingSimpleBasePlayer {

  private final Context context;

  public ErrorForwardingPlayer(Context context, Player player) {
    super(player);
    this.context = context;
  }

  @Override
  protected State getState() {
    State state = super.getState();
    if (state.playerError != null) {
      state =
          state.buildUpon()
              .setPlayerError(customizePlaybackException(state.playerError))
              .build();
    }
    return state;
  }

  private PlaybackException customizePlaybackException(PlaybackException error) {
    String buttonLabel;
    String errorMessage;
    switch (error.errorCode) {
      case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW:
        buttonLabel = context.getString(R.string.err_button_label_restart_stream);
        errorMessage = context.getString(R.string.err_msg_behind_live_window);
        break;
      default:
        buttonLabel = context.getString(R.string.err_button_label_ok);
        errorMessage = context.getString(R.string.err_message_default);
        break;
    }
    Bundle extras = new Bundle();
    extras.putString("button_label", buttonLabel);
    return new PlaybackException(errorMessage, error.getCause(), error.errorCode, extras);
  }
}

Erreurs non fatales

Les erreurs non fatales qui ne proviennent pas d'une exception technique peuvent être envoyées par une application à un ou plusieurs contrôleurs spécifiques :

Kotlin

val sessionError = SessionError(
  SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
  context.getString(R.string.error_message_authentication_expired),
)

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError)

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
mediaSession.mediaNotificationControllerInfo?.let {
  mediaSession.sendError(it, sessionError)
}

Java

SessionError sessionError = new SessionError(
    SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
    context.getString(R.string.error_message_authentication_expired));

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError);

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
ControllerInfo mediaNotificationControllerInfo =
    mediaSession.getMediaNotificationControllerInfo();
if (mediaNotificationControllerInfo != null) {
  mediaSession.sendError(mediaNotificationControllerInfo, sessionError);
}

Lorsqu'une erreur non fatale est envoyée au contrôleur de notification multimédia, le code d'erreur et le message d'erreur sont répliqués dans la session multimédia de la plate-forme, tandis que PlaybackState.state n'est pas remplacé par STATE_ERROR.

Recevoir des erreurs non fatales

Un MediaController reçoit une erreur non fatale en implémentant MediaController.Listener.onError :

Kotlin

val future = MediaController.Builder(context, sessionToken)
  .setListener(object : MediaController.Listener {
    override fun onError(controller: MediaController, sessionError: SessionError) {
      // Handle nonfatal error.
    }
  })
  .buildAsync()

Java

MediaController.Builder future =
    new MediaController.Builder(context, sessionToken)
        .setListener(
            new MediaController.Listener() {
              @Override
              public void onError(MediaController controller, SessionError sessionError) {
                // Handle nonfatal error.
              }
            });