Novedades de productos
Mejorar la reproducción de contenido multimedia: presentamos la precarga con Media3 (parte 1)
Lectura de 8 minutos
En las aplicaciones centradas en contenido multimedia actuales, ofrecer una experiencia de reproducción fluida e ininterrumpida es fundamental para que los usuarios disfruten de la aplicación. Los usuarios esperan que sus vídeos empiecen a reproducirse al instante y que lo hagan de forma fluida, sin pausas.
El principal problema es la latencia. Tradicionalmente, un reproductor de vídeo solo empieza a funcionar (conectarse, descargar, analizar y almacenar en búfer) después de que el usuario haya elegido un elemento para reproducir. Este enfoque reactivo es lento para el contexto actual de los vídeos cortos. La solución es ser proactivo. Tenemos que anticipar lo que va a ver el usuario a continuación y preparar el contenido con antelación. Esta es la esencia de la precarga.
Estas son las principales ventajas de la precarga:
- 🚀 Inicio de reproducción más rápido: los vídeos ya están listos, lo que permite que las transiciones entre elementos sean más rápidas y que el inicio sea más inmediato.
- 📉 Reducción del almacenamiento en búfer: al cargar los datos de forma proactiva, es mucho menos probable que la reproducción se detenga, por ejemplo, debido a problemas de red.
- ✨ Experiencia de 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 perfecta para los usuarios.
En esta serie de tres partes, presentaremos y analizaremos en profundidad las potentes utilidades de Media3 para cargar componentes (previamente).
- En la parte 1, hablaremos de los aspectos básicos: las diferentes estrategias de precarga disponibles en Media3, cómo habilitar PreloadConfiguration y configurar DefaultPreloadManager, y cómo habilitar tu aplicación para que precargue elementos. Al final de esta entrada del blog, deberías poder precargar y reproducir elementos multimedia con la clasificación y la duración que hayas configurado.
- En la parte 2, profundizaremos en temas más avanzados de DefaultPreloadManager: cómo usar los listeners para las analíticas y cómo explorar las 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, profundizaremos en el almacenamiento en caché en disco con DefaultPreloadManager.
¡La precarga al rescate! 🦸♀️
La idea principal de la precarga es sencilla: cargar contenido multimedia antes de que lo necesites. Cuando un usuario desliza para ver el siguiente vídeo, los primeros segmentos ya se han descargado y están disponibles para reproducirse de inmediato.
Es como si fuera un restaurante. En una cocina con mucho trabajo, no se espera a que llegue un pedido para empezar a cortar cebollas. 🧅 Hacen el trabajo de preparación con antelación. La precarga es el trabajo de preparación del reproductor de vídeo.
Si se habilita, la precarga puede ayudar a minimizar la latencia de unión cuando un usuario salta al siguiente elemento antes de que el búfer de reproducción llegue a él. Se prepara el primer periodo de la siguiente ventana y se almacenan en el búfer muestras de vídeo, audio y texto. El periodo precargado se pone en cola en el reproductor con muestras almacenadas en búfer que están disponibles inmediatamente y listas para enviarse al códec para renderizarse.
En Media3, hay dos APIs principales para la precarga, cada una de ellas adecuada para diferentes casos prácticos. Elegir la API adecuada es el primer paso.
1. Precargar elementos de listas de reproducción con PreloadConfiguration
Este es el enfoque sencillo, útil para contenido multimedia lineal y secuencial, como listas de reproducción en las que el orden de reproducción es predecible (como una serie de episodios). Proporcionas al reproductor la lista completa de elementos multimedia mediante las APIs de lista de reproducción de ExoPlayer y defines la PreloadConfiguration del reproductor. A continuación, se precargan automáticamente los siguientes elementos de la secuencia según se haya configurado. Esta API intenta optimizar la latencia de unión cuando un usuario salta al siguiente elemento antes de que el búfer de reproducción ya se solape con el siguiente elemento.
La precarga solo se inicia cuando no se está cargando 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 sabes si necesitas precarga, esta API es una opción sencilla para probarla.
player.preloadConfiguration = PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)
Con la PreloadConfiguration anterior, el reproductor intenta precargar cinco segundos de contenido multimedia del siguiente elemento de la lista de reproducción.
Una vez que hayas habilitado esta opción, puedes volver a desactivarla con PreloadConfiguration.DEFAULT:
player.preloadConfiguration = PreloadConfiguration.DEFAULT
2. Precargar listas dinámicas con PreloadManager
La API PreloadManager es adecuada para interfaces de usuario dinámicas, como feeds verticales o carruseles, en las que el elemento "siguiente" se determina en función de la interacción del usuario. Se trata de un nuevo componente independiente y potente de la biblioteca Media3 ExoPlayer diseñado específicamente para precargar contenido de forma proactiva. Gestiona una colección de posibles MediaSources, priorizándolos en función de la proximidad a la posición actual del usuario, y ofrece un control granular sobre qué precargar, lo que resulta adecuado para situaciones complejas, como feeds dinámicos de vídeos cortos.
Configurar PreloadManager
DefaultPreloadManager es la implementación canónica de PreloadManager.
El creador de DefaultPreloadManager puede crear tanto el DefaultPreloadManager como cualquier instancia de ExoPlayer que reproduzca su contenido precargado. Para crear un DefaultPreloadManager, debes pasar un TargetPreloadStatusControl, que el gestor de precarga puede consultar para saber cuánto cargar de un elemento. En la sección siguiente, explicaremos y definiremos un ejemplo de TargetPreloadStatusControl.
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 tanto para ExoPlayer como para DefaultPreloadManager, lo que asegura que los componentes internos se compartan correctamente.
Así de fácil. Ahora tienes un gestor listo para recibir instrucciones.
Configurar la duración y la clasificación con TargetPreloadStatusControl
¿Qué ocurre si quieres precargar, por ejemplo, 10 segundos de vídeo? Puedes indicar la posición de tus elementos multimedia en el carrusel. DefaultPreloadManager prioriza la carga de los elementos en función de la distancia al elemento que está reproduciendo el usuario.
Si quieres controlar la duración del elemento que se va a precargar, puedes hacerlo con DefaultPreloadManager.PreloadStatus.
Por ejemplo,
- El elemento "A" tiene la prioridad más alta, por lo que se cargan 5 segundos de vídeo.
- El elemento "B" tiene una prioridad media, pero cuando llegues a él, carga 3 segundos de vídeo.
- El elemento "C" tiene menos prioridad, solo se registran las cargas.
- El elemento "D" tiene aún menos prioridad, solo hay que prepararlo.
- Cualquier otro elemento está lejos. No precargues nada.
Este control granular puede ayudarte a optimizar el uso de los recursos, lo que se recomienda para que la reproducción sea fluida.
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 tendrá en cuenta los elementos siguientes.
Gestionar elementos precargados
Una vez que hayas creado tu gestor, puedes empezar a decirle en qué debe trabajar. A medida que el usuario se desplace por un feed, identificarás los vídeos que se van a reproducir y los añadirás al gestor. La interacción con PreloadManager es una conversación basada en estados entre tu interfaz de usuario y el motor de precarga.
1. Añadir elementos multimedia
A medida que rellene el feed, debe informar al gestor de los medios que debe monitorizar. Si estás empezando, puedes añadir toda la lista que quieras precargar. Después, puedes seguir añadiendo elementos a la lista cuando sea necesario. Tienes control total sobre los elementos de la lista de precarga, lo que significa que también debes gestionar lo que se añade y se quita del gestor.
val initialMediaItems = pullMediaItemsFromService(/* count= */ 20)
for (index in 0 until initialMediaItems.size) {
preloadManager.add(
initialMediaItems.get(index),index)
)
}El gestor empezará a obtener datos de este MediaItem en segundo plano.
Después de añadirla, dile al gestor que vuelva a evaluar su nueva lista (indicando que algo ha cambiado, como que se ha añadido o quitado un elemento, o que el usuario ha cambiado a otro elemento).
preloadManager.invalidate()
2. Recuperar y reproducir un elemento
Aquí se encuentra la lógica de reproducción principal. Cuando el usuario decide reproducir ese vídeo, no es necesario que crees un nuevo MediaSource. En su lugar, pídele al PreloadManager que te muestre la que ya ha preparado. Puedes obtener el MediaSource del Preload Manager usando el MediaItem.
Si el elemento recuperado de PreloadManager es nulo, significa que mediaItem aún no se ha precargado o añadido a PreloadManager, por lo que puedes definir mediaItem 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()
Al preparar el MediaSource obtenido de PreloadManager, puedes pasar sin problemas de la precarga a la reproducción usando 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. Mantener el índice actual sincronizado con la interfaz de usuario
Como nuestro feed o lista puede ser dinámico, es importante notificar a PreloadManager el índice de reproducción actual para que siempre pueda priorizar los elementos más cercanos al índice actual para la precarga.
preloadManager.setCurrentPlayingIndex(currentIndex) // Need to call invalidate() to update the priorities preloadManager.invalidate()
4. Quitar un elemento
Para que el gestor siga siendo eficiente, debes quitar los elementos que ya no necesite monitorizar, como los 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. Lanzar el Gestor
Cuando ya no necesites PreloadManager (por ejemplo, cuando se destruya tu interfaz de usuario), debes liberarlo para liberar sus recursos. Un buen lugar para hacerlo es donde ya estés liberando los recursos de tu reproductor. Te recomendamos que lances el gestor antes que el reproductor, ya que el reproductor puede seguir reproduciendo contenido si no necesitas precargar más.
// In your Activity's onDestroy() or Composable's onDispose preloadManager.release()
Prácticas de demostración
Puedes verlo en directo 👍
En la demostración de abajo, 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 actual. También puedes ver el código de muestra de la demostración. Además, muestra la latencia de inicio de cada vídeo.
Pasos siguientes
¡Y hasta aquí la parte 1! Ahora tienes las herramientas para crear un sistema de precarga dinámico. Puedes usar PreloadConfiguration para precargar el siguiente elemento de una lista de reproducción en ExoPlayer o configurar un DefaultPreloadManager, añadir y quitar elementos sobre la marcha, configurar el estado de precarga de destino y recuperar correctamente el contenido precargado para reproducirlo.
En la parte 2, profundizaremos en el DefaultPreloadManager. Veremos cómo recibir información sobre los eventos de precarga, hablaremos de prácticas recomendadas, como usar una ventana deslizante para evitar problemas de memoria, y echaremos un vistazo a los componentes compartidos personalizados de ExoPlayer y DefaultPreloadManager.
¿Quieres enviarnos algún comentario? Estamos deseando saber de ti.
No te pierdas las novedades y empieza a optimizar tu aplicación. 🚀
Seguir leyendo
-
Noticias sobre productos
Te damos la bienvenida a la segunda parte de nuestra serie de tres artículos sobre la precarga de contenido multimedia con Media3. Esta serie está diseñada para guiarte en el proceso de creación de experiencias multimedia con alta capacidad de respuesta y baja latencia en tus aplicaciones Android.
Mayuri Khinvasara Khabya • Lectura de 9 minutos
-
Noticias sobre productos
Nos complace anunciar importantes actualizaciones de nuestros recursos de diseño, que te ofrecen la guía completa que necesitas para crear aplicaciones Android adaptables y de alta calidad en todos los factores de forma. Ahora tenemos una guía sobre la experiencia de escritorio y una galería de diseño de Android renovada.
Ivy Knight • Tiempo de lectura: 2 min
-
Noticias sobre productos
Se ha lanzado la primera versión alfa de Room 3.0. Room 3.0 es una versión principal de la biblioteca que introduce cambios importantes y se centra en Kotlin Multiplatform (KMP). Además, añade compatibilidad con JavaScript y WebAssembly (WASM) a la compatibilidad con Android, iOS y JVM para ordenadores.
Daniel Santiago Rivera • Tiempo de lectura: 4 min
Mantente al día
Recibe cada semana en tu bandeja de entrada las últimas novedades sobre el desarrollo para Android.