ExoPlayer รองรับความต้องการด้านการวิเคราะห์การเล่นที่หลากหลาย ท้ายที่สุดแล้ว ข้อมูลวิเคราะห์คือการรวบรวม ตีความ จัดกลุ่ม และสรุปข้อมูลจากการเล่น ข้อมูลนี้สามารถใช้ได้ทั้งในอุปกรณ์ เช่น สำหรับการบันทึก การแก้ไขข้อบกพร่อง หรือเพื่อแจ้งการตัดสินใจในการเล่นในอนาคต หรือรายงานไปยังเซิร์ฟเวอร์เพื่อตรวจสอบการเล่นในอุปกรณ์ทั้งหมด
โดยปกติแล้ว ระบบวิเคราะห์จะต้องรวบรวมเหตุการณ์ก่อน แล้วจึงประมวลผลเพิ่มเติมเพื่อให้เหตุการณ์มีความหมาย ดังนี้
- การรวบรวมเหตุการณ์:
ซึ่งทำได้โดยการลงทะเบียน
AnalyticsListenerในอินสแตนซ์ExoPlayerListener ของ Analytics ที่ลงทะเบียนไว้จะได้รับเหตุการณ์เมื่อเกิดขึ้นระหว่างการใช้งานเพลเยอร์ แต่ละเหตุการณ์จะเชื่อมโยงกับรายการสื่อที่เกี่ยวข้องในเพลย์ลิสต์ รวมถึงตำแหน่งการเล่นและข้อมูลเมตาการประทับเวลา - การประมวลผลเหตุการณ์:
ระบบวิเคราะห์บางระบบจะอัปโหลดเหตุการณ์ดิบไปยังเซิร์ฟเวอร์ โดยการประมวลผลเหตุการณ์ทั้งหมดจะดำเนินการฝั่งเซิร์ฟเวอร์ นอกจากนี้ คุณยังประมวลผลเหตุการณ์ในอุปกรณ์ได้ด้วย ซึ่งอาจทำได้ง่ายกว่าหรือลดปริมาณข้อมูลที่ต้องอัปโหลด ExoPlayer มี
PlaybackStatsListenerซึ่งช่วยให้คุณทำตามขั้นตอนการประมวลผลต่อไปนี้ได้- การตีความเหตุการณ์: เหตุการณ์ต้องได้รับการตีความในบริบทของการเล่นครั้งเดียวจึงจะเป็นประโยชน์ต่อวัตถุประสงค์ด้านการวิเคราะห์ ตัวอย่างเช่น เหตุการณ์ดิบของการเปลี่ยนสถานะเพลเยอร์เป็น
STATE_BUFFERINGอาจสอดคล้องกับการบัฟเฟอร์เริ่มต้น การบัฟเฟอร์ซ้ำ หรือการบัฟเฟอร์ที่เกิดขึ้นหลังจากการกรอ - การติดตามสถานะ: ขั้นตอนนี้จะแปลงเหตุการณ์เป็นตัวนับ ตัวอย่างเช่น เหตุการณ์การเปลี่ยนแปลงสถานะสามารถแปลงเป็นตัวนับที่ติดตามระยะเวลาที่ใช้ในสถานะการเล่นแต่ละสถานะได้ ผลลัพธ์ที่ได้คือชุดค่าข้อมูลการวิเคราะห์พื้นฐานสำหรับการเล่น 1 ครั้ง
- การรวมข้อมูล: ขั้นตอนนี้จะรวมข้อมูลการวิเคราะห์จากการเล่นหลายครั้ง โดยปกติจะทำโดยการเพิ่มตัวนับ
- การคำนวณเมตริกสรุป: เมตริกที่มีประโยชน์มากที่สุดหลายรายการคือเมตริกที่คำนวณค่าเฉลี่ยหรือรวมค่าข้อมูลการวิเคราะห์พื้นฐานด้วยวิธีอื่นๆ ระบบจะคำนวณเมตริกสรุปสำหรับการเล่นครั้งเดียวหรือหลายครั้ง
- การตีความเหตุการณ์: เหตุการณ์ต้องได้รับการตีความในบริบทของการเล่นครั้งเดียวจึงจะเป็นประโยชน์ต่อวัตถุประสงค์ด้านการวิเคราะห์ ตัวอย่างเช่น เหตุการณ์ดิบของการเปลี่ยนสถานะเพลเยอร์เป็น
การรวบรวมเหตุการณ์ด้วย 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 แยกกันสำหรับรายการสื่อแต่ละรายการในเพลย์ลิสต์ และโฆษณาฝั่งไคลเอ็นต์แต่ละรายการที่แทรกภายในรายการเหล่านี้ด้วย คุณสามารถระบุ Callback ไปยัง PlaybackStatsListener เพื่อรับแจ้งเกี่ยวกับการเล่นที่เสร็จสิ้น และใช้ EventTime ที่ส่งไปยัง Callback เพื่อระบุการเล่นที่เสร็จสิ้น คุณสามารถรวบรวมข้อมูลวิเคราะห์สำหรับการเล่นหลายรายการได้ นอกจากนี้ คุณยังค้นหา 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. });
การรายงานเหตุการณ์ Analytics ที่กำหนดเอง
ในกรณีที่ต้องการเพิ่มเหตุการณ์ที่กำหนดเองลงในข้อมูลการวิเคราะห์ คุณต้องบันทึกเหตุการณ์เหล่านี้ในโครงสร้างข้อมูลของคุณเอง แล้วรวมเข้ากับ PlaybackStats ที่รายงานในภายหลัง หากเป็นประโยชน์ คุณสามารถขยาย DefaultAnalyticsCollector เพื่อให้สามารถสร้างอินสแตนซ์ EventTime สำหรับเหตุการณ์ที่กำหนดเองและส่งไปยัง Listener ที่ลงทะเบียนไว้แล้วได้ ดังที่แสดงในตัวอย่างต่อไปนี้
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(); }
แฟล็กฟีเจอร์การติดตามและโหมด Canary
เมื่อทำการทดสอบ A/B หรือประเมินผลกระทบของการเพิ่มประสิทธิภาพ Media3 ใหม่ คุณสามารถเชื่อมโยงข้อมูลการวิเคราะห์การเล่นกับแฟล็กฟีเจอร์ที่ใช้งานอยู่และโหมด Canary ได้
คุณสามารถค้นหา Flags.getLogString หรือตรวจสอบการลบล้างแฟล็กที่เฉพาะเจาะจงได้โดยใช้ Flags.getEnabledOverrides และ Flags.getDisabledOverrides เพื่อรวมสถานะแฟล็กไว้ในเพย์โหลดการวิเคราะห์ที่กำหนดเองหรือข้อมูลเมตาของเซสชัน