前節までの実装方法のようにCastアイコンの表示やCastメニューのUIを自作するのは、自由度は高いですが面倒です。冒頭で紹介したMediaRouteActionBarやMediaRouteButtonを使えばUIの作成をライブラリに任せることができます。
ここではMediaRouteActionBarの使い方を説明します。MediaRouteActionBarを使う場合でも、ここまでに説明した実装は一部を除いて必要です。ここではMediaRouteActionBarを使う場合の追加部分と、実装が一部省略できる部分について述べます。
MediaRouteActionBarを使うと、Castアイコン表示機能を持ったActionBarを生成できます。
まず、アプリケーションのメニューを定義するXMLファイルに、MediaRouteActionProviderを追加します(コード10)。
- <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="android.support.v7.app.MediaRouteActionProvider"
- app:showAsAction="always"/>
- ...
- </menu>
また、MediaRouteActionProviderを適用するActivityはActionBarActivity を継承する必要があります(コード11)。
public class MainActivity extends ActionBarActivity { // 省略 }
ActivityのonCreateOptionsMenu()メソッドにて、MediaRouteActonProviderとMediaRouteSelectorの関連付けをします(コード12)。
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.main, menu);
- // MediaRouteActonProviderとMediaRouteSelectorを関連付ける
- MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
- MediaRouteActionProvider mediaRouteActionProvider = (MediaRouteActionProvider) MenuItemCompat
- .getActionProvider(mediaRouteMenuItem);
- mediaRouteActionProvider.setRouteSelector(mMediaRouteSelector);
- return true;
- }
以上により、次の機能を組み込んだActionBarが表示されるようになりました。
このため、UIを自作するときに必要だった次の実装は不要になります。
次にReceiverアプリと通信について説明します。
Receiverアプリとの通信にはチャンネルを用います。ここで言う「チャンネル」とは、Google Cast SDKが定義するSender-Receiver間の通信経路を指します。チャンネルには次のような特徴があります。
今回はメディアチャンネルによる動画再生について実装方法を示します。
中心となるクラスはRemoteMediaPlayerクラスで、Receiverアプリ上のメディアプレーヤーの操作や状態取得をつかさどります。まずは、RemoteMediaPlayer のインスタンスを生成します(コード13)。
- mRemoteMediaPlayer = new RemoteMediaPlayer();
- mRemoteMediaPlayer
- .setOnStatusUpdatedListener(new RemoteMediaPlayer.OnStatusUpdatedListener() {
- @Override
- public void onStatusUpdated() {
- // メディアプレーヤーの状態が変わったときに呼ばれる
- Log.d(TAG, "onStatusUpdated");
- // 状態の取得
- MediaStatus mediaStatus = mRemoteMediaPlayer
- .getMediaStatus();
- boolean isPlaying = mediaStatus.getPlayerState() == MediaStatus.PLAYER_STATE_PLAYING;
- }
- });
- mRemoteMediaPlayer
- .setOnMetadataUpdatedListener(new RemoteMediaPlayer.OnMetadataUpdatedListener() {
- @Override
- public void onMetadataUpdated() {
- // メディアプレーヤーが保持している動画のメタデータが変わったときに呼ばれる
- Log.d(TAG, "onMetadataUpdated");
- // メタデータの取得
- MediaInfo mediaInfo = mRemoteMediaPlayer.getMediaInfo();
- MediaMetadata metadata = mediaInfo.getMetadata();
- }
- });
RemoteMediaPlayerには、状態が変わったときの通知を受け取るOnStatusUpdatedListener、動画のメタデータが変わった時の通知を受け取るOnMetadataUpdatedListenerをセットします。
RemoteMediaPlayerのインスタンスを生成したら、メディアチャンネルを確立します(コード14)。
- try {
- Cast.CastApi.setMessageReceivedCallbacks(mApiClient,
- mRemoteMediaPlayer.getNamespace(), mRemoteMediaPlayer);
- } catch (IOException e) {
- Log.e(TAG, "Exception while creating media channel", e);
- }
チャンネル確立ときにmRemoteMediaPlayer.getNamespace()で得た文字列を渡していますが、この文字列がメディアチャンネルの識別子である「urn:x-cast:com.google.cast.media」となっています。
チャンネルを確立したら、まずはRemoteMediaPlayerのrequestStatus()メソッド を呼び出して現在の状態を取得します。後にコード13のonStatusUpdated()メソッドが呼ばれるのでそのタイミングで現在の状態を取得できます(コード15)。
- mRemoteMediaPlayer.requestStatus(mApiClient).setResultCallback(
- new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
- @Override
- public void onResult(MediaChannelResult result) {
- if (!result.getStatus().isSuccess()) {
- Log.e(TAG, "Failed to request status.");
- }
- }
- });
以上でReceiverアプリのメディアプレーヤーを制御する準備ができました。動画をロードして再生を開始するには、次のように実装します(コード16)。
- MediaMetadata mediaMetadata = new MediaMetadata(
- MediaMetadata.MEDIA_TYPE_MOVIE);
- mediaMetadata.putString(MediaMetadata.KEY_TITLE, "My video");
- MediaInfo mediaInfo = new MediaInfo.Builder(
- "http://your.server.com/video.mp4").setContentType("video/mp4")
- .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
- .setMetadata(mediaMetadata).setCustomData(customDataJson)
- .build();
- try {
- mRemoteMediaPlayer
- .load(mApiClient, mediaInfo, true)
- .setResultCallback(
- new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
- @Override
- public void onResult(MediaChannelResult result) {
- if (result.getStatus().isSuccess()) {
- Log.d(TAG, "Media loaded successfully");
- }
- }
- });
- } catch (IllegalStateException e) {
- Log.e(TAG, "Problem occurred with media during loading", e);
- } catch (Exception e) {
- Log.e(TAG, "Problem opening media during loading", e);
- }
ロードするコンテンツに関する情報をセットしたMediaInfoインスタンスを生成し、RemoteMediaPlayerのload()メソッドを呼び出します。メタデータに関する情報はMediaMetadataクラスのインスタンスに設定します。コンテンツのURLはMediaInfoのBuilder()メソッドの引数に指定します。また、setCustomData()メソッドでアプリに固有な付加情報を設定し、Receiverアプリに受け渡すことも可能です。
なお、カスタムデータを送信する際の注意点として、送信できるデータサイズは64KBまでという制約があります。それを超えるデータはメディアチャンネルではなくカスタムチャンネルを使って送信する必要がありますが、カスタムチャンネルも一度に送信できるデータサイズは64KBまでの制約があるため、いくつかに分割して送信するようプログラミングしなければなりません。
動画の再生が始まると、RemoteMediaPlayerのインスタンスを使用して一時停止やシークといったメディアプレーヤーの操作ができるようになります(コード17)。
- // 一時停止
- mRemoteMediaPlayer.pause(mApiClient).setResultCallback(
- new ResultCallback<MediaChannelResult>() {
- @Override
- public void onResult(MediaChannelResult result) {
- // 省略
- }
- });
- // シーク
- mRemoteMediaPlayer.seek(mApiClient, seekPosition).setResultCallback(
- new ResultCallback<MediaChannelResult>() {
- @Override
- public void onResult(MediaChannelResult result) {
- // 省略
- }
- });
メディアプレーヤーの操作が行われると、Receiverアプリのメディアプレーヤーの状態が変わり、コード13のOnStatusUpdatedListenerクラスのメソッドが呼ばれます。なお、コード13のOnStatusUpdatedListenerクラスのメソッドは、同じChromecastデバイスにつながっている他のSenderアプリが操作した場合や、Receiverアプリ自身の制御により状態が変わったときにも呼ばれます。