Créer une application de lecteur multimédia de base avec Media3 ExoPlayer

Jetpack Media3 définit une interface Player qui décrit les fonctionnalités de base pour la lecture de fichiers vidéo et audio. ExoPlayer est l'implémentation par défaut de cette interface dans Media3. Nous vous recommandons d'utiliser ExoPlayer, car il fournit un ensemble complet de fonctionnalités qui couvrent la plupart des cas d'utilisation de la lecture et est personnalisable pour gérer tous les cas d'utilisation supplémentaires que vous pourriez avoir. ExoPlayer fait également abstraction de la fragmentation de l'appareil et du système d'exploitation afin que votre code fonctionne de manière cohérente dans l'ensemble de l'écosystème Android. ExoPlayer comprend:

Cette page vous explique certaines des étapes clés de la création d'une application de lecture. Pour en savoir plus, consultez nos guides complets sur Media3 ExoPlayer.

Premiers pas

Pour commencer, ajoutez une dépendance sur les modules ExoPlayer, UI et Common de Jetpack Media3:

implementation "androidx.media3:media3-exoplayer:1.5.0"
implementation "androidx.media3:media3-ui:1.5.0"
implementation "androidx.media3:media3-common:1.5.0"

Selon votre cas d'utilisation, vous devrez peut-être également utiliser des modules supplémentaires de Media3, tels que exoplayer-dash pour lire des flux au format DASH.

Veillez à remplacer 1.5.0 par la version de la bibliothèque de votre choix. Vous pouvez consulter les notes de version pour connaître la dernière version.

Créer un lecteur multimédia

Avec Media3, vous pouvez utiliser l'implémentation incluse de l'interface Player, ExoPlayer, ou créer votre propre implémentation personnalisée.

Créer un ExoPlayer

Le moyen le plus simple de créer une instance ExoPlayer est le suivant:

Kotlin

val player = ExoPlayer.Builder(context).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();

Vous pouvez créer votre lecteur multimédia dans la méthode de cycle de vie onCreate() de Activity, Fragment ou Service.

Builder inclut une gamme d'options de personnalisation qui pourraient vous intéresser, par exemple:

Media3 fournit un composant d'interface utilisateur PlayerView que vous pouvez inclure dans le fichier de mise en page de votre application. Ce composant encapsule un PlayerControlView pour les commandes de lecture, un SubtitleView pour afficher les sous-titres et un Surface pour le rendu de la vidéo.

Préparer le lecteur

Ajoutez des éléments multimédias à une playlist pour la lire à l'aide de méthodes telles que setMediaItem() et addMediaItem(). Appelez ensuite prepare() pour commencer à charger les contenus multimédias et acquérir les ressources nécessaires.

Vous ne devez pas effectuer ces étapes avant que l'application ne soit au premier plan. Si votre lecteur se trouve dans un Activity ou un Fragment, cela signifie que vous devez le préparer dans la méthode de cycle de vie onStart() au niveau d'API 24 ou version ultérieure, ou dans la méthode de cycle de vie onResume() au niveau d'API 23 ou version antérieure. Pour un joueur qui se trouve dans un Service, vous pouvez le préparer dans onCreate().

Contrôler le lecteur

Une fois le lecteur préparé, vous pouvez contrôler la lecture en appelant des méthodes sur le lecteur, par exemple:

Les composants d'interface utilisateur tels que PlayerView ou PlayerControlView seront mis à jour en conséquence lorsqu'ils seront liés à un lecteur.

Libérer le lecteur

La lecture peut nécessiter des ressources limitées, telles que des décodeurs vidéo. Il est donc important d'appeler release() sur votre lecteur pour libérer des ressources lorsque le lecteur n'est plus nécessaire.

Si votre lecteur se trouve dans un Activity ou un Fragment, libérez-le dans la méthode de cycle de vie onStop() au niveau d'API 24 ou version ultérieure, ou dans la méthode onPause() au niveau d'API 23 ou version antérieure. Pour un joueur qui se trouve dans un Service, vous pouvez le libérer dans onDestroy().

Gérer la lecture avec une session multimédia

Sur Android, les sessions multimédias offrent un moyen standardisé d'interagir avec un lecteur multimédia au-delà des limites de processus. Connecter une session multimédia à votre lecteur vous permet de diffuser votre lecture multimédia en externe et de recevoir des commandes de lecture à partir de sources externes, par exemple pour vous intégrer aux commandes multimédias du système sur les appareils mobiles et à grand écran.

Pour utiliser des sessions multimédias, ajoutez une dépendance au module Session Media3:

implementation "androidx.media3:media3-session:1.5.0"

Créer une session multimédia

Vous pouvez créer un MediaSession après avoir initialisé un lecteur 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();

Media3 synchronise automatiquement l'état de Player avec l'état de MediaSession. Cela fonctionne avec n'importe quelle implémentation Player, y compris ExoPlayer, CastPlayer ou une implémentation personnalisée.

Accorder le contrôle à d'autres clients

Les applications clientes peuvent implémenter un contrôleur multimédia pour contrôler la lecture de votre session multimédia. Pour recevoir ces requêtes, définissez un objet callback lors de la création de votre MediaSession.

Lorsqu'une manette est sur le point de se connecter à votre session multimédia, la méthode onConnect() est appelée. Vous pouvez utiliser l'ControllerInfo fourni pour décider d'accepter ou de refuser la requête. Vous trouverez un exemple dans l'application de démonstration Media3 Session.

Une fois connecté, une manette 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 gérées automatiquement par la session.

D'autres méthodes de rappel vous permettent de gérer, par exemple, les requêtes de commandes de lecture personnalisées et de modifier la playlist. De même, ces rappels incluent un objet ControllerInfo afin que vous puissiez déterminer le contrôle des accès sur la base de chaque requête.

Lire des contenus multimédias en arrière-plan

Pour continuer à lire des contenus multimédias lorsque votre application n'est pas au premier plan (par exemple, pour lire de la musique, des livres audio ou des podcasts même lorsque l'utilisateur n'a pas ouvert votre application), vos Player et MediaSession doivent être encapsulés dans un service de premier plan. Media3 fournit l'interface MediaSessionService à cette fin.

Implémenter un MediaSessionService

Créez une classe qui étend MediaSessionService et instanciez votre MediaSession dans la méthode de cycle de vie onCreate().

Kotlin

class PlaybackService : MediaSessionService() {
    private var mediaSession: MediaSession? = null

    // Create your Player and MediaSession in the onCreate lifecycle event
    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player).build()
    }

    // Remember to release the player and media session in onDestroy
    override fun onDestroy() {
        mediaSession?.run {
            player.release()
            release()
            mediaSession = null
        }
        super.onDestroy()
    }
}

Java

public class PlaybackService extends MediaSessionService {
    private MediaSession mediaSession = null;

    @Override
    public void onCreate() {
        super.onCreate();
        ExoPlayer player = new ExoPlayer.Builder(this).build();
        mediaSession = new MediaSession.Builder(this, player).build();
    }

    @Override
    public void onDestroy() {
        mediaSession.getPlayer().release();
        mediaSession.release();
        mediaSession = null;
        super.onDestroy();
    }
}

Dans votre fichier manifeste, votre classe Service avec un filtre d'intent MediaSessionService et demandez l'autorisation FOREGROUND_SERVICE pour exécuter un service de premier plan:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Enfin, dans la classe que vous avez créée, remplacez la méthode onGetSession() pour contrôler l'accès client à votre session multimédia. Renvoyer un MediaSession pour accepter la demande de connexion ou null pour la refuser.

Kotlin

// This example always accepts the connection request
override fun onGetSession(
    controllerInfo: MediaSession.ControllerInfo
): MediaSession? = mediaSession

Java

@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
  // This example always accepts the connection request
  return mediaSession;
}

Se connecter à votre UI

Maintenant que votre session multimédia se trouve dans un Service distinct de l'Activity ou de l'Fragment où se trouve l'UI de votre lecteur, vous pouvez utiliser un MediaController pour les associer. Dans la méthode onStart() de Activity ou Fragment avec votre UI, créez un SessionToken pour votre MediaSession, puis utilisez le SessionToken pour créer un MediaController. La compilation d'un MediaController se produit de manière asynchrone.

Kotlin

override fun onStart() {
  val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
  val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
  controllerFuture.addListener(
    {
        // Call controllerFuture.get() to retrieve the MediaController.
        // MediaController implements the Player interface, so it can be
        // attached to the PlayerView UI component.
        playerView.setPlayer(controllerFuture.get())
      },
    MoreExecutors.directExecutor()
  )
}

Java

@Override
public void onStart() {
  SessionToken sessionToken =
    new SessionToken(this, new ComponentName(this, PlaybackService.class));
  ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(this, sessionToken).buildAsync();
  controllerFuture.addListener(() -> {
    // Call controllerFuture.get() to retrieve the MediaController.
    // MediaController implements the Player interface, so it can be
    // attached to the PlayerView UI component.
    playerView.setPlayer(controllerFuture.get());
  }, MoreExecutors.directExecutor())
}

MediaController implémente l'interface Player. Vous pouvez donc utiliser les mêmes méthodes, telles que play() et pause(), pour contrôler la lecture. Comme pour les autres composants, n'oubliez pas de libérer le MediaController lorsqu'il n'est plus nécessaire, comme la méthode de cycle de vie onStop() d'un Activity, en appelant MediaController.releaseFuture().

Publier une notification

Les services de premier plan doivent publier une notification lorsqu'ils sont actifs. Un MediaSessionService crée automatiquement une notification MediaStyle sous la forme d'un MediaNotification. Pour fournir une notification personnalisée, créez un MediaNotification.Provider avec DefaultMediaNotificationProvider.Builder ou en créant une implémentation personnalisée de l'interface du fournisseur. Ajoutez votre fournisseur à votre MediaSession avec setMediaNotificationProvider.

Promouvoir votre bibliothèque de contenus

Un MediaLibraryService s'appuie sur un MediaSessionService en permettant aux applications clientes de parcourir le contenu multimédia fourni par votre application. Les applications clientes implémentent un MediaBrowser pour interagir avec votre MediaLibraryService.

L'implémentation d'un MediaLibraryService est semblable à celle d'un MediaSessionService, sauf que dans onGetSession(), vous devez renvoyer un MediaLibrarySession au lieu d'un MediaSession. Par rapport à un MediaSession.Callback, MediaLibrarySession.Callback inclut des méthodes supplémentaires qui permettent à un client de navigateur de parcourir le contenu proposé par votre service de bibliothèque.

Comme pour MediaSessionService, déclarez MediaLibraryService dans votre fichier manifeste et demandez l'autorisation FOREGROUND_SERVICE pour exécuter un service de premier plan:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
        <action android:name="android.media.browse.MediaBrowserService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

L'exemple ci-dessus inclut un filtre d'intent pour MediaLibraryService et, pour la rétrocompatibilité, l'ancien MediaBrowserService. Le filtre d'intent supplémentaire permet aux applications clientes utilisant l'API MediaBrowserCompat de reconnaître votre Service.

Un MediaLibrarySession vous permet de diffuser votre bibliothèque de contenus dans une structure arborescente, avec un seul MediaItem racine. Chaque MediaItem de l'arborescence peut comporter autant de nœuds MediaItem enfants que nécessaire. Vous pouvez diffuser une racine ou un arbre différents en fonction de la requête de l'application cliente. Par exemple, l'arborescence que vous renvoyez à un client à la recherche d'une liste d'éléments multimédias recommandés ne peut contenir que la racine MediaItem et un seul niveau de nœuds enfants MediaItem, tandis que l'arborescence que vous renvoyez à une autre application cliente peut représenter une bibliothèque de contenus plus complète.

Créer un MediaLibrarySession

Un MediaLibrarySession étend l'API MediaSession pour ajouter des API de navigation dans le contenu. Par rapport au rappel MediaSession, le rappel MediaLibrarySession ajoute des méthodes telles que:

  • onGetLibraryRoot() pour qu'un client demande la racine MediaItem d'une arborescence de contenu
  • onGetChildren() pour lorsqu'un client demande les enfants d'un MediaItem dans l'arborescence de contenu
  • onGetSearchResult() pour qu'un client demande les résultats de recherche de l'arborescence de contenu pour une requête donnée

Les méthodes de rappel pertinentes incluront un objet LibraryParams avec des signaux supplémentaires sur le type d'arborescence de contenu qui intéresse une application cliente.