次にiOSについて見ていきます。グーグルからiOS用のサンプルプログラムもGitHub上で公開されています。
iOSでは、Androidと比べて開発言語上の違い以外にもいくつか違いがあります。
【1】標準的なUIのサポートがない
MediaRouteActionProviderやMediaRouteButton、CCLに相当するものがありません。従って、CastアイコンやCastメニューなどのUI、CCLが提供する機能等は全て自前で作成する必要があります。
【2】Google Play servicesがない
Androidであれば、Senderアプリ自体の更新がなくてもGoogle Play ServicesをアップデートすることでCast関連の不具合が修正される可能性がありますが、iOSの場合はその仕組みがないため、Senderアプリ自体が最新のGoogle Castフレームワークを取り込み、アップデートする必要があります。
【3】Google Cast Design Checklistに記載されているUX基準が異なる
OS上の制約の違いなどにより、Androidでは必須の項目がiOSでは必須でなくなっているものや、AndroidとiOSで機能の実現方法が異なっているものがあります。例えば、Chromecastデバイスの音量の調整はAndroidではハードウエアキーで行いますが、iOSではハードウエアキーでは行えないため、代わりに画面上に音量調整のUI(スライドバーなど)を表示します。
それでは、実際の開発方法を解説します。まずは、Senderアプリが使用するフレームワークを取り込みます。
XcodeプロジェクトのLinked Frameworks and Librariesに上記フレームワークを登録します。1.はGoogleが提供するフレームワークで、公式サイトからダウンロードする必要があります。それ以外はiOS SDKに同梱されているフレームワークなので、特にダウンロードする必要はありません。
次に実装を見ていきましょう。Chromecastデバイスの検出はGCKDeviceScannerクラスを使います(コード19)。
- self.deviceScanner = [[GCKDeviceScanner alloc] init];
- GCKFilterCriteria *filterCriteria = [[GCKFilterCriteria alloc] init];
- filterCriteria = [GCKFilterCriteria criteriaForAvailableApplicationWithID:@"APPLICATION_ID"];
- self.deviceScanner.filterCriteria = filterCriteria;
- [self.deviceScanner addListener:self];
- [self.deviceScanner startScan];
startScan:メソッドでデバイスの検出を開始し、addListener:メソッドで指定したリスナークラスに検出結果が通知されます。
CGKFilterCriteriaはなくてもデバイス検出はできますが、CGKFilterCriteriaを設定することで、指定したApplication IDのReceiverアプリを起動できるChromecastデバイスだけを検出させることが可能です。
例えば、iOSアプリはReceiverアプリに先立って公開するものの、後からReceiverアプリをCast Console上で公開するまでの間、iOSアプリのユーザーにCastアイコンを見せたくないケースがあったとします。このようなときにCGKFilterCriteriaを使うと、ReceiverアプリがCast Console上で公開に設定されるまではGCKDeviceScannerはデバイスを検出できないので、Castアイコンも表示されないといった制御が可能になります。
addListener:メソッドで指定したリスナークラスはGCKDeviceScannerListenerプロトコルが定義するメソッドを実装します(コード20)。
- #pragma mark - GCKDeviceScannerListener
- - (void)deviceDidComeOnline:(GCKDevice *)device {
- // Chromecastデバイスが見つかったときに呼び出される
- NSLog(@"device found!!!");
- }
- - (void)deviceDidGoOffline:(GCKDevice *)device {
- // 見つかっていたChromecastデバイスを見失ったときに呼び出される
- NSLog(@"device disappeared!!!");
- }
deviceDidComeOnline:メソッドが呼び出されると、引数のdeviceに検出したデバイスが渡されてきます。初めてこのメソッドが呼ばれたタイミングで、Castアイコンの表示などの制御を行うといいでしょう。
なお、ここで引数に渡されたdeviceの値をアプリが保持しなくても、GCKDeviceScanner のdevicesプロパティで全ての検出済みデバイスを取得できます。
ユーザーはCastメニューで接続先デバイスを選択できますが、そのUIはアプリ開発者が作成します。Castメニュー上で接続先デバイスが選択されたら、Chromecastデバイスとの接続を行います(コード21)。
- GCKDevice *selectedDevice = ...; //ユーザーが選択したデバイス
- deviceManager = [[GCKDeviceManager alloc] initWithDevice:selectedDevice
- clientPackageName:[info objectForKey:@"CFBundleIdentifier"]];
- deviceManager.delegate = self;
- [self.deviceManager connect];
Chromecastデバイスとの接続やReceiverアプリの起動に関する処理は、GCKDeviceManagerクラスを使います。GCKDeviceManagerのconnectメソッドで接続を行い、delegateプロパティに指定したデリゲートに接続の結果が通知されます。デリゲートではGCKDeviceManagerDelegateプロパティが定義するメソッドを実装します(コード22)。
- #pragma mark - GCKDeviceManagerDelegate
- - (void)deviceManagerDidConnect:(GCKDeviceManager *)deviceManager {
- // 接続が成功したときに呼び出される
- NSLog(@"connected!!");
- // Receiverアプリを起動
- [self.deviceManager launchApplication:@"APPLICATION_ID"];
- }
- - (void)deviceManager:(GCKDeviceManager *)deviceManager
- didFailToConnectWithError:(NSError *)error{
- // 接続が失敗したときに呼び出される
- NSLog(@"Connection failed!!");
- }
- - (void)deviceManager:(GCKDeviceManager *)deviceManager
- didConnectToCastApplication:(GCKApplicationMetadata *)applicationMetadata
- sessionID:(NSString *)sessionID
- launchedApplication:(BOOL)launchedApp {
- // Receiverアプリと接続したときに呼び出される
- NSLog(@"Application connected!!");
- }
接続が成功したら、Receiverアプリを起動します。上記のようにGCKDeviceManager のlaunchApplication:メソッドにReceiverアプリのApplication IDを指定して呼び出します。
Receiverアプリが無事に起動するとGCKDeviceManagerDelegateのdeviceManager:didConnectToCastApplication:sessionID:launchedApplication:メソッドが呼ばれます。
次にReceiverアプリとの通信と動画再生について解説します。Receiverアプリとの通信は、Androidと同様にメディアチャンネル・カスタムチャンネルを使います。ここではメディアチャンネルを使った動画再生方法について実装を見てみます。
メディアチャンネルの確立は以下の通りです。GCKMediaControlChannel がメディアチャンネルを表すクラスです(コード23)。
- self.mediaControlChannel = [[GCKMediaControlChannel alloc] init];
- self.mediaControlChannel.delegate = self;
- [self.deviceManager addChannel:self.mediaControlChannel];
GCKMediaControlChannelのdelegateプロパティにはGCKMediaControlChannelDelegateプロトコルを実装したクラスのインスタンスを設定します。GCKMediaControlChannelDelegateプロトコルのメソッドは、メディアプレーヤーの状態が更新されたときに呼び出されます。GCKMediaControlChannelDelegateプロトコルのメソッドは全てoptionalですが、一部の実装例を示します(コード24)。
- #pragma mark - GCKMediaControlChannelDelegate
- - (void)mediaControlChannelDidUpdateStatus:(GCKMediaControlChannel *)mediaControlChannel
- {
- // メディアプレーヤーの状態が更新されたときに呼び出される
- switch (mediaControlChannel.mediaStatus.playerState) {
- case GCKMediaPlayerStatePlaying:
- NSLog(@"Status : Playing");
- break;
- case GCKMediaPlayerStatePaused:
- NSLog(@"Status : Plaused");
- break;
- case GCKMediaPlayerStateIdle:
- NSLog(@"Status : Idle");
- break;
- case GCKMediaPlayerStateBuffering:
- NSLog(@"Status : Buffering");
- break;
- case GCKMediaPlayerStateUnknown:
- default:
- NSLog(@"Status : Unknown");
- break;
- }
- }
- - (void)mediaControlChannelDidUpdateMetadata:(GCKMediaControlChannel *)mediaControlChannel
- {
- // メディアプレーヤーのメタデータが更新されたときに呼び出される
- NSLog(@"metadata updated.");
- }
メディアチャンネルを確立すると、動画をロードして再生できるようになります。次のように実装します(コード25)。
- // メタデータの作成
- GCKMediaMetadata *metadata = [[GCKMediaMetadata alloc] init];
- [metadata setString:title forKey:kGCKMetadataKeyTitle];
- [metadata setString:subtitle forKey:kGCKMetadataKeySubtitle];
- [metadata addImage:[[GCKImage alloc] initWithURL:thumbnailURL width:200 height:100]];
- // GCKMediaInformationインスタンス作成
- GCKMediaInformation *mediaInformation =
- [[GCKMediaInformation alloc] initWithContentID:@"http://your.server.com/video.mp4"
- streamType:GCKMediaStreamTypeNone
- contentType:mimeType
- metadata:metaData
- streamDuration:123
- customData:customDataJson];
- // 動画のロードと再生開始
- [_mediaControlChannel loadMedia:mediaInformation autoplay:autoPlay playPosition:startTime];
Androidと同様に、ロードするコンテンツに関する情報をセットしたGCKMediaInformationのインスタンスを作り、GCKMediaControlChannelのloadMedia:autoplay:playPosition:メソッドを呼び出します。メタデータはGCKMediaMetadataのインスタンスで表します。コンテンツのURLはGCKMediaInformationのインスタンス生成ときに指定します。
メディアプレーヤーの一時停止やシークなどの操作はMediaControlChannelを使って次のようにします(コード26)。
- // 一時停止
- [_mediaControlChannel pause];
- //シーク
- NSTimeInterval position = 60.0;
- [_mediaControlChannel seekToTimeInterval:position];
一時停止などの操作によりメディアプレーヤーの状態が変わると、コード24のmediaControlChannelDidUpdateStatus:メソッドが呼び出されます。また、Androidと同様、他のSenderアプリの操作や、Receiver自身の制御によりメディアプレーヤーの状態が変わった時もmediaControlChannelDidUpdateStatus:メソッドは呼び出されます。
以上でiOSの実装についての解説を終わります。