O ExoPlayer oferece suporte a uma ampla variedade de necessidades de análise de reprodução. Em última análise, a análise consiste em coletar, interpretar, agregar e resumir dados das execuções. Esses dados podem ser usados no dispositivo (por exemplo, para geração de registros, depuração ou para informar decisões futuras de reprodução) ou informados a um servidor para monitorar reproduções em todos os dispositivos.
Um sistema de análise geralmente precisa coletar eventos primeiro e, em seguida, processá-los para que tenham significado:
- Coleta de eventos:
isso pode ser feito registrando um
AnalyticsListener
em uma instânciaExoPlayer
. Os listeners de análise registrados recebem eventos conforme eles ocorrem durante o uso do player. Cada evento é associado ao item de mídia correspondente na playlist, bem como à posição de reprodução e aos metadados do carimbo de data/hora. - Processamento de eventos:
alguns sistemas de análise fazem upload de eventos brutos para um servidor, com todo o processamento
de eventos realizado no servidor. Também é possível processar eventos no
dispositivo, o que pode ser mais simples ou reduzir a quantidade de informações que
precisa ser enviada. O ExoPlayer fornece
PlaybackStatsListener
, que permite executar as seguintes etapas de processamento:- Interpretação de eventos: para serem úteis para análise, os eventos precisam
ser interpretados no contexto de uma única reprodução. Por exemplo, o evento bruto
de uma mudança de estado do player para
STATE_BUFFERING
pode corresponder a bufferização inicial, rebufferização ou bufferização que ocorre após uma busca. - Rastreamento de estado: essa etapa converte eventos em contadores. Por exemplo, os eventos de mudança de estado podem ser convertidos em contadores que rastreiam quanto tempo é gastado em cada estado de reprodução. O resultado é um conjunto básico de valores de dados de análise para uma única reprodução.
- Agregação: nessa etapa, os dados de análise são combinados em várias reproduções, geralmente somando os contadores.
- Cálculo de métricas resumidas: muitas das métricas mais úteis são aquelas que calculam médias ou combinam os valores de dados básicos de análise de outras maneiras. As métricas resumidas podem ser calculadas para uma ou várias reproduções.
- Interpretação de eventos: para serem úteis para análise, os eventos precisam
ser interpretados no contexto de uma única reprodução. Por exemplo, o evento bruto
de uma mudança de estado do player para
Coleta de eventos com o AnalyticsListener
Os eventos de reprodução bruta do player são informados às implementações
AnalyticsListener
. É possível adicionar seu próprio listener e substituir apenas os
métodos de seu interesse:
Kotlin
exoPlayer.addAnalyticsListener( object : AnalyticsListener { override fun onPlaybackStateChanged( eventTime: EventTime, @Player.State state: Int ) {} override fun onDroppedVideoFrames( eventTime: EventTime, droppedFrames: Int, elapsedMs: Long, ) {} } )
Java
exoPlayer.addAnalyticsListener( new AnalyticsListener() { @Override public void onPlaybackStateChanged( EventTime eventTime, @Player.State int state) {} @Override public void onDroppedVideoFrames( EventTime eventTime, int droppedFrames, long elapsedMs) {} });
O EventTime
transmitido para cada callback associa o evento a um item
de mídia na playlist, bem como a posição de reprodução e os metadados do carimbo de data/hora:
realtimeMs
: o horário do evento.timeline
,windowIndex
emediaPeriodId
: definem a playlist e o item na playlist a que o evento pertence. OmediaPeriodId
contém informações adicionais opcionais, por exemplo, indicando se o evento pertence a um anúncio do item.eventPlaybackPositionMs
: a posição de reprodução no item quando o evento ocorreu.currentTimeline
,currentWindowIndex
,currentMediaPeriodId
ecurrentPlaybackPositionMs
: como acima, mas para o item que está sendo reproduzido. O item que está sendo reproduzido pode ser diferente do item ao qual o evento pertence, por exemplo, se o evento corresponde ao pré-buffer do próximo item a ser reproduzido.
Processamento de eventos com PlaybackStatsListener
PlaybackStatsListener
é um AnalyticsListener
que implementa o processamento de eventos
no dispositivo. Ele calcula PlaybackStats
, com contadores e métricas
derivadas, incluindo:
- métricas de resumo, por exemplo, o tempo total de reprodução;
- Métricas de qualidade de reprodução adaptável, como a resolução média do vídeo.
- Renderizar métricas de qualidade, por exemplo, a taxa de frames descartados.
- Métricas de uso de recursos, por exemplo, o número de bytes lidos pela rede.
Uma lista completa das contagens e métricas derivadas disponíveis está disponível no
Javadoc do PlaybackStats
.
O PlaybackStatsListener
calcula PlaybackStats
separados para cada item de mídia
na playlist e também para cada anúncio do lado do cliente inserido nesses itens. Você
pode fornecer um callback para PlaybackStatsListener
para receber informações sobre as
reproduções concluídas e usar o EventTime
transmitido para o callback para identificar qual
reprodução foi concluída. É possível agregar os dados de análise para
várias reproduções. Também é possível consultar o PlaybackStats
da
sessão de reprodução atual a qualquer momento usando
PlaybackStatsListener.getPlaybackStats()
.
Kotlin
exoPlayer.addAnalyticsListener( PlaybackStatsListener(/* keepHistory= */ true) { eventTime: EventTime?, playbackStats: PlaybackStats?, -> // Analytics data for the session started at `eventTime` is ready. } )
Java
exoPlayer.addAnalyticsListener( new PlaybackStatsListener( /* keepHistory= */ true, (eventTime, playbackStats) -> { // Analytics data for the session started at `eventTime` is ready. }));
O construtor de PlaybackStatsListener
oferece a opção de manter o histórico
completo dos eventos processados. Isso pode gerar um overhead de memória desconhecido,
dependendo da duração da reprodução e do número de eventos. Portanto, ative-a somente se precisar de acesso ao histórico completo de eventos processados, em vez de apenas aos dados de análise finais.
PlaybackStats
usa um conjunto estendido de estados para indicar não apenas
o estado da mídia, mas também a intenção do usuário de reproduzir e informações mais detalhadas,
como o motivo da interrupção ou término da reprodução:
Estado da reprodução | Intenção do usuário de jogar | Não tem intenção de jogar |
---|---|---|
Antes da reprodução | JOINING_FOREGROUND |
NOT_STARTED , JOINING_BACKGROUND |
Reprodução ativa | PLAYING |
|
Reprodução interrompida | BUFFERING , SEEKING |
PAUSED , PAUSED_BUFFERING , SUPPRESSED , SUPPRESSED_BUFFERING , INTERRUPTED_BY_AD |
Estados finais | ENDED , STOPPED , FAILED , ABANDONED |
A intenção do usuário de jogar é importante para distinguir os momentos em que ele estava
esperando ativamente a reprodução dos tempos de espera passivos. Por exemplo,
PlaybackStats.getTotalWaitTimeMs
retorna o tempo total gasto nos
estados JOINING_FOREGROUND
, BUFFERING
e SEEKING
, mas não o tempo em que
a reprodução foi pausada. Da mesma forma, PlaybackStats.getTotalPlayAndWaitTimeMs
vai
retornar o tempo total com a intenção do usuário de jogar, ou seja, o tempo de espera
ativo total e o tempo total gasto no estado PLAYING
.
Eventos processados e interpretados
É possível registrar eventos processados e interpretados usando PlaybackStatsListener
com keepHistory=true
. O PlaybackStats
resultante vai conter as
seguintes listas de eventos:
playbackStateHistory
: uma lista ordenada de estados de reprodução estendidos com oEventTime
em que eles começaram a ser aplicados. Também é possível usarPlaybackStats.getPlaybackStateAtTime
para procurar o estado em um determinado tempo decorrido.mediaTimeHistory
: um histórico de pares de tempo do relógio e da mídia, permitindo reconstruir quais partes da mídia foram reproduzidas em qual momento. Também é possível usarPlaybackStats.getMediaTimeMsAtRealtimeMs
para procurar a posição de reprodução em um determinado horário.videoFormatHistory
eaudioFormatHistory
: listas ordenadas de formatos de vídeo e áudio usados durante a reprodução com oEventTime
em que elas começaram a ser usadas.fatalErrorHistory
enonFatalErrorHistory
: listas ordenadas de erros fatais e não fatais com aEventTime
em que eles ocorreram. Erros fatais são aqueles que encerraram a reprodução, enquanto erros não fatais podem ter sido recuperáveis.
Dados de análise de reprodução única
Esses dados serão coletados automaticamente se você usar PlaybackStatsListener
, mesmo
com keepHistory=false
. Os valores finais são os campos públicos que podem ser
encontrados no Javadoc PlaybackStats
e as durações do estado de reprodução
retornadas por getPlaybackStateDurationMs
. Para sua conveniência, também há
métodos como getTotalPlayTimeMs
e getTotalWaitTimeMs
que retornam a
duração de combinações específicas de estado de reprodução.
Kotlin
Log.d( "DEBUG", "Playback summary: " + "play time = " + playbackStats.totalPlayTimeMs + ", rebuffers = " + playbackStats.totalRebufferCount )
Java
Log.d( "DEBUG", "Playback summary: " + "play time = " + playbackStats.getTotalPlayTimeMs() + ", rebuffers = " + playbackStats.totalRebufferCount);
Agregar dados de análise de várias reproduções
Chame PlaybackStats.merge
para combinar vários PlaybackStats
. O PlaybackStats
resultante vai conter os dados agregados
de todas as reproduções mescladas. Ele não vai conter o histórico de
eventos de reprodução individuais, já que eles não podem ser agregados.
PlaybackStatsListener.getCombinedPlaybackStats
pode ser usado para ter uma
visualização agregada de todos os dados de análise coletados durante o ciclo de vida de uma
PlaybackStatsListener
.
Métricas de resumo calculadas
Além dos dados básicos de análise, PlaybackStats
oferece muitos métodos
para calcular métricas resumidas.
Kotlin
Log.d( "DEBUG", "Additional calculated summary metrics: " + "average video bitrate = " + playbackStats.meanVideoFormatBitrate + ", mean time between rebuffers = " + playbackStats.meanTimeBetweenRebuffers )
Java
Log.d( "DEBUG", "Additional calculated summary metrics: " + "average video bitrate = " + playbackStats.getMeanVideoFormatBitrate() + ", mean time between rebuffers = " + playbackStats.getMeanTimeBetweenRebuffers());
Temas avançados
Como associar dados de análise a metadados de reprodução
Ao coletar dados de análise para transmissões individuais, é possível associar os dados de análise de reprodução a metadados sobre a mídia que está sendo tocada.
É recomendável definir metadados específicos da mídia com MediaItem.Builder.setTag
.
A tag de mídia faz parte do EventTime
informado para eventos brutos e quando
PlaybackStats
são concluídos, para que possa ser facilmente recuperada ao processar os
dados de análise correspondentes:
Kotlin
PlaybackStatsListener(/* keepHistory= */ false) { eventTime: EventTime, playbackStats: PlaybackStats -> val mediaTag = eventTime.timeline .getWindow(eventTime.windowIndex, Timeline.Window()) .mediaItem .localConfiguration ?.tag // Report playbackStats with mediaTag metadata. }
Java
new PlaybackStatsListener( /* keepHistory= */ false, (eventTime, playbackStats) -> { Object mediaTag = eventTime.timeline.getWindow(eventTime.windowIndex, new Timeline.Window()) .mediaItem .localConfiguration .tag; // Report playbackStats with mediaTag metadata. });
Como gerar relatórios de eventos de análise personalizados
Se for necessário adicionar eventos personalizados aos dados de análise, salve esses eventos na sua própria estrutura de dados e combine-os com o PlaybackStats
informado posteriormente. Se isso ajudar, você pode estender DefaultAnalyticsCollector
para gerar instâncias de EventTime
para seus eventos personalizados e enviá-las
aos listeners já registrados, conforme mostrado no exemplo a seguir.
Kotlin
private interface ExtendedListener : AnalyticsListener { fun onCustomEvent(eventTime: EventTime) } private class ExtendedCollector : DefaultAnalyticsCollector(Clock.DEFAULT) { fun customEvent() { val eventTime = generateCurrentPlayerMediaPeriodEventTime() sendEvent(eventTime, CUSTOM_EVENT_ID) { listener: AnalyticsListener -> if (listener is ExtendedListener) { listener.onCustomEvent(eventTime) } } } } // Usage - Setup and listener registration. val player = ExoPlayer.Builder(context).setAnalyticsCollector(ExtendedCollector()).build() player.addAnalyticsListener( object : ExtendedListener { override fun onCustomEvent(eventTime: EventTime?) { // Save custom event for analytics data. } } ) // Usage - Triggering the custom event. (player.analyticsCollector as ExtendedCollector).customEvent()
Java
private interface ExtendedListener extends AnalyticsListener { void onCustomEvent(EventTime eventTime); } private static class ExtendedCollector extends DefaultAnalyticsCollector { public ExtendedCollector() { super(Clock.DEFAULT); } public void customEvent() { AnalyticsListener.EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); sendEvent( eventTime, CUSTOM_EVENT_ID, listener -> { if (listener instanceof ExtendedListener) { ((ExtendedListener) listener).onCustomEvent(eventTime); } }); } } // Usage - Setup and listener registration. ExoPlayer player = new ExoPlayer.Builder(context).setAnalyticsCollector(new ExtendedCollector()).build(); player.addAnalyticsListener( (ExtendedListener) eventTime -> { // Save custom event for analytics data. }); // Usage - Triggering the custom event. ((ExtendedCollector) player.getAnalyticsCollector()).customEvent();