Analytics

ExoPlayer hỗ trợ nhiều nhu cầu phân tích quá trình phát. Cuối cùng, hoạt động phân tích phục vụ việc thu thập, diễn giải, tổng hợp và tóm tắt dữ liệu từ các lượt phát. Dữ liệu này có thể được sử dụng trên thiết bị (ví dụ: để ghi nhật ký, gỡ lỗi hoặc để thông báo các quyết định phát trong tương lai) hoặc được báo cáo cho máy chủ để theo dõi các lượt phát trên tất cả thiết bị.

Trước tiên, hệ thống phân tích thường cần thu thập các sự kiện, sau đó xử lý thêm các sự kiện đó để các sự kiện đó có ý nghĩa:

  • Thu thập sự kiện: Bạn có thể thực hiện việc này bằng cách đăng ký AnalyticsListener trên một thực thể ExoPlayer. Các trình nghe phân tích đã đăng ký sẽ nhận được sự kiện khi sự kiện đó xảy ra trong quá trình sử dụng trình phát. Mỗi sự kiện được liên kết với mục nội dung nghe nhìn tương ứng trong danh sách phát, cũng như siêu dữ liệu về vị trí phát và dấu thời gian.
  • Xử lý sự kiện: Một số hệ thống phân tích tải các sự kiện thô lên máy chủ, trong đó toàn bộ quá trình xử lý sự kiện được thực hiện ở phía máy chủ. Bạn cũng có thể xử lý các sự kiện trên thiết bị. Việc này có thể đơn giản hơn hoặc giảm lượng thông tin cần tải lên. ExoPlayer cung cấp PlaybackStatsListener, cho phép bạn thực hiện các bước xử lý sau:
    1. Diễn giải sự kiện: Để hữu ích cho mục đích phân tích, các sự kiện cần được diễn giải trong ngữ cảnh của một lần phát. Ví dụ: sự kiện thô của một thay đổi trạng thái trình phát thành STATE_BUFFERING có thể tương ứng với việc lưu vào bộ đệm ban đầu, lưu vào bộ đệm lại hoặc lưu vào bộ đệm xảy ra sau khi tìm kiếm.
    2. Theo dõi trạng thái: Bước này chuyển đổi sự kiện thành bộ đếm. Ví dụ: bạn có thể chuyển đổi các sự kiện thay đổi trạng thái thành bộ đếm theo dõi thời gian dành cho mỗi trạng thái phát. Kết quả là một tập hợp các giá trị dữ liệu phân tích cơ bản cho một lượt phát.
    3. Tổng hợp: Bước này kết hợp dữ liệu phân tích trên nhiều lượt phát, thường là bằng cách cộng các bộ đếm.
    4. Tính toán chỉ số tóm tắt: Nhiều chỉ số hữu ích nhất là những chỉ số tính toán giá trị trung bình hoặc kết hợp các giá trị dữ liệu phân tích cơ bản theo những cách khác. Hệ thống có thể tính các chỉ số tóm tắt cho một hoặc nhiều lượt phát.

Thu thập sự kiện bằng AnalyticsListener

Các sự kiện phát thô từ trình phát được báo cáo cho các hoạt động triển khai AnalyticsListener. Bạn có thể dễ dàng thêm trình nghe của riêng mình và chỉ ghi đè những phương thức mà bạn quan tâm:

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 được truyền đến mỗi lệnh gọi lại sẽ liên kết sự kiện với một mục nội dung đa phương tiện trong danh sách phát, cũng như với siêu dữ liệu về vị trí phát và dấu thời gian:

  • realtimeMs: Thời gian theo đồng hồ treo tường của sự kiện.
  • timeline, windowIndexmediaPeriodId: Xác định danh sách phát và mục trong danh sách phát chứa sự kiện. mediaPeriodId chứa thông tin bổ sung không bắt buộc, ví dụ: cho biết liệu sự kiện có thuộc về một quảng cáo trong mặt hàng hay không.
  • eventPlaybackPositionMs: Vị trí phát trong mục khi sự kiện xảy ra.
  • currentTimeline, currentWindowIndex, currentMediaPeriodIdcurrentPlaybackPositionMs: Tương tự như trên nhưng dành cho mục đang phát. Mục đang phát có thể khác với mục thuộc sự kiện, ví dụ: nếu sự kiện tương ứng với việc lưu vào bộ đệm trước của mục tiếp theo sẽ phát.

Xử lý sự kiện bằng PlaybackStatsListener

PlaybackStatsListener là một AnalyticsListener triển khai quá trình xử lý sự kiện trên thiết bị. Phương thức này tính toán PlaybackStats, với các bộ đếm và chỉ số phái sinh bao gồm:

  • Các chỉ số tóm tắt, ví dụ như tổng thời gian phát.
  • Các chỉ số về chất lượng phát thích ứng, chẳng hạn như độ phân giải video trung bình.
  • Chỉ số chất lượng kết xuất, ví dụ: tỷ lệ khung hình bị bỏ qua.
  • Chỉ số sử dụng tài nguyên, ví dụ như số byte đọc qua mạng.

Bạn sẽ thấy danh sách đầy đủ các số lượng và chỉ số phái sinh có sẵn trong PlaybackStats Javadoc.

PlaybackStatsListener tính toán PlaybackStats riêng cho từng mục nội dung đa phương tiện trong danh sách phát và cả quảng cáo phía máy khách được chèn trong các mục này. Bạn có thể cung cấp lệnh gọi lại cho PlaybackStatsListener để được thông báo về các lần phát đã kết thúc và sử dụng EventTime được truyền đến lệnh gọi lại để xác định lần phát nào đã kết thúc. Bạn có thể tổng hợp dữ liệu phân tích cho nhiều lần phát. Bạn cũng có thể truy vấn PlaybackStats cho phiên phát hiện tại bất cứ lúc nào bằng cách sử dụng 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.
        }));

Hàm khởi tạo của PlaybackStatsListener cung cấp tuỳ chọn giữ lại toàn bộ nhật ký của các sự kiện đã xử lý. Xin lưu ý rằng việc này có thể làm phát sinh mức hao tổn bộ nhớ không xác định, tuỳ thuộc vào thời lượng phát và số lượng sự kiện. Do đó, bạn chỉ nên bật tính năng này nếu cần truy cập vào toàn bộ nhật ký sự kiện đã xử lý, thay vì chỉ truy cập vào dữ liệu phân tích cuối cùng.

Xin lưu ý rằng PlaybackStats sử dụng một tập hợp trạng thái mở rộng để không chỉ cho biết trạng thái của nội dung nghe nhìn mà còn cho biết ý định phát của người dùng và thông tin chi tiết hơn, chẳng hạn như lý do quá trình phát bị gián đoạn hoặc kết thúc:

Trạng thái phát Ý định chơi của người dùng Không có ý định chơi
Trước khi phát JOINING_FOREGROUND NOT_STARTED, JOINING_BACKGROUND
Phát chủ động PLAYING
Phát bị gián đoạn BUFFERING, SEEKING PAUSED, PAUSED_BUFFERING, SUPPRESSED, SUPPRESSED_BUFFERING, INTERRUPTED_BY_AD
Trạng thái kết thúc ENDED, STOPPED, FAILED, ABANDONED

Ý định phát của người dùng là yếu tố quan trọng để phân biệt thời điểm người dùng chủ động chờ tiếp tục phát với thời gian chờ thụ động. Ví dụ: PlaybackStats.getTotalWaitTimeMs trả về tổng thời gian ở trạng thái JOINING_FOREGROUND, BUFFERINGSEEKING, nhưng không phải thời gian tạm dừng phát. Tương tự, PlaybackStats.getTotalPlayAndWaitTimeMs sẽ trả về tổng thời gian người dùng có ý định chơi, tức là tổng thời gian chờ đang hoạt động và tổng thời gian ở trạng thái PLAYING.

Sự kiện đã xử lý và diễn giải

Bạn có thể ghi lại các sự kiện đã xử lý và diễn giải bằng cách sử dụng PlaybackStatsListener với keepHistory=true. PlaybackStats thu được sẽ chứa các danh sách sự kiện sau:

  • playbackStateHistory: Danh sách theo thứ tự các trạng thái phát mở rộng với EventTime mà các trạng thái này bắt đầu áp dụng. Bạn cũng có thể sử dụng PlaybackStats.getPlaybackStateAtTime để tra cứu trạng thái tại một thời điểm đồng hồ thực tế nhất định.
  • mediaTimeHistory: Nhật ký về các cặp thời gian đồng hồ và thời gian phát nội dung nghe nhìn cho phép bạn tái tạo những phần nội dung nghe nhìn đã phát tại thời điểm nào. Bạn cũng có thể sử dụng PlaybackStats.getMediaTimeMsAtRealtimeMs để tra cứu vị trí phát tại một thời gian nhất định theo đồng hồ.
  • videoFormatHistoryaudioFormatHistory: Danh sách thứ tự các định dạng video và âm thanh được sử dụng trong quá trình phát với EventTime tại thời điểm bắt đầu sử dụng.
  • fatalErrorHistorynonFatalErrorHistory: Danh sách theo thứ tự các lỗi nghiêm trọng và lỗi không nghiêm trọng cùng với EventTime nơi các lỗi đó xảy ra. Lỗi nghiêm trọng là những lỗi đã kết thúc quá trình phát, trong khi lỗi không nghiêm trọng có thể khôi phục được.

Dữ liệu phân tích về một lần phát

Dữ liệu này được thu thập tự động nếu bạn sử dụng PlaybackStatsListener, ngay cả với keepHistory=false. Giá trị cuối cùng là các trường công khai mà bạn có thể tìm thấy trong PlaybackStats Javadoc và thời lượng trạng thái phát do getPlaybackStateDurationMs trả về. Để thuận tiện, bạn cũng sẽ thấy các phương thức như getTotalPlayTimeMsgetTotalWaitTimeMs trả về thời lượng của các tổ hợp trạng thái phát cụ thể.

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

Tổng hợp dữ liệu phân tích của nhiều lượt phát

Bạn có thể kết hợp nhiều PlaybackStats với nhau bằng cách gọi PlaybackStats.merge. PlaybackStats thu được sẽ chứa dữ liệu tổng hợp của tất cả các lượt phát được hợp nhất. Xin lưu ý rằng nhật ký này sẽ không chứa nhật ký của từng sự kiện phát, vì các sự kiện này không thể được tổng hợp.

Bạn có thể sử dụng PlaybackStatsListener.getCombinedPlaybackStats để xem tổng hợp tất cả dữ liệu phân tích được thu thập trong vòng đời của PlaybackStatsListener.

Chỉ số tóm tắt được tính toán

Ngoài dữ liệu phân tích cơ bản, PlaybackStats còn cung cấp nhiều phương thức để tính toán các chỉ số tóm tắt.

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

Chủ đề nâng cao

Liên kết dữ liệu phân tích với siêu dữ liệu phát

Khi thu thập dữ liệu phân tích cho từng lượt phát, bạn nên liên kết dữ liệu phân tích lượt phát với siêu dữ liệu về nội dung nghe nhìn đang phát.

Bạn nên đặt siêu dữ liệu dành riêng cho nội dung nghe nhìn bằng MediaItem.Builder.setTag. Thẻ nội dung nghe nhìn là một phần của EventTime được báo cáo cho các sự kiện thô và khi PlaybackStats kết thúc, vì vậy, bạn có thể dễ dàng truy xuất thẻ này khi xử lý dữ liệu phân tích tương ứng:

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

Báo cáo sự kiện phân tích tuỳ chỉnh

Trong trường hợp cần thêm sự kiện tuỳ chỉnh vào dữ liệu phân tích, bạn cần lưu các sự kiện này trong cấu trúc dữ liệu của riêng mình và kết hợp các sự kiện này với PlaybackStats được báo cáo sau. Nếu hữu ích, bạn có thể mở rộng DefaultAnalyticsCollector để có thể tạo các thực thể EventTime cho sự kiện tuỳ chỉnh và gửi các thực thể đó đến trình nghe đã đăng ký như trong ví dụ sau.

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