ExoPlayer ライブラリの中核となるのは、Player
インターフェースです。Player
は、メディアのバッファリング、再生、一時停止、シークなど、従来のメディア プレーヤーの高レベル機能を公開します。デフォルトの実装 ExoPlayer
は、再生されるメディアの種類、保存方法と保存場所、レンダリング方法についての前提条件をほとんど持たないように設計されています(そのため、制限もほとんど課されません)。ExoPlayer
の実装では、メディアの読み込みとレンダリングを直接実装するのではなく、プレーヤーが作成されたときや、新しいメディアソースがプレーヤーに渡されたときに挿入されるコンポーネントにこの作業を委任します。すべての ExoPlayer
実装に共通するコンポーネントは次のとおりです。
- 再生するメディアを定義し、メディアを読み込みます。また、読み込まれたメディアの読み取り元となるメディアを定義する
MediaSource
インスタンスです。MediaSource
インスタンスは、プレーヤー内のMediaSource.Factory
によってMediaItem
から作成されます。また、メディアソース ベースの再生リスト API を使用してプレーヤーに直接渡すこともできます。 MediaItem
をMediaSource
に変換するMediaSource.Factory
インスタンス。MediaSource.Factory
は、プレーヤーの作成時に挿入されます。- メディアの個々のコンポーネントをレンダリングする
Renderer
インスタンス。これらはプレーヤーの作成時に挿入されます。 MediaSource
によって提供されるトラックを選択し、利用可能な各Renderer
で処理するTrackSelector
。TrackSelector
は、プレーヤーの作成時に挿入されます。MediaSource
がバッファリングするメディアの量と量を制御するLoadControl
。LoadControl
は、プレーヤーの作成時に挿入されます。LivePlaybackSpeedControl
: ライブ再生中の再生速度を制御し、設定されたライブ オフセットにプレーヤーが近付くようにします。プレーヤーが作成されるとLivePlaybackSpeedControl
が挿入されます。
プレーヤー機能を実装するコンポーネントを挿入するという概念は、ライブラリ全体に存在します。一部のコンポーネントのデフォルト実装では、挿入されたコンポーネントに作業を委任しています。これにより、多くのサブコンポーネントを、カスタム方法で構成された実装に個別に置き換えることができます。
プレーヤーのカスタマイズ
コンポーネントを挿入してプレーヤーをカスタマイズする一般的な例を以下に示します。
ネットワーク スタックの構成
ExoPlayer が使用するネットワーク スタックのカスタマイズに関するページを用意しています。
ネットワークから読み込まれたキャッシュ データ
一時的オンザフライ キャッシュとメディアのダウンロードのガイドをご覧ください。
サーバー操作のカスタマイズ
アプリによっては、HTTP リクエストと 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
メソッドを使用して、個々の extractor に設定できます。サポートするすべての extractor で固定ビットレート シークを有効にするには、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();
レンダラを直接インスタンス化する場合は、AsynchronousMediaCodecAdapter.Factory
を MediaCodecVideoRenderer
コンストラクタと MediaCodecAudioRenderer
コンストラクタに渡します。
ForwardingPlayer
でメソッド呼び出しをインターセプトする
Player
インスタンスの動作の一部をカスタマイズするには、インスタンスを ForwardingPlayer
のサブクラスでラップし、メソッドをオーバーライドして、次の操作を行うことができます。
- パラメータにアクセスしてから、委任
Player
に渡します。 - 返す前に、デリゲート
Player
からの戻り値にアクセスします。 - メソッドを完全に再実装します。
ForwardingPlayer
メソッドをオーバーライドする場合、特に同一または関連する動作を意図したメソッドを扱う場合は、実装の自己整合性を維持し、Player
インターフェースに準拠することが重要です。次に例を示します。
- すべての「再生」オペレーションをオーバーライドする場合は、
ForwardingPlayer.play
とForwardingPlayer.setPlayWhenReady
の両方をオーバーライドする必要があります。これは、呼び出し元は、playWhenReady = true
の場合にこれらのメソッドの動作が同じであると期待するためです。 - シーク順の増分を変更する場合は、カスタマイズした増分でシークを実行する
ForwardingPlayer.seekForward
と、カスタマイズした正しい増分を呼び出し元に報告するためにForwardingPlayer.getSeekForwardIncrement
の両方をオーバーライドする必要があります。 - プレーヤー インスタンスでアドバタイズされる
Player.Commands
を制御するには、Player.getAvailableCommands()
とPlayer.isCommandAvailable()
の両方をオーバーライドするとともに、Player.Listener.onAvailableCommandsChanged()
コールバックをリッスンして、基になるプレーヤーからの変更の通知を取得する必要があります。
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
実装がすでに含まれています。カスタム プロトコル、カスタム HTTP スタック、カスタム永続キャッシュなど、別の方法でデータを読み込むために、独自のDataSource
クラスを実装することもできます。
カスタム コンポーネントを作成する場合は、次のことをおすすめします。
- カスタム コンポーネントがアプリにイベントをレポートする必要がある場合は、既存の ExoPlayer コンポーネントと同じモデル(
EventDispatcher
クラスを使用する、またはHandler
とリスナーをコンポーネントのコンストラクタに渡すなど)を使用してレポートすることをおすすめします。 - 再生中にアプリで再構成できるように、カスタム コンポーネントでは既存の ExoPlayer コンポーネントと同じモデルを使用することをおすすめします。そのためには、カスタム コンポーネントで
PlayerMessage.Target
を実装し、handleMessage
メソッドで構成の変更を受け取る必要があります。アプリコードは、ExoPlayer のcreateMessage
メソッドを呼び出してメッセージを設定し、PlayerMessage.send
を使用してコンポーネントに送信することで、構成の変更を渡す必要があります。再生スレッドで配信するメッセージを送信すると、プレーヤーで行われる他の操作と並行して、メッセージが順番に実行されるようになります。