ExoPlayer admite una amplia variedad de necesidades de análisis de reproducción. En última instancia, el análisis se trata de recopilar, interpretar, agregar y resumir datos de las reproducciones. Estos datos se pueden usar en el dispositivo (por ejemplo, para el registro, la depuración o para informar futuras decisiones de reproducción) o se pueden informar a un servidor para supervisar las reproducciones en todos los dispositivos.
Por lo general, un sistema de análisis debe recopilar eventos primero y, luego, procesarlos más para que sean significativos:
- Recopilación de eventos: Esto se puede hacer registrando un
AnalyticsListeneren una instancia deExoPlayer. Los objetos de escucha de análisis registrados reciben eventos a medida que ocurren durante el uso del reproductor. Cada evento se asocia con el elemento multimedia correspondiente en la playlist, así como con la posición de reproducción y los metadatos de la marca de tiempo. - Procesamiento de eventos: Algunos sistemas de análisis suben eventos sin procesar a un servidor, y todo el procesamiento de eventos se realiza del lado del servidor. También es posible procesar eventos en el dispositivo, lo que puede ser más simple o reducir la cantidad de información que se debe subir. ExoPlayer proporciona
PlaybackStatsListener, que te permite realizar los siguientes pasos de procesamiento:- Interpretación de eventos: Para que sean útiles a los fines del análisis, los eventos deben interpretarse en el contexto de una sola reproducción. Por ejemplo, el evento sin procesar de un cambio de estado del reproductor a
STATE_BUFFERINGpuede corresponder al almacenamiento en búfer inicial, a un nuevo almacenamiento en búfer o al almacenamiento en búfer que se produce después de una búsqueda. - Seguimiento del estado: En este paso, se convierten los eventos en contadores. Por ejemplo, los eventos de cambio de estado se pueden convertir en contadores que registren cuánto tiempo se dedica a cada estado de reproducción. El resultado es un conjunto básico de valores de datos de análisis para una sola reproducción.
- Agregación: En este paso, se combinan los datos de análisis de varias reproducciones, por lo general, sumando los contadores.
- Cálculo de métricas de resumen: Muchas de las métricas más útiles son aquellas que calculan promedios o combinan los valores de los datos de Analytics básicos de otras maneras. Las métricas de resumen se pueden calcular para una o varias reproducciones.
- Interpretación de eventos: Para que sean útiles a los fines del análisis, los eventos deben interpretarse en el contexto de una sola reproducción. Por ejemplo, el evento sin procesar de un cambio de estado del reproductor a
Recopilación de eventos con AnalyticsListener
Los eventos de reproducción sin procesar del reproductor se registran en las implementaciones de AnalyticsListener. Puedes agregar fácilmente tu propio objeto de escucha y anular solo los métodos que te interesen:
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) {} });
El EventTime que se pasa a cada devolución de llamada asocia el evento a un elemento multimedia de la playlist, así como a la posición de reproducción y a los metadatos de la marca de tiempo:
realtimeMs: Hora del reloj de pared del evento.timeline,windowIndexymediaPeriodId: Definen la playlist y el elemento dentro de la playlist al que pertenece el evento. El objetomediaPeriodIdcontiene información adicional opcional, por ejemplo, para indicar si el evento pertenece a un anuncio dentro del elemento.eventPlaybackPositionMs: Es la posición de reproducción en el elemento cuando ocurrió el evento.currentTimeline,currentWindowIndex,currentMediaPeriodIdycurrentPlaybackPositionMs: Igual que arriba, pero para el elemento que se está reproduciendo. El elemento que se está reproduciendo actualmente puede ser diferente del elemento al que pertenece el evento, por ejemplo, si el evento corresponde a la carga previa del siguiente elemento que se reproducirá.
Procesamiento de eventos con PlaybackStatsListener
PlaybackStatsListener es un AnalyticsListener que implementa el procesamiento de eventos en el dispositivo. Calcula PlaybackStats, con contadores y métricas derivadas, incluidos los siguientes:
- Métricas de resumen, por ejemplo, el tiempo de reproducción total
- Métricas de calidad de reproducción adaptativa, por ejemplo, la resolución promedio del video
- Métricas de calidad de la renderización, por ejemplo, la tasa de fotogramas descartados.
- Métricas de uso de recursos, por ejemplo, la cantidad de bytes leídos a través de la red.
Encontrarás una lista completa de los recuentos disponibles y las métricas derivadas en el Javadoc de PlaybackStats.
PlaybackStatsListener calcula un PlaybackStats independiente para cada elemento multimedia de la playlist y para cada anuncio insertado del lado del cliente dentro de estos elementos. Puedes proporcionar una devolución de llamada a PlaybackStatsListener para recibir información sobre las reproducciones finalizadas y usar el EventTime que se pasa a la devolución de llamada para identificar qué reproducción finalizó. Es posible agregar los datos de análisis de varias reproducciones. También es posible consultar el objeto PlaybackStats para la sesión de reproducción actual en cualquier momento con 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. }));
El constructor de PlaybackStatsListener ofrece la opción de conservar el historial completo de los eventos procesados. Ten en cuenta que esto puede generar una sobrecarga de memoria desconocida según la duración de la reproducción y la cantidad de eventos. Por lo tanto, solo debes activarlo si necesitas acceder al historial completo de los eventos procesados, en lugar de solo a los datos de análisis finales.
Ten en cuenta que PlaybackStats usa un conjunto extendido de estados para indicar no solo el estado del contenido multimedia, sino también la intención del usuario de reproducir y más información detallada, como por qué se interrumpió o finalizó la reproducción:
| Estado de reproducción | Intención del usuario de reproducir contenido | No hay intención de jugar |
|---|---|---|
| Antes de la reproducción | JOINING_FOREGROUND |
NOT_STARTED, JOINING_BACKGROUND |
| Reproducción activa | PLAYING |
|
| Se interrumpió la reproducción | BUFFERING, SEEKING |
PAUSED, PAUSED_BUFFERING, SUPPRESSED, SUPPRESSED_BUFFERING, INTERRUPTED_BY_AD |
| Estados finales | ENDED, STOPPED, FAILED, ABANDONED |
La intención del usuario de reproducir contenido es importante para distinguir los momentos en que el usuario esperaba activamente que se reanudara la reproducción de los tiempos de espera pasivos. Por ejemplo, PlaybackStats.getTotalWaitTimeMs devuelve el tiempo total dedicado a los estados JOINING_FOREGROUND, BUFFERING y SEEKING, pero no el tiempo en el que se pausó la reproducción. Del mismo modo, PlaybackStats.getTotalPlayAndWaitTimeMs devolverá el tiempo total con la intención del usuario de jugar, es decir, el tiempo total de espera activa y el tiempo total dedicado al estado PLAYING.
Eventos procesados e interpretados
Puedes registrar eventos procesados e interpretados con PlaybackStatsListener y keepHistory=true. El PlaybackStats resultante contendrá las siguientes listas de eventos:
playbackStateHistory: Es una lista ordenada de estados de reproducción extendidos con elEventTimeen el que comenzaron a aplicarse. También puedes usarPlaybackStats.getPlaybackStateAtTimepara buscar el estado en un momento determinado del reloj.mediaTimeHistory: Es un historial de pares de hora del reloj y hora de los medios que te permite reconstruir qué partes del contenido multimedia se reprodujeron en qué momento. También puedes usarPlaybackStats.getMediaTimeMsAtRealtimeMspara buscar la posición de reproducción en un momento determinado.videoFormatHistoryyaudioFormatHistory: Son listas ordenadas de formatos de audio y video que se usan durante la reproducción, con elEventTimeen el que comenzaron a usarse.fatalErrorHistoryynonFatalErrorHistory: Listas ordenadas de errores recuperables y no recuperables con elEventTimeen el que se produjeron. Los errores fatales son aquellos que detuvieron la reproducción, mientras que los errores recuperables pueden haberse solucionado.
Datos de análisis de reproducciones únicas
Estos datos se recopilan automáticamente si usas PlaybackStatsListener, incluso con keepHistory=false. Los valores finales son los campos públicos que puedes encontrar en el Javadoc de PlaybackStats y las duraciones del estado de reproducción que devuelve getPlaybackStateDurationMs. Para mayor comodidad, también encontrarás métodos como getTotalPlayTimeMs y getTotalWaitTimeMs que devuelven la duración de combinaciones específicas de estados de reproducción.
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);
Datos de estadísticas agregados de varias reproducciones
Puedes combinar varios PlaybackStats llamando a PlaybackStats.merge. El PlaybackStats resultante contendrá los datos agregados de todas las reproducciones combinadas. Ten en cuenta que no contendrá el historial de eventos de reproducción individuales, ya que estos no se pueden agregar.
PlaybackStatsListener.getCombinedPlaybackStats se puede usar para obtener una vista agregada de todos los datos de estadísticas recopilados durante el ciclo de vida de un PlaybackStatsListener.
Métricas de resumen calculadas
Además de los datos de estadísticas básicos, PlaybackStats proporciona muchos métodos para calcular métricas de resumen.
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 avanzados
Asociación de datos de análisis con metadatos de reproducción
Cuando recopiles datos de análisis para reproducciones individuales, es posible que desees asociar los datos de análisis de la reproducción con metadatos sobre el contenido multimedia que se está reproduciendo.
Es recomendable establecer metadatos específicos de los medios con MediaItem.Builder.setTag.
La etiqueta de medios forma parte del objeto EventTime que se informa para los eventos sin procesar y cuando finalizan los objetos PlaybackStats, por lo que se puede recuperar fácilmente cuando se controlan los datos de análisis correspondientes:
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. });
Informa eventos de análisis personalizados
En caso de que necesites agregar eventos personalizados a los datos de Analytics, debes guardar estos eventos en tu propia estructura de datos y combinarlos con el PlaybackStats informado más adelante. Si te sirve, puedes extender DefaultAnalyticsCollector para poder generar instancias de EventTime para tus eventos personalizados y enviarlas a los objetos de escucha ya registrados, como se muestra en el siguiente ejemplo.
Kotlin
@OptIn(UnstableApi::class) private interface ExtendedListener : AnalyticsListener { fun onCustomEvent(eventTime: EventTime) } @OptIn(UnstableApi::class) private class ExtendedCollector : DefaultAnalyticsCollector(Clock.DEFAULT) { fun customEvent() { val eventTime = super.generateCurrentPlayerMediaPeriodEventTime() super.sendEvent(eventTime, CUSTOM_EVENT_ID) { listener: AnalyticsListener -> if (listener is ExtendedListener) { listener.onCustomEvent(eventTime) } } } } @OptIn(UnstableApi::class) fun useExtendedAnalyticsCollector(context: Context) { // 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
@OptIn(markerClass = UnstableApi.class) private interface ExtendedListener extends AnalyticsListener { void onCustomEvent(EventTime eventTime); } @OptIn(markerClass = UnstableApi.class) 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); } }); } } @OptIn(markerClass = UnstableApi.class) public static void useExtendedAnalyticsCollector(Context context) { // 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(); }