Analytics

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

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

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

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

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

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

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

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

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

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

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

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

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