Analytics

ExoPlayer รองรับความต้องการด้านการวิเคราะห์การเล่นที่หลากหลาย สุดท้ายแล้ว Analytics คือการรวบรวม ตีความ การรวบรวม และการสรุปข้อมูลจากการเล่น ข้อมูลนี้ใช้ในอุปกรณ์ก็ได้ เช่น การบันทึก การแก้ไขข้อบกพร่อง หรือเพื่อใช้ประกอบการตัดสินใจเกี่ยวกับการเล่นในอนาคต หรือจะรายงานไปยังเซิร์ฟเวอร์เพื่อตรวจสอบการเล่นในอุปกรณ์ทั้งหมดก็ได้

โดยปกติแล้ว ระบบวิเคราะห์จะต้องรวบรวมเหตุการณ์ก่อน แล้วจึงประมวลผลเพิ่มเติมเพื่อให้เหตุการณ์มีความหมาย

  • การเก็บรวบรวมเหตุการณ์: ทำได้โดยการลงทะเบียน AnalyticsListener ในอินสแตนซ์ ExoPlayer Listener ข้อมูลวิเคราะห์ที่ลงทะเบียนไว้จะรับเหตุการณ์ที่เกิดขึ้นระหว่างการใช้งานเพลเยอร์ แต่ละเหตุการณ์จะเชื่อมโยงกับรายการสื่อที่เกี่ยวข้องในเพลย์ลิสต์ รวมถึงข้อมูลเมตาตำแหน่งการเล่นและการประทับเวลา
  • การประมวลผลเหตุการณ์: ระบบวิเคราะห์บางระบบจะอัปโหลดเหตุการณ์ดิบไปยังเซิร์ฟเวอร์ โดยทำการประมวลผลเหตุการณ์ทั้งหมดฝั่งเซิร์ฟเวอร์ นอกจากนี้ คุณยังประมวลผลเหตุการณ์ในอุปกรณ์ได้ด้วย ซึ่งอาจทําได้ง่ายขึ้นหรือลดปริมาณข้อมูลที่จําเป็นต้องอัปโหลด ExoPlayer มี PlaybackStatsListener ซึ่งจะช่วยให้คุณทำตามขั้นตอนการประมวลผลต่อไปนี้ได้
    1. การตีความเหตุการณ์: เหตุการณ์ต้องได้รับการตีความในบริบทของการเล่นครั้งเดียวจึงจะมีประโยชน์สําหรับวัตถุประสงค์ด้านการวิเคราะห์ เช่น เหตุการณ์ดิบของสถานะโปรแกรมเล่นที่เปลี่ยนเป็น STATE_BUFFERING อาจสอดคล้องกับการบัฟเฟอร์ครั้งแรก การบัฟเฟอร์อีกครั้ง หรือการบัฟเฟอร์ที่เกิดขึ้นหลังจากการกรอ
    2. การติดตามสถานะ: ขั้นตอนนี้จะแปลงเหตุการณ์เป็นตัวนับ เช่น เหตุการณ์การเปลี่ยนแปลงสถานะสามารถแปลงเป็นเคาน์เตอร์ที่ติดตามระยะเวลาที่ใช้ในแต่ละสถานะการเล่น ผลที่ได้คือชุดค่าข้อมูลการวิเคราะห์พื้นฐาน สำหรับการเล่นครั้งเดียว
    3. การรวมข้อมูล: ขั้นตอนนี้จะรวบรวมข้อมูลการวิเคราะห์จากการเล่นหลายๆ ช่วง ซึ่งโดยปกติแล้วจะเป็นการรวมตัวนับเข้าด้วยกัน
    4. การคํานวณเมตริกสรุป: เมตริกที่มีประโยชน์มากที่สุดหลายรายการคือเมตริกที่คํานวณค่าเฉลี่ยหรือรวมค่าข้อมูลวิเคราะห์พื้นฐานเข้าด้วยกันด้วยวิธีอื่นๆ ระบบจะคํานวณเมตริกสรุปสําหรับการเล่นรายการเดียวหรือหลายรายการ

การเก็บรวบรวมเหตุการณ์ด้วย AnalyticsListener

ระบบจะรายงานเหตุการณ์การเล่นแบบดิบจากโปรแกรมเล่นไปยังAnalyticsListener การใช้งาน คุณเพิ่มตัวรับฟังของคุณเองและลบล้างเฉพาะเมธอดที่สนใจได้โดยง่าย ดังนี้

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 ที่ส่งไปยังการเรียกกลับแต่ละรายการจะเชื่อมโยงเหตุการณ์กับรายการสื่อในเพลย์ลิสต์ รวมถึงข้อมูลเมตาตำแหน่งการเล่นและการประทับเวลา

  • realtimeMs: เวลาแขวนผนังของเหตุการณ์
  • timeline, windowIndex และ mediaPeriodId: กำหนดเพลย์ลิสต์และรายการภายในเพลย์ลิสต์ที่เหตุการณ์นั้นอยู่ mediaPeriodId จะมีข้อมูลเพิ่มเติมที่ไม่บังคับ เช่น ระบุว่าเหตุการณ์เป็นของโฆษณาภายในรายการหรือไม่
  • eventPlaybackPositionMs: ตำแหน่งการเล่นในรายการเมื่อเกิดเหตุการณ์
  • currentTimeline, currentWindowIndex, currentMediaPeriodId และ currentPlaybackPositionMs: เหมือนกับด้านบน แต่สำหรับรายการที่เล่นอยู่ รายการที่เล่นอยู่อาจแตกต่างจากรายการที่เหตุการณ์นั้นเกี่ยวข้อง เช่น หากเหตุการณ์เกี่ยวข้องกับการบัฟเฟอร์ล่วงหน้าของรายการถัดไปที่จะเล่น

การประมวลผลเหตุการณ์ด้วย PlaybackStatsListener

PlaybackStatsListener คือ AnalyticsListener ที่ใช้การประมวลผลเหตุการณ์ในอุปกรณ์ เครื่องมือดังกล่าวจะคำนวณ PlaybackStats โดยใช้ตัวนับและเมตริกที่ดึงมา ได้แก่

  • เมตริกสรุป เช่น เวลาเล่นทั้งหมด
  • เมตริกคุณภาพการเล่นแบบปรับเปลี่ยนได้ เช่น ความละเอียดวิดีโอเฉลี่ย
  • เมตริกคุณภาพการแสดงผล เช่น อัตราเฟรมที่หลุด
  • เมตริกการใช้งานทรัพยากร เช่น จำนวนไบต์ที่อ่านผ่านเครือข่าย

คุณจะเห็นรายการจํานวนทั้งหมดและเมตริกที่ดึงข้อมูลได้ใน PlaybackStats Javadoc

PlaybackStatsListener คำนวณ PlaybackStats แยกกันสำหรับรายการสื่อแต่ละรายการในเพลย์ลิสต์ และโฆษณาฝั่งไคลเอ็นต์แต่ละรายการที่แทรกอยู่ภายในรายการเหล่านี้ คุณสามารถระบุการเรียกกลับไปยัง PlaybackStatsListener เพื่อรับข้อมูลเกี่ยวกับการเล่นที่เสร็จสิ้นแล้ว และใช้ EventTime ที่ส่งไปยังการเรียกกลับเพื่อระบุการเล่นที่เสร็จสิ้น คุณสามารถรวบรวมข้อมูลวิเคราะห์สำหรับการเล่นหลายครั้งได้ นอกจากนี้คุณยังค้นหา PlaybackStats สำหรับเซสชันการเล่นปัจจุบันได้ทุกเมื่อโดยใช้ 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.
        }));

เครื่องมือสร้าง PlaybackStatsListener จะมีตัวเลือกให้เก็บประวัติทั้งหมดของเหตุการณ์ที่ประมวลผลแล้ว โปรดทราบว่าการดำเนินการนี้อาจทำให้มีการใช้หน่วยความจำที่ไม่รู้จัก ทั้งนี้ขึ้นอยู่กับระยะเวลาในการเล่นและจำนวนเหตุการณ์ ดังนั้น คุณจึงควรเปิดใช้เฉพาะในกรณีที่ต้องการเข้าถึงประวัติทั้งหมดของเหตุการณ์ที่ประมวลผลแล้ว ไม่ใช่แค่ข้อมูลวิเคราะห์ขั้นสุดท้าย

โปรดทราบว่า PlaybackStats ใช้ชุดสถานะที่ขยายออกไปเพื่อระบุไม่เพียงสถานะของสื่อเท่านั้น แต่ยังระบุความตั้งใจของผู้ใช้ในการเล่นและข้อมูลโดยละเอียดเพิ่มเติม เช่น สาเหตุที่การเล่นถูกขัดจังหวะหรือสิ้นสุด

สถานะการเล่น ความตั้งใจของผู้ใช้ที่จะเล่น ไม่มีความตั้งใจที่จะเล่น
ก่อนเล่น JOINING_FOREGROUND NOT_STARTED, JOINING_BACKGROUND
การเล่นที่ใช้งานอยู่ PLAYING
การเล่นถูกขัดจังหวะ BUFFERING, SEEKING PAUSED, PAUSED_BUFFERING, SUPPRESSED, SUPPRESSED_BUFFERING, INTERRUPTED_BY_AD
สถานะสิ้นสุด ENDED, STOPPED, FAILED, ABANDONED

ความตั้งใจของผู้ใช้ในการเล่นเป็นสิ่งสำคัญในการแยกแยะเวลาที่ผู้ใช้รอให้การเล่นดำเนินต่อไปอย่างตั้งใจกับเวลาที่รอแบบไม่ได้ตั้งใจ ตัวอย่างเช่น PlaybackStats.getTotalWaitTimeMs จะแสดงเวลาทั้งหมดที่ใช้ในรัฐ JOINING_FOREGROUND BUFFERING และ SEEKING แต่ไม่ใช่เวลาที่หยุดเล่นชั่วคราว ในทำนองเดียวกัน PlaybackStats.getTotalPlayAndWaitTimeMs จะแสดงเวลาทั้งหมดที่ผู้ใช้ตั้งใจจะเล่น ซึ่งก็คือเวลารอทั้งหมดที่ใช้งานอยู่ และระยะเวลาทั้งหมดที่ใช้ในสถานะ PLAYING

เหตุการณ์ที่ประมวลผลและตีความแล้ว

คุณสามารถบันทึกเหตุการณ์ที่ประมวลผลและตีความแล้วได้โดยใช้ PlaybackStatsListener กับ keepHistory=true PlaybackStats ที่ได้จะมีรายการเหตุการณ์ต่อไปนี้

  • playbackStateHistory: รายการสถานะการเล่นแบบขยายเวลาตามลำดับพร้อมEventTimeที่เริ่มมีผล นอกจากนี้ คุณยังใช้ PlaybackStats.getPlaybackStateAtTime เพื่อค้นหาสถานะ ณ เวลาตามจริงที่ระบุได้อีกด้วย
  • mediaTimeHistory: ประวัติคู่เวลาตามนาฬิกาและเวลาของสื่อ ซึ่งช่วยให้คุณสร้างส่วนที่เล่นของสื่อขึ้นใหม่ได้ และคุณยังใช้ PlaybackStats.getMediaTimeMsAtRealtimeMs เพื่อค้นหาตำแหน่งการเล่นตามเวลาที่นาฬิกากำหนดไว้ได้ด้วย
  • videoFormatHistory และ audioFormatHistory: รายการรูปแบบวิดีโอและเสียงตามลำดับซึ่งใช้ในระหว่างการเล่นด้วย EventTime ซึ่งเป็นจุดที่เริ่มใช้
  • fatalErrorHistory และ nonFatalErrorHistory: รายการข้อผิดพลาดร้ายแรงและไม่ร้ายแรงที่จัดเรียงตาม EventTime ที่ข้อผิดพลาดเกิดขึ้น ข้อผิดพลาดร้ายแรงคือข้อผิดพลาดที่ทำให้การเล่นสิ้นสุดลง ส่วนข้อผิดพลาดที่ไม่ร้ายแรงอาจกู้คืนได้

ข้อมูลวิเคราะห์แบบเล่นครั้งเดียว

ระบบจะรวบรวมข้อมูลนี้โดยอัตโนมัติหากคุณใช้ PlaybackStatsListener แม้จะใช้ keepHistory=false ค่าสุดท้ายคือช่องสาธารณะที่คุณดูได้ใน PlaybackStats Javadoc และระยะเวลาสถานะการเล่นที่ getPlaybackStateDurationMs แสดง นอกจากนี้ คุณยังจะเห็นเมธอดต่างๆ เช่น getTotalPlayTimeMs และ getTotalWaitTimeMs ซึ่งจะแสดงระยะเวลาของชุดค่าผสมสถานะการเล่นที่เฉพาะเจาะจงเพื่อความสะดวก

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

รวบรวมข้อมูลการวิเคราะห์ของการเล่นหลายรายการ

คุณรวม PlaybackStats หลายรายการเข้าด้วยกันได้โดยเรียกใช้ PlaybackStats.merge PlaybackStats ที่ได้จะมีข้อมูลการเล่นที่รวมไว้ทั้งหมดที่ผสาน โปรดทราบว่าข้อมูลนี้จะไม่มีประวัติของเหตุการณ์การเล่นแต่ละรายการ เนื่องจากไม่สามารถรวบรวมข้อมูลเหล่านี้ได้

PlaybackStatsListener.getCombinedPlaybackStats สามารถใช้เพื่อดูมุมมองรวมของข้อมูลวิเคราะห์ทั้งหมดที่รวบรวมตลอดอายุของ PlaybackStatsListener

เมตริกสรุปที่คำนวณแล้ว

นอกจากข้อมูลวิเคราะห์พื้นฐานแล้ว PlaybackStats ยังมีวิธีการมากมายในการคำนวณเมตริกสรุปอีกด้วย

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

หัวข้อขั้นสูง

การเชื่อมโยงข้อมูลการวิเคราะห์กับข้อมูลเมตาการเล่น

เมื่อรวบรวมข้อมูลการวิเคราะห์สำหรับการเล่นแต่ละครั้ง คุณอาจต้องการเชื่อมโยงข้อมูลการวิเคราะห์การเล่นกับข้อมูลเมตาเกี่ยวกับสื่อที่กำลังเล่น

ขอแนะนำให้ตั้งค่าข้อมูลเมตาเฉพาะสื่อด้วย MediaItem.Builder.setTag แท็กสื่อเป็นส่วนหนึ่งของ EventTime ที่รายงานสำหรับเหตุการณ์ดิบ และเมื่อ PlaybackStats ทำงานเสร็จแล้ว เพื่อให้ดึงข้อมูลได้อย่างง่ายดายเมื่อจัดการข้อมูลวิเคราะห์ที่เกี่ยวข้อง ดังนี้

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

การรายงานเหตุการณ์ข้อมูลวิเคราะห์ที่กําหนดเอง

ในกรณีที่คุณต้องเพิ่มเหตุการณ์ที่กําหนดเองลงในข้อมูลวิเคราะห์ คุณต้องบันทึกเหตุการณ์เหล่านี้ในโครงสร้างข้อมูลของคุณเอง แล้วรวมเข้ากับPlaybackStatsที่รายงานในภายหลัง หากต้องการ คุณขยาย DefaultAnalyticsCollector ได้เพื่อให้สร้างอินสแตนซ์ EventTime สําหรับเหตุการณ์ที่กําหนดเองและส่งไปยัง Listeners ที่ลงทะเบียนไว้แล้วได้ ดังที่แสดงในตัวอย่างต่อไปนี้

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