Media3 ExoPlayer を使用して基本的なメディア プレーヤー アプリを作成する

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.7.1"
implementation "androidx.media3:media3-ui:1.7.1"
implementation "androidx.media3:media3-common:1.7.1"

ユースケースによっては、DASH 形式でストリームを再生するために exoplayer-dash などの Media3 の追加モジュールが必要になることもあります。

1.7.1 は、使用するライブラリのバージョンに置き換えてください。最新バージョンについては、リリースノートをご覧ください。

メディア プレーヤーの作成

Media3 では、Player インターフェースの組み込み実装である ExoPlayer を使用するか、独自のカスタム実装を構築できます。

ExoPlayer を作成する

ExoPlayer インスタンスを作成する最も簡単な方法は次のとおりです。

Kotlin

val player = ExoPlayer.Builder(context).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();

メディア プレーヤーは、それが存在する ActivityFragment、または ServiceonCreate() ライフサイクル メソッドで作成できます。

Builder には、次のようなさまざまなカスタマイズ オプションが含まれています。

Media3 には、アプリのレイアウト ファイルに含めることができる PlayerView UI コンポーネントが用意されています。このコンポーネントは、再生コントロール用の PlayerControlView、字幕表示用の SubtitleView、動画レンダリング用の Surface をカプセル化します。

プレーヤーの準備

setMediaItem()addMediaItem() などのメソッドを使用して、再生するメディア アイテムをプレイリストに追加します。次に、prepare() を呼び出して、メディアの読み込みを開始し、必要なリソースを取得します。

アプリがフォアグラウンドになる前に、これらの手順を実行しないでください。プレーヤーが Activity または Fragment の状態にある場合は、API レベル 24 以上では onStart() ライフサイクル メソッド、API レベル 23 以下では onResume() ライフサイクル メソッドでプレーヤーを準備します。Service の状態のプレーヤーは、onCreate() で準備できます。

プレーヤーを操作する

プレーヤーの準備が整ったら、プレーヤーで次のようなメソッドを呼び出して再生を制御できます。

PlayerViewPlayerControlView などの UI コンポーネントは、プレーヤーにバインドされると、それに応じて更新されます。

プレーヤーをリリースする

再生には、動画デコーダなど、供給が限られているリソースが必要になることがあります。そのため、プレーヤーが不要になったら、プレーヤーで release() を呼び出してリソースを解放することが重要です。

プレーヤーが Activity または Fragment にある場合は、API レベル 24 以上では onStop() ライフサイクル メソッドで、API レベル 23 以下では onPause() メソッドでプレーヤーをリリースします。Service にいるプレーヤーは、onDestroy() でリリースできます。

メディア セッションを使用した再生の管理

Android では、メディア セッションはプロセス境界を越えてメディア プレーヤーを操作するための標準化された方法を提供します。メディア セッションをプレーヤーに接続すると、メディアの再生を外部に通知したり、外部ソースから再生コマンドを受け取ったりできます。たとえば、モバイル デバイスや大画面デバイスのシステム メディア コントロールと統合できます。

メディア セッションを使用するには、Media3 Session モジュールへの依存関係を追加します。

implementation "androidx.media3:media3-session:1.7.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 の状態と自動的に同期します。これは、ExoPlayerCastPlayer、カスタム実装など、任意の Player 実装で機能します。

他のクライアントに制御を許可する

クライアント アプリは、メディア コントローラを実装して、メディア セッションの再生を制御できます。これらのリクエストを受信するには、MediaSession をビルドするときにコールバック オブジェクトを設定します。

コントローラがメディア セッションに接続しようとすると、onConnect() メソッドが呼び出されます。提供された ControllerInfo を使用して、リクエストを承認するか拒否するかを決定できます。この例については、Media3 Session デモアプリをご覧ください。

接続されると、コントローラはセッションに再生コマンドを送信できます。セッションは、これらのコマンドをプレーヤーに委任します。Player インターフェースで定義された再生コマンドと再生リスト コマンドは、セッションによって自動的に処理されます。

他のコールバック メソッドを使用すると、カスタム再生コマンドのリクエストやプレイリストの変更などを処理できます。これらのコールバックにも同様に ControllerInfo オブジェクトが含まれているため、リクエストごとにアクセス制御を決定できます。

バックグラウンドでメディアを再生する

アプリがフォアグラウンドにないときにメディアの再生を続けるには(たとえば、ユーザーがアプリを開いていないときでも音楽、オーディオブック、ポッドキャストを再生するには)、PlayerMediaSessionフォアグラウンド サービスにカプセル化する必要があります。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 または FragmentonStart() メソッドで、MediaSessionSessionToken を作成し、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())
}

MediaControllerPlayer インターフェースを実装しているため、play()pause() などの同じメソッドを使用して再生を制御できます。他のコンポーネントと同様に、MediaController が不要になったら(ActivityonStop() ライフサイクル メソッドなど)、MediaController.releaseFuture() を呼び出してリリースしてください。

通知の公開

フォアグラウンド サービスは、アクティブな間は通知を公開する必要があります。MediaSessionService は、MediaNotification の形式で MediaStyle 通知を自動的に作成します。カスタム通知を提供するには、DefaultMediaNotificationProvider.Builder を使用して MediaNotification.Provider を作成するか、プロバイダ インターフェースのカスタム実装を作成します。setMediaNotificationProvider を使用して、プロバイダを MediaSession に追加します。

コンテンツ ライブラリの宣伝

MediaLibraryServiceMediaSessionService をベースに、クライアント アプリがアプリから提供されるメディア コンテンツをブラウズできるようにします。クライアント アプリは MediaBrowser を実装して MediaLibraryService とやり取りします。

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 オブジェクトが含まれます。