ExoPlayer поддерживает широкий спектр потребностей в аналитике воспроизведения. В конечном счете, аналитика — это сбор, интерпретация, агрегирование и обобщение данных, полученных при воспроизведении. Эти данные могут использоваться либо на устройстве (например, для регистрации, отладки или для информирования о будущих решениях по воспроизведению), либо передаваться на сервер для мониторинга воспроизведения на всех устройствах.
Аналитической системе обычно необходимо сначала собирать события, а затем обрабатывать их, чтобы сделать их значимыми:
- Сбор событий : это можно сделать, зарегистрировав
AnalyticsListener
в экземпляреExoPlayer
. Зарегистрированные прослушиватели аналитики получают события по мере их возникновения во время использования проигрывателя. Каждое событие связано с соответствующим медиа-элементом в списке воспроизведения, а также с метаданными позиции воспроизведения и метки времени. - Обработка событий . Некоторые аналитические системы загружают необработанные события на сервер, при этом вся обработка событий выполняется на стороне сервера. Также можно обрабатывать события на устройстве, и это может быть проще или уменьшить объем информации, которую необходимо загрузить. ExoPlayer предоставляет
PlaybackStatsListener
, который позволяет выполнять следующие этапы обработки:- Интерпретация событий . Чтобы быть полезными для целей аналитики, события необходимо интерпретировать в контексте одного воспроизведения. Например, необработанное событие изменения состояния игрока на
STATE_BUFFERING
может соответствовать начальной буферизации, повторной буферизации или буферизации, которая происходит после поиска. - Отслеживание состояния : на этом этапе события преобразуются в счетчики. Например, события изменения состояния можно преобразовать в счетчики, отслеживающие, сколько времени потрачено в каждом состоянии воспроизведения. Результатом является базовый набор значений аналитических данных для одного воспроизведения.
- Агрегация . На этом этапе аналитические данные из нескольких воспроизведений объединяются, обычно путем суммирования счетчиков.
- Расчет сводных показателей . Многие из наиболее полезных показателей — это те, которые вычисляют средние значения или объединяют значения основных аналитических данных другими способами. Сводные показатели можно рассчитать для одного или нескольких воспроизведений.
- Интерпретация событий . Чтобы быть полезными для целей аналитики, события необходимо интерпретировать в контексте одного воспроизведения. Например, необработанное событие изменения состояния игрока на
Сбор событий с помощью AnalyticsListener
Необработанные события воспроизведения из проигрывателя передаются реализациям AnalyticsListener
. Вы можете легко добавить свой собственный прослушиватель и переопределить только те методы, которые вас интересуют:
Котлин
exoPlayer.addAnalyticsListener( object : AnalyticsListener { override fun onPlaybackStateChanged( eventTime: EventTime, @Player.State state: Int ) {} override fun onDroppedVideoFrames( eventTime: EventTime, droppedFrames: Int, elapsedMs: Long, ) {} } )
Ява
exoPlayer.addAnalyticsListener( new AnalyticsListener() { @Override public void onPlaybackStateChanged( EventTime eventTime, @Player.State int state) {} @Override public void onDroppedVideoFrames( EventTime eventTime, int droppedFrames, long elapsedMs) {} });
EventTime
, передаваемый каждому обратному вызову, связывает событие с медиа-элементом в списке воспроизведения, а также с метаданными позиции воспроизведения и метки времени:
-
realtimeMs
: время события на настенных часах. -
timeline
,windowIndex
иmediaPeriodId
: определяет список воспроизведения и элемент в списке воспроизведения, которому принадлежит событие.mediaPeriodId
содержит необязательную дополнительную информацию, например указывающую, принадлежит ли событие рекламе в элементе. -
eventPlaybackPositionMs
: позиция воспроизведения элемента в момент возникновения события. -
currentTimeline
,currentWindowIndex
,currentMediaPeriodId
иcurrentPlaybackPositionMs
: то же, что и выше, но для текущего воспроизводимого элемента. Воспроизводимый в данный момент элемент может отличаться от элемента, которому принадлежит событие, например, если событие соответствует предварительной буферизации следующего воспроизводимого элемента.
Обработка событий с помощью PlaybackStatsListener
PlaybackStatsListener
— это AnalyticsListener
, реализующий обработку событий на устройстве. Он вычисляет PlaybackStats
со счетчиками и производными показателями, включая:
- Сводные показатели, например общее время воспроизведения.
- Адаптивные показатели качества воспроизведения, например среднее разрешение видео.
- Показатели качества рендеринга, например частота пропущенных кадров.
- Показатели использования ресурсов, например количество байтов, прочитанных по сети.
Полный список доступных счетчиков и производных показателей можно найти в PlaybackStats
Javadoc .
PlaybackStatsListener
вычисляет отдельные PlaybackStats
для каждого элемента мультимедиа в списке воспроизведения, а также для каждого клиентского объявления, вставленного в эти элементы. Вы можете предоставить обратный вызов PlaybackStatsListener
чтобы получать информацию о завершенном воспроизведении, и использовать EventTime
, передаваемый обратному вызову, чтобы определить, какое воспроизведение завершилось. Можно агрегировать аналитические данные для нескольких воспроизведений. Также можно в любой момент запросить PlaybackStats
для текущего сеанса воспроизведения с помощью PlaybackStatsListener.getPlaybackStats()
.
Котлин
exoPlayer.addAnalyticsListener( PlaybackStatsListener(/* keepHistory= */ true) { eventTime: EventTime?, playbackStats: PlaybackStats?, -> // Analytics data for the session started at `eventTime` is ready. } )
Ява
exoPlayer.addAnalyticsListener( new PlaybackStatsListener( /* keepHistory= */ true, (eventTime, playbackStats) -> { // Analytics data for the session started at `eventTime` is ready. }));
Конструктор PlaybackStatsListener
дает возможность сохранять полную историю обработанных событий. Обратите внимание, что это может повлечь за собой неизвестную нагрузку на память в зависимости от продолжительности воспроизведения и количества событий. Поэтому включать его следует только в том случае, если вам нужен доступ к полной истории обработанных событий, а не только к окончательным данным аналитики.
Обратите внимание, что PlaybackStats
использует расширенный набор состояний, чтобы указать не только состояние мультимедиа, но и намерение пользователя воспроизвести, а также более подробную информацию, например, почему воспроизведение было прервано или прекращено:
Состояние воспроизведения | Намерение пользователя играть | Нет намерения играть |
---|---|---|
Перед воспроизведением | JOINING_FOREGROUND | NOT_STARTED , JOINING_BACKGROUND |
Активное воспроизведение | PLAYING | |
Прерванное воспроизведение | BUFFERING , SEEKING | PAUSED , PAUSED_BUFFERING , SUPPRESSED , SUPPRESSED_BUFFERING , INTERRUPTED_BY_AD |
Конечные состояния | ENDED , STOPPED , FAILED , ABANDONED |
Намерение пользователя воспроизвести важно для того, чтобы отличать времена, когда пользователь активно ждал продолжения воспроизведения, от времени пассивного ожидания. Например, PlaybackStats.getTotalWaitTimeMs
возвращает общее время, проведенное в состояниях JOINING_FOREGROUND
, BUFFERING
и SEEKING
, но не время, когда воспроизведение было приостановлено. Аналогичным образом, PlaybackStats.getTotalPlayAndWaitTimeMs
вернет общее время, в течение которого пользователь намеревался играть, то есть общее время активного ожидания и общее время, проведенное в состоянии PLAYING
.
Обработанные и интерпретированные события
Вы можете записывать обработанные и интерпретированные события с помощью PlaybackStatsListener
с keepHistory=true
. Полученный PlaybackStats
будет содержать следующие списки событий:
-
playbackStateHistory
: упорядоченный список расширенных состояний воспроизведения сEventTime
, в котором они начали применяться. Вы также можете использоватьPlaybackStats.getPlaybackStateAtTime
для поиска состояния в заданное время настенных часов. -
mediaTimeHistory
: история пар времени настенных часов и времени мультимедиа, позволяющая реконструировать, какие части мультимедиа воспроизводились в какое время. Вы также можете использоватьPlaybackStats.getMediaTimeMsAtRealtimeMs
для поиска позиции воспроизведения в заданное время настенных часов. -
videoFormatHistory
иaudioFormatHistory
: упорядоченные списки видео- и аудиоформатов, используемых во время воспроизведения, с указаниемEventTime
, в котором они начали использоваться. -
fatalErrorHistory
иnonFatalErrorHistory
: упорядоченные списки фатальных и нефатальных ошибок с указаниемEventTime
, в котором они произошли. Фатальные ошибки — это те, которые привели к прекращению воспроизведения, тогда как нефатальные ошибки можно было исправить.
Аналитические данные однократного воспроизведения
Эти данные собираются автоматически, если вы используете PlaybackStatsListener
, даже если keepHistory=false
. Окончательные значения — это общедоступные поля, которые вы можете найти в Javadoc PlaybackStats
, а также длительность состояния воспроизведения, возвращаемая getPlaybackStateDurationMs
. Для удобства вы также найдете такие методы, как getTotalPlayTimeMs
и getTotalWaitTimeMs
, которые возвращают продолжительность определенных комбинаций состояний воспроизведения.
Котлин
Log.d( "DEBUG", "Playback summary: " + "play time = " + playbackStats.totalPlayTimeMs + ", rebuffers = " + playbackStats.totalRebufferCount )
Ява
Log.d( "DEBUG", "Playback summary: " + "play time = " + playbackStats.getTotalPlayTimeMs() + ", rebuffers = " + playbackStats.totalRebufferCount);
Совокупные аналитические данные нескольких воспроизведений
Вы можете объединить несколько PlaybackStats
вместе, вызвав PlaybackStats.merge
. Полученная PlaybackStats
будет содержать агрегированные данные всех объединенных воспроизведений. Обратите внимание, что он не будет содержать историю отдельных событий воспроизведения, поскольку их нельзя агрегировать.
PlaybackStatsListener.getCombinedPlaybackStats
можно использовать для получения агрегированного представления всех аналитических данных, собранных за время существования PlaybackStatsListener
.
Рассчитываемые сводные показатели
Помимо базовых аналитических данных, PlaybackStats
предоставляет множество методов расчета сводных показателей.
Котлин
Log.d( "DEBUG", "Additional calculated summary metrics: " + "average video bitrate = " + playbackStats.meanVideoFormatBitrate + ", mean time between rebuffers = " + playbackStats.meanTimeBetweenRebuffers )
Ява
Log.d( "DEBUG", "Additional calculated summary metrics: " + "average video bitrate = " + playbackStats.getMeanVideoFormatBitrate() + ", mean time between rebuffers = " + playbackStats.getMeanTimeBetweenRebuffers());
Расширенные темы
Связывание аналитических данных с метаданными воспроизведения
При сборе аналитических данных для отдельных воспроизведений вы можете связать данные аналитики воспроизведения с метаданными о воспроизводимом медиафайле.
Рекомендуется установить метаданные, специфичные для носителя, с помощью MediaItem.Builder.setTag
. Медиа-тег является частью EventTime
, сообщаемого для необработанных событий и после завершения работы PlaybackStats
, поэтому его можно легко получить при обработке соответствующих аналитических данных:
Котлин
PlaybackStatsListener(/* keepHistory= */ false) { eventTime: EventTime, playbackStats: PlaybackStats -> val mediaTag = eventTime.timeline .getWindow(eventTime.windowIndex, Timeline.Window()) .mediaItem .localConfiguration ?.tag // Report playbackStats with mediaTag metadata. }
Ява
new PlaybackStatsListener( /* keepHistory= */ false, (eventTime, playbackStats) -> { Object mediaTag = eventTime.timeline.getWindow(eventTime.windowIndex, new Timeline.Window()) .mediaItem .localConfiguration .tag; // Report playbackStats with mediaTag metadata. });
Отчеты о событиях пользовательской аналитики
Если вам нужно добавить пользовательские события к аналитическим данным, вам необходимо сохранить эти события в своей собственной структуре данных и позже объединить их с отчетами PlaybackStats
. Если это поможет, вы можете расширить DefaultAnalyticsCollector
чтобы иметь возможность создавать экземпляры EventTime
для ваших пользовательских событий и отправлять их уже зарегистрированным прослушивателям, как показано в следующем примере.
Котлин
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()
Ява
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();