Les sessions multimédias fournissent 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
. Connecter la session multimédia au lecteur permet à une application d'annoncer la lecture des contenus multimédias en externe et de recevoir des commandes de lecture provenant de sources externes.
Les commandes peuvent provenir de boutons physiques, comme le bouton de lecture d'un casque ou la télécommande d'un téléviseur. Elles peuvent également provenir d'applications clientes dotées d'un contrôleur multimédia, par exemple en demandant à l'Assistant Google de "mettre en pause". 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:
- Via leur casque Il existe souvent des boutons ou des interactions tactiles qu'un utilisateur peut effectuer sur son casque pour lire ou mettre en pause un contenu multimédia, ou pour passer à la piste suivante ou précédente.
- En parlant à l'Assistant Google En général, il est possible de dire "Ok Google, mets en pause" pour mettre en pause tous les contenus multimédias en cours de lecture sur l'appareil.
- Via sa montre Wear OS Cela permet d'accéder plus facilement aux commandes de lecture les plus courantes lorsqu'ils jouent sur leur téléphone.
- Via les commandes multimédias Ce carrousel présente des commandes pour chaque session multimédia en cours d'exécution.
- Sur un téléviseur Autorise les actions avec les boutons de lecture physiques, les commandes de lecture de la plate-forme et la gestion de l'alimentation (par exemple, si le téléviseur, la barre de son ou le récepteur A/V s'éteignent ou que l'entrée est modifiée, la lecture doit s'arrêter dans l'application).
- ainsi que tout autre processus externe qui doit influencer la lecture.
C'est la solution idéale dans de nombreux cas d'utilisation. En particulier, vous devez fortement envisager d'utiliser MediaSession
dans les cas suivants:
- Vous diffusez du contenu vidéo de longue durée, comme des films ou la télévision en direct.
- Vous diffusez du contenu audio de longue durée, comme des podcasts ou des playlists musicales.
- Vous développez une application TV.
Cependant, tous les cas d'utilisation ne sont pas adaptés à MediaSession
. Vous pouvez n'utiliser que la propriété Player
dans les cas suivants:
- Vous diffusez des contenus courts dans lesquels l'engagement et l'interaction des utilisateurs sont essentiels.
- Il n'y a pas une seule vidéo active (par exemple, un utilisateur fait défiler une liste et plusieurs vidéos s'affichent en même temps à l'écran).
- Vous lisez une vidéo d'introduction ou d'explication ponctuelle que l'utilisateur doit regarder activement.
- Votre contenu est sensible à la confidentialité et vous ne souhaitez pas que des processus externes accèdent aux métadonnées multimédias (par exemple, en mode navigation privée dans un navigateur).
Si votre cas d'utilisation ne correspond à aucun des cas énumérés ci-dessus, déterminez si vous acceptez que votre application continue la lecture lorsque l'utilisateur n'interagit pas activement avec le contenu. Si la réponse est oui, vous souhaiterez probablement choisir MediaSession
. Si la réponse est non, vous devrez probablement utiliser Player
à la place.
Créer une session multimédia
Les sessions multimédias accompagnent le lecteur qu'il gère. Vous pouvez construire une session multimédia avec les objets Context
et Player
. Vous devez créer et initialiser une session multimédia si 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 le 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. Ainsi, vous n'avez pas besoin de gérer manuellement le mappage d'un lecteur à l'autre.
Il s'agit d'une rupture par rapport à l'approche traditionnelle, dans laquelle vous deviez créer et gérer un PlaybackStateCompat
indépendamment du lecteur lui-même, par exemple pour signaler des erreurs.
ID de session unique
Par défaut, MediaSession.Builder
crée une session avec une chaîne vide comme ID de session. C'est suffisant si une application ne prévoit 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 lorsque vous créez la session avec MediaSession.Builder.setId(String id)
.
Si un IllegalStateException
plante 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 la libération d'une instance précédemment créée avec le même ID. Pour éviter que les sessions ne soient divulguées par une erreur de programmation, de tels cas sont détectés et notifiés en générant une exception.
Accorder le contrôle à d'autres clients
La session multimédia est essentielle pour contrôler la lecture. Elle vous permet d'acheminer les commandes provenant de sources externes vers le lecteur chargé de lire votre contenu multimédia. Ces sources peuvent être des boutons physiques, tels que le bouton de lecture d'un casque ou de la télécommande d'un téléviseur, ou des commandes indirectes telles que "pause" pour demander à l'Assistant Google de le mettre en pause. De même, vous pouvez autoriser l'accès au système Android pour faciliter les commandes de notification et de verrouillage de l'écran, ou à une montre Wear OS afin de 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. Celles-ci sont reçues par votre session multimédia, qui délègue les commandes au lecteur multimédia.
Lorsqu'un contrôleur est sur le point de se connecter à votre session multimédia, la méthode onConnect()
est appelée. Vous pouvez utiliser la ControllerInfo
fournie pour décider d'accepter ou de rejeter la requête. Consultez un exemple d'acceptation d'une requête de connexion dans la section Déclarer les commandes disponibles.
Une fois connectée, une manette peut envoyer des commandes de lecture à la session. La session délègue ensuite ces commandes au joueur. 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, par exemple, de gérer les requêtes de commandes de lecture personnalisées et de modifier la playlist.
Ces rappels incluent également un objet ControllerInfo
afin que vous puissiez modifier la façon dont vous répondez à chaque requête pour chaque 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
est disponible pour les manettes.
Lorsque vous ajoutez des éléments à la playlist, le lecteur a généralement besoin d'instances MediaItem
avec un URI défini pour les rendre lisibles. Par défaut, les éléments nouvellement ajoutés sont automatiquement transférés vers les méthodes du lecteur telles que player.addMediaItem
s'ils disposent d'un URI défini.
Si vous souhaitez personnaliser les instances MediaItem
ajoutées au lecteur, vous pouvez ignorer 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. Au lieu de cela, MediaItem
dispose généralement d'un ou plusieurs des champs suivants 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 lisible directement par le lecteur.MediaItem.RequestMetadata.searchQuery
: requête de recherche textuelle, par exemple issue de l'Assistant Google.MediaItem.MediaMetadata
: métadonnées structurées telles que "title" (titre) ou "artist" (artiste).
Pour plus d'options de personnalisation pour les nouvelles playlists, vous pouvez également remplacer onSetMediaItems()
, qui vous permet de définir l'élément de départ et la position dans la playlist. Par exemple, vous pouvez développer un seul élément demandé dans une playlist entière et demander au joueur de commencer par 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 la session.
Gérer la mise en page personnalisée et les commandes personnalisées
Les sections suivantes expliquent comment annoncer une disposition personnalisée de boutons de commande personnalisés aux applications clientes et autoriser les manettes à envoyer les commandes personnalisées.
Définir la mise en page personnalisée de la session
Pour indiquer aux applications clientes les commandes de lecture que vous souhaitez présenter à l'utilisateur, définissez la mise en page personnalisée de la session lorsque vous créez l'MediaSession
dans la méthode onCreate()
de votre service.
Kotlin
override fun onCreate() { super.onCreate() val likeButton = CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build() val favoriteButton = CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(SessionCommand(SAVE_TO_FAVORITES, Bundle())) .build() session = MediaSession.Builder(this, player) .setCallback(CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build() }
Java
@Override public void onCreate() { super.onCreate(); CommandButton likeButton = new CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(new SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build(); CommandButton favoriteButton = new CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); Player player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player) .setCallback(new CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build(); }
Déclarer le lecteur disponible et les commandes personnalisées
Les applications multimédias peuvent définir des commandes personnalisées qui peuvent, par exemple, être utilisées dans une mise en page personnalisée. Par exemple, vous pouvez implémenter des boutons permettant à l'utilisateur d'enregistrer un élément multimédia dans une liste d'éléments favoris. Le MediaController
envoie des commandes personnalisées et le MediaSession.Callback
les reçoit.
Vous pouvez définir les commandes de session personnalisées disponibles pour un MediaController
lorsqu'il se connecte à votre session multimédia. Pour ce faire, remplacez MediaSession.Callback.onConnect()
. Configurez et renvoyez l'ensemble des commandes disponibles lorsque vous acceptez une requête de connexion à partir d'un MediaController
dans la méthode de rappel onConnect
:
Kotlin
private inner 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 commande personnalisées à partir d'un MediaController
, remplacez la méthode onCustomCommand()
dans Callback
.
Kotlin
private inner 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 envoie 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 si elle provient du système, de votre propre application ou d'autres applications clientes.
Mettre à jour la mise en page personnalisée après une interaction utilisateur
Après avoir traité une commande personnalisée ou toute autre interaction avec votre lecteur, vous pouvez mettre à jour la disposition affichée dans l'UI de la manette. Un exemple typique est un bouton d'activation qui change d'icône après avoir déclenché l'action associée à ce bouton. Pour mettre à jour la mise en page, vous pouvez utiliser MediaSession.setCustomLayout
:
Kotlin
val removeFromFavoritesButton = CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(SessionCommand(REMOVE_FROM_FAVORITES, Bundle())) .build() mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton))
Java
CommandButton removeFromFavoritesButton = new CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(new SessionCommand(REMOVE_FROM_FAVORITES, new Bundle())) .build(); mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton));
Personnaliser le comportement des commandes de lecture
Pour personnaliser le comportement d'une commande définie dans l'interface Player
, telle que play()
ou seekToNext()
, encapsulez votre Player
dans un ForwardingPlayer
.
Kotlin
val player = ExoPlayer.Builder(context).build() val forwardingPlayer = object : ForwardingPlayer(player) { override fun play() { // Add custom logic super.play() } override fun setPlayWhenReady(playWhenReady: Boolean) { // Add custom logic super.setPlayWhenReady(playWhenReady) } } val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player) { @Override public void play() { // Add custom logic super.play(); } @Override public void setPlayWhenReady(boolean playWhenReady) { // Add custom logic super.setPlayWhenReady(playWhenReady); } }; MediaSession mediaSession = new MediaSession.Builder(context, forwardingPlayer).build();
Pour en savoir plus sur ForwardingPlayer
, consultez le guide ExoPlayer sur la personnalisation.
Identifier le contrôleur à l'origine de la demande d'une commande du joueur
Lorsqu'un appel à une méthode Player
provient d'un élément MediaController
, vous pouvez identifier la source d'origine avec MediaSession.controllerForCurrentRequest
et acquérir le ControllerInfo
pour la requête actuelle:
Kotlin
class CallerAwareForwardingPlayer(player: Player) : ForwardingPlayer(player) { override fun seekToNext() { Log.d( "caller", "seekToNext called from package ${session.controllerForCurrentRequest?.packageName}" ) super.seekToNext() } }
Java
public class CallerAwareForwardingPlayer extends ForwardingPlayer { public CallerAwareForwardingPlayer(Player player) { super(player); } @Override public void seekToNext() { Log.d( "caller", "seekToNext called from package: " + session.getControllerForCurrentRequest().getPackageName()); super.seekToNext(); } }
Réagir aux 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 lecture/pause d'un casque Bluetooth. Media3 gère les événements de boutons multimédias à votre place lorsqu'ils arrivent à la session et appelle la méthode Player
appropriée sur le lecteur de session.
Une application peut ignorer le comportement par défaut en remplaçant MediaSession.Callback.onMediaButtonEvent(Intent)
. Dans ce cas, l'application peut ou doit gérer elle-même toutes les spécificités de l'API.