Analytics

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

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

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

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

  • 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 สำหรับเหตุการณ์ที่กำหนดเองและส่งไปยังผู้ฟังที่ลงทะเบียนไว้แล้วได้ตามตัวอย่างต่อไปนี้

Kotlin

@OptIn(UnstableApi::class)
private interface ExtendedListener : AnalyticsListener {
  fun onCustomEvent(eventTime: EventTime)
}

@OptIn(UnstableApi::class)
private class ExtendedCollector : DefaultAnalyticsCollector(Clock.DEFAULT) {

  fun customEvent() {
    val eventTime = super.generateCurrentPlayerMediaPeriodEventTime()
    super.sendEvent(eventTime, CUSTOM_EVENT_ID) { listener: AnalyticsListener ->
      if (listener is ExtendedListener) {
        listener.onCustomEvent(eventTime)
      }
    }
  }
}

@OptIn(UnstableApi::class)
fun useExtendedAnalyticsCollector(context: Context) {
  // 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

@OptIn(markerClass = UnstableApi.class)
private interface ExtendedListener extends AnalyticsListener {
  void onCustomEvent(EventTime eventTime);
}

@OptIn(markerClass = UnstableApi.class)
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);
          }
        });
  }
}

@OptIn(markerClass = UnstableApi.class)
public static void useExtendedAnalyticsCollector(Context context) {
  // 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();
}