ExoPlayer รองรับความต้องการด้านการวิเคราะห์การเล่นที่หลากหลาย สุดท้ายแล้ว Analytics คือการรวบรวม ตีความ การรวบรวม และการสรุปข้อมูลจากการเล่น ข้อมูลนี้ใช้ในอุปกรณ์ก็ได้ เช่น การบันทึก การแก้ไขข้อบกพร่อง หรือเพื่อใช้ประกอบการตัดสินใจเกี่ยวกับการเล่นในอนาคต หรือจะรายงานไปยังเซิร์ฟเวอร์เพื่อตรวจสอบการเล่นในอุปกรณ์ทั้งหมดก็ได้
โดยปกติแล้ว ระบบวิเคราะห์จะต้องรวบรวมเหตุการณ์ก่อน แล้วจึงประมวลผลเพิ่มเติมเพื่อให้เหตุการณ์มีความหมาย
- การเก็บรวบรวมเหตุการณ์:
ทำได้โดยการลงทะเบียน
AnalyticsListener
ในอินสแตนซ์ExoPlayer
Listener ข้อมูลวิเคราะห์ที่ลงทะเบียนไว้จะรับเหตุการณ์ที่เกิดขึ้นระหว่างการใช้งานเพลเยอร์ แต่ละเหตุการณ์จะเชื่อมโยงกับรายการสื่อที่เกี่ยวข้องในเพลย์ลิสต์ รวมถึงข้อมูลเมตาตำแหน่งการเล่นและการประทับเวลา - การประมวลผลเหตุการณ์: ระบบวิเคราะห์บางระบบจะอัปโหลดเหตุการณ์ดิบไปยังเซิร์ฟเวอร์ โดยทำการประมวลผลเหตุการณ์ทั้งหมดฝั่งเซิร์ฟเวอร์ นอกจากนี้ คุณยังประมวลผลเหตุการณ์ในอุปกรณ์ได้ด้วย ซึ่งอาจทําได้ง่ายขึ้นหรือลดปริมาณข้อมูลที่จําเป็นต้องอัปโหลด ExoPlayer มี
PlaybackStatsListener
ซึ่งจะช่วยให้คุณทำตามขั้นตอนการประมวลผลต่อไปนี้ได้- การตีความเหตุการณ์: เหตุการณ์ต้องได้รับการตีความในบริบทของการเล่นครั้งเดียวจึงจะมีประโยชน์สําหรับวัตถุประสงค์ด้านการวิเคราะห์ เช่น เหตุการณ์ดิบของสถานะโปรแกรมเล่นที่เปลี่ยนเป็น
STATE_BUFFERING
อาจสอดคล้องกับการบัฟเฟอร์ครั้งแรก การบัฟเฟอร์อีกครั้ง หรือการบัฟเฟอร์ที่เกิดขึ้นหลังจากการกรอ - การติดตามสถานะ: ขั้นตอนนี้จะแปลงเหตุการณ์เป็นตัวนับ เช่น เหตุการณ์การเปลี่ยนแปลงสถานะสามารถแปลงเป็นเคาน์เตอร์ที่ติดตามระยะเวลาที่ใช้ในแต่ละสถานะการเล่น ผลที่ได้คือชุดค่าข้อมูลการวิเคราะห์พื้นฐาน สำหรับการเล่นครั้งเดียว
- การรวมข้อมูล: ขั้นตอนนี้จะรวบรวมข้อมูลการวิเคราะห์จากการเล่นหลายๆ ช่วง ซึ่งโดยปกติแล้วจะเป็นการรวมตัวนับเข้าด้วยกัน
- การคํานวณเมตริกสรุป: เมตริกที่มีประโยชน์มากที่สุดหลายรายการคือเมตริกที่คํานวณค่าเฉลี่ยหรือรวมค่าข้อมูลวิเคราะห์พื้นฐานเข้าด้วยกันด้วยวิธีอื่นๆ ระบบจะคํานวณเมตริกสรุปสําหรับการเล่นรายการเดียวหรือหลายรายการ
- การตีความเหตุการณ์: เหตุการณ์ต้องได้รับการตีความในบริบทของการเล่นครั้งเดียวจึงจะมีประโยชน์สําหรับวัตถุประสงค์ด้านการวิเคราะห์ เช่น เหตุการณ์ดิบของสถานะโปรแกรมเล่นที่เปลี่ยนเป็น
การเก็บรวบรวมเหตุการณ์ด้วย AnalyticsListener
ระบบจะรายงานเหตุการณ์การเล่นแบบดิบจากโปรแกรมเล่นไปยังAnalyticsListener
การใช้งาน คุณเพิ่มตัวรับฟังของคุณเองและลบล้างเฉพาะเมธอดที่สนใจได้โดยง่าย ดังนี้
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()
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
ซึ่งจะแสดงระยะเวลาของชุดค่าผสมสถานะการเล่นที่เฉพาะเจาะจงเพื่อความสะดวก
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
ยังมีวิธีการมากมายในการคำนวณเมตริกสรุปอีกด้วย
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
ทำงานเสร็จแล้ว เพื่อให้ดึงข้อมูลได้อย่างง่ายดายเมื่อจัดการข้อมูลวิเคราะห์ที่เกี่ยวข้อง ดังนี้
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 ที่ลงทะเบียนไว้แล้วได้ ดังที่แสดงในตัวอย่างต่อไปนี้
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();