Analytics

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

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

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

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

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

KotlinJava
exoPlayer.addAnalyticsListener(
 
object : AnalyticsListener {
   
override fun onPlaybackStateChanged(
      eventTime
: EventTime, @Player.State state: Int
   
) {}

   
override fun onDroppedVideoFrames(
      eventTime
: EventTime,
      droppedFrames
: Int,
      elapsedMs
: Long,
   
) {}
 
}
)
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()

KotlinJava
exoPlayer.addAnalyticsListener(
 
PlaybackStatsListener(/* keepHistory= */ true) {
    eventTime
: EventTime?,
    playbackStats
: PlaybackStats?,
   
-> // Analytics data for the session started at `eventTime` is ready.
 
}
)
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 ซึ่งจะแสดงระยะเวลาของชุดค่าผสมสถานะการเล่นที่เฉพาะเจาะจงเพื่อความสะดวก

KotlinJava
Log.d(
 
"DEBUG",
 
"Playback summary: " +
   
"play time = " +
    playbackStats
.totalPlayTimeMs +
   
", rebuffers = " +
    playbackStats
.totalRebufferCount
)
Log.d(
   
"DEBUG",
   
"Playback summary: "
       
+ "play time = "
       
+ playbackStats.getTotalPlayTimeMs()
       
+ ", rebuffers = "
       
+ playbackStats.totalRebufferCount);

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

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

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

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

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

KotlinJava
Log.d(
 
"DEBUG",
 
"Additional calculated summary metrics: " +
   
"average video bitrate = " +
    playbackStats
.meanVideoFormatBitrate +
   
", mean time between rebuffers = " +
    playbackStats
.meanTimeBetweenRebuffers
)
Log.d(
   
"DEBUG",
   
"Additional calculated summary metrics: "
       
+ "average video bitrate = "
       
+ playbackStats.getMeanVideoFormatBitrate()
       
+ ", mean time between rebuffers = "
       
+ playbackStats.getMeanTimeBetweenRebuffers());

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

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

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

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

KotlinJava
PlaybackStatsListener(/* keepHistory= */ false) {
  eventTime
: EventTime,
  playbackStats
: PlaybackStats ->
 
val mediaTag =
    eventTime
.timeline
     
.getWindow(eventTime.windowIndex, Timeline.Window())
     
.mediaItem
     
.localConfiguration
     
?.tag
   
// Report playbackStats with mediaTag metadata.
}
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 ที่ลงทะเบียนไว้แล้วได้ ดังที่แสดงในตัวอย่างต่อไปนี้

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