Detectar eventos de reprodução
Eventos, como mudanças de estado e erros de reprodução, são informados às instâncias
Player.Listener registradas. Para registrar um listener e receber esses eventos:
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 tem métodos padrão vazios, então você só precisa implementar os
métodos que interessam. Consulte o Javadoc para uma descrição completa dos métodos e quando eles são chamados. Alguns dos métodos mais importantes são descritos em mais detalhes abaixo.
Os listeners podem implementar callbacks de eventos individuais ou um callback onEvents genérico que é chamado depois que um ou mais eventos ocorrem juntos. Consulte Individual callbacks vs onEvents para saber qual é a melhor opção para diferentes casos de uso.
Mudanças no estado da reprodução
As mudanças no estado do player podem ser recebidas implementando
onPlaybackStateChanged(@State int state) em um Player.Listener registrado.
O player pode estar em um dos quatro estados de reprodução:
Player.STATE_IDLE: esse é o estado inicial, quando o player está parado e quando a reprodução falhou. O player vai manter apenas recursos limitados nesse estado.Player.STATE_BUFFERING: o player não consegue iniciar a reprodução imediatamente da posição atual. Isso acontece principalmente porque mais dados precisam ser carregados.Player.STATE_READY: o player inicia a reprodução imediatamente da posição atual.Player.STATE_ENDED: o player terminou de reproduzir toda a mídia.
Além desses estados, o player tem uma flag playWhenReady para indicar
a intenção do usuário de reproduzir. As mudanças nessa flag podem ser recebidas implementando
onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason).
Um jogador está jogando (ou seja, a posição está avançando e a mídia está sendo apresentada ao usuário) quando todas as três condições a seguir são atendidas:
- O player está no estado
Player.STATE_READY. playWhenReadyétrue- A reprodução não é suprimida por um motivo retornado por
Player.getPlaybackSuppressionReason
Em vez de verificar essas propriedades individualmente, é possível chamar Player.isPlaying. As mudanças nesse estado podem ser recebidas implementando
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. } } });
Erros de reprodução
Para receber erros que causam falha na reprodução, implemente
onPlayerError(PlaybackException error) em um Player.Listener registrado. Quando
ocorre uma falha, esse método é chamado imediatamente antes da transição do
estado de reprodução para Player.STATE_IDLE. As reproduções com falha ou interrompidas podem ser
tentadas novamente chamando ExoPlayer.prepare.
Algumas implementações de Player transmitem instâncias de subclasses de PlaybackException para fornecer mais informações sobre a falha. Por exemplo, ExoPlayer transmite ExoPlaybackException, que tem type, rendererIndex e outros campos específicos do ExoPlayer.
O exemplo a seguir mostra como detectar quando uma reprodução falhou devido a um problema de rede 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. } } } });
Transições de playlist
Sempre que o player muda para um novo item de mídia na playlist,
onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int
reason) é chamado em objetos Player.Listener registrados. O motivo indica se foi uma transição automática, uma busca (por exemplo, depois de chamar player.next()), uma repetição do mesmo item ou causada por uma mudança na playlist (por exemplo, se o item em reprodução for removido).
Metadados
Os metadados retornados de player.getCurrentMediaMetadata() podem mudar por vários motivos: transições de playlist, atualizações de metadados in-stream ou atualização do MediaItem atual durante a reprodução.
Se você tiver interesse em mudanças de metadados, por exemplo, para atualizar uma interface que mostra
o título atual, ouça onMediaMetadataChanged.
Procurando
Chamar métodos Player.seekTo resulta em uma série de callbacks para instâncias Player.Listener registradas:
onPositionDiscontinuitycomreason=DISCONTINUITY_REASON_SEEK. Esse é o resultado direto da chamada dePlayer.seekTo. O callback tem camposPositionInfopara a posição antes e depois da busca.onPlaybackStateChangedcom qualquer mudança de estado imediata relacionada à busca. Talvez não haja uma mudança desse tipo.
Callbacks individuais x onEvents
Os listeners podem escolher entre implementar callbacks individuais, como
onIsPlayingChanged(boolean isPlaying), e o callback genérico onEvents(Player
player, Events events). O callback genérico fornece acesso ao objeto Player e especifica o conjunto de events que ocorreram juntos. Esse
callback é sempre chamado depois dos callbacks correspondentes aos
eventos individuais.
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); } }
Os eventos individuais são preferíveis nos seguintes casos:
- O ouvinte se interessa pelos motivos das mudanças. Por exemplo, os motivos fornecidos para
onPlayWhenReadyChangedouonMediaItemTransition. - O listener só age nos novos valores fornecidos por parâmetros de callback ou aciona algo mais que não depende desses parâmetros.
- A implementação do listener prefere uma indicação clara e legível do que acionou o evento no nome do método.
- O listener informa um sistema de análise que precisa saber sobre todos os eventos individuais e mudanças de estado.
O onEvents(Player player, Events events) genérico é preferível nos seguintes casos:
- O listener quer acionar a mesma lógica para vários eventos. Por
exemplo, atualizar uma interface para
onPlaybackStateChangedeonPlayWhenReadyChanged. - O listener precisa acessar o objeto
Playerpara acionar mais eventos, por exemplo, a busca após uma transição de item de mídia. - O listener pretende usar vários valores de estado informados por
callbacks separados juntos ou em combinação com métodos getter
Player. Por exemplo, usarPlayer.getCurrentWindowIndex()com oTimelinefornecido emonTimelineChangedsó é seguro no callbackonEvents. - O listener tem interesse em saber se os eventos ocorreram logicamente juntos.
Por exemplo, de
onPlaybackStateChangedparaSTATE_BUFFERINGdevido a uma transição de item de mídia.
Em alguns casos, os listeners precisam combinar os callbacks individuais com o
callback genérico onEvents. Por exemplo, para gravar motivos de mudança de item de mídia
com onMediaItemTransition, mas só agir quando todas as mudanças de estado puderem ser usadas
juntas em onEvents.
Como usar o AnalyticsListener
Ao usar ExoPlayer, um AnalyticsListener pode ser registrado com o player
chamando addAnalyticsListener. As implementações do AnalyticsListener podem detectar eventos detalhados que podem ser úteis para fins de análise e geração de registros. Consulte a página de análise para mais detalhes.
Como usar o EventLogger
EventLogger é um AnalyticsListener fornecido diretamente pela biblioteca para fins de registro. Adicione EventLogger a um ExoPlayer para ativar o registro adicional útil com uma única linha:
Kotlin
player.addAnalyticsListener(EventLogger())
Java
player.addAnalyticsListener(new EventLogger());
Consulte a página de registro de depuração para mais detalhes.
Acionamento de eventos em posições de reprodução especificadas
Alguns casos de uso exigem o disparo de eventos em posições de reprodução específicas. Isso é compatível com PlayerMessage. É possível criar um PlayerMessage usando
ExoPlayer.createMessage. A posição de reprodução em que ele deve ser executado
pode ser definida usando PlayerMessage.setPosition. As mensagens são executadas na
linha de execução de reprodução por padrão, mas isso pode ser personalizado usando
PlayerMessage.setLooper. PlayerMessage.setDeleteAfterDelivery pode ser usado para controlar se a mensagem será executada toda vez que a posição de reprodução especificada for encontrada (isso pode acontecer várias vezes devido aos modos de busca e repetição) ou apenas na primeira vez. Depois que o PlayerMessage for configurado, ele poderá ser programado usando 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();