Jetpack Media3 は、動画ファイルと音声ファイルの再生の基本機能を概説する Player
インターフェースを定義します。ExoPlayer
は、Media3 でこのインターフェースのデフォルト実装です。ExoPlayer の使用をおすすめします。ExoPlayer には、ほとんどの再生ユースケースに対応する包括的な機能セットが用意されており、追加のユースケースを処理するようにカスタマイズできます。また、ExoPlayer はデバイスと OS の分散を抽象化するため、Android エコシステム全体でコードが一貫して機能します。ExoPlayer には以下が含まれます。
- 再生リストのサポート
- さまざまなプログレッシブ ストリーミングとアダプティブ ストリーミングの形式をサポート
- クライアントサイドとサーバーサイドの広告挿入の両方をサポート
- DRM で保護された再生のサポート
このページでは、再生アプリの作成に関する主な手順について説明します。詳細については、Media3 ExoPlayer の完全なガイドをご覧ください。
はじめに
始めに、Jetpack Media3 の ExoPlayer、UI、Common モジュールの依存関係を追加します。
implementation "androidx.media3:media3-exoplayer:1.4.1" implementation "androidx.media3:media3-ui:1.4.1" implementation "androidx.media3:media3-common:1.4.1"
ユースケースによっては、DASH 形式でストリームを再生する exoplayer-dash
など、Media3 の追加モジュールが必要になる場合があります。
1.4.1
は、使用するライブラリのバージョンに置き換えてください。最新バージョンについては、リリースノートをご覧ください。
メディア プレーヤーを作成する
Media3 では、Player
インターフェースの付属の実装である ExoPlayer
を使用するか、独自のカスタム実装を構築できます。
ExoPlayer の作成
ExoPlayer
インスタンスを作成する最も簡単な方法は次のとおりです。
Kotlin
val player = ExoPlayer.Builder(context).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build();
メディア プレーヤーは、存在する Activity
、Fragment
、または Service
の onCreate()
ライフサイクル メソッドで作成できます。
Builder
には、次のようなさまざまなカスタマイズ オプションが含まれています。
setAudioAttributes()
: 音声フォーカスの処理を構成します。setHandleAudioBecomingNoisy()
オーディオ出力デバイスが切断されたときの再生動作を構成します。setTrackSelector()
トラックの選択を構成する
Media3 には、アプリのレイアウト ファイルに含めることができる PlayerView
UI コンポーネントが用意されています。このコンポーネントは、再生コントロール用の PlayerControlView
、字幕の表示用の SubtitleView
、動画のレンダリング用の Surface
をカプセル化します。
プレーヤーの準備
setMediaItem()
や addMediaItem()
などのメソッドを使用して、再生するプレイリストにメディア アイテムを追加します。次に、prepare()
を呼び出してメディアの読み込みを開始し、必要なリソースを取得します。
アプリがフォアグラウンドになる前に、これらの手順を実行しないでください。プレーヤーが Activity
または Fragment
にある場合、これは API レベル 24 以降では onStart()
ライフサイクル メソッドで、API レベル 23 以前では onResume()
ライフサイクル メソッドでプレーヤーを準備することを意味します。Service
にあるプレーヤーの場合は、onCreate()
で準備できます。
プレーヤーを操作する
プレーヤーの準備が整ったら、次のようなプレーヤーのメソッドを呼び出して再生を制御できます。
play()
とpause()
- 再生を開始または一時停止します。seekTo()
: 現在のメディア アイテム内の位置までシークします。seekToNextMediaItem()
、seekToPreviousMediaItem()
: 再生リスト内を移動する
PlayerView
や PlayerControlView
などの UI コンポーネントは、プレーヤーにバインドされると、それに応じて更新されます。
プレーヤーをリリースする
再生には、動画デコーダなど、供給量が限られているリソースが必要になる場合があります。そのため、プレーヤーが不要になったときにプレーヤーで release()
を呼び出してリソースを解放することが重要です。
プレーヤーが Activity
または Fragment
にある場合は、API レベル 24 以降の onStop()
ライフサイクル メソッド、または API レベル 23 以前の onPause()
メソッドでプレーヤーを解放します。Service
にいるプレーヤーは、onDestroy()
で解放できます。
メディア セッションを使用した再生の管理
Android では、メディア セッションにより、プロセスの境界を越えてメディア プレーヤーを操作するための標準化された方法が提供されます。メディア セッションをプレーヤーに接続すると、メディアの再生を外部に宣伝したり、外部ソースから再生コマンドを受け取ったりできます。たとえば、モバイル デバイスや大画面デバイスのシステム メディア コントロールと統合できます。
メディア セッションを使用するには、Media3 Session モジュールへの依存関係を追加します。
implementation "androidx.media3:media3-session:1.4.1"
メディア セッションを作成する
プレーヤーを初期化した後、次のように MediaSession
を作成できます。
Kotlin
val player = ExoPlayer.Builder(context).build() val mediaSession = MediaSession.Builder(context, player).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); MediaSession mediaSession = new MediaSession.Builder(context, player).build();
Media3 は、Player
の状態と MediaSession
の状態を自動的に同期します。これは、ExoPlayer
、CastPlayer
、カスタム実装など、任意の Player
実装で機能します。
他のクライアントに管理権限を付与する
クライアント アプリは、メディア コントローラを実装して、メディア セッションの再生を制御できます。これらのリクエストを受信するには、MediaSession
を作成するときにコールバック オブジェクトを設定します。
コントローラがメディア セッションに接続しようとすると、onConnect()
メソッドが呼び出されます。提供されている ControllerInfo
を使用して、リクエストを承認するか拒否するかを決定できます。例については、Media3 Session デモアプリをご覧ください。
接続すると、コントローラはセッションに再生コマンドを送信できます。セッションは、これらのコマンドをプレーヤーに委任します。Player
インターフェースで定義された再生コマンドと再生リスト コマンドは、セッションによって自動的に処理されます。
他のコールバック メソッドを使用すると、カスタム再生コマンドのリクエストやプレイリストの変更などを処理できます。これらのコールバックにも同様に ControllerInfo
オブジェクトが含まれているため、リクエストごとにアクセス制御を決定できます。
バックグラウンドでメディアを再生する
アプリがフォアグラウンドにない場合でもメディアの再生を続けるには(たとえば、ユーザーがアプリを開いていないときに音楽、オーディオブック、ポッドキャストを再生する場合)、Player
と MediaSession
をフォアグラウンド サービスにカプセル化する必要があります。Media3 には、この目的のための MediaSessionService
インターフェースが用意されています。
MediaSessionService
の実装
MediaSessionService
を拡張するクラスを作成し、onCreate()
ライフサイクル メソッドで MediaSession
をインスタンス化します。
Kotlin
class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null // Create your Player and MediaSession in the onCreate lifecycle event override fun onCreate() { super.onCreate() val player = ExoPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, player).build() } // Remember to release the player and media session in onDestroy override fun onDestroy() { mediaSession?.run { player.release() release() mediaSession = null } super.onDestroy() } }
Java
public class PlaybackService extends MediaSessionService { private MediaSession mediaSession = null; @Override public void onCreate() { super.onCreate(); ExoPlayer player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player).build(); } @Override public void onDestroy() { mediaSession.getPlayer().release(); mediaSession.release(); mediaSession = null; super.onDestroy(); } }
マニフェストで、MediaSessionService
インテント フィルタを持つ Service
クラスを宣言し、フォアグラウンド サービスを実行するための FOREGROUND_SERVICE
権限をリクエストします。
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
最後に、作成したクラスで onGetSession()
メソッドをオーバーライドして、メディア セッションへのクライアント アクセスを制御します。接続リクエストを承認するには MediaSession
を返します。リクエストを拒否するには null
を返します。
Kotlin
// This example always accepts the connection request override fun onGetSession( controllerInfo: MediaSession.ControllerInfo ): MediaSession? = mediaSession
Java
@Override public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) { // This example always accepts the connection request return mediaSession; }
UI への接続
メディア セッションが、プレーヤー UI が存在する Activity
または Fragment
とは別の Service
にあるため、MediaController
を使用してそれらをリンクできます。UI の Activity
または Fragment
の onStart()
メソッドで、MediaSession
の SessionToken
を作成し、SessionToken
を使用して MediaController
を作成します。MediaController
のビルドは非同期で行われます。
Kotlin
override fun onStart() { val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java)) val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync() controllerFuture.addListener( { // Call controllerFuture.get() to retrieve the MediaController. // MediaController implements the Player interface, so it can be // attached to the PlayerView UI component. playerView.setPlayer(controllerFuture.get()) }, MoreExecutors.directExecutor() ) }
Java
@Override public void onStart() { SessionToken sessionToken = new SessionToken(this, new ComponentName(this, PlaybackService.class)); ListenableFuture<MediaController> controllerFuture = new MediaController.Builder(this, sessionToken).buildAsync(); controllerFuture.addListener(() -> { // Call controllerFuture.get() to retrieve the MediaController. // MediaController implements the Player interface, so it can be // attached to the PlayerView UI component. playerView.setPlayer(controllerFuture.get()); }, MoreExecutors.directExecutor()) }
MediaController
は Player
インターフェースを実装しているため、play()
や pause()
などの同じメソッドを使用して再生を制御できます。他のコンポーネントと同様に、Activity
の onStop()
ライフサイクル メソッドなど、必要なくなったら MediaController
をリリースしてください。MediaController.releaseFuture()
を呼び出します。
通知の公開
フォアグラウンド サービスは、アクティブなときに通知を公開する必要があります。MediaSessionService
は、MediaNotification
の形式でMediaStyle
通知を自動的に作成します。カスタム通知を提供する場合は、DefaultMediaNotificationProvider.Builder
を使用して MediaNotification.Provider
を作成するか、プロバイダ インターフェースのカスタム実装を作成します。setMediaNotificationProvider
を使用して、プロバイダを MediaSession
に追加します。
コンテンツ ライブラリの宣伝
MediaLibraryService
は MediaSessionService
を基盤として、クライアント アプリがアプリが提供するメディア コンテンツをブラウジングできるようにします。クライアント アプリは、MediaLibraryService
とやり取りするために MediaBrowser
を実装します。
MediaLibraryService
の実装は MediaSessionService
の実装に似ていますが、onGetSession()
では MediaSession
ではなく MediaLibrarySession
を返す必要があります。MediaSession.Callback
と比較して、MediaLibrarySession.Callback
には、ブラウザ クライアントがライブラリ サービスが提供するコンテンツを操作できる追加メソッドが含まれています。
MediaSessionService
と同様に、マニフェストで MediaLibraryService
を宣言し、フォアグラウンド サービスを実行するための FOREGROUND_SERVICE
権限をリクエストします。
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaLibraryService"/>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
上記の例には、MediaLibraryService
と、下位互換性のために以前の MediaBrowserService
の両方のインテント フィルタが含まれています。追加されたインテント フィルタにより、MediaBrowserCompat
API を使用するクライアント アプリが Service
を認識できるようになります。
MediaLibrarySession
を使用すると、単一のルート MediaItem
を使用してコンテンツ ライブラリをツリー構造で提供できます。ツリーの各 MediaItem
には、任意の数の子 MediaItem
ノードを設定できます。クライアント アプリのリクエストに基づいて、別のルートまたは別のツリーを提供できます。たとえば、おすすめのメディア アイテムのリストを検索しているクライアントに返すツリーには、ルート MediaItem
と 1 つのレベルの子 MediaItem
ノードのみが含まれる場合がありますが、別のクライアント アプリに返すツリーには、より完全なコンテンツ ライブラリが含まれる場合があります。
MediaLibrarySession
の作成
MediaLibrarySession
は MediaSession
API を拡張して、コンテンツ ブラウジング API を追加します。MediaSession
コールバックと比較して、MediaLibrarySession
コールバックには次のようなメソッドが追加されています。
onGetLibraryRoot()
- クライアントがコンテンツ ツリーのルートMediaItem
をリクエストする場合onGetChildren()
クライアントがコンテンツ ツリー内のMediaItem
の子孫をリクエストする場合onGetSearchResult()
: クライアントが特定のクエリに対してコンテンツ ツリーから検索結果をリクエストする場合
関連するコールバック メソッドには、クライアント アプリが関心を持っているコンテンツ ツリーのタイプに関する追加のシグナルを含む LibraryParams
オブジェクトが含まれます。