Cómo escuchar eventos de reproducción
Los eventos, como los cambios de estado y los errores de reproducción, se informan a las instancias de Player.Listener registradas. Para registrar un objeto de escucha para recibir estos eventos, haz lo siguiente:
Kotlin
// Add a listener to receive events from the player. player.addListener(listener)
Java
// Add a listener to receive events from the player. player.addListener(listener);
Player.Listener tiene métodos predeterminados vacíos, por lo que solo debes implementar los métodos que te interesen. Consulta el Javadoc para obtener una descripción completa de los métodos y cuándo se llaman. A continuación, se describen con más detalle algunos de los métodos más importantes.
Los objetos de escucha pueden implementar devoluciones de llamada de eventos individuales o una devolución de llamada genérica onEvents que se llama después de que ocurren uno o más eventos juntos. Consulta Individual callbacks vs onEvents para obtener una explicación sobre cuál se debe preferir para diferentes casos de uso.
Cambios en el estado de reproducción
Los cambios en el estado del reproductor se pueden recibir implementando onPlaybackStateChanged(@State int state) en un Player.Listener registrado.
El reproductor puede estar en uno de los siguientes cuatro estados de reproducción:
Player.STATE_IDLE: Es el estado inicial, el estado cuando el reproductor está detenido y cuando falló la reproducción. En este estado, el reproductor solo tendrá recursos limitados.Player.STATE_BUFFERING: El reproductor no puede reproducir de inmediato desde su posición actual. Esto sucede principalmente porque se deben cargar más datos.Player.STATE_READY: El reproductor puede reproducir de inmediato desde su posición actual.Player.STATE_ENDED: El reproductor terminó de reproducir todo el contenido multimedia.
Además de estos estados, el reproductor tiene una marca playWhenReady para indicar la intención del usuario de reproducir contenido. Los cambios en esta marca se pueden recibir implementando onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason).
Un reproductor está en funcionamiento (es decir, su posición avanza y se presenta contenido multimedia al usuario) cuando se cumplen las tres condiciones siguientes:
- El reproductor está en el estado
Player.STATE_READY. playWhenReadyestrue.- La reproducción no se suprime por un motivo que devuelve
Player.getPlaybackSuppressionReason.
En lugar de tener que verificar estas propiedades de forma individual, se puede llamar a Player.isPlaying. Para recibir los cambios en este estado, implementa onIsPlayingChanged(boolean isPlaying):
Kotlin
player.addListener( object : Player.Listener { override fun onIsPlayingChanged(isPlaying: Boolean) { if (isPlaying) { // Active playback. } else { // Not playing because playback is paused, ended, suppressed, or the player // is buffering, stopped or failed. Check player.playWhenReady, // player.playbackState, player.playbackSuppressionReason and // player.playerError for details. } } } )
Java
player.addListener( new Player.Listener() { @Override public void onIsPlayingChanged(boolean isPlaying) { if (isPlaying) { // Active playback. } else { // Not playing because playback is paused, ended, suppressed, or the player // is buffering, stopped or failed. Check player.getPlayWhenReady, // player.getPlaybackState, player.getPlaybackSuppressionReason and // player.getPlaybackError for details. } } });
Errores de reproducción
Para recibir los errores que provocan que falle la reproducción, implementa onPlayerError(PlaybackException error) en un Player.Listener registrado. Cuando se produce un error, se llama a este método inmediatamente antes de que el estado de reproducción cambie a Player.STATE_IDLE. Para volver a intentar las reproducciones detenidas o fallidas, llama a ExoPlayer.prepare.
Ten en cuenta que algunas implementaciones de Player pasan instancias de subclases de PlaybackException para proporcionar información adicional sobre la falla. Por ejemplo, ExoPlayer pasa ExoPlaybackException, que tiene type, rendererIndex y otros campos específicos de ExoPlayer.
En el siguiente ejemplo, se muestra cómo detectar cuándo falló una reproducción debido a un problema de red HTTP:
Kotlin
player.addListener( object : Player.Listener { override fun onPlayerError(error: PlaybackException) { val cause = error.cause if (cause is HttpDataSourceException) { // An HTTP error occurred. val httpError = cause // It's possible to find out more about the error both by casting and by querying // the cause. if (httpError is InvalidResponseCodeException) { // Cast to InvalidResponseCodeException and retrieve the response code, message // and headers. } else { // Try calling httpError.getCause() to retrieve the underlying cause, although // note that it may be null. } } } } )
Java
player.addListener( new Player.Listener() { @Override public void onPlayerError(PlaybackException error) { @Nullable Throwable cause = error.getCause(); if (cause instanceof HttpDataSourceException) { // An HTTP error occurred. HttpDataSourceException httpError = (HttpDataSourceException) cause; // It's possible to find out more about the error both by casting and by querying // the cause. if (httpError instanceof HttpDataSource.InvalidResponseCodeException) { // Cast to InvalidResponseCodeException and retrieve the response code, message // and headers. } else { // Try calling httpError.getCause() to retrieve the underlying cause, although // note that it may be null. } } } });
Transiciones de playlist
Cada vez que el reproductor cambia a un nuevo elemento multimedia en la playlist, se llama a onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int
reason) en los objetos Player.Listener registrados. El motivo indica si se trató de una transición automática, una búsqueda (por ejemplo, después de llamar a player.next()), una repetición del mismo elemento o si se debió a un cambio en la playlist (por ejemplo, si se quitó el elemento que se estaba reproduciendo).
Metadatos
Los metadatos que se devuelven de player.getCurrentMediaMetadata() pueden cambiar por muchos motivos: transiciones de playlists, actualizaciones de metadatos intercalados o actualizaciones del MediaItem actual durante la reproducción.
Si te interesan los cambios en los metadatos, por ejemplo, para actualizar una IU que muestra el título actual, puedes escuchar onMediaMetadataChanged.
Buscando
Llamar a los métodos Player.seekTo genera una serie de devoluciones de llamada a las instancias Player.Listener registradas:
onPositionDiscontinuityconreason=DISCONTINUITY_REASON_SEEK. Este es el resultado directo de la llamada aPlayer.seekTo. La devolución de llamada tiene camposPositionInfopara la posición antes y después de la búsqueda.onPlaybackStateChangedcon cualquier cambio de estado inmediato relacionado con la búsqueda. Ten en cuenta que es posible que no haya ningún cambio de ese tipo.
Devoluciones de llamada individuales frente a onEvents
Los objetos de escucha pueden elegir entre implementar devoluciones de llamada individuales, como onIsPlayingChanged(boolean isPlaying), y la devolución de llamada genérica onEvents(Player
player, Events events). La devolución de llamada genérica proporciona acceso al objeto Player y especifica el conjunto de events que ocurrieron juntos. Esta devolución de llamada siempre se llama después de las devoluciones de llamada que corresponden a los eventos individuales.
Kotlin
override fun onEvents(player: Player, events: Player.Events) { if ( events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED) ) { uiModule.updateUi(player) } }
Java
@Override public void onEvents(Player player, Events events) { if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) { uiModule.updateUi(player); } }
Se deben preferir los eventos individuales en los siguientes casos:
- El objeto de escucha se interesa en los motivos de los cambios. Por ejemplo, los motivos proporcionados para
onPlayWhenReadyChangedoonMediaItemTransition. - El objeto de escucha solo actúa sobre los valores nuevos proporcionados a través de parámetros de devolución de llamada o activa algo más que no depende de los parámetros de devolución de llamada.
- La implementación del objeto de escucha prefiere una indicación clara y legible de lo que activó el evento en el nombre del método.
- El objeto de escucha informa a un sistema de análisis que necesita conocer todos los eventos individuales y los cambios de estado.
Se debe preferir el onEvents(Player player, Events events) genérico en los siguientes casos:
- El objeto de escucha desea activar la misma lógica para varios eventos. Por ejemplo, actualizar una IU para
onPlaybackStateChangedyonPlayWhenReadyChanged - El objeto de escucha necesita acceder al objeto
Playerpara activar más eventos, por ejemplo, la búsqueda después de una transición de elementos multimedia. - El objeto de escucha pretende usar varios valores de estado que se informan a través de devoluciones de llamada separadas, o bien en combinación con métodos de obtención de
Player. Por ejemplo, usarPlayer.getCurrentWindowIndex()con elTimelineproporcionado enonTimelineChangedsolo es seguro desde la devolución de llamadaonEvents. - El objeto de escucha se interesa en si los eventos ocurrieron lógicamente juntos.
Por ejemplo, de
onPlaybackStateChangedaSTATE_BUFFERINGdebido a una transición de elemento multimedia.
En algunos casos, es posible que los objetos de escucha deban combinar las devoluciones de llamada individuales con la devolución de llamada genérica onEvents, por ejemplo, para registrar los motivos de cambio de elementos multimedia con onMediaItemTransition, pero solo actuar una vez que todos los cambios de estado se puedan usar juntos en onEvents.
En uso: AnalyticsListener
Cuando se usa ExoPlayer, se puede registrar un AnalyticsListener con el reproductor llamando a addAnalyticsListener. Las implementaciones de AnalyticsListener pueden escuchar eventos detallados que pueden ser útiles para fines de registro y análisis. Consulta la página de Analytics para obtener más detalles.
En uso: EventLogger
EventLogger es un AnalyticsListener que proporciona directamente la biblioteca para fines de registro. Agrega EventLogger a un ExoPlayer para habilitar el registro adicional útil con una sola línea:
Kotlin
player.addAnalyticsListener(EventLogger())
Java
player.addAnalyticsListener(new EventLogger());
Consulta la página de registro de depuración para obtener más detalles.
Activación de eventos en posiciones de reproducción especificadas
Algunos casos de uso requieren que se activen eventos en posiciones de reproducción especificadas. Esto se admite con PlayerMessage. Se puede crear un PlayerMessage con ExoPlayer.createMessage. La posición de reproducción en la que se debe ejecutar se puede establecer con PlayerMessage.setPosition. De forma predeterminada, los mensajes se ejecutan en el subproceso de reproducción, pero esto se puede personalizar con PlayerMessage.setLooper. PlayerMessage.setDeleteAfterDelivery se puede usar para controlar si el mensaje se ejecutará cada vez que se encuentre la posición de reproducción especificada (esto puede ocurrir varias veces debido a los modos de búsqueda y repetición) o solo la primera vez. Una vez que se configura el objeto PlayerMessage, se puede programar con PlayerMessage.send.
Kotlin
player .createMessage { messageType: Int, payload: Any? -> } .setLooper(Looper.getMainLooper()) .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120000) .setPayload(customPayloadData) .setDeleteAfterDelivery(false) .send()
Java
player .createMessage( (messageType, payload) -> { // Do something at the specified playback position. }) .setLooper(Looper.getMainLooper()) .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120_000) .setPayload(customPayloadData) .setDeleteAfterDelivery(false) .send();