Analytics

ExoPlayer hỗ trợ nhiều nhu cầu phân tích lượt phát. Mục tiêu cuối cùng của hoạt động phân tích là 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. Bạn có thể sử dụng dữ liệu này trên thiết bị (ví dụ: ghi nhật ký, gỡ lỗi hoặc cung cấp thông tin cho các quyết định phát trong tương lai) hoặc báo cáo cho máy chủ để theo dõi số 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 để khiến các sự kiện đó trở nê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. Trình nghe phân tích đã đăng ký sẽ nhận được các sự kiện khi các 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 sự kiện thô lên máy chủ, trong đó tất cả sự kiện được xử lý 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 đây:
    1. Diễn giải sự kiện: Để có thể giúp ích cho mục đích phân tích, sự kiện cần được diễn giải trong một lượt phát. Ví dụ: sự kiện thô về việc thay đổi trạng thái của người chơi thành STATE_BUFFERING có thể tương ứng với quá trình lưu vào bộ đệm ban đầu, bộ đệm lại hoặc quá trình lưu vào bộ đệm xảy ra sau khi tua.
    2. Theo dõi trạng thái: Bước này chuyển đổi sự kiện thành bộ đếm. Ví dụ: các sự kiện thay đổi trạng thái có thể được chuyển đổi thành các 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 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 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. Chỉ số tóm tắt có thể được tính toán cho một hoặc nhiều lần phát lại.

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

Các sự kiện phát lại thô từ trình phát được báo cáo cho các phương thức 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 đè các 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 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ư siêu dữ liệu về dấu thời gian và vị trí phát:

  • realtimeMs: Giờ trên đồ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, chẳng hạn như cho biết sự kiện có thuộc về một quảng cáo trong mục 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 nội dung đang phát. Mục đang phát có thể khác với mục chứa sự kiện, chẳng hạn như nếu sự kiện tương ứng với việc lưu trước 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ị. Công cụ này tính toán PlaybackStats, với các bộ đếm và chỉ số phát sinh bao gồm:

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

Bạn sẽ thấy danh sách đầy đủ các số lượng có sẵn và chỉ số phát sinh 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à cho mỗi quảng cáo phía máy khách được chèn vào các mục này. Bạn có thể cung cấp lệnh gọi lại cho PlaybackStatsListener để nhận thông báo về lượt phát đã kết thúc, đồng thời sử dụng EventTime được truyền đến lệnh gọi lại để xác định lượt 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ượt phát. Bạn cũng có thể truy vấn PlaybackStats cho phiên phát hiện tại vào bất cứ lúc nào bằ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 chi phí 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ý của các sự kiện đã xử lý, thay vì chỉ bật dữ liệu phân tích cuối cùng.

Xin lưu ý rằng PlaybackStats sử dụng một nhóm trạng thái mở rộng để cho biết không chỉ 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, cũng như cho biết thêm thông tin chi tiết như lý do khiến 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
Đang phát PLAYING
Quá trình 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 của người dùng là chơi là cần để phân biệt những thời điểm người dùng chủ động chờ việc phát để tiếp tục với thời gian chờ thụ động. Ví dụ: PlaybackStats.getTotalWaitTimeMs trả về tổng thời gian dành cho các trạng thái JOINING_FOREGROUND, BUFFERINGSEEKING, chứ không trả về thời gian khi quá trình phát bị tạm dừng. Tương tự, PlaybackStats.getTotalPlayAndWaitTimeMs sẽ trả về tổng thời gian có ý định chơi của người dùng, tức là tổng thời gian chờ hoạt động và tổng thời gian dành cho trạng thái PLAYING.

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

Bạn có thể ghi lại các sự kiện đã được 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 các trạng thái phát mở rộng theo thứ tự có EventTime mà các trạng thái đó 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 gian đồng hồ nhất định trên tường.
  • mediaTimeHistory: Lịch sử về thời gian trên đồng hồ treo tường và các cặp thời gian của nội dung nghe nhìn cho phép bạn tạo lại phần nào của nội dung nghe nhìn đã phát tại thời điểm đó. 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 đồng hồ treo nhất định.
  • videoFormatHistoryaudioFormatHistory: Danh sách theo thứ tự gồm các định dạng video và âm thanh được dùng trong quá trình phát bằng EventTime mà các định dạng này bắt đầu được sử dụng.
  • fatalErrorHistorynonFatalErrorHistory: Danh sách các lỗi nghiêm trọng và không nghiêm trọng theo thứ tự với EventTime 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ắc phục được.

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

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 của trạng thái phát do getPlaybackStateDurationMs trả về. Để thuận tiện, bạn cũng sẽ tìm 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);

Dữ liệu phân tích tổng hợp 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 hợp nhất. Xin lưu ý rằng danh sách này sẽ không chứa nhật ký của từng sự kiện phát vì hệ thống không thể tổng hợp các sự kiện này.

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

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

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 lại

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 về 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ẻ đa phương tiệ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 các 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 với PlaybackStats được báo cáo sau này. Nếu cách này hữu ích, bạn có thể mở rộng DefaultAnalyticsCollector để có thể tạo các thực thể EventTime cho các sự kiện tuỳ chỉnh và gửi các thực thể đó cho các 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();