Améliorer la lecture de contenus multimédias : présentation du préchargement avec Media3 – Partie 1
Temps de lecture : 8 min
Dans les applications multimédias d'aujourd'hui, il est essentiel d'offrir une expérience de lecture fluide et ininterrompue pour satisfaire les utilisateurs. Les utilisateurs s'attendent à ce que leurs vidéos démarrent instantanément et soient lues de manière fluide, sans interruption.
Le principal défi est la latence. En général, un lecteur vidéo ne commence à fonctionner (connexion, téléchargement, analyse, mise en mémoire tampon) qu'après que l'utilisateur a choisi un élément à lire. Cette approche réactive est trop lente pour le contexte actuel des vidéos courtes. La solution consiste à être proactif. Nous devons anticiper ce que l'utilisateur va regarder ensuite et préparer le contenu à l'avance. C'est l'essence même du préchargement.
Voici les principaux avantages du préchargement :
- 🚀 Démarrage plus rapide de la lecture : les vidéos sont déjà prêtes à être lues, ce qui permet des transitions plus rapides entre les éléments et un démarrage plus immédiat.
- 📉 Moins de mise en mémoire tampon : en chargeant les données de manière proactive, la lecture est beaucoup moins susceptible de s'interrompre, par exemple en raison de problèmes de réseau.
- ✨ Expérience utilisateur plus fluide : la combinaison d'un démarrage plus rapide et d'une mise en mémoire tampon réduite crée une interaction plus fluide et plus transparente pour les utilisateurs.
Dans cette série en trois parties, nous allons présenter et examiner en détail les puissants utilitaires de Media3 pour le (pré)chargement des composants.
- Dans la première partie, nous aborderons les principes de base : comprendre les différentes stratégies de préchargement disponibles dans Media3, activer PreloadConfiguration et configurer DefaultPreloadManager, et permettre à votre application de précharger des éléments. À la fin de ce blog, vous devriez être en mesure de précharger et de lire des éléments multimédias avec le classement et la durée que vous avez configurés.
- Dans la deuxième partie, nous aborderons des sujets plus avancés concernant DefaultPreloadManager : l'utilisation d'écouteurs pour l'analyse, l'exploration des bonnes pratiques prêtes pour la production, comme le modèle de fenêtre glissante, et les composants partagés personnalisés de DefaultPreloadManager et ExoPlayer.
- Dans la troisième partie, nous nous intéresserons de plus près à la mise en cache du disque avec DefaultPreloadManager.
Le préchargement à la rescousse ! 🦸♀️
L'idée de base du préchargement est simple : charger le contenu multimédia avant d'en avoir besoin. Lorsque l'utilisateur balaie l'écran pour passer à la vidéo suivante, les premiers segments de la vidéo sont déjà téléchargés et disponibles, prêts à être lus immédiatement.
Imaginez un restaurant. Dans une cuisine très occupée, on n'attend pas qu'une commande arrive pour commencer à émincer les oignons. 🧅 Ils font leur travail de préparation à l'avance. Le préchargement est le travail de préparation de votre lecteur vidéo.
Lorsqu'il est activé, le préchargement peut aider à réduire la latence de jointure lorsqu'un utilisateur passe à l'élément suivant avant que la mémoire tampon de lecture n'atteigne l'élément suivant. La première période de la fenêtre suivante est préparée, et les échantillons vidéo, audio et texte sont mis en mémoire tampon. La période préchargée est ensuite mise en file d'attente dans le lecteur, avec des échantillons mis en mémoire tampon immédiatement disponibles et prêts à être transmis au codec pour le rendu.
Media3 propose deux API principales pour le préchargement, chacune adaptée à des cas d'utilisation différents. La première étape consiste à choisir la bonne API.
1. Précharger des éléments de playlist avec PreloadConfiguration
Il s'agit de l'approche simple, utile pour les contenus multimédias linéaires et séquentiels tels que les playlists où l'ordre de lecture est prévisible (comme une série d'épisodes). Vous fournissez au lecteur la liste complète des éléments multimédias à l'aide des API de playlist d'ExoPlayer et définissez la PreloadConfiguration pour le lecteur. Il précharge ensuite automatiquement les éléments suivants de la séquence, comme configuré. Cette API tente d'optimiser la latence de jointure lorsqu'un utilisateur passe à l'élément suivant avant que la mémoire tampon de lecture ne chevauche déjà l'élément suivant.
Le préchargement ne démarre que lorsqu'aucun contenu multimédia n'est chargé pour la lecture en cours, ce qui l'empêche d'entrer en concurrence avec la lecture principale pour la bande passante.
Si vous ne savez toujours pas si vous avez besoin du préchargement, cette API est une excellente option pour l'essayer sans trop d'efforts.
player.preloadConfiguration =
PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)Avec la PreloadConfiguration ci-dessus, le lecteur tente de précharger cinq secondes de contenu multimédia pour l'élément suivant de la playlist.
Une fois activé, le préchargement de playlist peut être désactivé à l'aide de PreloadConfiguration.DEFAULT :
player.preloadConfiguration = PreloadConfiguration.DEFAULT
2. Précharger des listes dynamiques avec PreloadManager
Pour les UI dynamiques telles que les flux verticaux ou les carrousels, où l'élément "suivant" est déterminé par l'interaction de l'utilisateur, l'API PreloadManager est appropriée. Il s'agit d'un nouveau composant autonome et puissant de la bibliothèque Media3 ExoPlayer, spécialement conçu pour le préchargement proactif. Il gère une collection de MediaSources potentielles, en les priorisant en fonction de leur proximité avec la position actuelle de l'utilisateur, et offre un contrôle précis sur ce qu'il faut précharger, ce qui est adapté aux scénarios complexes tels que les flux dynamiques de vidéos courtes.
Configurer votre PreloadManager
DefaultPreloadManager est l'implémentation canonique de PreloadManager.
Le compilateur de DefaultPreloadManager peut compiler à la fois DefaultPreloadManager et toutes les instances ExoPlayer qui liront son contenu préchargé. Pour créer un DefaultPreloadManager, vous devez transmettre un TargetPreloadStatusControl, que le gestionnaire de préchargement peut interroger pour déterminer la quantité de données à charger pour un élément. Nous allons expliquer et définir un exemple de TargetPreloadStatusControl dans la section ci-dessous.
val preloadManagerBuilder = DefaultPreloadManager.Builder(context, targetPreloadStatusControl) val preloadManager = val preloadManagerBuilder.build() // Build ExoPlayer with DefaultPreloadManager.Builder val player = preloadManagerBuilder.buildExoPlayer()
Il est nécessaire d'utiliser le même générateur pour ExoPlayer et DefaultPreloadManager, ce qui garantit que les composants sous-jacents sont correctement partagés.
Et voilà ! Vous disposez maintenant d'un gestionnaire prêt à recevoir des instructions.
Configurer la durée et le classement avec TargetPreloadStatusControl
Que faire si vous souhaitez précharger, par exemple, 10 secondes de vidéo ? Vous pouvez indiquer la position de vos éléments multimédias dans le carrousel, et DefaultPreloadManager priorise le chargement des éléments en fonction de leur proximité avec l'élément que l'utilisateur est en train de lire.
Si vous souhaitez contrôler la durée de l'élément à précharger, vous pouvez l'indiquer avec le DefaultPreloadManager.PreloadStatus que vous renvoyez.
Par exemple,
- L'élément A est prioritaire. Chargez cinq secondes de vidéo.
- L'élément B est de priorité moyenne, mais lorsque vous l'atteignez, chargez trois secondes de vidéo.
- L'élément "C" est moins prioritaire, ne chargez que les pistes.
- L'élément D est encore moins prioritaire. Il suffit de le préparer.
- Tous les autres éléments sont éloignés. Ne préchargez rien.
Ce contrôle précis peut vous aider à optimiser l'utilisation de vos ressources, ce qui est recommandé pour une lecture fluide.
import androidx.media3.exoplayer.DefaultPreloadManager.PreloadStatus
class MyTargetPreloadStatusControl(
currentPlayingIndex: Int = C.INDEX_UNSET
) : TargetPreloadStatusControl<Int,PreloadStatus> {
// The app is responsible for updating this based on UI state
override fun getTargetPreloadStatus(index: Int): PreloadStatus? {
val distance = index - currentPlayingIndex
// Adjacent items (Next): preload 5 seconds
if (distance == 1) {
// Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED and suggest loading // 5000ms from the default start position
return PreloadStatus.specifiedRangeLoaded(5000L)
}
// Adjacent items (Previous): preload 3 seconds
else if (distance == -1) {
// Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED //and suggest loading 3000ms from the default start position
return PreloadStatus.specifiedRangeLoaded(3000L)
}
// Items two positions away: just select tracks
else if (distance) == 2) {
// Return a PreloadStatus that is labelled by STAGE_TRACKS_SELECTED
return PreloadStatus.TRACKS_SELECTED
}
// Items four positions away: just select prepare
else if (abs(distance) <= 4) {
// Return a PreloadStatus that is labelled by STAGE_SOURCE_PREPARED
return PreloadStatus.SOURCE_PREPARED
}
// All other items are too far away
return null
}
}Remarque : PreloadManager peut précharger à la fois l'élément précédent et l'élément suivant, tandis que PreloadConfiguration ne précharge que les éléments suivants.
Gérer les éléments de préchargement
Une fois votre gestionnaire créé, vous pouvez commencer à lui indiquer sur quoi travailler. Lorsqu'un utilisateur fait défiler un flux, vous identifiez les vidéos à venir et les ajoutez au gestionnaire. L'interaction avec PreloadManager est une conversation basée sur l'état entre votre UI et le moteur de préchargement.
1. Ajouter des éléments multimédias
Lorsque vous remplissez votre flux, vous devez indiquer au gestionnaire les contenus multimédias qu'il doit suivre. Si vous débutez, vous pouvez ajouter la liste complète des éléments que vous souhaitez précharger. Vous pouvez ensuite continuer à ajouter un seul élément à la liste, selon vos besoins. Vous contrôlez entièrement les éléments de la liste de préchargement, ce qui signifie que vous devez également gérer ce qui est ajouté et supprimé du gestionnaire.
val initialMediaItems = pullMediaItemsFromService(/* count= */ 20)
for (index in 0 until initialMediaItems.size) {
preloadManager.add(
initialMediaItems.get(index),index)
)
}Le gestionnaire va maintenant commencer à récupérer les données de ce MediaItem en arrière-plan.
Après l'ajout, demandez au gestionnaire de réévaluer sa nouvelle liste (en indiquant que quelque chose a changé, comme l'ajout ou la suppression d'un élément, ou que l'utilisateur passe à la lecture d'un nouvel élément).
preloadManager.invalidate()
2. Récupérer et lire un élément
Voici la logique de lecture principale. Lorsque l'utilisateur décide de lire cette vidéo, vous n'avez pas besoin de créer un autre MediaSource. À la place, vous demandez à PreloadManager celui qu'il a déjà préparé. Vous pouvez récupérer MediaSource à partir de Preload Manager à l'aide de MediaItem.
Si l'élément récupéré à partir de PreloadManager est nul, cela signifie que l'élément mediaItem n'est pas encore préchargé ou ajouté à PreloadManager. Vous choisissez donc de définir directement l'élément mediaItem.
// When a media item is about to display on the screen
val mediaSource = preloadManager.getMediaSource(mediaItem)
if (mediaSource!= null) {
player.setMediaSource(mediaSource)
} else {
// If mediaSource is null, that mediaItem hasn't been added yet.
// So, send it directly to the player.
player.setMediaItem(mediaItem)
}
player.prepare()
// When the media item is displaying at the center of the screen
player.play()En préparant la MediaSource récupérée à partir de PreloadManager, vous passez facilement du préchargement à la lecture, en utilisant les données déjà en mémoire. C'est ce qui permet d'accélérer le démarrage.
3. Synchroniser l'index actuel avec l'UI
Étant donné que notre flux / liste peut être dynamique, il est important d'indiquer l'index de lecture actuel à PreloadManager afin qu'il puisse toujours prioriser les éléments les plus proches de l'index actuel pour le préchargement.
preloadManager.setCurrentPlayingIndex(currentIndex) // Need to call invalidate() to update the priorities preloadManager.invalidate()
4. Supprimer un élément
Pour que le gestionnaire reste efficace, vous devez supprimer les éléments qu'il n'a plus besoin de suivre, par exemple ceux qui sont éloignés de la position actuelle de l'utilisateur.
// When an item is too far from the current playing index preloadManager.remove(mediaItem)
Si vous devez effacer tous les éléments en même temps, vous pouvez appeler preloadManager.reset().
5. Libérer le responsable
Lorsque vous n'avez plus besoin de PreloadManager (par exemple, lorsque votre UI est détruite), vous devez le libérer pour libérer ses ressources. Il est recommandé d'effectuer cette opération là où vous libérez déjà les ressources de votre lecteur. Il est recommandé de libérer le gestionnaire avant le lecteur, car celui-ci peut continuer à lire si vous n'avez plus besoin de précharger.
// In your Activity's onDestroy() or Composable's onDispose preloadManager.release()
Démonstration
Découvrez-le en action 👍
Dans la démo ci-dessous, nous voyons l'impact de PreloadManager sur le côté droit, qui présente des temps de chargement plus rapides, tandis que le côté gauche montre l'expérience existante. Vous pouvez également consulter l'exemple de code de la démonstration. (Bonus : il affiche également la latence de démarrage de chaque vidéo.)
Et maintenant ?
C'est tout pour la partie 1 ! Vous disposez désormais des outils nécessaires pour créer un système de préchargement dynamique. Vous pouvez utiliser PreloadConfiguration pour précharger l'élément suivant d'une playlist dans ExoPlayer ou configurer un DefaultPreloadManager, ajouter et supprimer des éléments à la volée, configurer l'état de préchargement cible et récupérer correctement le contenu préchargé pour la lecture.
Dans la partie 2, nous nous intéresserons plus en détail à DefaultPreloadManager. Nous verrons comment écouter les événements de préchargement, nous aborderons les bonnes pratiques telles que l'utilisation d'une fenêtre glissante pour éviter les problèmes de mémoire, et nous examinerons les composants partagés personnalisés d'ExoPlayer et de DefaultPreloadManager.
Avez-vous des commentaires à partager ? Nous avons hâte de vous lire.
Restez à l'écoute et accélérez votre application ! 🚀
-
Actualités sur les produitsBienvenue dans le deuxième volet de notre série en trois parties sur le préchargement de contenus multimédias avec Media3. Cette série est conçue pour vous guider dans la création d'expériences multimédias très réactives et à faible latence dans vos applications Android.
Mayuri Khinvasara Khabya • Temps de lecture : 9 min -
Actualités sur les produitsÀ mesure que le développement agentique s'accélère, l'ingénierie Android atteint une nouvelle frontière passionnante : passer de la complétion de code assistée à des agents qui s'exécutent de bout en bout sous votre direction. Vous disposez d'un choix et d'une flexibilité totaux concernant les modèles et les agents que vous utilisez.
Jason Buchel • Temps de lecture : 3 min -
Actualités sur les produitsAujourd'hui, nous avons expliqué comment nous faisions évoluer notre vision "plus qu'un magasin". De l'onglet "Vous", qui sert désormais d'espace personnel à plus de 250 millions d'utilisateurs actifs par mois, à notre plate-forme de jeux unifiée qui connecte plus de 700 millions de profils de joueurs, Google Play répond aux besoins des utilisateurs où qu'ils se trouvent.
Paul Feng • Temps de lecture : 3 min
Recevez chaque semaine les dernières informations sur le développement Android dans votre boîte de réception.