Analytics

ExoPlayer unterstützt eine Vielzahl von Anforderungen an die Wiedergabeanalyse. Letztendlich geht es bei der Analyse um das Sammeln, Interpretieren, Aggregieren und Zusammenfassen von Daten aus Wiedergaben. Diese Daten können entweder auf dem Gerät verwendet werden – z. B. für Protokollierung, Fehlerbehebung oder zur Entscheidungsfindung bei zukünftigen Wiedergaben – oder an einen Server gesendet werden, um Wiedergaben auf allen Geräten zu überwachen.

In einem Analysesystem müssen in der Regel zuerst Ereignisse erfasst und dann weiterverarbeitet werden, damit sie aussagekräftig sind:

  • Ereignisabfrage: Dazu können Sie eine AnalyticsListener in einer ExoPlayer-Instanz registrieren. Registrierte Analyse-Listener erhalten Ereignisse, die während der Nutzung des Players auftreten. Jedes Ereignis ist mit dem entsprechenden Medienelement in der Playlist sowie den Metadaten zur Wiedergabeposition und zum Zeitstempel verknüpft.
  • Ereignisverarbeitung: Einige Analysesysteme laden Rohereignisse auf einen Server hoch. Die gesamte Ereignisverarbeitung erfolgt dann serverseitig. Es ist auch möglich, Ereignisse auf dem Gerät zu verarbeiten. Das kann einfacher sein oder die Menge der hochzuladenden Informationen reduzieren. ExoPlayer bietet PlaybackStatsListener, mit dem Sie die folgenden Verarbeitungsschritte ausführen können:
    1. Ereignisinterpretation: Damit Ereignisse für Analysen verwendet werden können, müssen sie im Kontext einer einzelnen Wiedergabe interpretiert werden. Das Rohereignis einer Spielerstatusänderung zu STATE_BUFFERING kann beispielsweise der anfänglichen Pufferung, einer erneuten Pufferung oder der Pufferung nach einem Suchvorgang entsprechen.
    2. Status-Tracking: In diesem Schritt werden Ereignisse in Zähler umgewandelt. So können Statusänderungsereignisse beispielsweise in Zähler umgewandelt werden, mit denen erfasst wird, wie viel Zeit in jedem Wiedergabestatus verbracht wird. Das Ergebnis ist ein grundlegender Satz von Analysedatenwerten für eine einzelne Wiedergabe.
    3. Aggregation: In diesem Schritt werden die Analysedaten mehrerer Wiedergaben zusammengeführt, in der Regel durch Addition der Zähler.
    4. Berechnung von Zusammenfassungsmesswerten: Viele der nützlichsten Messwerte sind solche, mit denen Durchschnittswerte berechnet oder die grundlegenden Analysedaten auf andere Weise kombiniert werden. Zusammenfassungsmesswerte können für einzelne oder mehrere Wiedergaben berechnet werden.

Ereigniserhebung mit AnalyticsListener

Die vom Player wiedergegebenen Rohdaten werden an AnalyticsListener-Implementierungen gemeldet. Sie können ganz einfach einen eigenen Listener hinzufügen und nur die Methoden überschreiben, die Sie interessieren:

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) {}
    });

Über EventTime, das an jeden Callback übergeben wird, wird das Ereignis mit einem Medienelement in der Playlist sowie mit Wiedergabeposition und Zeitstempelmetadaten verknüpft:

  • realtimeMs: Die Uhrzeit des Ereignisses.
  • timeline, windowIndex und mediaPeriodId: Hiermit werden die Playlist und das Element in der Playlist definiert, zu dem das Ereignis gehört. mediaPeriodId enthält optionale zusätzliche Informationen, z. B. ob das Ereignis zu einer Anzeige im Element gehört.
  • eventPlaybackPositionMs: Die Wiedergabeposition im Artikel, an der das Ereignis aufgetreten ist.
  • currentTimeline, currentWindowIndex, currentMediaPeriodId und currentPlaybackPositionMs: Wie oben, aber für das gerade wiedergegebene Element. Das aktuell wiedergegebene Element kann sich von dem Element unterscheiden, zu dem das Ereignis gehört, z. B. wenn das Ereignis der Vorabpufferung des nächsten wiedergegebenen Elements entspricht.

Ereignisverarbeitung mit PlaybackStatsListener

PlaybackStatsListener ist ein AnalyticsListener, der die Ereignisverarbeitung auf dem Gerät implementiert. PlaybackStats wird mit Zählern und abgeleiteten Messwerten berechnet, darunter:

  • Zusammenfassungsmesswerte, z. B. die Gesamtwiedergabezeit.
  • Messwerte zur adaptiven Wiedergabequalität, z. B. die durchschnittliche Videoauflösung.
  • Rendering-Qualitätsmesswerte, z. B. die Rate der ausgelassenen Frames.
  • Messwerte zur Ressourcennutzung, z. B. die Anzahl der über das Netzwerk gelesenen Byte.

Eine vollständige Liste der verfügbaren Zählungen und abgeleiteten Messwerte finden Sie in der PlaybackStatsJavadoc-Anleitung.

PlaybackStatsListener berechnet separate PlaybackStats für jedes Medienelement in der Playlist und jede clientseitige Anzeige, die in diese Elemente eingefügt wird. Du kannst einen Callback für PlaybackStatsListener bereitstellen, um über beendete Wiedergaben informiert zu werden. Mit dem EventTime, das an den Callback übergeben wurde, kannst du feststellen, welche Wiedergabe beendet ist. Du kannst die Analysedaten für mehrere Wiedergaben zusammenfassen. Es ist auch möglich, den PlaybackStats für die aktuelle Wiedergabesitzung jederzeit mit PlaybackStatsListener.getPlaybackStats() abzufragen.

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.
        }));

Der Konstruktor von PlaybackStatsListener bietet die Möglichkeit, den vollständigen Verlauf der verarbeiteten Ereignisse beizubehalten. Je nach Länge der Wiedergabe und Anzahl der Ereignisse kann dies zu einem unbekannten Arbeitsspeicheraufwand führen. Sie sollten sie daher nur aktivieren, wenn Sie Zugriff auf den vollständigen Verlauf der verarbeiteten Ereignisse und nicht nur auf die endgültigen Analysedaten benötigen.

Hinweis: Bei PlaybackStats wird eine erweiterte Reihe von Status verwendet, um nicht nur den Status der Medien, sondern auch die Wiedergabeabsicht des Nutzers und detailliertere Informationen wie den Grund für die Unterbrechung oder Beendigung der Wiedergabe anzugeben:

Wiedergabestatus Spielabsicht der Nutzer Keine Absicht, zu spielen
Vor der Wiedergabe JOINING_FOREGROUND NOT_STARTED, JOINING_BACKGROUND
Aktive Wiedergabe PLAYING
Unterbrochene Wiedergabe BUFFERING, SEEKING PAUSED, PAUSED_BUFFERING, SUPPRESSED, SUPPRESSED_BUFFERING, INTERRUPTED_BY_AD
Endzustände ENDED, STOPPED, FAILED, ABANDONED

Die Wiedergabeabsicht des Nutzers ist wichtig, um Zeiten zu unterscheiden, in denen der Nutzer aktiv auf die Fortsetzung der Wiedergabe gewartet hat, von passiven Wartezeiten. PlaybackStats.getTotalWaitTimeMs gibt beispielsweise die Gesamtzeit zurück, die im Status JOINING_FOREGROUND, BUFFERING und SEEKING verbracht wurde, aber nicht die Zeit, in der die Wiedergabe pausiert wurde. In ähnlicher Weise gibt PlaybackStats.getTotalPlayAndWaitTimeMs die Gesamtzeit mit der Nutzerabsicht zurück, also die gesamte aktive Wartezeit und die Gesamtzeit, die im Status PLAYING verbracht wurde.

Verarbeitete und interpretierte Ereignisse

Sie können verarbeitete und interpretierte Ereignisse mit PlaybackStatsListener und keepHistory=true erfassen. Die resultierende PlaybackStats enthält die folgenden Ereignislisten:

  • playbackStateHistory: Eine sortierte Liste der erweiterten Wiedergabestatus mit dem EventTime, zu dem sie angewendet wurden. Sie können auch PlaybackStats.getPlaybackStateAtTime verwenden, um den Status zu einem bestimmten Zeitpunkt abzurufen.
  • mediaTimeHistory: Ein Verlauf von Zeitpaaren aus Echtzeit und Medienzeit, mit dem Sie nachvollziehen können, welche Teile der Medien zu welcher Zeit abgespielt wurden. Mit PlaybackStats.getMediaTimeMsAtRealtimeMs kannst du auch die Wiedergabeposition zu einer bestimmten Uhrzeit abrufen.
  • videoFormatHistory und audioFormatHistory: Sortierte Listen der Video- und Audioformate, die während der Wiedergabe verwendet wurden, mit dem EventTime, ab dem sie verwendet wurden.
  • fatalErrorHistory und nonFatalErrorHistory: sortierte Listen schwerwiegender und nicht schwerwiegender Fehler mit dem EventTime, zu dem sie aufgetreten sind. Schwerwiegende Fehler führen zur Beendigung der Wiedergabe, während nicht schwerwiegende Fehler möglicherweise wiederhergestellt werden können.

Analysedaten für die Wiedergabe einzelner Videos

Diese Daten werden automatisch erhoben, wenn Sie PlaybackStatsListener verwenden, auch wenn Sie keepHistory=false verwenden. Die endgültigen Werte sind die öffentlichen Felder, die du im PlaybackStats-Javadoc findest, und die von getPlaybackStateDurationMs zurückgegebene Dauer des Wiedergabestatus. Für den schnellen Zugriff gibt es auch Methoden wie getTotalPlayTimeMs und getTotalWaitTimeMs, mit denen die Dauer bestimmter Kombinationen von Wiedergabestatus zurückgegeben wird.

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);

Analysedaten mehrerer Wiedergaben aggregieren

Sie können mehrere PlaybackStats kombinieren, indem Sie PlaybackStats.merge aufrufen. Die resultierende PlaybackStats enthält die aggregierten Daten aller zusammengeführten Wiedergaben. Der Verlauf enthält jedoch nicht die einzelnen Wiedergabeereignisse, da diese nicht zusammengefasst werden können.

Mit PlaybackStatsListener.getCombinedPlaybackStats können Sie eine aggregierte Ansicht aller Analysedaten abrufen, die während der Lebensdauer einer PlaybackStatsListener erfasst wurden.

Berechnete zusammenfassende Messwerte

Neben den grundlegenden Analysedaten bietet PlaybackStats viele Methoden zum Berechnen von Zusammenfassungsmesswerten.

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());

Erweiterte Themen

Analysedaten mit Wiedergabemetadaten verknüpfen

Wenn du Analysedaten für einzelne Wiedergaben erhebst, kannst du die Analysedaten für die Wiedergabe mit Metadaten zu den wiedergegebenen Medien verknüpfen.

Wir empfehlen, medienspezifische Metadaten mit MediaItem.Builder.setTag festzulegen. Das Media-Tag ist Teil der EventTime, die für Rohereignisse und für abgeschlossene PlaybackStats erfasst werden. Es kann daher beim Umgang mit den entsprechenden Analysedaten ganz einfach abgerufen werden:

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.
    });

Berichte zu benutzerdefinierten Analytics-Ereignissen

Falls Sie den Analysedaten benutzerdefinierte Ereignisse hinzufügen müssen, müssen Sie diese Ereignisse in Ihrer eigenen Datenstruktur speichern und später mit dem gemeldeten PlaybackStats kombinieren. Sie können DefaultAnalyticsCollector erweitern, um EventTime-Instanzen für Ihre benutzerdefinierten Ereignisse zu generieren und an die bereits registrierten Listener zu senden, wie im folgenden Beispiel gezeigt.

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();