Novedades sobre productos
Mejora la reproducción de contenido multimedia: Presentamos la carga previa con Media3 (Parte 1)
Lectura de 8 min
En las apps centradas en contenido multimedia de hoy en día, brindar una experiencia de reproducción fluida y sin interrupciones es clave para una experiencia del usuario agradable. Los usuarios esperan que sus videos comiencen de inmediato y se reproduzcan sin problemas ni pausas.
El desafío principal es la latencia. Tradicionalmente, un reproductor de video solo comienza a funcionar (conectarse, descargar, analizar y almacenar en búfer) después de que el usuario elige un elemento para reproducir. Este enfoque reactivo es lento para el contexto actual de videos de formato corto. La solución es ser proactivo. Debemos anticipar lo que el usuario mirará a continuación y preparar el contenido con anticipación. Esta es la esencia de la carga previa.
Entre los beneficios clave de la carga previa, se incluyen los siguientes:
- 🚀 Inicio de reproducción más rápido: Los videos ya están listos, lo que permite transiciones más rápidas entre los elementos y un inicio más inmediato.
- 📉 Almacenamiento en búfer reducido: Al cargar datos de forma proactiva, es mucho menos probable que la reproducción se detenga, por ejemplo, debido a problemas de red.
- ✨ Experiencia del usuario más fluida: La combinación de inicios más rápidos y menos almacenamiento en búfer crea una interacción más fluida y sin interrupciones para que los usuarios disfruten.
En esta serie de tres partes, presentaremos y analizaremos en detalle las potentes utilidades de Media3 para cargar (previamente) componentes.
- En la Parte 1, abordaremos los conceptos básicos: comprender las diferentes estrategias de carga previa disponibles en Media3, habilitar PreloadConfiguration y configurar DefaultPreloadManager, lo que permite que tu app cargue previamente elementos. Al final de esta entrada de blog, deberías poder cargar previamente y reproducir elementos multimedia con la clasificación y la duración configuradas.
- En la Parte 2, abordaremos temas más avanzados de DefaultPreloadManager: usar objetos de escucha para estadísticas, explorar prácticas recomendadas listas para producción, como el patrón de ventana deslizante y los componentes compartidos personalizados de DefaultPreloadManager y ExoPlayer.
- En la Parte 3, analizaremos en detalle el almacenamiento en caché en disco con DefaultPreloadManager.
¡La carga previa al rescate! 🦸♀️
La idea principal detrás de la carga previa es simple: cargar contenido multimedia antes de que lo necesites. Cuando un usuario desliza el dedo hacia el siguiente video, los primeros segmentos del video ya se descargaron y están disponibles, listos para la reproducción inmediata.
Piensa en un restaurante. Una cocina ocupada no espera un pedido para comenzar a cortar cebollas. 🧅 Hacen su trabajo de preparación con anticipación. La carga previa es el trabajo de preparación para tu reproductor de video.
Cuando está habilitada, la carga previa puede ayudar a minimizar la latencia de unión cuando un usuario omite el siguiente elemento antes de que el búfer de reproducción llegue a él. Se prepara el primer período de la siguiente ventana y se almacenan en búfer muestras de video, audio y texto. El período precargado se pone en cola más tarde en el reproductor con muestras almacenadas en búfer disponibles de inmediato y listas para enviarse al códec para la renderización.
En Media3, hay dos APIs principales para la carga previa, cada una adecuada para diferentes casos de uso. Elegir la API correcta es el primer paso.
1. Carga previa de elementos de la playlist con PreloadConfiguration
Este es el enfoque simple, útil para contenido multimedia lineal y secuencial, como playlists en las que el orden de reproducción es predecible (como una serie de episodios). Le proporcionas al reproductor la lista completa de elementos multimedia con las APIs de playlist de ExoPlayer y configuras PreloadConfiguration para el reproductor. Luego, se precargan automáticamente los siguientes elementos de la secuencia según lo configurado. Esta API intenta optimizar la latencia de unión cuando un usuario omite el siguiente elemento antes de que el búfer de reproducción ya se superponga en el siguiente elemento.
La carga previa solo se inicia cuando no se carga contenido multimedia para la reproducción en curso, lo que evita que compita por el ancho de banda con la reproducción principal.
Si aún no estás seguro de si necesitas la carga previa, esta API es una excelente opción de bajo esfuerzo para probarla.
player.preloadConfiguration =
PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)Con la PreloadConfiguration anterior, el reproductor intenta precargar cinco segundos de contenido multimedia para el siguiente elemento de la playlist.
Una vez que se habilita, la carga previa de la playlist se puede volver a desactivar con PreloadConfiguration.DEFAULT para inhabilitar la carga previa de la playlist:
player.preloadConfiguration = PreloadConfiguration.DEFAULT
2. Carga previa de listas dinámicas con PreloadManager
Para las IUs dinámicas, como feeds verticales o carruseles, en las que el elemento "siguiente" se determina por la interacción del usuario, es adecuada la API de PreloadManager. Este es un componente nuevo, potente e independiente dentro de la biblioteca Media3 ExoPlayer, diseñado específicamente para precargar de forma proactiva. Administra una colección de MediaSources potenciales, priorizándolas según la proximidad a la posición actual del usuario y ofrece un control detallado sobre qué precargar, adecuado para situaciones complejas como feeds dinámicos de videos de formato corto.
Cómo configurar PreloadManager
El DefaultPreloadManager es la implementación canónica de PreloadManager.
El compilador de DefaultPreloadManager puede compilar DefaultPreloadManager y cualquier instancia de ExoPlayer que reproduzca su contenido precargado. Para crear un DefaultPreloadManager, deberás pasar un TargetPreloadStatusControl, que el administrador de precarga puede consultar para averiguar cuánto cargar para un elemento. Explicaremos y definiremos un ejemplo de TargetPreloadStatusControl en la siguiente sección.
val preloadManagerBuilder = DefaultPreloadManager.Builder(context, targetPreloadStatusControl) val preloadManager = val preloadManagerBuilder.build() // Build ExoPlayer with DefaultPreloadManager.Builder val player = preloadManagerBuilder.buildExoPlayer()
Es necesario usar el mismo compilador para ExoPlayer y DefaultPreloadManager, lo que garantiza que los componentes internos de ellos se compartan correctamente.
Eso es todo. Ahora tienes un administrador listo para recibir instrucciones.
Cómo configurar la duración y la clasificación con TargetPreloadStatusControl
¿Qué sucede si quieres precargar, por ejemplo, 10 segundos de video? Puedes proporcionar la posición de tus elementos multimedia en el carrusel, y DefaultPreloadManager prioriza la carga de los elementos según la proximidad al elemento que el usuario está reproduciendo actualmente.
Si quieres controlar la duración del elemento que se precargará, puedes indicarlo con DefaultPreloadManager.PreloadStatus que devuelves.
Por ejemplo:
- El elemento "A" es la prioridad más alta, carga 5 segundos de video.
- El elemento "B" tiene prioridad media, pero cuando llegues a él, carga 3 segundos de video.
- El elemento "C" tiene menos prioridad, carga solo pistas.
- El elemento "D" tiene aún menos prioridad, solo prepáralo.
- Cualquier otro elemento está lejos, no precargues nada.
Este control detallado puede ayudarte a optimizar el uso de recursos, lo que se recomienda para una reproducción sin interrupciones.
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
}
}Nota: PreloadManager puede mantener precargados los elementos anteriores y siguientes, mientras que PreloadConfiguration solo buscará los siguientes elementos.
Cómo administrar elementos de carga previa
Una vez que se crea el administrador, puedes comenzar a indicarle en qué trabajar. A medida que el usuario se desplaza por un feed, identificarás los próximos videos y los agregarás al administrador. La interacción con PreloadManager es una conversación basada en el estado entre tu IU y el motor de carga previa.
1. Agrega elementos multimedia
A medida que propagas tu feed, debes informar al administrador del contenido multimedia que debe hacer un seguimiento. Si estás comenzando, puedes agregar toda la lista que deseas precargar. Posteriormente, puedes seguir agregando un solo elemento a la lista según sea necesario. Tienes control total sobre los elementos que se encuentran en la lista de carga previa, lo que significa que también debes administrar lo que se agrega y se quita del administrador.
val initialMediaItems = pullMediaItemsFromService(/* count= */ 20)
for (index in 0 until initialMediaItems.size) {
preloadManager.add(
initialMediaItems.get(index),index)
)
}El administrador ahora comenzará a recuperar datos para este MediaItem en segundo plano.
Después de agregar, dile al administrador que vuelva a evaluar su nueva lista (lo que indica que algo cambió, como agregar o quitar un elemento, o que el usuario cambia para reproducir un elemento nuevo).
preloadManager.invalidate()
2. Recupera y reproduce un elemento
Aquí viene la lógica de reproducción principal. Cuando el usuario decide reproducir ese video, no es necesario que crees un MediaSource nuevo. En su lugar, le pides al PreloadManager el que ya preparó. Puedes recuperar MediaSource del administrador de precarga con MediaItem.
Si el elemento recuperado de PreloadManager es nulo, significa que el elemento multimedia aún no se precargó ni se agregó a PreloadMamager, por lo que eliges configurar el elemento multimedia directamente.
// 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()Cuando preparas el MediaSource recuperado de PreloadManager, realizas una transición sin problemas de la carga previa a la reproducción, con los datos que ya están en la memoria. Esto es lo que hace que el tiempo de inicio sea más rápido.
3. Mantén el índice actual sincronizado con la IU
Dado que nuestro feed o lista puede ser dinámico, es importante notificarle a PreloadManager tu índice de reproducción actual para que siempre pueda priorizar los elementos más cercanos a tu índice actual para la precarga.
preloadManager.setCurrentPlayingIndex(currentIndex) // Need to call invalidate() to update the priorities preloadManager.invalidate()
4. Quita un elemento
Para que el administrador sea eficiente, debes quitar los elementos que ya no necesite hacer un seguimiento, como los elementos que están lejos de la posición actual del usuario.
// When an item is too far from the current playing index preloadManager.remove(mediaItem)
Si necesitas borrar todos los elementos a la vez, puedes llamar a preloadManager.reset().
5. Libera el administrador
Cuando ya no necesites PreloadManager (p.ej., cuando se destruye tu IU), debes liberarlo para liberar sus recursos. Un buen lugar para hacerlo es donde ya estás liberando los recursos de tu reproductor. Se recomienda liberar el administrador antes que el reproductor, ya que el reproductor puede seguir reproduciendo si no necesitas más carga previa.
// In your Activity's onDestroy() or Composable's onDispose preloadManager.release()
Tiempo de demostración
Mira cómo funciona en vivo 👍
En la siguiente demostración, vemos el impacto de PreloadManager en el lado derecho, que tiene tiempos de carga más rápidos, mientras que el lado izquierdo muestra la experiencia existente. También puedes ver la muestra de código sample de la demostración. (Además, también muestra la latencia de inicio de cada video).
¿Qué sigue?
Y con esto terminamos la Parte 1. Ahora tienes las herramientas para compilar un sistema de carga previa dinámica. Puedes usar PreloadConfiguration para precargar el siguiente elemento de una playlist en ExoPlayer o configurar un DefaultPreloadManager, agregar y quitar elementos sobre la marcha, configurar el estado de carga previa de destino y recuperar correctamente el contenido precargado para la reproducción.
En Parte 2, profundizaremos en DefaultPreloadManager. Exploraremos cómo escuchar eventos de carga previa, analizaremos las prácticas recomendadas, como usar una ventana deslizante para evitar problemas de memoria, y analizaremos los componentes compartidos personalizados de ExoPlayer y DefaultPreloadManager.
¿Tienes algún comentario para compartir? Nos encantaría conocer tu opinión.
Mantente al tanto y haz que tu app sea más rápida. 🚀
Seguir leyendo
-
Novedades sobre productos
Te damos la bienvenida a la segunda entrega de nuestra serie de tres partes sobre la carga previa de contenido multimedia con Media3. Esta serie está diseñada para guiarte en el proceso de compilación de experiencias de contenido multimedia de baja latencia y alta capacidad de respuesta en tus apps para Android.
Mayuri Khinvasara Khabya • Lectura de 9 min
-
Novedades sobre productos
En Google I/O 2026, presentamos el cambio de Android de un sistema operativo a un sistema de inteligencia. También mostramos cómo puedes compilar experiencias inteligentes de forma nativa con el sistema y llevar la potencia de la IA de Google a tus apps.
Jingyu Shi • Lectura de 2 min
-
Novedades sobre productos
Nos complace anunciar que llegó la compatibilidad oficial con Unreal Engine y Godot para Android XR. También lanzamos nuevas herramientas diseñadas para aumentar tu productividad y habilitar nuevas capacidades de XR: Android XR Engine Hub y Android XR Interaction Framework.
Luke Hopkins, Ryan Bartley • Lectura de 4 min
Mantente al día
Recibe la información más reciente sobre el desarrollo de Android en tu bandeja de entrada todas las semanas.