ExoPlayer 支援各種播放分析需求。最後 Analytics 的用途是收集、解讀、匯總及匯總資料 播放次數。這類資料可以在裝置上使用,例如: 記錄、偵錯或告知日後的播放決定,或者 伺服器監控所有裝置上的播放次數。
分析系統通常必須先收集事件,然後處理事件 以便讓他們覺得具有意義
- 事件收集:
只要在
ExoPlayer
上註冊AnalyticsListener
即可 執行個體。已註冊的數據分析事件監聽器會在事件發生期間收到事件 。每個事件都會與對應的媒體建立關聯 播放清單中的項目,以及播放位置和時間戳記中繼資料。 - 事件處理:
某些數據分析系統會將原始事件上傳至伺服器,且涵蓋所有事件
並在伺服器端執行您也可以處理
可能會比較簡單,或減少資訊量
必須上傳。ExoPlayer 提供
PlaybackStatsListener
可讓您執行以下處理步驟:- 事件解讀:事件必須
會在單一播放時解讀。例如
玩家狀態變更至
STATE_BUFFERING
的事件,可能會與 自動緩衝處理、重新緩衝或緩衝處理; - 狀態追蹤:此步驟會將事件轉換為計數器。例如: 狀態變更事件可轉換為計數器,以追蹤 。結果是一組基本的數據分析資料 則代表單次播放的值。
- 匯總:這個步驟會合併多個來源的 Analytics 資料 播放次數,通常是增加計數器。
- 摘要指標的計算方式:許多最實用的指標 並設定用來計算平均值或結合 。摘要指標可以計算一或多個項目 播放。
- 事件解讀:事件必須
會在單一播放時解讀。例如
玩家狀態變更至
使用 AnalyticsListener 收集事件
系統會將播放器的原始播放事件回報給 AnalyticsListener
。您可以輕鬆新增自己的事件監聽器,並只覆寫
您感興趣的方法:
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
找出
播放完畢。您可匯總 Google 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
建構函式提供選項,讓
包括已處理事件的歷史記錄請注意,這可能會產生不明的記憶體負擔
取決於播放長度和事件數量因此,您
建議您只在需要存取完整資料處理記錄時,才開啟這項功能
事件,而不只是最終 Analytics 資料
請注意,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
可用於取得
您可以查看整個 YouTube 平台中收集的所有數據分析資料
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
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();