アプリ内で MediaRouter フレームワークを使用するには、MediaRouter
オブジェクトのインスタンスを取得し、MediaRouter.Callback
オブジェクトをアタッチしてルーティング イベントをリッスンする必要があります。
メディアルート経由で送信されるコンテンツは、ルートに関連付けられた MediaRouteProvider
を通過します(Bluetooth 出力デバイスなどの特殊なケースを除く)。図 1 は、デバイス間でのコンテンツのルーティングに使用されるクラスの概要を示しています。
注: アプリで Google Cast デバイスに対応する場合は、Cast SDK を使用し、アプリを Cast センダーとしてビルドする必要があります。MediaRouter フレームワークを直接使用するのではなく、Cast のドキュメントの手順に沿って操作します。
メディアルート ボタン
Android アプリでは、メディアルート ボタンを使用してメディアのルーティングを制御します。MediaRouter フレームワークは、ボタン用の標準インターフェースを提供します。これにより、ユーザーはルーティングが利用可能であれば、それを認識して使用できます。メディアルートボタンは通常、図 2 に示すように、アプリのアクションバーの右側に配置されます。
ユーザーがメディアルート ボタンを選択すると、利用可能なメディアルートのリストが表示されます(図 3 参照)。
メディアルート ボタンの作成手順は次のとおりです。
- AppCompatActivity を使用する
- メディアルート ボタンのメニュー項目を定義する
- MediaRouteSelector を作成する
- メディアルート ボタンをアクションバーに追加する
- アクティビティのライフサイクルで MediaRouter.Callback メソッドを作成し管理する
このセクションでは、最初の 4 つの手順について説明します。Callback メソッドについては、その次のセクションで説明します。
AppCompatActivity を使用する
アクティビティでメディア ルーター フレームワークを使用する場合は、AppCompatActivity
からアクティビティを拡張し、パッケージ androidx.appcompat.app
をインポートする必要があります。サポート ライブラリ androidx.appcompat:appcompat と androidx.mediarouter:mediarouter をアプリ開発プロジェクトに追加する必要があります。プロジェクトにサポート ライブラリを追加する方法について詳しくは、Android Jetpack スタートガイドをご覧ください。
注意: 必ずメディア ルーター フレームワークの androidx
実装を使用してください。古い android.media
パッケージは使用しないでください。
メディアルート ボタンのメニュー項目を定義する
メディアルート ボタンのメニュー項目を定義する xml ファイルを作成します。アイテムのアクションは MediaRouteActionProvider
クラスにする必要があります。
ファイルの例を以下に示します。
// myMediaRouteButtonMenuItem.xml <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <item android:id="@+id/media_route_menu_item" android:title="@string/media_route_menu_title" app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider" app:showAsAction="always" /> </menu>
MediaRouteSelector を作成する
メディアルート ボタンのメニューに表示されるルートは、MediaRouteSelector
によって決定されます。次のコードサンプルに示すように、AppCompatActivity
からアクティビティを拡張し、アクティビティが作成されたときに onCreate() メソッドから MediaRouteSelector.Builder
を呼び出してセレクタを作成します。セレクタはクラス変数に保存され、MediaControlIntent
オブジェクトを追加することで、許可されるルートタイプを指定します。
Kotlin
class MediaRouterPlaybackActivity : AppCompatActivity() { private var mSelector: MediaRouteSelector? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Create a route selector for the type of routes your app supports. mSelector = MediaRouteSelector.Builder() // These are the framework-supported intents .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) .build() } }
Java
public class MediaRouterPlaybackActivity extends AppCompatActivity { private MediaRouteSelector mSelector; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Create a route selector for the type of routes your app supports. mSelector = new MediaRouteSelector.Builder() // These are the framework-supported intents .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) .build(); } }
ほとんどのアプリケーションでは、必要なルートタイプは CATEGORY_REMOTE_PLAYBACK
のみです。このルートタイプは、アプリを実行しているデバイスをリモコンとして扱います。
接続された受信デバイスが、すべてのコンテンツ データの取得、デコード、再生を処理します。これは、Chromecast など、Google Cast をサポートするアプリの仕組みです。
メーカーによっては、「セカンダリ出力」と呼ばれる特別なルーティング オプションをサポートしています。このルーティングにより、メディアアプリは動画または音楽を取得してレンダリングし、選択したリモート レシーバー デバイスの画面やスピーカーに直接ストリーミングします。セカンダリ出力を使用すれば、ワイヤレス対応の音楽システムやビデオ ディスプレイにコンテンツを送信できます。このようなデバイスの検出と選択を有効にするには、CATEGORY_LIVE_AUDIO
または CATEGORY_LIVE_VIDEO
のコントロール カテゴリを MediaRouteSelector に追加する必要があります。さらに、独自の Presentation
ダイアログを作成して処理する必要があります。
メディアルート ボタンをアクションバーに追加する
メディアルート メニューと MediaRouteSelector を定義して、メディアルート ボタンをアクティビティに追加できるようになりました。各アクティビティの onCreateOptionsMenu()
メソッドをオーバーライドして、オプション メニューを追加します。
Kotlin
override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) // Inflate the menu and configure the media router action provider. menuInflater.inflate(R.menu.sample_media_router_menu, menu) // Attach the MediaRouteSelector to the menu item val mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item) val mediaRouteActionProvider = MenuItemCompat.getActionProvider(mediaRouteMenuItem) as MediaRouteActionProvider // Attach the MediaRouteSelector that you built in onCreate() selector?.also(mediaRouteActionProvider::setRouteSelector) // Return true to show the menu. return true }
Java
@Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); // Inflate the menu and configure the media router action provider. getMenuInflater().inflate(R.menu.sample_media_router_menu, menu); // Attach the MediaRouteSelector to the menu item MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item); MediaRouteActionProvider mediaRouteActionProvider = (MediaRouteActionProvider)MenuItemCompat.getActionProvider( mediaRouteMenuItem); // Attach the MediaRouteSelector that you built in onCreate() mediaRouteActionProvider.setRouteSelector(selector); // Return true to show the menu. return true; }
アプリにアクションバーを実装する方法について詳しくは、アクションバーのデベロッパー ガイドをご覧ください。
メディアルート ボタンを任意のビューに MediaRouteButton
として追加することもできます。setRouteSelector()
メソッドを使用して、MediaRouteSelector をボタンにアタッチする必要があります。メディアルート ボタンをアプリに組み込む際のガイドラインについては、Google Cast 設計チェックリストをご覧ください。
MediaRouter コールバック
同じデバイスで実行されているすべてのアプリは、1 つの MediaRouter
インスタンスとそのルートを共有します(アプリの MediaRouteSelector によってアプリごとにフィルタ)。各アクティビティは、MediaRouter.Callback
メソッドの独自の実装を使用して MediaRouter と通信します。MediaRouter は、ユーザーがルートを選択、変更、接続解除するたびに、コールバック メソッドを呼び出します。
コールバックには、ルーティング イベントに関する情報を受け取るためにオーバーライドできるメソッドがいくつかあります。少なくとも、MediaRouter.Callback
クラスの実装では、onRouteSelected()
と onRouteUnselected()
をオーバーライドする必要があります。
MediaRouter は共有リソースであるため、アプリは通常のアクティビティ ライフサイクル コールバックに応じて MediaRouter コールバックを管理する必要があります。
- アクティビティが作成されたとき(
onCreate(Bundle)
)にMediaRouter
へのポインタを取得し、アプリの有効期間中はそのポインタを保持する。 - アクティビティが表示されたとき(
onStart()
)にコールバックを MediaRouter にアタッチし、非表示になったら(onStop()
)デタッチします。
次のコードサンプルは、コールバック オブジェクトを作成して保存する方法、MediaRouter
のインスタンスを取得する方法、コールバックを管理する方法を示しています。
onStart()
でコールバックをアタッチするときに、CALLBACK_FLAG_REQUEST_DISCOVERY
フラグを使用します。これにより、MediaRouteSelector でメディアルート ボタンの利用可能なルートのリストを更新できます。
Kotlin
class MediaRouterPlaybackActivity : AppCompatActivity() { private var mediaRouter: MediaRouter? = null private var mSelector: MediaRouteSelector? = null // Variables to hold the currently selected route and its playback client private var mRoute: MediaRouter.RouteInfo? = null private var remotePlaybackClient: RemotePlaybackClient? = null // Define the Callback object and its methods, save the object in a class variable private val mediaRouterCallback = object : MediaRouter.Callback() { override fun onRouteSelected(router: MediaRouter, route: MediaRouter.RouteInfo) { Log.d(TAG, "onRouteSelected: route=$route") if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { // Stop local playback (if necessary) // ... // Save the new route mRoute = route // Attach a new playback client remotePlaybackClient = RemotePlaybackClient(this@MediaRouterPlaybackActivity, mRoute) // Start remote playback (if necessary) // ... } } override fun onRouteUnselected( router: MediaRouter, route: MediaRouter.RouteInfo, reason: Int ) { Log.d(TAG, "onRouteUnselected: route=$route") if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { // Changed route: tear down previous client mRoute?.also { remotePlaybackClient?.release() remotePlaybackClient = null } // Save the new route mRoute = route when (reason) { MediaRouter.UNSELECT_REASON_ROUTE_CHANGED -> { // Resume local playback (if necessary) // ... } } } } } // Retain a pointer to the MediaRouter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Get the media router service. mediaRouter = MediaRouter.getInstance(this) ... } // Use this callback to run your MediaRouteSelector to generate the // list of available media routes override fun onStart() { mSelector?.also { selector -> mediaRouter?.addCallback(selector, mediaRouterCallback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY) } super.onStart() } // Remove the selector on stop to tell the media router that it no longer // needs to discover routes for your app. override fun onStop() { mediaRouter?.removeCallback(mediaRouterCallback) super.onStop() } ... }
Java
public class MediaRouterPlaybackActivity extends AppCompatActivity { private MediaRouter mediaRouter; private MediaRouteSelector mSelector; // Variables to hold the currently selected route and its playback client private MediaRouter.RouteInfo mRoute; private RemotePlaybackClient remotePlaybackClient; // Define the Callback object and its methods, save the object in a class variable private final MediaRouter.Callback mediaRouterCallback = new MediaRouter.Callback() { @Override public void onRouteSelected(MediaRouter router, RouteInfo route) { Log.d(TAG, "onRouteSelected: route=" + route); if (route.supportsControlCategory( MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){ // Stop local playback (if necessary) // ... // Save the new route mRoute = route; // Attach a new playback client remotePlaybackClient = new RemotePlaybackClient(this, mRoute); // Start remote playback (if necessary) // ... } } @Override public void onRouteUnselected(MediaRouter router, RouteInfo route, int reason) { Log.d(TAG, "onRouteUnselected: route=" + route); if (route.supportsControlCategory( MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){ // Changed route: tear down previous client if (mRoute != null && remotePlaybackClient != null) { remotePlaybackClient.release(); remotePlaybackClient = null; } // Save the new route mRoute = route; if (reason != MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) { // Resume local playback (if necessary) // ... } } } } // Retain a pointer to the MediaRouter @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Get the media router service. mediaRouter = MediaRouter.getInstance(this); ... } // Use this callback to run your MediaRouteSelector to generate the list of available media routes @Override public void onStart() { mediaRouter.addCallback(mSelector, mediaRouterCallback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); super.onStart(); } // Remove the selector on stop to tell the media router that it no longer // needs to discover routes for your app. @Override public void onStop() { mediaRouter.removeCallback(mediaRouterCallback); super.onStop(); } ... }
メディア ルーター フレームワークには、アクティビティのコールバックの追加と削除を処理する MediaRouteDiscoveryFragment
クラスも用意されています。
注: 音楽再生アプリを作成する際に、バックグラウンドで音楽を再生するには、再生用の Service
を作成し、サービスのライフサイクル コールバックからメディア ルーター フレームワークを呼び出す必要があります。
リモート再生ルートの制御
リモート再生ルートを選択した場合は、アプリはリモコンとして機能します。ルートの終端にあるデバイスが、すべてのコンテンツ データの取得、デコード、再生機能を処理します。アプリの UI のコントロールは、RemotePlaybackClient
オブジェクトを使用して受信デバイスと通信します。
RemotePlaybackClient
クラスには、コンテンツ再生を管理するための追加のメソッドが用意されています。以下に、RemotePlaybackClient
クラスの主要な再生メソッドをいくつか示します。
play()
-Uri
で指定された特定のメディア ファイルを再生します。pause()
- 現在再生中のメディア トラックを一時停止します。resume()
- 一時停止コマンドの後、現在のトラックの再生を続行します。seek()
- 現在のトラック内の特定の位置に移動します。release()
- アプリからリモートの再生デバイスへの接続を破棄します。
これらのメソッドを使用して、アプリが提供する再生コントロールにアクションをアタッチできます。これらのメソッドのほとんどでは、コールバック オブジェクトを含めることもできるため、再生タスクまたはコントロール リクエストの進行状況をモニタリングできます。
また、RemotePlaybackClient
クラスは、メディアキューの再生と管理を目的とした複数のメディア アイテムのキューイングもサポートしています。
サンプルコード
Android BasicMediaRouter と MediaRouter のサンプルでは、MediaRouter API の使用方法が詳しく説明されています。