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, ) {} } )
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) {} });
Передаваемое в каждую функцию обратного вызова 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. } )
Java
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 . Итоговые значения — это общедоступные поля, которые можно найти в документации PlaybackStats Javadoc , и длительность состояний воспроизведения, возвращаемая методом getPlaybackStateDurationMs . Для удобства вы также найдете такие методы, как getTotalPlayTimeMs и getTotalWaitTimeMs , которые возвращают длительность определенных комбинаций состояний воспроизведения.
Котлин
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);
Сводные аналитические данные по нескольким воспроизведениям
Вы можете объединить несколько 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, )
Java
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. }
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. });
Создание отчетов по пользовательским аналитическим событиям
Если вам необходимо добавить пользовательские события в аналитические данные, вам нужно сохранить эти события в собственной структуре данных и объединить их с сообщаемыми данными PlaybackStats позже. При необходимости вы можете расширить DefaultAnalyticsCollector , чтобы он мог генерировать экземпляры EventTime для ваших пользовательских событий и отправлять их уже зарегистрированным слушателям, как показано в следующем примере.
Котлин
@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(); }