ExoPlayer は、再生分析に関する幅広いニーズをサポートしています。最終的に分析とは、再生からデータを収集、解釈、集計、要約することです。このデータは、デバイス上で使用して(ロギング、デバッグ、または今後の再生判断の通知など)、またはサーバーに報告して、すべてのデバイスの再生をモニタリングすることができます。
分析システムは通常、最初にイベントを収集し、それらを有意義なものにするためにさらに処理する必要があります。
- イベントの収集:
ExoPlayer
インスタンスにAnalyticsListener
を登録することで行うことができます。登録済みのアナリティクス リスナーは、プレーヤー使用中に発生したイベントを受信します。各イベントは、再生リスト内の対応するメディア アイテムと、再生位置とタイムスタンプ メタデータに関連付けられます。 - イベント処理: 一部の分析システムでは、未加工のイベントをサーバーにアップロードし、すべてのイベント処理をサーバーサイドで処理します。また、デバイス上でイベントを処理することもできます。その場合は、その方がよりシンプルか、アップロードする必要がある情報の量が減ります。ExoPlayer は
PlaybackStatsListener
を提供し、それによって次の処理手順を実行できます。- イベントの解釈: 分析に役立てるには、イベントを 1 回の再生のコンテキストで解釈する必要があります。たとえば、プレーヤーの状態が
STATE_BUFFERING
に変化したときの未加工のイベントは、初期バッファリング、再バッファリング、またはシーク後に発生するバッファリングに対応する場合があります。 - 状態のトラッキング: このステップでは、イベントをカウンタに変換します。たとえば、状態変更イベントをカウンタに変換して、各再生状態に費やした時間を追跡できます。その結果、1 回の再生の分析データ値の基本セットが返されます。
- 集計: このステップでは、通常は追加カウンタを追加することで、複数の再生の分析データを結合します。
- サマリー指標の計算: 最も有用な指標の多くは、平均値を計算するか、基本的な分析データ値を他の方法で結合する指標です。サマリー指標は、1 つまたは複数の再生に対して計算できます。
- イベントの解釈: 分析に役立てるには、イベントを 1 回の再生のコンテキストで解釈する必要があります。たとえば、プレーヤーの状態が
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
を使用してどの再生が終了したかを特定できます。複数の再生の分析データを集計できます。PlaybackStatsListener.getPlaybackStats()
を使用して、いつでも現在の再生セッションの PlaybackStats
をクエリすることもできます。
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
で設定することをおすすめします。
メディアタグは、未加工のイベントと PlaybackStats
の完了時にレポートされる EventTime
の一部であるため、対応する分析データを処理する際に簡単に取得できます。
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();