ExoPlayer ライブラリのコアは Player
インターフェースです。Player
は、メディアのバッファリング、再生、一時停止、シークなどの従来の高度なメディア プレーヤー機能を公開します。デフォルトの実装 ExoPlayer
は、再生されるメディアの種類、保存方法と保存場所、レンダリング方法についてほとんど前提を持たないように設計されています(したがって、制限もほとんどありません)。ExoPlayer
の実装では、メディアの読み込みとレンダリングを直接実装するのではなく、プレーヤーの作成時や新しいメディアソースがプレーヤーに渡されたときに挿入されるコンポーネントにこの処理を委任します。すべての ExoPlayer
実装に共通するコンポーネントは次のとおりです。
- 再生するメディアを定義し、メディアを読み込み、読み込まれたメディアを読み取ることができる
MediaSource
インスタンス。MediaSource
インスタンスは、プレーヤー内のMediaSource.Factory
によってMediaItem
から作成されます。メディアソースベースのプレイリスト API を使用してプレーヤーに直接渡すこともできます。 MediaItem
をMediaSource
に変換するMediaSource.Factory
インスタンス。MediaSource.Factory
は、プレーヤーの作成時に挿入されます。- メディアの個々のコンポーネントをレンダリングする
Renderer
インスタンス。これらは、プレーヤーの作成時に挿入されます。 - 利用可能な各
Renderer
で使用されるMediaSource
から提供されるトラックを選択するTrackSelector
。プレーヤーの作成時にTrackSelector
が挿入されます。 MediaSource
がメディアをバッファリングするタイミングと、バッファリングするメディアの量を制御するLoadControl
。プレーヤーが作成されると、LoadControl
が挿入されます。- ライブ再生中の再生速度を制御し、プレーヤーが設定されたライブ オフセットに近づける
LivePlaybackSpeedControl
。LivePlaybackSpeedControl
は、プレーヤーの作成時に挿入されます。
プレーヤー機能の一部を実装するコンポーネントを挿入するというコンセプトは、ライブラリ全体に存在します。一部のコンポーネントのデフォルト実装では、作業がさらに挿入されたコンポーネントに委任されます。これにより、多くのサブコンポーネントを個別に、カスタム方法で構成された実装に置き換えることができます。
プレーヤーのカスタマイズ
コンポーネントを挿入してプレーヤーをカスタマイズする一般的な例を以下に示します。
ネットワーク スタックの構成
ExoPlayer で使用されるネットワーク スタックのカスタマイズに関するページがあります。
ネットワークから読み込まれたデータのキャッシュ保存
一時的なオンザフライ キャッシュとメディアのダウンロードに関するガイドをご覧ください。
サーバーとのやり取りのカスタマイズ
一部のアプリでは、HTTP リクエストとレスポンスをインターセプトする必要があります。カスタム リクエスト ヘッダーを挿入したり、サーバーのレスポンス ヘッダーを読み取ったり、リクエストの URI を変更したりする場合があります。たとえば、アプリは、メディア セグメントをリクエストするときにヘッダーとしてトークンを挿入することで、自身を認証できます。
次の例は、カスタム DataSource.Factory
を DefaultMediaSourceFactory
に挿入して、これらの動作を実装する方法を示しています。
Kotlin
val dataSourceFactory = DataSource.Factory { val dataSource = httpDataSourceFactory.createDataSource() // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value") dataSource } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory) ) .build()
Java
DataSource.Factory dataSourceFactory = () -> { HttpDataSource dataSource = httpDataSourceFactory.createDataSource(); // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value"); return dataSource; }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)) .build();
上記のコード スニペットでは、挿入された HttpDataSource
に、すべての HTTP リクエストにヘッダー "Header: Value"
が含まれています。この動作は、HTTP ソースとのやり取りごとに修正されます。
よりきめ細かいアプローチでは、ResolvingDataSource
を使用してジャストインタイムの動作を挿入できます。次のコード スニペットは、HTTP ソースを操作する直前にリクエスト ヘッダーを挿入する方法を示しています。
Kotlin
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time request headers. dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)) }
Java
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time request headers. dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));
次のスニペットに示すように、ResolvingDataSource
を使用して URI のジャストインタイム変更を実行することもできます。
Kotlin
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time URI resolution logic. dataSpec.withUri(resolveUri(dataSpec.uri)) }
Java
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time URI resolution logic. dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));
エラー処理のカスタマイズ
カスタム LoadErrorHandlingPolicy
を実装すると、アプリは ExoPlayer が読み込みエラーにどのように対応するかをカスタマイズできます。たとえば、アプリで何度も再試行するのではなく、早期に失敗させる場合や、プレーヤーが再試行するまでの待ち時間を制御するバックオフ ロジックをカスタマイズする場合などです。次のスニペットは、カスタム バックオフ ロジックを実装する方法を示しています。
Kotlin
val loadErrorHandlingPolicy: LoadErrorHandlingPolicy = object : DefaultLoadErrorHandlingPolicy() { override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorInfo): Long { // Implement custom back-off logic here. return 0 } } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy) ) .build()
Java
LoadErrorHandlingPolicy loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy() { @Override public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) { // Implement custom back-off logic here. return 0; } }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context) .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)) .build();
LoadErrorInfo
引数には、失敗した読み込みに関する詳細情報が含まれており、エラーの種類や失敗したリクエストに基づいてロジックをカスタマイズできます。
エクストラクタ フラグのカスタマイズ
エクストラクタ フラグを使用すると、プログレッシブ メディアから個々の形式を抽出する方法をカスタマイズできます。DefaultMediaSourceFactory
に渡される DefaultExtractorsFactory
に設定できます。次の例では、MP3 ストリームのインデックスベースのシーキングを有効にするフラグを渡します。
Kotlin
val extractorsFactory = DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING) val player = ExoPlayer.Builder(context) .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory)) .build()
Java
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING); ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory(new DefaultMediaSourceFactory(context, extractorsFactory)) .build();
定数ビットレートのシーキングを有効にする
MP3、ADTS、AMR ストリームの場合、FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
フラグを使用して一定のビットレートを想定して近似シークを有効にできます。これらのフラグは、上記のように個々の DefaultExtractorsFactory.setXyzExtractorFlags
メソッドを使用して個々の抽出ツールに設定できます。これをサポートするすべてのエクストラクタで一定のビットレートのシークを有効にするには、DefaultExtractorsFactory.setConstantBitrateSeekingEnabled
を使用します。
Kotlin
val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)
Java
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
ExtractorsFactory
は、上記のエクストラクタ フラグのカスタマイズで説明されているように、DefaultMediaSourceFactory
を介して挿入できます。
非同期バッファ キューイングを有効にする
非同期バッファ キューイングは、ExoPlayer のレンダリング パイプラインの機能強化です。MediaCodec
インスタンスを非同期モードで動作させ、追加のスレッドを使用してデータのデコードとレンダリングをスケジュールします。これを有効にすると、フレーム落ちや音声アンダーランを減らすことができます。
非同期バッファ キューイングは、Android 12(API レベル 31)以降を搭載したデバイスではデフォルトで有効になっています。Android 6.0(API レベル 23)以降では、手動で有効にできます。フレーム落ちや音声の不足が発生している特定のデバイスで、この機能を有効にすることを検討してください。特に、DRM で保護されたコンテンツや高フレームレートのコンテンツを再生する場合は有効にすることをおすすめします。
最も単純なケースでは、次のようにプレーヤーに DefaultRenderersFactory
を挿入する必要があります。
Kotlin
val renderersFactory = DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing() val exoPlayer = ExoPlayer.Builder(context, renderersFactory).build()
Java
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing(); ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build();
レンダラを直接インスタンス化する場合は、MediaCodecVideoRenderer
コンストラクタと MediaCodecAudioRenderer
コンストラクタに AsynchronousMediaCodecAdapter.Factory
を渡します。
ForwardingSimpleBasePlayer
によるオペレーションのカスタマイズ
Player
インスタンスを ForwardingSimpleBasePlayer
のサブクラスにラップすることで、Player
インスタンスの動作の一部をカスタマイズできます。このクラスを使用すると、Player
メソッドを直接実装しなくても、特定の「オペレーション」をインターセプトできます。これにより、play()
、pause()
、setPlayWhenReady(boolean)
などの動作の一貫性が保たれます。また、すべての状態変化が登録された Player.Listener
インスタンスに正しく伝播されます。ほとんどのカスタマイズ ユースケースでは、整合性の保証が得られるため、エラーが発生しやすい ForwardingPlayer
よりも ForwardingSimpleBasePlayer
を使用することをおすすめします。
たとえば、再生の開始または停止時にカスタム ロジックを追加するには、次のようにします。
Kotlin
class PlayerWithCustomPlay(player: Player) : ForwardingSimpleBasePlayer(player) { override fun handleSetPlayWhenReady(playWhenReady: Boolean): ListenableFuture<*> { // Add custom logic return super.handleSetPlayWhenReady(playWhenReady) } }
Java
class PlayerWithCustomPlay extends ForwardingSimpleBasePlayer { public PlayerWithCustomPlay(Player player) { super(player); } @Override protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) { // Add custom logic return super.handleSetPlayWhenReady(playWhenReady); } }
または、SEEK_TO_NEXT
コマンドを禁止し(Player.seekToNext
が no-op になるようにします)、次のようにします。
Kotlin
class PlayerWithoutSeekToNext(player: Player) : ForwardingSimpleBasePlayer(player) { override fun getState(): State { val state = super.getState() return state .buildUpon() .setAvailableCommands( state.availableCommands.buildUpon().remove(COMMAND_SEEK_TO_NEXT).build() ) .build() } // We don't need to override handleSeek, because it is guaranteed not to be called for // COMMAND_SEEK_TO_NEXT since we've marked that command unavailable. }
Java
class PlayerWithoutSeekToNext extends ForwardingSimpleBasePlayer { public PlayerWithoutSeekToNext(Player player) { super(player); } @Override protected State getState() { State state = super.getState(); return state .buildUpon() .setAvailableCommands( state.availableCommands.buildUpon().remove(COMMAND_SEEK_TO_NEXT).build()) .build(); } // We don't need to override handleSeek, because it is guaranteed not to be called for // COMMAND_SEEK_TO_NEXT since we've marked that command unavailable. }
MediaSource のカスタマイズ
上記の例では、プレーヤーに渡されるすべての MediaItem
オブジェクトの再生中に使用するために、カスタマイズされたコンポーネントを挿入しています。きめ細かいカスタマイズが必要な場合は、カスタマイズされたコンポーネントを個々の MediaSource
インスタンスに挿入することもできます。このインスタンスはプレーヤーに直接渡すことができます。次の例は、カスタム DataSource.Factory
、ExtractorsFactory
、LoadErrorHandlingPolicy
を使用するように ProgressiveMediaSource
をカスタマイズする方法を示しています。
Kotlin
val mediaSource = ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri))
Java
ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri));
カスタム コンポーネントの作成
このライブラリには、一般的なユースケース向けに、このページの上部に記載されているコンポーネントのデフォルト実装が用意されています。ExoPlayer
はこれらのコンポーネントを使用できますが、標準以外の動作が必要な場合は、カスタム実装を使用するようにビルドすることもできます。カスタム実装のユースケースは次のとおりです。
Renderer
- ライブラリで提供されるデフォルトの実装でサポートされていないメディアタイプを処理するために、カスタムRenderer
を実装できます。TrackSelector
- カスタムTrackSelector
を実装すると、アプリ デベロッパーは、MediaSource
によって公開されたトラックが、利用可能な各Renderer
によって消費される方法を変更できます。LoadControl
- カスタムLoadControl
を実装すると、アプリ デベロッパーはプレーヤーのバッファリング ポリシーを変更できます。Extractor
- ライブラリで現在サポートされていないコンテナ形式をサポートする必要がある場合は、カスタムExtractor
クラスの実装を検討してください。MediaSource
- カスタムMediaSource
クラスの実装は、レンダラにフィードするメディア サンプルをカスタム方法で取得する場合や、カスタムMediaSource
合成動作を実装する場合に適しています。MediaSource.Factory
- カスタムMediaSource.Factory
を実装すると、アプリでMediaItem
からMediaSource
を作成する方法をカスタマイズできます。DataSource
- ExoPlayer のアップストリーム パッケージには、さまざまなユースケース向けのDataSource
実装がすでに含まれています。独自のDataSource
クラスを実装して、カスタム プロトコル経由、カスタム HTTP スタックを使用、カスタム永続キャッシュからの読み込みなど、別の方法でデータを読み込むこともできます。
カスタム コンポーネントを作成する場合は、次のことをおすすめします。
- カスタム コンポーネントがアプリにイベントを報告する必要がある場合は、既存の ExoPlayer コンポーネントと同じモデルを使用して報告することをおすすめします。たとえば、
EventDispatcher
クラスを使用するか、リスナーとともにHandler
をコンポーネントのコンストラクタに渡します。 - 再生中にアプリによる再構成を可能にするため、カスタム コンポーネントは既存の ExoPlayer コンポーネントと同じモデルを使用することをおすすめします。そのためには、カスタム コンポーネントで
PlayerMessage.Target
を実装し、handleMessage
メソッドで構成変更を受け取る必要があります。アプリケーション コードは、ExoPlayer のcreateMessage
メソッドを呼び出してメッセージを構成し、PlayerMessage.send
を使用してコンポーネントに送信することで、構成変更を渡す必要があります。再生スレッドで配信されるメッセージを送信すると、プレーヤーで実行される他のオペレーションとともに順番に実行されます。