Analytics

ExoPlayer unterstützt eine Vielzahl von Anforderungen an Wiedergabeanalysen. Letztendlich geht es bei Analysen um das Erfassen, 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 für zukünftige Wiedergabeentscheidungen – oder an einen Server gemeldet werden, um Wiedergaben auf allen Geräten zu überwachen.

Ein Analysesystem muss normalerweise zuerst Ereignisse erfassen und diese dann weiter verarbeiten, um sie aussagekräftig zu machen:

  • Ereigniserfassung: Dazu können Sie ein AnalyticsListener in einer ExoPlayer-Instanz registrieren. Registrierte Analyse-Listener empfangen Ereignisse, wenn sie während der Nutzung des Players auftreten. Jedes Ereignis ist mit dem entsprechenden Medienelement in der Playlist sowie mit den Metadaten der Wiedergabeposition und des Zeitstempels verknüpft.
  • Ereignisverarbeitung: Einige Analysesysteme laden Rohereignisse auf einen Server hoch, wobei die gesamte Ereignisverarbeitung serverseitig erfolgt. Es ist auch möglich, Ereignisse auf dem Gerät zu verarbeiten, was einfacher ist oder die Menge der Informationen, die hochgeladen werden muss, reduziert. ExoPlayer stellt PlaybackStatsListener bereit, mit dem Sie die folgenden Verarbeitungsschritte ausführen können:
    1. Ereignisinterpretation: Ereignisse müssen für Analysezwecke im Kontext einer einzelnen Wiedergabe interpretiert werden. Beispielsweise kann das Rohereignis einer Player-Statusänderung in STATE_BUFFERING einer anfänglichen Pufferung, einer erneuten Zwischenspeicherung oder einer Zwischenspeicherung nach einer Suche entsprechen.
    2. Status-Tracking: In diesem Schritt werden Ereignisse in Zähler umgewandelt. Beispielsweise können Statusänderungsereignisse in Zähler umgewandelt werden, die erfassen, 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 aus mehreren Wiedergaben kombiniert. In der Regel werden dabei Zähler addiert.
    4. Berechnung der zusammenfassenden Messwerte: Zu den nützlichsten Messwerten zählen die, mit denen Durchschnittswerte berechnet oder die grundlegenden Analysedatenwerte auf andere Weise kombiniert werden. Zusammenfassende Messwerte können für einzelne oder mehrere Wiedergaben berechnet werden.

Ereigniserfassung mit AnalyticsListener

Unformatierte Wiedergabeereignisse aus dem Player werden an AnalyticsListener-Implementierungen gemeldet. Sie können einfach Ihren eigenen Listener hinzufügen und nur die für Sie relevanten Methoden überschreiben:

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

Die EventTime, die an jeden Callback übergeben wird, verknüpft das Ereignis mit einem Medienelement in der Playlist sowie mit den Metadaten der Wiedergabeposition und des Zeitstempels:

  • realtimeMs: Die tatsächlich verstrichene Zeit der Veranstaltung.
  • timeline, windowIndex und mediaPeriodId: Definiert die Playlist und das Element in der Playlist, zu der das Ereignis gehört. mediaPeriodId enthält optionale zusätzliche Informationen, z. B. gibt an, ob das Ereignis zu einer Anzeige im Element gehört.
  • eventPlaybackPositionMs: Die Wiedergabeposition im Element, an der das Ereignis aufgetreten ist.
  • currentTimeline, currentWindowIndex, currentMediaPeriodId und currentPlaybackPositionMs: wie oben, aber für das aktuell wiedergegebene Element. Das aktuell wiedergegebene Element kann sich von dem Element unterscheiden, zu dem das Ereignis gehört, z. B. wenn das Ereignis der Vorpufferung des nächsten abzuspielenden Elements entspricht.

Ereignisverarbeitung mit WiedergabeStatsListener

PlaybackStatsListener ist ein AnalyticsListener, der die On-Device-Ereignisverarbeitung implementiert. Dabei wird PlaybackStats mit Zählern und abgeleiteten Messwerten berechnet, darunter:

  • Zusammengefasste Messwerte, z. B. die gesamte Wiedergabezeit
  • Messwerte zur adaptiven Wiedergabequalität, z. B. die durchschnittliche Videoauflösung.
  • Qualitätsmesswerte rendern, z. B. die Rate der abgebrochenen Frames
  • Messwerte zur Ressourcennutzung, z. B. die Anzahl der im Netzwerk gelesenen Byte.

Eine vollständige Liste der verfügbaren Anzahlen und abgeleiteten Messwerte finden Sie im PlaybackStats-Javadoc.

PlaybackStatsListener berechnet separate PlaybackStats für jedes Medienelement in der Playlist sowie für jede clientseitige Anzeige, die innerhalb dieser Elemente eingefügt wird. Du kannst einen Callback für PlaybackStatsListener bereitstellen, um über abgeschlossene Wiedergaben informiert zu werden, und die an den Callback übergebene EventTime verwenden, um anzugeben, welche Wiedergabe beendet wurde. Es ist möglich, die Analysedaten für mehrere Wiedergaben zusammenfassen. Du kannst auch jederzeit mit PlaybackStatsListener.getPlaybackStats() den PlaybackStats nach der aktuellen Wiedergabesitzung abfragen.

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 gesamten Verlauf der verarbeiteten Ereignisse beizubehalten. Hinweis: Je nach Länge der Wiedergabe und Anzahl der Ereignisse kann dadurch ein unbekannter Arbeitsspeicher-Overhead entstehen. Daher sollten Sie sie nur aktivieren, wenn Sie Zugriff auf den gesamten Verlauf der verarbeiteten Ereignisse und nicht nur auf die endgültigen Analysedaten benötigen.

Beachten Sie, dass PlaybackStats einen erweiterten Satz von Status verwendet, um nicht nur den Status der Medien, sondern auch die Absicht des Nutzers zum Abspielen und detailliertere Informationen anzugeben, z. B. warum die Wiedergabe unterbrochen oder beendet wurde:

Wiedergabestatus Spielabsicht des Nutzers Keine Spielabsicht
Vor der Wiedergabe JOINING_FOREGROUND NOT_STARTED, JOINING_BACKGROUND
Aktive Wiedergabe PLAYING
Wiedergabe unterbrochen BUFFERING, SEEKING PAUSED, PAUSED_BUFFERING, SUPPRESSED, SUPPRESSED_BUFFERING, INTERRUPTED_BY_AD
Endzustände ENDED, STOPPED, FAILED, ABANDONED

Die Spielabsicht 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 in den 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 Absicht des Nutzers zurück, d. h. die gesamte aktive Wartezeit und die Gesamtzeit, die im Status PLAYING verbracht wurde.

Verarbeitete und interpretierte Ereignisse

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

  • playbackStateHistory: Eine sortierte Liste erweiterter Wiedergabestatus mit dem EventTime, ab dem sie angewendet wurden. Sie können auch PlaybackStats.getPlaybackStateAtTime verwenden, um den Status zu einer bestimmten tatsächlichen Uhrzeit abzurufen.
  • mediaTimeHistory: Ein Verlauf der Echtzeit- und Medienzeitpaare, mit denen Sie rekonstruieren können, welche Teile der Medien zu welchem Zeitpunkt abgespielt wurden. Du kannst auch PlaybackStats.getMediaTimeMsAtRealtimeMs verwenden, um die Wiedergabeposition zu einer bestimmten tatsächlich verstrichenen Zeit zu ermitteln.
  • videoFormatHistory und audioFormatHistory: geordnete Listen der Video- und Audioformate, die während der Wiedergabe mit dem EventTime verwendet wurden, ab dem sie zu verwenden begannen.
  • fatalErrorHistory und nonFatalErrorHistory: geordnete Listen von schwerwiegenden und nicht schwerwiegenden Fehlern mit der EventTime, an der sie aufgetreten sind. Schwerwiegende Fehler sind Fehler, die die Wiedergabe beendet haben, während nicht schwerwiegende Fehler möglicherweise behoben werden konnten.

Analysedaten für eine einzelne Wiedergabe

Diese Daten werden automatisch erhoben, wenn Sie PlaybackStatsListener verwenden, auch mit keepHistory=false. Die endgültigen Werte sind die öffentlichen Felder, die du im PlaybackStats-Javadoc findest, und die Dauer der Wiedergabestatus, die von getPlaybackStateDurationMs zurückgegeben wird. Der Einfachheit halber findest du hier auch Methoden wie getTotalPlayTimeMs und getTotalWaitTimeMs, die die Dauer bestimmter Wiedergabestatuskombinationen zurückgeben.

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 durch Aufrufen von PlaybackStats.merge kombinieren. Das resultierende PlaybackStats enthält die aggregierten Daten aller zusammengeführten Wiedergaben. Beachte, dass sie nicht den Verlauf einzelner Wiedergabeereignisse enthalten, da diese nicht zusammengefasst werden können.

Mit PlaybackStatsListener.getCombinedPlaybackStats können Sie eine zusammengefasste Ansicht aller Analysedaten abrufen, die während der Laufzeit eines PlaybackStatsListener erfasst wurden.

Berechnete zusammenfassende Messwerte

Zusätzlich zu den grundlegenden Analysedaten bietet PlaybackStats viele Methoden zum Berechnen zusammenfassender Messwerte.

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

Beim Erfassen von Analysedaten für einzelne Wiedergaben empfiehlt es sich, die Wiedergabeanalysedaten mit Metadaten über die wiedergegebenen Medien zu verknüpfen.

Es empfiehlt sich, medienspezifische Metadaten mit MediaItem.Builder.setTag festzulegen. Das Medien-Tag ist Teil des EventTime, der für Rohereignisse gemeldet wird und wenn PlaybackStats abgeschlossen ist, sodass es bei der Verarbeitung der entsprechenden Analysedaten einfach abgerufen werden kann:

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 Analyseereignissen

Falls Sie den Analysedaten benutzerdefinierte Ereignisse hinzufügen möchten, 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();