ExoPlayer obsługuje szeroki zakres potrzeb związanych ze statystykami odtwarzania. Statystyki dotyczą przede wszystkim zbierania, interpretowania, agregowania i podsumowywania danych z odtworzeń. Dane te mogą być używane na urządzeniu – na przykład do rejestrowania, debugowania lub do podejmowania przyszłych decyzji dotyczących odtwarzania – lub przekazywane do serwera w celu monitorowania odtwarzania na wszystkich urządzeniach.
System analityczny zwykle musi najpierw zarejestrować zdarzenia, a potem je dokładniej przetworzyć, aby nadać im znaczenie:
- Rejestrowanie zdarzeń: można to zrobić, rejestrując obiekt
AnalyticsListener
w instancjiExoPlayer
. Zarejestrowani detektorzy statystyk otrzymują zdarzenia w chwili, gdy występują podczas korzystania z odtwarzacza. Każde zdarzenie jest powiązane z odpowiadającym mu elementem multimedialnym na playliście, a także z metadanymi pozycji odtwarzania i sygnatury czasowej. - Przetwarzanie zdarzeń: niektóre systemy analityczne przesyłają na serwer nieprzetworzone zdarzenia, a cała przetwarzanie zdarzeń odbywa się po stronie serwera. Można również przetwarzać zdarzenia na urządzeniu, co pozwala uprościć proces lub zmniejszyć ilość informacji do przesłania. ExoPlayer udostępnia pakiet
PlaybackStatsListener
, który pozwala na wykonanie tych czynności:- Interpretacja zdarzeń: aby były przydatne do celów analitycznych, zdarzenia muszą być interpretowane w kontekście pojedynczego odtworzenia. Na przykład nieprzetworzone zdarzenie zmiany stanu odtwarzacza na
STATE_BUFFERING
może odpowiadać wstępnemu buforowaniu, wstrzykiwaniu lub buforowaniu, które ma miejsce po wyszukiwaniu. - Śledzenie stanu: w tym kroku zdarzenia są konwertowane na liczniki. Na przykład zdarzenia zmiany stanu można przekształcić w liczniki śledzące ilość czasu spędzonego w poszczególnych stanach odtwarzania. W efekcie powstaje podstawowy zestaw wartości danych statystycznych dotyczących pojedynczego odtworzenia.
- Agregacja: na tym etapie połączono dane analityczne z wielu odtworzeń. Zwykle odbywa się to przez zsumowanie liczników.
- Obliczanie danych podsumowujących: wiele najbardziej przydatnych danych to te, które obliczają średnie wartości lub łączą podstawowe wartości danych statystycznych na inne sposoby. Podsumowanie można wyliczyć dla pojedynczych lub wielu odtworzeń.
- Interpretacja zdarzeń: aby były przydatne do celów analitycznych, zdarzenia muszą być interpretowane w kontekście pojedynczego odtworzenia. Na przykład nieprzetworzone zdarzenie zmiany stanu odtwarzacza na
Zbieranie zdarzeń za pomocą AnalyticsListener
Nieprzetworzone zdarzenia odtwarzania z odtwarzacza są zgłaszane do implementacji AnalyticsListener
. Możesz łatwo dodać własny detektor i zastąpić tylko te metody, które Cię interesują:
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) {} });
Element EventTime
przekazywany do każdego wywołania zwrotnego wiąże zdarzenie z elementem multimedialnym na playliście, a także metadane pozycji odtwarzania i sygnatury czasowej:
realtimeMs
: zegar zegarowy wydarzenia.timeline
,windowIndex
imediaPeriodId
: definiuje playlistę i jej element, do którego należy zdarzenie.mediaPeriodId
zawiera opcjonalne informacje dodatkowe, np. wskazujące, czy zdarzenie należy do reklamy w elemencie.eventPlaybackPositionMs
: pozycja odtwarzania w elemencie, w którym wystąpiło zdarzenie.currentTimeline
,currentWindowIndex
,currentMediaPeriodId
icurrentPlaybackPositionMs
: jak wyżej, ale dotyczy aktualnie odtwarzanego elementu. Aktualnie odtwarzany element może się różnić od elementu, do którego należy zdarzenie, np. jeśli zdarzenie odpowiada wstępnemu buforowaniu następnego elementu, który ma zostać odtworzony.
Przetwarzanie zdarzeń za pomocą PlaybackStatsListener
PlaybackStatsListener
to element typu AnalyticsListener
, który implementuje przetwarzanie zdarzeń na urządzeniu. Oblicza wartość PlaybackStats
, korzystając z liczników i danych pochodnych, takich jak:
- Dane podsumowania, np. łączny czas odtwarzania.
- Dane dotyczące jakości odtwarzania adaptacyjnego, np. średnia rozdzielczość wideo.
- danych o jakości renderowania, np. liczbie pominiętych klatek;
- Wskaźniki wykorzystania zasobów, na przykład liczba bajtów odczytanych przez sieć.
Pełną listę dostępnych liczb i danych pochodnych znajdziesz w dokumencie Java PlaybackStats
.
PlaybackStatsListener
oblicza osobne wartości PlaybackStats
dla każdego elementu multimedialnego na playliście oraz dla każdej reklamy po stronie klienta wstawionej do tych elementów. Możesz podać wywołanie zwrotne do PlaybackStatsListener
, które informuje o zakończonych odtworzeniach, i użycie EventTime
przekazane do wywołania zwrotnego, aby określić, które odtwarzanie się zakończyło. Możesz zagregować dane statystyczne dla wielu odtworzeń. W każdej chwili możesz też wysłać zapytanie do PlaybackStats
dotyczące bieżącej sesji odtwarzania, korzystając z metody 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. }));
Konstruktor PlaybackStatsListener
umożliwia zachowanie pełnej historii przetworzonych zdarzeń. Pamiętaj, że w zależności od długości odtwarzania i liczby zdarzeń może to spowodować zużycie pamięci. Dlatego też warto włączyć ją tylko wtedy, gdy potrzebujesz dostępu do pełnej historii przetworzonych zdarzeń, a nie tylko do ostatecznych danych analitycznych.
Pamiętaj, że PlaybackStats
używa rozszerzonego zestawu stanów, aby pokazywać nie tylko stan multimediów, ale także zamiar odtwarzania treści i bardziej szczegółowe informacje, np. o przyczynach przerwania lub zakończenia odtwarzania:
Stan odtwarzania | Zamiar użytkownika | Brak zamiaru gry |
---|---|---|
Przed rozpoczęciem odtwarzania | JOINING_FOREGROUND |
NOT_STARTED , JOINING_BACKGROUND |
Aktywne odtwarzanie | PLAYING |
|
Przerwane odtwarzanie | BUFFERING , SEEKING |
PAUSED , PAUSED_BUFFERING , SUPPRESSED , SUPPRESSED_BUFFERING , INTERRUPTED_BY_AD |
Stany zakończenia | ENDED , STOPPED , FAILED , ABANDONED |
Zamiar użytkownika pozwala odróżnić czas oczekiwania na kontynuację odtwarzania od czasu pasywnego oczekiwania. Na przykład PlaybackStats.getTotalWaitTimeMs
zwraca łączny czas podany w stanach JOINING_FOREGROUND
, BUFFERING
i SEEKING
, ale nie zwraca czasu wstrzymania odtwarzania. Analogicznie PlaybackStats.getTotalPlayAndWaitTimeMs
zwraca łączny czas, jeśli użytkownik zamierza grać w grze, czyli łączny czas oczekiwania i łączny czas w stanie PLAYING
.
Przetworzone i zinterpretowane zdarzenia
Aby rejestrować przetworzone i interpretowane zdarzenia, użyj funkcji PlaybackStatsListener
z ustawieniem keepHistory=true
. Powstały w ten sposób PlaybackStats
będzie zawierał te listy zdarzeń:
playbackStateHistory
: uporządkowana lista stanów rozszerzonego odtwarzania –EventTime
, od której zaczęły obowiązywać. Możesz też użyć funkcjiPlaybackStats.getPlaybackStateAtTime
, aby sprawdzić stan o danej godzinie zegara.mediaTimeHistory
: historia par czasu zegara i czasu multimediów, co pozwala zrekonstruować, które fragmenty multimediów były w danym momencie odtwarzane. Możesz też użyć funkcjiPlaybackStats.getMediaTimeMsAtRealtimeMs
, aby sprawdzić pozycję odtwarzania o danej godzinie.videoFormatHistory
iaudioFormatHistory
: uporządkowane listy formatów wideo i audio używanych podczas odtwarzania za pomocą właściwościEventTime
, od których zaczęły być używane.fatalErrorHistory
inonFatalErrorHistory
: uporządkowane listy błędów krytycznych i niekrytycznych (EventTime
), w których wystąpiły. Błędy krytyczne to te, które zakończyły odtwarzanie, natomiast błędy niekrytyczne mogły zostać naprawione.
Dane analityczne dotyczące pojedynczego odtworzenia
Jeśli używasz PlaybackStatsListener
, te dane są zbierane automatycznie, nawet jeśli używasz keepHistory=false
. Ostateczne wartości to pola publiczne, które można znaleźć w pliku Javadoc PlaybackStats
, i czasy trwania stanu odtwarzania zwracane przez parametr getPlaybackStateDurationMs
. Dla wygody podajemy też metody takie jak getTotalPlayTimeMs
i getTotalWaitTimeMs
, które zwracają czas trwania określonych kombinacji stanów odtwarzania.
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);
Zbiorcze dane analityczne dotyczące wielu odtworzeń
Możesz połączyć ze sobą kilka elementów PlaybackStats
, wywołując metodę PlaybackStats.merge
. Powstały w ten sposób PlaybackStats
będzie zawierał dane zbiorcze o wszystkich scalonych odtworzeniach. Nie będzie on zawierać historii poszczególnych zdarzeń odtwarzania, ponieważ nie można ich gromadzić.
PlaybackStatsListener.getCombinedPlaybackStats
może posłużyć do uzyskania zagregowanego podglądu wszystkich danych analitycznych zgromadzonych w okresie objętym PlaybackStatsListener
.
Obliczone dane podsumowujące
Oprócz podstawowych danych analitycznych PlaybackStats
udostępnia wiele metod obliczania danych podsumowujących.
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());
Tematy zaawansowane
Powiązanie danych analitycznych z metadanymi odtwarzania
Jeśli zbierasz dane statystyczne dotyczące poszczególnych odtworzeń, możesz je powiązać z metadanymi dotyczącymi odtwarzanych multimediów.
Zalecamy ustawienie metadanych specyficznych dla multimediów za pomocą parametru MediaItem.Builder.setTag
.
Tag multimediów jest częścią zbioru danych EventTime
raportowanych dla nieprzetworzonych zdarzeń i po zakończeniu PlaybackStats
, więc można go łatwo pobrać przy obsłudze odpowiednich danych analitycznych:
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. });
Raportowanie niestandardowych zdarzeń Analytics
Jeśli musisz dodać do danych Analytics zdarzenia niestandardowe, musisz je zapisać w swojej strukturze danych, a potem połączyć z raportowanymi zdarzeniami typu PlaybackStats
. Jeśli to pomoże, możesz rozszerzyć zakres DefaultAnalyticsCollector
, aby móc generować EventTime
instancje zdarzeń niestandardowych i wysyłać je do już zarejestrowanych detektorów, jak pokazano w poniższym przykładzie.
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();