En el centro de la biblioteca de ExoPlayer, se encuentra la interfaz Player
. Un Player
expone la funcionalidad tradicional del reproductor multimedia de alto nivel, como la capacidad de
almacenar contenido multimedia en búfer, reproducir, pausar y buscar contenido multimedia. La implementación predeterminada de ExoPlayer
es la siguiente:
diseñada para hacer pocas suposiciones (y, por lo tanto, imponer pocas restricciones sobre)
el tipo de contenido multimedia que se reproduce, cómo y dónde se almacenan, y cómo se
se renderizan. En lugar de implementar la carga y la renderización de contenido multimedia directamente,
Las implementaciones de ExoPlayer
delegan este trabajo a los componentes que se insertan.
Cuando se crea un reproductor o cuando se pasan nuevas fuentes de contenido multimedia al reproductor.
Los componentes comunes a todas las implementaciones de ExoPlayer
son los siguientes:
- Instancias de
MediaSource
que definen el contenido multimedia que se reproducirá, cargan el contenido multimedia y desde la cual se pueden leer los medios cargados. Se crea una instanciaMediaSource
desde un elementoMediaItem
por un elementoMediaSource.Factory
dentro del reproductor. También pueden pasar directamente al reproductor mediante la API de listas de reproducción basadas en fuentes de medios - Una instancia de
MediaSource.Factory
que convierta unMediaItem
en unMediaSource
El Se insertaMediaSource.Factory
cuando se crea el reproductor. - Instancias
Renderer
que renderizan componentes individuales del contenido multimedia. Son insertar cuando se crea el reproductor. - Un
TrackSelector
que selecciona las pistas que proporciona elMediaSource
que se consumidas por cadaRenderer
disponible. Se inserta unTrackSelector
. cuando se crea el reproductor. - Un
LoadControl
que controla cuándo elMediaSource
almacena en búfer más contenido multimedia la cantidad de contenido multimedia almacenado en búfer. Se inserta unLoadControl
cuando el reproductor está crear. - Un objeto
LivePlaybackSpeedControl
que controla la velocidad de reproducción durante la transmisión en vivo reproducciones que permiten que el reproductor se mantenga cerca de un desplazamiento en vivo configurado R Se insertaLivePlaybackSpeedControl
cuando se crea el reproductor.
El concepto de inyectar componentes que implementan partes del jugador está presente en toda la biblioteca. Las implementaciones predeterminadas algunos de los componentes delegan trabajo para insertar otros componentes. Esto permite que muchas subcomponentes que se reemplazarán individualmente por implementaciones que sean de forma personalizada.
Personalización del reproductor
Algunos ejemplos comunes de personalización del reproductor a través de la inserción de componentes son que se describe a continuación.
Configura la pila de red
Tenemos una página sobre cómo personalizar la pila de red que usa ExoPlayer.
Almacenar datos en caché cargados desde la red
Consulta las guías para almacenamiento temporal en caché sobre la marcha y descarga de contenido multimedia.
Personaliza las interacciones del servidor
Es posible que algunas apps quieran interceptar solicitudes y respuestas HTTP. Te recomendamos insertar encabezados de solicitud personalizados, leer los encabezados de respuesta del servidor, modificar solicitudes URI, etc. Por ejemplo, tu app puede autenticarse mediante la inyección un token como encabezado cuando se solicitan segmentos multimedia.
En el siguiente ejemplo, se muestra cómo implementar estos comportamientos
Cómo insertar un DataSource.Factory
personalizado en DefaultMediaSourceFactory
:
Kotlin
val dataSourceFactory = DataSource.Factory { val dataSource = httpDataSourceFactory.createDataSource() // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value") dataSource } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory) ) .build()
Java
DataSource.Factory dataSourceFactory = () -> { HttpDataSource dataSource = httpDataSourceFactory.createDataSource(); // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value"); return dataSource; }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)) .build();
En el fragmento de código anterior, el HttpDataSource
insertado incluye el encabezado.
"Header: Value"
en cada solicitud HTTP. Este comportamiento se corrige para cada
interacción con una fuente HTTP.
Para obtener un enfoque más detallado, puedes incorporar un comportamiento justo a tiempo con un
ResolvingDataSource
En el siguiente fragmento de código, se muestra cómo insertar
de solicitud de acceso justo antes de interactuar con una fuente HTTP:
Kotlin
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time request headers. dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)) }
Java
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time request headers. dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));
También puedes usar un ResolvingDataSource
para realizar
modificaciones oportunas del URI, como se muestra en el siguiente fragmento:
Kotlin
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time URI resolution logic. dataSpec.withUri(resolveUri(dataSpec.uri)) }
Java
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time URI resolution logic. dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));
Personaliza el manejo de errores
La implementación de un LoadErrorHandlingPolicy
personalizado permite que las apps personalicen la
en la que ExoPlayer reacciona a los errores de carga. Por ejemplo, es posible que una app quiera fallar rápido
en lugar de intentarlo muchas veces, o puede personalizar la lógica de retirada
controla cuánto tiempo espera el jugador entre cada reintento. El siguiente fragmento
se muestra cómo implementar una lógica de retirada personalizada:
Kotlin
val loadErrorHandlingPolicy: LoadErrorHandlingPolicy = object : DefaultLoadErrorHandlingPolicy() { override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorInfo): Long { // Implement custom back-off logic here. return 0 } } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy) ) .build()
Java
LoadErrorHandlingPolicy loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy() { @Override public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) { // Implement custom back-off logic here. return 0; } }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context) .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)) .build();
El argumento LoadErrorInfo
contiene más información sobre la carga con errores en
personalizar la lógica según el tipo de error o la solicitud con errores
Personaliza las marcas del extractor
Las marcas de extractor se pueden usar para personalizar la forma en que se extraen los formatos individuales
de los medios progresivos. Se pueden configurar en el DefaultExtractorsFactory
que es
proporcionados a DefaultMediaSourceFactory
. En el siguiente ejemplo, se pasa una marca
que permite la búsqueda basada en índices para transmisiones de MP3.
Kotlin
val extractorsFactory = DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING) val player = ExoPlayer.Builder(context) .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory)) .build()
Java
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING); ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory(new DefaultMediaSourceFactory(context, extractorsFactory)) .build();
Cómo habilitar la búsqueda de tasa de bits constante
Para transmisiones MP3, ADTS y AMR, puedes habilitar la búsqueda aproximada usando un
Suposición de tasa de bits constante con marcas FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
.
Estas marcas se pueden configurar para extractores individuales que
DefaultExtractorsFactory.setXyzExtractorFlags
, como se describió más arriba. Para
habilita la búsqueda de tasa de bits constante para todos los extractores compatibles, usa
DefaultExtractorsFactory.setConstantBitrateSeekingEnabled
Kotlin
val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)
Java
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
Luego, el ExtractorsFactory
se puede insertar a través de DefaultMediaSourceFactory
como
que se describieron para personalizar las marcas de extractor antes mencionadas.
Habilita la cola de búfer asíncrona
La cola de búfer asíncrona es una mejora en la renderización de ExoPlayer.
por lotes, que opera instancias MediaCodec
en modo asíncrono y
usa subprocesos adicionales para programar la decodificación y renderización de datos. Habilitando
puede reducir la pérdida de fotogramas
y los subdesbordamientos de audio.
La cola de búfer asíncrona está habilitada de forma predeterminada en dispositivos que ejecutan Android 12 (nivel de API 31) y versiones posteriores, y se puede habilitar manualmente a partir de Android 6.0 (nivel de API 23). Considera habilitar la función para dispositivos específicos en los que observes caídas fotogramas o subdesbordamiento de audio, particularmente al reproducir contenido protegido por DRM o de una velocidad de fotogramas alta contenido.
En el caso más simple, debes insertar un DefaultRenderersFactory
en el
reproductor de la siguiente manera:
Kotlin
val renderersFactory = DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing() val exoPlayer = ExoPlayer.Builder(context, renderersFactory).build()
Java
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing(); ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build();
Si creas instancias de renderizadores directamente, pasa un
AsynchronousMediaCodecAdapter.Factory
a MediaCodecVideoRenderer
y
MediaCodecAudioRenderer
.
Interceptación de llamadas a métodos con ForwardingPlayer
Puedes personalizar parte del comportamiento de una instancia de Player
si la unes
una subclase de ForwardingPlayer
y métodos de anulación para realizar cualquiera de
lo siguiente:
- Accede a los parámetros antes de pasarlos al delegado
Player
. - Accede al valor que se muestra del delegado
Player
antes de mostrarlo. - Vuelve a implementar el método por completo.
Cuando anules métodos ForwardingPlayer
, es importante garantizar que se cumpla lo siguiente:
implementación sigue siendo autocoherente y cumple con el Player
especialmente cuando se trata de métodos destinados a lograr
comportamiento idéntico o relacionado. Por ejemplo:
- Si quieres anular cada operación de reproducción una única operación, debes anular ambas
ForwardingPlayer.play
yForwardingPlayer.setPlayWhenReady
, ya que un llamador esperará que el comportamiento de estos métodos sea idéntico cuandoplayWhenReady = true
- Si quieres cambiar el incremento de avance, debes anular ambas
ForwardingPlayer.seekForward
para realizar un salto con la función yForwardingPlayer.getSeekForwardIncrement
para informar el incremento personalizado correcto al emisor. - Si quieres controlar los
Player.Commands
que promociona un jugador debes anularPlayer.getAvailableCommands()
yPlayer.isCommandAvailable()
y también escuchar Es la devolución de llamadaPlayer.Listener.onAvailableCommandsChanged()
para recibir notificaciones. cambios provenientes del reproductor subyacente.
Personalización de MediaSource
Con los ejemplos anteriores, se insertan componentes personalizados para usar durante la reproducción de todos
Objetos MediaItem
que se pasan al reproductor Dónde es la personalización detallada
se requiere, también es posible insertar componentes personalizados en objetos
Instancias de MediaSource
, que se pueden pasar directamente al reproductor. El ejemplo
a continuación, se muestra cómo personalizar un ProgressiveMediaSource
para usar un
DataSource.Factory
, ExtractorsFactory
y LoadErrorHandlingPolicy
:
Kotlin
val mediaSource = ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri))
Java
ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri));
Crea componentes personalizados
La biblioteca proporciona implementaciones predeterminadas de los componentes enumerados en la parte superior.
de esta página para casos de uso comunes. Un ExoPlayer
puede usar estos componentes, pero
pueden compilarse para usar implementaciones personalizadas si no se cumplen
como en los productos necesarios. Estos son algunos casos de uso para las implementaciones personalizadas:
Renderer
: Te recomendamos que implementes unRenderer
personalizado para controlar un El tipo de medio no es compatible con las implementaciones predeterminadas que proporciona el biblioteca.TrackSelector
: La implementación de unTrackSelector
personalizado permite que una app desarrollador que cambia la forma en que los segmentos expuestos por unMediaSource
se seleccionado para consumo por cada uno de losRenderer
disponibles.LoadControl
: La implementación de unLoadControl
personalizado permite que una app desarrollador para cambiar la política de almacenamiento en búfer del jugador.Extractor
: si necesitas admitir un formato de contenedor que no está disponible en este momento que admite la biblioteca, considera implementar una claseExtractor
personalizada.MediaSource
: Implementar una claseMediaSource
personalizada puede ser apropiado si deseas obtener muestras de contenido multimedia para enviar a los renderizadores de una de forma personalizada o si quieres implementar la composiciónMediaSource
personalizada. el comportamiento de los usuarios.MediaSource.Factory
: Cómo implementar unMediaSource.Factory
personalizado permite que una aplicación personalice la forma en que se crea unMediaSource
desde unMediaItem
.DataSource
: El paquete upstream de ExoPlayer ya contiene varias Implementaciones deDataSource
para diferentes casos de uso Te recomendamos implementas tu propia claseDataSource
para cargar datos de otra manera, como en un protocolo personalizado, con una pila HTTP personalizada la caché.
Cuando compiles componentes personalizados, te recomendamos lo siguiente:
- Si un componente personalizado necesita informar eventos a la aplicación, recomendamos
hacerlo con el mismo modelo que los componentes existentes de ExoPlayer, por
ejemplo con clases
EventDispatcher
o pasando unHandler
junto con un objeto de escucha al constructor del componente. - Recomendamos que los componentes personalizados usen el mismo modelo que el ExoPlayer existente.
componentes para permitir la reconfiguración de la app durante la reproducción. Para ello,
los componentes personalizados deben implementar
PlayerMessage.Target
y recibir cambios de configuración en el métodohandleMessage
El código de la aplicación pasar cambios de configuración llamando al métodocreateMessage
de ExoPlayer configurar el mensaje y enviarlo al componente usandoPlayerMessage.send
Cómo enviar mensajes para que se entreguen en la conversación de reproducción garantiza que se ejecuten de acuerdo con que se ejecute cualquier otra operación en el reproductor.