1. Avant de commencer
Capture d'écran : application YouTube sur Android
ExoPlayer est un lecteur multimédia au niveau de l'application, conçu à partir d'API multimédias de base dans Android. Il présente certains avantages par rapport à MediaPlayer, le lecteur intégré à Android. Il est compatible avec une grande partie des formats multimédias de MediaPlayer, ainsi qu'avec les formats adaptatifs tels que DASH et SmoothStreaming. ExoPlayer est extrêmement personnalisable et extensible. C'est pourquoi il convient à de nombreux cas d'utilisation avancés. Il s'agit d'un projet Open Source utilisé par les applications Google, y compris YouTube et Google Play Films et TV.
Prérequis
- Une certaine connaissance du développement sur Android et d'Android Studio
Objectifs de l'atelier
- Créer une instance
SimpleExoPlayer
, qui prépare et lit les contenus multimédias depuis différentes sources - Intégrer ExoPlayer au cycle de vie d'une activité de l'application, pour permettre le passage en arrière-plan et au premier plan, ainsi que la reprise de la lecture dans un environnement à une ou plusieurs fenêtres
- Utiliser des
MediaItem
pour créer une playlist - Lire des flux vidéo adaptatifs, qui adaptent la qualité du contenu multimédia à la bande passante disponible
- Enregistrer des écouteurs d'événements pour surveiller l'état de la lecture et afficher la façon dont les écouteurs peuvent être utilisés pour mesure la qualité de la lecture
- Utiliser des composants standards de l'UI d'ExoPlayer, puis les personnaliser pour les adapter au style de votre application
Ce dont vous avez besoin
- La dernière version stable d'Android Studio
- Un appareil Android fonctionnant sous JellyBean (4.1) ou version ultérieure (idéalement Nougat (7.1) ou version ultérieure, car il est possible d'utiliser plusieurs fenêtres avec cette version de l'OS)
2. Configuration
Obtenir le code
Pour commencer, téléchargez le projet Android Studio :
Vous pouvez également cloner le dépôt GitHub :
git clone https://github.com/googlecodelabs/exoplayer-intro.git
Structure des répertoires
Une fois le dépôt cloné ou décompressé, vous pouvez accéder au dossier racine (exoplayer-intro
), qui contient un projet Gradle composé de plusieurs modules : un module d'application et un module pour chaque étape de cet atelier de programmation, avec toutes les ressources nécessaires.
Importer le projet
- Lancez Android Studio.
- Sélectionnez Fichier > Nouveau > Importer un projet*.*
- Sélectionnez le fichier racine
build.gradle
.
Capture d'écran : structure du projet importé
Une fois le projet compilé, six modules s'affichent : le module app
(de type application) et cinq modules nommés exoplayer-codelab-N
(où N
correspond à un chiffre compris entre 00
et 04,
, chacun de type bibliothèque). Le module app
ne contient qu'un fichier manifeste. L'intégralité du module exoplayer-codelab-N
spécifié fusionne lorsque l'application est compilée avec une dépendance Gradle dans app/build.gradle
.
app/build.gradle
dependencies {
implementation project(":exoplayer-codelab-00")
}
L'activité du lecteur multimédia est conservée dans le module exoplayer-codelab-N
. Si elle reste stockée dans un module de bibliothèque distinct, c'est pour que vous puissiez la partager entre des APK ciblant différentes plates-formes (mobile et Android TV, par exemple). De plus, vous pouvez ainsi profiter de certaines fonctionnalités, comme Dynamic Delivery, qui permet d'installer votre fonctionnalité de lecture de contenus multimédias uniquement lorsque l'utilisateur en a besoin.
- Déployez et exécutez l'application pour vérifier que tout fonctionne bien. L'application doit remplir l'écran avec un arrière-plan noir.
Capture d'écran : application vide en cours d'exécution
3. Diffuser des contenus en streaming
Ajouter une dépendance ExoPlayer
ExoPlayer est un projet Open Source hébergé sur GitHub. Chaque version est distribuée via Google Maven, l'un des dépôts de packages par défaut utilisés par Android Studio et Gradle. Chacune est identifiée de manière unique par une chaîne au format suivant :
com.google.android.exoplayer:exoplayer:X.X.X
Vous pouvez ajouter ExoPlayer à votre projet en important simplement ses classes et composants d'UI. Ce projet est plutôt de petite taille, avec une empreinte minime d'environ 70 à 300 ko, selon les fonctionnalités incluses et les formats acceptés. La bibliothèque d'ExoPlayer est divisée en modules pour que les développeurs puissent importer uniquement la fonctionnalité dont ils ont besoin. Pour en savoir plus sur la structure modulaire d'ExoPlayer, reportez-vous à la section Ajouter des modules ExoPlayer.
- Ouvrez le fichier
build.gradle
du moduleplayer-lib
. - Ajoutez les lignes ci-dessous à la section
dependencies
, puis synchronisez le projet.
exoplayer-codelab-00/build.gradle
dependencies {
[...]
implementation 'com.google.android.exoplayer:exoplayer-core:2.12.0'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.12.0'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.12.0'
}
Ajouter l'PlayerView element
- Ouvrez le fichier de ressources de mise en page
activity_player.xml
depuis le moduleexoplayer-codelab-00
. - Placez le curseur dans l'élément
FrameLayout
. - Commencez à saisir
<PlayerView
et laissez Android Studio remplir automatiquement l'élémentPlayerView
. - Utilisez
match_parent
comme valeur pourwidth
etheight
. - Déclarez
video_view
comme ID.
activity_player.xml
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Dans la suite de cet atelier, cet élément d'interface utilisateur sera appelé "vue de la vidéo".
- Dans
PlayerActivity
, vous pouvez à présent obtenir une référence pour l'arborescence de vues, créée à partir du fichier XML que vous venez de modifier.
PlayerActivity.kt
private val viewBinding by lazy(LazyThreadSafetyMode.NONE) {
ActivityPlayerBinding.inflate(layoutInflater)
}
- Définissez la racine de votre arborescence de vues comme vue de contenu de votre activité. Vérifiez également que la propriété
videoView
est visible dans votre référenceviewBinding
et que son type estPlayerView
.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(viewBinding.root)
}
Créer une instance d'ExoPlayer
Pour lire un contenu multimédia en streaming, vous avez besoin d'un objet ExoPlayer
. Le plus simple pour en créer un consiste à utiliser la classe SimpleExoPlayer.Builder
. Comme son nom l'indique, elle se sert du schéma de compilateur pour compiler une instance SimpleExoPlayer
.
L'instance SimpleExoPlayer
est une implémentation polyvalente pratique de l'interface ExoPlayer
.
Ajoutez une méthode privée initializePlayer
pour créer votre instance SimpleExoPlayer
.
PlayerActivity.kt
private var player: SimpleExoPlayer? = null
[...]
private fun initializePlayer() {
player = SimpleExoPlayer.Builder(this)
.build()
.also { exoPlayer ->
viewBinding.videoView.player = exoPlayer
}
}
Créez une classe SimpleExoPlayer.Builder
en utilisant votre contexte, puis appelez build
pour créer votre objet SimpleExoPlayer
. Celui-ci est ensuite attribué à player
, que vous devez déclarer comme champ de membre. Ensuite, utilisez la propriété modifiable viewBinding.videoView.player
pour lier player
à la vue correspondante.
Créer un élément multimédia
Vous avez à présent besoin de contenus à lire pour votre objet player
. Pour cela, créez un MediaItem
. Il existe de nombreux types d'éléments MediaItem
. Pour commencer, vous allez en créer un pour un fichier MP3 sur Internet.
La façon la plus simple de créer un MediaItem
consiste à utiliser MediaItem.fromUri
, qui accepte l'URI d'un fichier multimédia. Ajoutez le MediaItem
au player
à l'aide de player.setMediaItem
.
- Ajoutez le code suivant à
initializePlayer
dans le blocalso
:
PlayerActivity.kt
private fun initializePlayer() {
[...]
.also { exoPlayer ->
[...]
val mediaItem = MediaItem.fromUri(getString(R.string.media_url_mp3))
exoPlayer.setMediaItem(mediaItem)
}
}
Notez que la valeur de R.string.media_url_mp3
est https://storage.googleapis.com/exoplayer-test-media-0/play.mp3 dans strings.xml
.
Optimiser le cycle de vie de l'activité
L'instance player
peut consommer énormément de ressources, y compris pour la mémoire, le processeur, les connexions réseau et les codecs matériels. Un grand nombre de ces ressources est limité, surtout pour les codecs matériels qui ne peuvent avoir qu'une seule ressource. Il est important que vous libériez ces ressources pour les autres applications à utiliser lorsque vous ne vous en servez pas, par exemple quand votre application est mise en arrière-plan.
Autrement dit, le cycle de vie de votre lecteur doit être lié au cycle de vie de votre application. Pour cela, vous devez ignorer les quatre méthodes de PlayerActivity
: onStart
, onResume
, onPause
et onStop
.
- Ouvrez
PlayerActivity
, puis cliquez sur Code menu (Menu du code) > Override methods (Ignorer les méthodes). - Sélectionnez
onStart
,onResume
,onPause
etonStop
. - Initialisez le lecteur dans le rappel
onStart
ouonResume
, selon le niveau d'API.
PlayerActivity.kt
public override fun onStart() {
super.onStart()
if (Util.SDK_INT >= 24) {
initializePlayer()
}
}
public override fun onResume() {
super.onResume()
hideSystemUi()
if ((Util.SDK_INT < 24 || player == null)) {
initializePlayer()
}
}
Le niveau d'API Android 24 (et supérieur) gère plusieurs fenêtres. Comme votre application peut être visible et inactive en mode fenêtre partagée, vous devez initialiser le lecteur dans onStart
. Pour les niveaux d'API Android 24 et inférieurs, vous devez patienter jusqu'à ce que vous récupériez les ressources. Vous devez donc attendre onResume
pour pouvoir initialiser le lecteur.
- Ajoutez la méthode
hideSystemUi
.
PlayerActivity.kt
@SuppressLint("InlinedApi")
private fun hideSystemUi() {
viewBinding.videoView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE
or View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
}
hideSystemUi
est une méthode d'assistance appelée dans onResume
. Cette méthode vous permet d'afficher le lecteur en plein écran.
- Libérez les ressources avec
releasePlayer
(que vous allez créer juste après) dansonPause
etonStop
.
PlayerActivity.kt
public override fun onPause() {
super.onPause()
if (Util.SDK_INT < 24) {
releasePlayer()
}
}
public override fun onStop() {
super.onStop()
if (Util.SDK_INT >= 24) {
releasePlayer()
}
}
Pour les niveaux d'API 24 et inférieurs, vous n'avez aucune certitude qu'onStop
sera appelé. Vous devez donc libérer le lecteur dès que possible dans onPause
. Pour les niveaux d'API 24 et supérieurs (qui proposent les modes Multifenêtre et Fenêtre partagée), onStop
sera obligatoirement appelé. Lorsqu'elle est en pause, votre activité reste visible. Vous devez donc attendre onStop
pour libérer le lecteur.
Vous devez à présent créer une méthode releasePlayer
, qui libère les ressources du lecteur et le détruit.
- Ajoutez le code suivant à l'activité :
PlayerActivity.kt
private var playWhenReady = true
private var currentWindow = 0
private var playbackPosition = 0L
[...]
private fun releasePlayer() {
player?.run {
playbackPosition = this.currentPosition
currentWindow = this.currentWindowIndex
playWhenReady = this.playWhenReady
release()
}
player = null
}
Avant de libérer et de détruire le lecteur, stockez les informations suivantes :
- État de lecture/pause avec
playWhenReady
- Position actuelle de la lecture avec
currentPosition
- Index de la fenêtre active avec
currentWindowIndex
(pour en savoir plus sur les fenêtres, reportez-vous à la section Chronologie)
Il vous suffit de fournir ces informations d'état lorsque vous initialisez le lecteur pour que l'utilisateur puisse reprendre la lecture là où il l'avait arrêtée.
Derniers préparatifs
À présent, il ne vous reste plus qu'à fournir à votre lecteur, pendant l'initialisation, les informations d'état que vous avez enregistrées dans releasePlayer
.
- Ajoutez le code suivant à
initializePlayer
:
PlayerActivity.kt
private fun initializePlayer() {
[...]
exoPlayer.playWhenReady = playWhenReady
exoPlayer.seekTo(currentWindow, playbackPosition)
exoPlayer.prepare()
}
Voilà ce qui se passe :
playWhenReady
indique au lecteur s'il doit démarrer la lecture dès que toutes les ressources pour la lecture ont été acquises. CommeplayWhenReady
est défini surtrue
au départ, la lecture démarre automatiquement la première fois que l'application est lancée.seekTo
indique au lecteur de rechercher une certaine position dans une fenêtre spécifique.currentWindow
etplaybackPosition
sont tous deux mis à zéro afin que la lecture démarre dès le début la première fois que l'application est lancée.prepare
signale au lecteur qu'il doit acquérir toutes les ressources requises pour la lecture.
Lire le fichier audio
Vous avez enfin terminé ! Lancez l'application pour lire le fichier MP3 et voir l'image intégrée.
Capture d'écran : l'application lit un seul titre.
Tester le cycle de vie de l'activité
Testez si l'application fonctionne dans tous les états du cycle de vie de l'activité.
- Lancez une autre application et remettez votre application au premier plan. Est-ce qu'elle reprend à la bonne position ?
- Mettez l'application en pause et passez-la en arrière-plan, puis à nouveau au premier plan. Est-ce qu'elle est toujours en pause lorsqu'elle passe en arrière-plan ?
- Faites pivoter l'application. Son comportement change-t-il si vous passez du mode Portrait au mode Paysage et inversement ?
Lire une vidéo
Pour lire une vidéo, il vous suffit de remplacer l'URI de l'élément multimédia par un fichier MP4.
- Remplacez l'URI dans
initializePlayer
parR.string.media_url_mp4
. - Relancez l'application et testez son comportement, y compris lorsqu'elle est mise en arrière-plan alors qu'une vidéo est en cours de lecture.
PlayerActivity.kt
private fun initializePlayer() {
[...]
val mediaItem = MediaItem.fromUri(getString(R.string.media_url_mp4));
[...]
}
PlayerView
se charge de tout. Ici, c'est la vidéo qui est affichée en plein écran, pas l'image.
Capture d'écran : l'application lit une vidéo.
Félicitations ! Vous venez de créer une application pour diffuser un fichier multimédia sur Android en streaming et en plein écran, avec la gestion du cycle de vie, les enregistrements des états et les commandes d'interface utilisateur.
4. Créer une playlist
Votre application actuelle lit un seul fichier multimédia, mais que faire si vous voulez en lire plusieurs d'affilée ? Pour cela, vous avez besoin d'une playlist.
Vous pouvez créer une playlist en ajoutant plusieurs éléments MediaItem
au player
à l'aide d'addMediaItem
. Cela permet d'enchaîner la lecture des fichiers de manière fluide. La mise en mémoire tampon étant gérée en arrière-plan, aucune icône de chargement ne s'affiche lorsque l'utilisateur passe d'un élément multimédia à un autre.
- Ajoutez le code suivant à
initializePlayer
:
PlayerActivity.kt
private void initializePlayer() {
[...]
exoPlayer.addMediaItem(mediaItem) // Existing code
val secondMediaItem = MediaItem.fromUri(getString(R.string.media_url_mp3));
exoPlayer.addMediaItem(secondMediaItem);
[...]
}
Vérifiez le comportement des commandes du lecteur. Vous pouvez utiliser les boutons et pour parcourir la liste des éléments multimédias.
Capture d'écran : commandes de lecture, avec les boutons Précédent et Suivant
C'est plutôt pratique ! Pour en savoir plus, reportez-vous à la documentation pour les développeurs sur les éléments multimédias et les playlists, ainsi qu'à cet article sur l'API Playlist.
5. Streaming adaptatif
Le streaming adaptatif est une technique de streaming de fichiers multimédias qui consiste à adapter la qualité du flux à la bande passante réseau disponible. L'utilisateur bénéficie ainsi de la meilleure qualité possible pour sa bande passante.
Généralement, le même contenu multimédia est divisé en plusieurs titres de différentes qualités (débits et résolutions). Le lecteur choisit le titre en fonction de la bande passante réseau disponible.
Chaque titre est sectionné en fragments d'une certaine durée, généralement entre 2 et 10 secondes. Cela permet au lecteur de passer rapidement d'un titre à l'autre en fonction de l'évolution de la disponibilité de la bande passante. Le lecteur se charge ensuite d'assembler à nouveau ces fragments pour que la lecture soit fluide.
Sélection de titres adaptative
Sélectionner le titre le plus adapté à l'environnement actuel est une opération centrale du streaming adaptatif. Mettez à jour votre application pour lire des fichiers multimédias en streaming en utilisant la sélection de titres adaptative.
- Mettez à jour
initializePlayer
avec le code suivant :
PlayerActivity.kt
private fun initializePlayer() {
val trackSelector = DefaultTrackSelector(this).apply {
setParameters(buildUponParameters().setMaxVideoSizeSd())
}
player = SimpleExoPlayer.Builder(this)
.setTrackSelector(trackSelector)
.build()
[...]
}
Tout d'abord, créez un DefaultTrackSelector
, qui sera chargé de sélectionner les titres dans l'élément multimédia. Ensuite, indiquez à votre trackSelector
de ne sélectionner que les titres de définition standard ou inférieure. Il s'agit d'un moyen efficace d'économiser les données de l'utilisateur (au détriment de la qualité). Enfin, transférez votre trackSelector
à votre compilateur pour qu'il soit utilisé lors de la compilation de l'instance SimpleExoPlayer
.
Créer un MediaItem adaptatif
DASH est un format de streaming adaptatif couramment utilisé. Pour diffuser en streaming un contenu DASH, vous devez créer un MediaItem
, comme précédemment. Cependant, ici, vous devez utiliser MediaItem.Builder
au lieu de fromUri
,
car fromUri
utilise l'extension de fichier pour déterminer le format multimédia sous-jacent. Or, l'URI DASH ne possède pas d'extension de fichier. De ce fait, nous devons fournir un type MIME d'APPLICATION_MPD
pour créer le MediaItem
.
- Mettez à jour
initializePlayer
comme indiqué ci-dessous :
PlayerActivity.kt
private void initializePlayer() {
[...]
// Replace this line
val mediaItem = MediaItem.fromUri(getString(R.string.media_url_mp4));
// With this
val mediaItem = MediaItem.Builder()
.setUri(getString(R.string.media_url_dash))
.setMimeType(MimeTypes.APPLICATION_MPD)
.build()
// Also remove the following lines
val secondMediaItem = MediaItem.fromUri(getString(R.string.media_url_mp3))
exoPlayer.addMediaItem(secondMediaItem)
}
- Redémarrez l'application et observez le fonctionnement du streaming vidéo adaptatif avec DASH. C'est plutôt simple avec ExoPlayer.
Autres formats de streaming adaptatif
HLS (MimeTypes.APPLICATION_M3U8
) et SmoothStreaming (MimeTypes.APPLICATION_SS
) sont d'autres formats de streaming adaptatif courants qui sont compatibles avec ExoPlayer. Pour savoir comment créer d'autres sources multimédias adaptatives, reportez-vous à l'application de démonstration ExoPlayer.
6. Écouter les événements
Lors des étapes précédentes, vous avez vu comment lire en streaming des flux multimédias progressifs et adaptatifs. ExoPlayer effectue de nombreuses opérations en arrière-plan. Par exemple :
- Allouer la mémoire
- Télécharger les fichiers du conteneur
- Extraire les métadonnées du conteneur
- Décoder les données
- Afficher la vidéo et le texte à l'écran, et diffuser l'audio sur les enceintes
Il est parfois utile de savoir ce qu'ExoPlayer fait lors de l'exécution afin de comprendre et d'améliorer l'expérience de lecture pour vos utilisateurs.
Par exemple, vous pouvez décider de refléter les changements de l'état de lecture dans l'interface utilisateur en procédant comme suit :
- En affichant une icône de chargement lorsque le lecteur met les données en mémoire tampon
- En affichant le texte "À regarder ensuite" en superposition à la fin de la lecture du titre
ExoPlayer propose plusieurs interfaces d'écouteur qui fournissent des rappels pour les événements utiles. Un écouteur vous permet de consigner l'état dans lequel se trouve le lecteur.
Rester à l'écoute
- Créez une constante
TAG
en dehors de la classePlayerActivity
, que vous utiliserez par la suite pour la journalisation.
PlayerActivity.kt
private const val TAG = "PlayerActivity"
- Implémentez l'interface
Player.EventListener
dans une fonction de fabrique en dehors de la classePlayerActivity
. Elle servira à vous signaler les événements importants du lecteur, y compris les erreurs et ses changements d'état. - Ignorez
onPlaybackStateChanged
en ajoutant le code suivant :
PlayerActivity.kt
private fun playbackStateListener() = object : Player.EventListener {
override fun onPlaybackStateChanged(playbackState: Int) {
val stateString: String = when (playbackState) {
ExoPlayer.STATE_IDLE -> "ExoPlayer.STATE_IDLE -"
ExoPlayer.STATE_BUFFERING -> "ExoPlayer.STATE_BUFFERING -"
ExoPlayer.STATE_READY -> "ExoPlayer.STATE_READY -"
ExoPlayer.STATE_ENDED -> "ExoPlayer.STATE_ENDED -"
else -> "UNKNOWN_STATE -"
}
Log.d(TAG, "changed state to $stateString")
}
}
- Déclarez un membre privé de type
Player.EventListener
dansPlayerActivity
.
PlayerActivity.kt
class PlayerActivity : AppCompatActivity() {
[...]
private val playbackStateListener: Player.EventListener = playbackStateListener()
}
onPlaybackStateChanged
est appelé lorsque l'état de lecture change. Le nouvel état est donné par le paramètre playbackState
.
Les états du lecteur sont au nombre de quatre :
État | Description |
| Le lecteur a été instancié, mais n'a pas encore été préparé. |
| Le lecteur ne peut pas lire de fichiers depuis la position actuelle, car la quantité de données en mémoire tampon n'est pas suffisante. |
| Le lecteur peut démarrer immédiatement la lecture depuis la position actuelle. Cela signifie qu'il commencera à lire le fichier multimédia automatiquement si la valeur de la propriété playWhenReady est définie sur |
| Le lecteur a terminé de lire le fichier multimédia. |
Enregistrer votre écouteur
Pour que vos rappels soient appelés, vous devez enregistrer playbackStateListener
auprès du lecteur, dans initializePlayer
.
- Enregistrez l'écouteur avant que la lecture ne soit préparée.
PlayerActivity.kt
private void initializePlayer() {
[...]
exoPlayer.seekTo(currentWindow, playbackPosition)
exoPlayer.addListener(playbackStateListener)
[...]
}
Là aussi, vous devez nettoyer le code pour éviter que des références du lecteur restent présentes et provoquent une fuite de mémoire.
- Supprimez l'écouteur de
releasePlayer
:
PlayerActivity.kt
private void releasePlayer() {
player?.run {
[...]
removeListener(playbackStateListener)
release()
}
player = null
}
- Ouvrez logcat et exécutez l'application.
- Utilisez les commandes d'interface utilisateur pour rechercher, mettre en pause et reprendre la lecture. Vous devriez voir l'état de la lecture changer dans les journaux.
Aller plus loin
ExoPlayer propose d'autres écouteurs pratiques pour comprendre l'expérience de lecture de l'utilisateur. Il existe des écouteurs pour l'audio et la vidéo, ainsi qu'un écouteur AnalyticsListener
, qui contient les rappels de tous les écouteurs. Voici quelques-unes des méthodes les plus importantes :
- La méthode
onRenderedFirstFrame
est appelée lorsque le premier frame d'une vidéo est rendu. Vous pouvez alors calculer combien de temps l'utilisateur a dû attendre pour qu'un contenu satisfaisant s'affiche à l'écran. - La méthode
onDroppedVideoFrames
est appelée lorsque des frames vidéo ont été perdus. Une perte de frames indique que la lecture est mauvaise et que l'expérience utilisateur est probablement médiocre. - La méthode
onAudioUnderrun
est appelée si la mémoire tampon audio est sous-utilisée. Une telle sous-utilisation peut être à l'origine de problèmes audio plus perceptibles que la perte de frames.
AnalyticsListener
peut être ajouté à player
avec addAnalyticsListener
. Il existe également des méthodes équivalentes pour les écouteurs audio et vidéo.
Déterminez les événements importants pour votre application et vos utilisateurs. Pour en savoir plus, consultez Écouter des événements du lecteur. C'est tout pour les lecteurs d'événements !
7. Personnaliser l'interface utilisateur
Jusqu'à présent, vous avez utilisé la fonction PlayerControlView
d'ExoPlayer pour présenter une barre de lecture à l'utilisateur.
Capture d'écran : barre de lecture par défaut
Que faire si vous voulez modifier la fonctionnalité ou l'aspect de ces commandes ? Heureusement, ces commandes sont ultra-personnalisables.
La première personnalisation simple consiste à ne pas du tout utiliser la barre. Pour cela, il suffit d'utiliser l'attribut use_controller
sur l'élément PlayerView
dans activity_player.xml
.
- Définissez
use_controller
surfalse
. La barre ne s'affiche plus :
activity_player.xml
<com.google.android.exoplayer2.ui.PlayerView
[...]
app:use_controller="false"/>
- Ajoutez l'espace de noms suivant à
FrameLayout
:
activity_player.xml
<FrameLayout
[...]
xmlns:app="http://schemas.android.com/apk/res-auto">
Essayez.
Personnaliser le comportement
PlayerControlView
possède plusieurs attributs qui influent sur son comportement. Par exemple, vous pouvez utiliser show_timeout
pour personnaliser le délai en millisecondes entre la dernière interaction de l'utilisateur avec la barre et le masquage de celle-ci. Pour ce faire, procédez comme suit :
- Supprimez
app:use_controller="false"
. - Remplacez la vue du lecteur par
show_timeout
:
activity_player.xml
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:show_timeout="10000"/>
Les attributs de PlayerControlView
peuvent également être définis par programmatique.
Personnaliser l'aspect
C'est un bon début, mais comment faire pour changer l'aspect de PlayerControlView
ou les boutons affichés ? L'implémentation de PlayerControlView
ne suppose pas l'existence de boutons. Il est donc facile d'en supprimer et d'en ajouter.
Voyons comment vous pouvez personnaliser PlayerControlView
.
- Créez un fichier de mise en page
custom_player_control_view.xml
dans le dossierplayer-lib/res/layout/
. - Dans le menu contextuel du dossier de mise en page, sélectionnez New > Layout resource file (Nouveau > Fichier des ressources de mise en page), puis nommez-le
custom_player_control_view.xml
.
Capture d'écran : le fichier de mise en page pour la vue des commandes du lecteur a été créé.
- Copiez le fichier de mise en page d'origine depuis cet emplacement dans
custom_player_control_view.xml
. - Supprimez les éléments
ImageButton
associés aux ID@id/exo_prev
et@id/exo_next
.
Pour utiliser votre mise en page personnalisée, vous devez définir l'attribut app:controller_layout_id
de l'élément PlayerView
dans le fichier activity_player.xml
.
- Utilisez l'ID de mise en page de votre fichier personnalisé comme dans l'extrait de code suivant :
activity_player.xml
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:controller_layout_id="@layout/custom_player_control_view"/>
- Redémarrez l'application. La vue des commandes du lecteur ne contient plus les boutons Précédent et Suivant.
Capture d'écran : vue personnalisée des commandes du lecteur, sans les boutons Précédent et Suivant
Vous pouvez appliquer les modifications que vous voulez dans le fichier de mise en page. Par défaut, les couleurs du thème Android sont sélectionnées. Vous pouvez les ignorer et choisir des couleurs adaptées au design de votre application.
- Ajoutez un attribut
android:tint
à chaque élémentImageButton
:
custom_player_control_view.xml
<ImageButton android:id="@id/exo_rew"
android:tint="#FF00A6FF"
style="@style/ExoMediaButton.Rewind"/>
- Définissez la même couleur (
#FF00A6FF
) pour tous les attributsandroid:textColor
que vous trouvez dans votre fichier personnalisé.
custom_player_control_view.xml
<TextView android:id="@id/exo_position"
[...]
android:textColor="#FF00A6FF"/>
<TextView android:id="@id/exo_duration"
[...]
android:textColor="#FF00A6FF"/>
- Exécutez l'application. Vous avez à présent de beaux éléments d'UI colorés !
Capture d'écran : boutons et affichage de texte colorés
Ignorer le style par défaut
Vous venez de créer un fichier de mise en page personnalisé et de le référencer à l'aide de controller_layout_id
dans activity_player.xml
.
Une autre approche consiste à ignorer le fichier de mise en page par défaut qu'utilise PlayerControlView
. Le code source de PlayerControlView
nous indique qu'il se sert de R.layout.exo_player_control_view
pour la mise en page. Si vous créez votre propre fichier de mise en page avec le même nom, PlayerControlView
utilise votre fichier à la place.
- Supprimez l'attribut
controller_layout_id
que vous venez d'ajouter. - Supprimez le fichier
custom_player_control_view.xml
.
Dans activity_player.xml
, PlayerView
devrait désormais se présenter comme ceci :
activity_player.xml
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- Créez un fichier nommé
exo_player_control_view.xml
dans le dossierres/layout
de votre module de bibliothèqueplayer-lib
. - Insérez le code suivant dans
exo_player_control_view.xml
pour ajouter un bouton de lecture, un bouton de pause et un élémentImageView
avec un logo :
exo_player**_control_view.xml**
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layoutDirection="ltr"
android:background="#CC000000"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="4dp"
android:orientation="horizontal">
<ImageButton android:id="@id/exo_play"
style="@style/ExoMediaButton.Play"/>
<ImageButton android:id="@id/exo_pause"
style="@style/ExoMediaButton.Pause"/>
</LinearLayout>
<ImageView
android:contentDescription="@string/logo"
android:src="@drawable/google_logo"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Voilà comment vous pouvez ajouter vos propres éléments et les mélanger aux éléments de commande standards. ExoPlayerView
utilise à présent vos commandes personnalisées, et toute la logique pour masquer et afficher les commandes lorsque l'utilisateur interagit avec celles-ci est préservée.
8. Félicitations
Félicitations ! Vous avez appris beaucoup de choses concernant l'intégration d'ExoPlayer dans votre application.
En savoir plus
Pour en savoir plus sur ExoPlayer, consultez le guide du développeur et le code source, et abonnez-vous au blog ExoPlayer.