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 pause.
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 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 de démarrages plus rapides et de moins de mise en mémoire tampon crée une interaction plus fluide et 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 partie 1, nous aborderons les bases : comprendre les différentes stratégies de préchargement disponibles dans Media3, activer PreloadConfiguration et configurer DefaultPreloadManager pour 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 balaye l'écran pour passer à la vidéo suivante, les premiers segments de la vidéo sont déjà téléchargés et disponibles pour une lecture immédiate.
Imaginez un restaurant. Dans une cuisine très occupée, on n'attend pas qu'une commande arrive pour commencer à émincer les oignons. 🧅 Ils préparent leur travail à 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. Ensuite, il précharge automatiquement les éléments suivants de la séquence selon la configuration. 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 en cours de chargement pour la lecture en cours, ce qui l'empêche de concurrencer 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 l'option activée, vous pouvez la désactiver à 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 classant par ordre de priorité en fonction de leur proximité avec la position actuelle de l'utilisateur. Il offre un contrôle précis sur les éléments à précharger, ce qui convient 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 compilateur 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. 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 conserver les éléments précédents et suivants préchargés, tandis que PreloadConfiguration ne s'intéresse qu'aux éléments suivants.
Gérer les éléments de préchargement
Maintenant que vous avez créé votre gestionnaire, vous pouvez commencer à lui dire sur quoi travailler. Lorsque l'utilisateur fait défiler un flux, vous identifiez les vidéos à venir et les ajoutez au gestionnaire. L'interaction avec PreloadManager est une conversation axé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 informer le gestionnaire des mé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 compte administrateur 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 qu'un élément a été ajouté ou supprimé, ou que l'utilisateur a choisi de lire 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. Au lieu de cela, vous demandez à PreloadManager celui qu'il a déjà préparé. Vous pouvez récupérer MediaSource à partir du gestionnaire de préchargement à l'aide de MediaItem.
Si l'élément récupéré à partir de PreloadManager est nul, cela signifie que mediaItem n'est pas encore préchargé ni ajouté à PreloadManager. Vous pouvez donc choisir de définir mediaItem directement.
// 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 du 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 temps de 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 du 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'endroit 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 pour la démo. (Bonus : il affiche également la latence de démarrage de chaque vidéo.)
Et maintenant ?
C'est tout pour la partie 1 ! Vous disposez maintenant 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 approfondirons le 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 contenu multimédia 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 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 -
Actualités sur les produitsEn tant que développeurs Android, vous avez le choix entre de nombreux agents, LLM, outils et interfaces de ligne de commande (CLI) pour le développement d'applications. Notre objectif est de vous aider à créer des applications Android de haute qualité, quelle que soit la méthode que vous choisissez.
Simona Milanovic • Temps de lecture : 4 min
Recevez chaque semaine les dernières informations sur le développement Android dans votre boîte de réception.