Analytics

ExoPlayer mendukung berbagai kebutuhan analisis pemutaran. Pada akhirnya, analitik adalah tentang mengumpulkan, menafsirkan, menggabungkan, dan meringkas data dari pemutaran. Data ini dapat digunakan di perangkat—misalnya untuk pembuatan log, proses debug, atau untuk menginformasikan keputusan pemutaran di masa mendatang—atau dilaporkan ke server untuk memantau pemutaran di semua perangkat.

Sistem analisis biasanya perlu mengumpulkan peristiwa terlebih dahulu, kemudian memprosesnya lebih lanjut untuk membuatnya bermakna:

  • Pengumpulan acara: Hal ini dapat dilakukan dengan mendaftarkan AnalyticsListener di ExoPlayer di instance Compute Engine. Pemroses analisis yang terdaftar akan menerima peristiwa saat terjadi selama penggunaan pemutar. Setiap peristiwa dikaitkan dengan media yang sesuai dalam playlist, serta metadata posisi pemutaran dan stempel waktu.
  • Pemrosesan peristiwa: Beberapa sistem analisis mengupload peristiwa mentah ke server, dengan semua peristiwa dan pemrosesan yang dilakukan di sisi server. Dimungkinkan juga untuk memproses peristiwa di perangkat, dan melakukan hal itu mungkin lebih sederhana atau mengurangi jumlah informasi yang perlu diupload. ExoPlayer menyediakan PlaybackStatsListener, yang memungkinkan Anda melakukan langkah pemrosesan berikut:
    1. Penafsiran peristiwa: Agar berguna untuk tujuan analisis, peristiwa memerlukan untuk ditafsirkan dalam konteks pemutaran tunggal. Misalnya data mentah peristiwa perubahan status pemutar menjadi STATE_BUFFERING mungkin sesuai dengan {i>buffering<i} awal, {i>rebuffer<i}, atau {i>buffer <i}yang terjadi setelah pencarian.
    2. Pelacakan status: Langkah ini mengonversi peristiwa menjadi penghitung. Misalnya, peristiwa perubahan status dapat dikonversi ke penghitung yang melacak berapa banyak waktu yang dihabiskan di setiap status pemutaran. Hasilnya adalah kumpulan dasar data analitik untuk satu pemutaran.
    3. Agregasi: Langkah ini menggabungkan data analisis di berbagai pemutaran, biasanya dengan menambahkan penghitung.
    4. Penghitungan metrik ringkasan: Banyak dari metrik yang paling berguna adalah mereka yang menghitung rata-rata atau menggabungkan nilai data analitik dasar dalam dengan cara lain. Metrik ringkasan dapat dihitung untuk satu atau beberapa pemutaran.

Pengumpulan peristiwa dengan AnalyticsListener

Peristiwa pemutaran mentah dari pemutar dilaporkan ke AnalyticsListener implementasi yang tepat. Anda bisa dengan mudah menambahkan pemroses Anda sendiri dan hanya mengganti metode yang Anda minati:

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

EventTime yang diteruskan ke setiap callback akan mengaitkan peristiwa tersebut ke media item dalam playlist, serta metadata posisi pemutaran dan stempel waktu:

  • realtimeMs: Waktu jam dinding acara.
  • timeline, windowIndex, dan mediaPeriodId: Menentukan playlist dan item dalam playlist yang memiliki peristiwa tersebut. mediaPeriodId berisi informasi tambahan opsional, misalnya menunjukkan apakah pada iklan dalam item.
  • eventPlaybackPositionMs: Posisi pemutaran item saat peristiwa terjadi.
  • currentTimeline, currentWindowIndex, currentMediaPeriodId, dan currentPlaybackPositionMs: Seperti di atas, tetapi untuk item yang sedang diputar. Tujuan yang sedang diputar mungkin berbeda dari item dengan peristiwa misalnya, jika peristiwa sesuai dengan pra-buffering yang akan diputar.

Pemrosesan peristiwa dengan PlaybackStatsListener

PlaybackStatsListener adalah AnalyticsListener yang mengimplementasikan di perangkat peristiwa. Compute Engine menghitung PlaybackStats, dengan penghitung dan turunan metrik, meliputi:

  • Ringkasan metrik, misalnya total waktu pemutaran.
  • Metrik kualitas pemutaran adaptif, misalnya rata-rata resolusi video.
  • Melakukan rendering metrik kualitas, misalnya kecepatan penurunan frame.
  • Metrik penggunaan resource, misalnya jumlah byte yang dibaca melalui jaringan.

Anda akan menemukan daftar lengkap jumlah yang tersedia dan metrik turunan di PlaybackStats Javadoc.

PlaybackStatsListener menghitung PlaybackStats terpisah untuk setiap item media dalam playlist, dan juga setiap iklan dari sisi klien yang disisipkan ke dalam item ini. Anda dapat memberikan callback ke PlaybackStatsListener untuk diberi tahu tentang penyelesaian pemutaran, dan menggunakan EventTime yang diteruskan ke callback untuk mengidentifikasi pemutaran selesai. Anda dapat menggabungkan data analisis untuk beberapa pemutaran. Anda juga dapat membuat kueri PlaybackStats untuk kapan pun, dengan menggunakan 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 memberikan opsi untuk mempertahankan nilai penuh histori peristiwa yang diproses. Perlu diperhatikan bahwa tindakan ini dapat menimbulkan overhead memori yang tidak diketahui tergantung pada durasi pemutaran dan jumlah peristiwa. Oleh karena itu, Anda sebaiknya hanya aktifkan jika Anda memerlukan akses ke histori lengkap dari proses peristiwa, bukan hanya pada data analisis akhir.

Perhatikan bahwa PlaybackStats menggunakan kumpulan status yang diperluas untuk menunjukkan tidak hanya keadaan media, tetapi juga niat pengguna untuk memutar media, informasi seperti alasan pemutaran terganggu atau berakhir:

Status pemutaran Keinginan pengguna untuk bermain Tidak ada niat untuk bermain
Sebelum pemutaran JOINING_FOREGROUND NOT_STARTED, JOINING_BACKGROUND
Pemutaran aktif PLAYING
Pemutaran terganggu BUFFERING, SEEKING PAUSED, PAUSED_BUFFERING, SUPPRESSED, SUPPRESSED_BUFFERING, INTERRUPTED_BY_AD
Status akhir ENDED, STOPPED, FAILED, ABANDONED

Niat pengguna untuk bermain penting untuk membedakan waktu ketika pengguna secara aktif menunggu pemutaran dilanjutkan dari waktu tunggu pasif. Misalnya, PlaybackStats.getTotalWaitTimeMs menampilkan total waktu yang dihabiskan di Status JOINING_FOREGROUND, BUFFERING, dan SEEKING, tetapi bukan waktu saat pemutaran dijeda. Demikian pula, PlaybackStats.getTotalPlayAndWaitTimeMs akan mengembalikan total waktu dengan maksud pengguna untuk bermain, yaitu total waktu waktu tunggu dan total waktu yang dihabiskan dalam status PLAYING.

Peristiwa yang diproses dan ditafsirkan

Anda dapat merekam peristiwa yang diproses dan ditafsirkan dengan menggunakan PlaybackStatsListener dengan keepHistory=true. PlaybackStats yang dihasilkan akan berisi daftar peristiwa berikut:

  • playbackStateHistory: Daftar urut status pemutaran yang diperpanjang dengan EventTime tempat mereka mulai mendaftar. Anda juga dapat menggunakan PlaybackStats.getPlaybackStateAtTime untuk melihat status pada dinding tertentu waktu.
  • mediaTimeHistory: Histori waktu jam dinding dan pasangan waktu media yang memungkinkan Anda untuk merekonstruksi bagian media mana yang diputar pada saat itu. Anda dapat gunakan juga PlaybackStats.getMediaTimeMsAtRealtimeMs untuk mencari pemutaran pada waktu dinding tertentu.
  • videoFormatHistory dan audioFormatHistory: Daftar video dan urutan yang diurutkan format audio yang digunakan selama pemutaran dengan EventTime yang dimulai dapat digunakan.
  • fatalErrorHistory dan nonFatalErrorHistory: Daftar urutan error fatal dan error non-fatal dengan EventTime saat error tersebut terjadi. Error fatal adalah yang mengakhiri pemutaran, sedangkan {i>error<i} non-fatal dapat dipulihkan.

Data analisis pemutaran tunggal

Data ini otomatis dikumpulkan jika Anda menggunakan PlaybackStatsListener, bahkan dengan keepHistory=false. Nilai akhir adalah {i>field <i} publik yang bisa Anda temukan di PlaybackStats Javadoc dan durasi status pemutaran ditampilkan oleh getPlaybackStateDurationMs. Untuk memudahkan, Anda juga akan menemukan metode seperti getTotalPlayTimeMs dan getTotalWaitTimeMs yang menampilkan dari kombinasi status pemutaran tertentu.

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

Data analisis gabungan dari beberapa pemutaran

Anda dapat menggabungkan beberapa PlaybackStats bersama-sama dengan memanggil PlaybackStats.merge. PlaybackStats yang dihasilkan akan berisi kolom data dari semua pemutaran yang digabungkan. Perhatikan bahwa log tidak akan berisi histori setiap peristiwa pemutaran, karena peristiwa ini tidak dapat digabungkan.

PlaybackStatsListener.getCombinedPlaybackStats dapat digunakan untuk mendapatkan tampilan gabungan dari semua data analisis yang dikumpulkan selama masa aktif PlaybackStatsListener.

Metrik ringkasan yang dihitung

Selain data analisis dasar, PlaybackStats menyediakan banyak metode untuk menghitung metrik ringkasan.

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

Topik lanjutan

Mengaitkan data analisis dengan metadata pemutaran

Saat mengumpulkan data analisis untuk masing-masing pemutaran, sebaiknya mengaitkan data analisis pemutaran dengan metadata tentang media yang yang dimainkan.

Sebaiknya setel metadata khusus media dengan MediaItem.Builder.setTag. Tag media adalah bagian dari EventTime yang dilaporkan untuk peristiwa mentah dan jika PlaybackStats telah selesai, sehingga dapat dengan mudah diambil saat menangani data analisis yang sesuai:

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

Melaporkan peristiwa analisis kustom

Jika perlu menambahkan peristiwa kustom ke data analisis, Anda harus menyimpan peristiwa ini dalam struktur data Anda sendiri dan menggabungkannya dengan laporan PlaybackStats setelahnya. Jika membantu, Anda dapat memperluas DefaultAnalyticsCollector Anda dapat membuat instance EventTime untuk peristiwa kustom Anda dan mengirimkan ke pemroses yang sudah terdaftar seperti ditunjukkan dalam contoh berikut.

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