サービスに関する最新情報

メディア再生の向上: Media3 によるプリロードの導入 - パート 1

所要時間: 8 分
Mayuri Khinvasara Khabya さんのプロフィールを表示
Mayuri Khinvasara Khabya デベロッパー リレーションズ エンジニア

今日のメディア中心のアプリでは、スムーズで中断のない再生エクスペリエンスを提供することが、優れたユーザー エクスペリエンスの鍵となります。ユーザーは、動画がすぐに開始され、途切れることなくシームレスに再生されることを期待しています。

主な課題はレイテンシです。従来、動画プレーヤーは、ユーザーが再生するアイテムを選択した後にのみ、接続、ダウンロード、解析、バッファリングなどの処理を開始します。この事後対応型のアプローチは、今日のショート動画のコンテキストでは時間がかかります。解決策は、プロアクティブになることです。ユーザーが次に視聴するコンテンツを予測し、事前に準備しておく必要があります。これがプリロードの本質です。

プリロードの主なメリットは次のとおりです。

  • 🚀 再生開始の高速化: 動画がすでに準備されているため、アイテム間の切り替えが速くなり、すぐに再生が開始されます。
  • 📉 バッファリングの削減: データを事前に読み込むことで、ネットワークの不具合などによる再生の停止が大幅に減少します。
  • ✨ ユーザー エクスペリエンスの向上: 起動時間の短縮とバッファリングの削減により、ユーザーはよりスムーズでシームレスな操作を楽しめるようになります。

この 3 部構成のシリーズでは、コンポーネントの(プリ)ロードに役立つ Media3 の強力なユーティリティを紹介し、特集します。

  • パート 1 では、Media3 で利用可能なさまざまなプリロード戦略の理解、PreloadConfiguration の有効化、DefaultPreloadManager の設定、アプリでのアイテムのプリロードの有効化など、基礎について説明します。このブログを読み終える頃には、設定したランキングと再生時間でメディア アイテムをプリロードして再生できるようになります。
  • パート 2 では、DefaultPreloadManager のより高度なトピックについて説明します。分析にリスナーを使用する方法、スライディング ウィンドウ パターンや DefaultPreloadManager と ExoPlayer のカスタム共有コンポーネントなど、本番環境に対応したベスト プラクティスについて説明します。
  • パート 3 では、DefaultPreloadManager を使用したディスク キャッシュについて詳しく説明します。

プリロードの活用🦸‍♀️

プリロードの基本的な考え方はシンプルです。メディア コンテンツを必要になる前に読み込むことです。ユーザーが次の動画にスワイプするまでに、動画の最初のセグメントがすでにダウンロードされて利用可能になり、すぐに再生できるようになります。

レストランに例えてみましょう。忙しい厨房では、注文が入るのを待ってから玉ねぎを刻み始めることはありません。🧅 事前に準備作業を行う。プリロードは、動画プレーヤーの準備作業です。

プリロードを有効にすると、再生バッファが次のアイテムに達する前にユーザーが次のアイテムにスキップしたときに、結合のレイテンシを最小限に抑えることができます。次のウィンドウの導入部が準備され、動画、音声、テキストのサンプルがバッファリングされます。プリロード部分は後ほどプレーヤーのキューに追加されます。バッファリングされたサンプルは、すぐに利用可能になり、コーデックにフィードしてレンダリングをすることができます。

Media3 には、プリロード用の主な API が 2 つあり、それぞれ異なるユースケースに適しています。適切な API を選択することが最初のステップです。

1. PreloadConfiguration を使用したプレイリスト アイテムのプリロード

これはシンプルなアプローチで、再生順序が予測可能なプレイリスト(一連のエピソードなど)のような、線形で連続的なメディアに適しています。ExoPlayer のプレイリスト API を使用してメディア アイテムの完全なリストをプレーヤーに提供し、プレーヤーに PreloadConfiguration を設定すると、設定に従ってシーケンス内の次のアイテムが自動的にプリロードされます。この API は、再生バッファが次のアイテムにオーバーラップする前にユーザーが次のアイテムにスキップしたときに、結合のレイテンシを最適化しようとします。

プリロードは、進行中の再生に必要なメディアが読み込まれていない場合にのみ開始されます。これにより、プリロードとメインの再生が帯域幅を奪い合うことがなくなります。

プリロードが必要かどうかわからない場合は、この API を使用して簡単に試すことができます。

player.preloadConfiguration =
    PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)

上記の PreloadConfiguration では、プレーヤーは再生リスト内の次のアイテムの 5 秒間のメディアをプリロードしようとします。

オプトインした後、プレイリスト プリロードを再び無効にするには、PreloadConfiguration.DEFAULT を使用します。

player.preloadConfiguration = PreloadConfiguration.DEFAULT

2. PreloadManager を使用して動的リストをプリロードする

縦型フィードやカルーセルなど、ユーザー操作によって「次」のアイテムが決まる動的な UI の場合は、PreloadManager API が適しています。これは、Media3 ExoPlayer ライブラリ内の新しい強力なスタンドアロン コンポーネントで、プロアクティブなプリロード専用に設計されています。MediaSource の候補のコレクションを管理し、ユーザーの現在位置への近接性に基づいて優先順位を付けます。また、プリロードする対象をきめ細かく制御できるため、ショート動画のダイナミック フィードなどの複雑なシナリオに適しています。

PreloadManager の設定

DefaultPreloadManager は、PreloadManager の標準的な実装です。

DefaultPreloadManager のビルダーは、DefaultPreloadManager と、プリロードされたコンテンツを再生する ExoPlayer インスタンスの両方をビルドできます。DefaultPreloadManager を作成するには、TargetPreloadStatusControl を渡す必要があります。プリロード マネージャーは、このオブジェクトをクエリして、アイテムの読み込み量を判断します。以下のセクションでは、TargetPreloadStatusControl の例を説明し、定義します。

val preloadManagerBuilder =
DefaultPreloadManager.Builder(context, targetPreloadStatusControl)
val preloadManager = val preloadManagerBuilder.build()

// Build ExoPlayer with DefaultPreloadManager.Builder
val player = preloadManagerBuilder.buildExoPlayer()

ExoPlayer と DefaultPreloadManager の両方に同じビルダーを使用する必要があります。これにより、内部のコンポーネントが正しく共有されます。

以上で、これで、指示を受け取る準備ができたマネージャーが作成されました。

TargetPreloadStatusControl を使用した期間とランキングの構成

たとえば、10 秒の動画をプリロードする場合はどうすればよいでしょうか。カルーセル内のメディア アイテムの位置を指定すると、DefaultPreloadManager は、ユーザーが現在再生しているアイテムに近いアイテムを優先して読み込みます。

プリロードするアイテムの時間を制御する場合は、返される DefaultPreloadManager.PreloadStatus で指定できます。

次に例を示します。

  • アイテム「A」は優先度が最も高く、5 秒間の動画を読み込みます。
  • アイテム「B」は優先度が中ですが、読み込む際は 3 秒分の動画を読み込みます。
  • アイテム「C」は優先度が低く、トラックのみを読み込みます。
  • 項目「D」はさらに優先度が低く、準備のみ。
  • 他のアイテムは遠くにあるため、何もプリロードしません。

このきめ細かい制御により、リソースの使用率を最適化できます。これは、シームレスな再生に推奨されます。

import androidx.media3.exoplayer.DefaultPreloadManager.PreloadStatus


class MyTargetPreloadStatusControl(
    currentPlayingIndex: Int = C.INDEX_UNSET
) : TargetPreloadStatusControl<Int,PreloadStatus> {


    // The app is responsible for updating this based on UI state
    override fun getTargetPreloadStatus(index: Int): PreloadStatus? {

        val distance = index - currentPlayingIndex

        // Adjacent items (Next): preload 5 seconds
        if (distance == 1) { 
        // Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED and suggest loading // 5000ms from the default start position
                    return PreloadStatus.specifiedRangeLoaded(5000L)
                } 

        // Adjacent items (Previous): preload 3 seconds
        else if (distance == -1) { 
        // Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED //and suggest loading 3000ms from the default start position
                    return PreloadStatus.specifiedRangeLoaded(3000L)
                } 

        // Items two positions away: just select tracks
        else if (distance) == 2) {
        // Return a PreloadStatus that is labelled by STAGE_TRACKS_SELECTED
                    return PreloadStatus.TRACKS_SELECTED
                } 

        // Items four positions away: just select prepare
        else if (abs(distance) <= 4) {
        // Return a PreloadStatus that is labelled by STAGE_SOURCE_PREPARED
                    return PreloadStatus.SOURCE_PREPARED
                }

             // All other items are too far away
             return null
            }
}

ヒント: PreloadManager は、前後のアイテムをプリロードしたままにできますが、PreloadConfiguration は次のアイテムのみをプリロードします。

プリロード アイテムの管理

マネージャーを作成したら、何を行うかを指示できます。ユーザーがフィードをスクロールすると、次の動画が特定され、マネージャーに追加されます。PreloadManager とのやり取りは、UI とプリロード エンジン間の状態駆動型の会話です。

1. メディア アイテムを追加する

フィードにデータを入力する際は、トラッキングが必要なメディアをマネージャーに通知する必要があります。開始時に、プリロードするリスト全体を追加できます。その後、必要に応じてリストに 1 つずつアイテムを追加していくことができます。プリロード リスト内のアイテムは完全に制御できます。つまり、マネージャーに追加または削除するアイテムも管理する必要があります。

val initialMediaItems = pullMediaItemsFromService(/* count= */ 20)
for (index in 0 until initialMediaItems.size) {
    preloadManager.add(
        initialMediaItems.get(index),index)
    )
}

マネージャーは、この MediaItem のデータの取得をバックグラウンドで開始します。

追加したら、マネージャーに新しいリストを再評価するよう指示します(アイテムの追加や削除など、何かが変更されたことや、ユーザーが新しいアイテムの再生に切り替えたことを示唆します)。

preloadManager.invalidate()

2. アイテムを取得して再生する

メインの再生ロジックは次のとおりです。ユーザーがその動画を再生することを選択した場合、新しい MediaSource を作成する必要はありません。代わりに、PreloadManager に、すでに準備されているものをリクエストします。MediaItem を使用して、プリロード マネージャーから MediaSource を取得できます。

PreloadManager から取得したアイテムが null の場合、mediaItem はまだプリロードされていないか、PreloadMamager に追加されていないため、mediaItem を直接設定することを選択します。

// When a media item is about to displ​​ay on the screen
val mediaSource = preloadManager.getMediaSource(mediaItem)
if (mediaSource!= null) {
  player.setMediaSource(mediaSource)
} else {
  // If mediaSource is null, that mediaItem hasn't been added yet.
  // So, send it directly to the player.
  player.setMediaItem(mediaItem)
}
player.prepare()
// When the media item is displaying at the center of the screen
player.play()

PreloadManager から取得した MediaSource を準備することで、すでにメモリ内にあるデータを使用して、プリロードから再生にシームレスに移行できます。これにより、開始時間が短縮されます。

3. 現在のインデックスを UI と同期する

フィード / リストは動的な場合があるため、PreloadManager に現在の再生インデックスを通知することが重要です。これにより、PreloadManager は常に現在のインデックスに最も近いアイテムを優先してプリロードできます。

preloadManager.setCurrentPlayingIndex(currentIndex)
// Need to call invalidate() to update the priorities
preloadManager.invalidate()

4. アイテムを削除する

マネージャーの効率を維持するため、追跡する必要がなくなったアイテム(ユーザーの現在位置から遠く離れているアイテムなど)を削除してください。

// When an item is too far from the current playing index
preloadManager.remove(mediaItem)

すべてのアイテムを一度に消去する必要がある場合は、preloadManager.reset() を呼び出します。

5. マネージャーを解放する

PreloadManager が不要になった場合(UI が破棄された場合など)は、リソースを解放するために PreloadManager を解放する必要があります。これを行うのに適した場所は、プレーヤーのリソースをすでに解放している場所です。プレーヤーの前にマネージャーを解放することをおすすめします。プリロードが不要になった場合でも、プレーヤーは再生を継続できます。

// In your Activity's onDestroy() or Composable's onDispose
preloadManager.release()

デモの時間

実際の動作を確認してみましょう 👍

以下のデモでは、右側で PreloadManager の効果により読み込み時間が短縮されているのに対し、左側は既存のエクスペリエンスを示しています。デモのコードサンプルもご覧いただけます。(ボーナス: すべての動画の起動レイテンシも表示されます)

Demo-PreloadManager_2.webp

次のステップ

パート 1 は以上です。これで、動的なプリロード システムを構築するためのツールが揃いました。PreloadConfiguration を使用して ExoPlayer でプレイリストの次のアイテムをプリロードするか、DefaultPreloadManager を設定して、アイテムをオンザフライで追加および削除し、ターゲットのプリロード ステータスを構成して、プリロードされたコンテンツを再生用に正しく取得できます。

パート 2 では、DefaultPreloadManager について詳しく説明します。Preload イベントをリッスンする方法、メモリの問題を回避するためにスライディング ウィンドウを使用するなどのベスト プラクティス、ExoPlayer と DefaultPreloadManager のカスタム共有コンポーネントの内部構造について説明します。

フィードバックをお寄せください。皆様からのご意見をお待ちしております。

アプリの高速化にぜひお役立てください。🚀

作成者:
続きを読む