最終更新日:190227原本2015-05-13 

Chromecastアプリ開発入門:
Google Cast SDKを使ったAndroid/iOSアプリの作り方と注意点 (5/6)

iOSアプリ開発方法

 次にiOSについて見ていきます。グーグルからiOS用のサンプルプログラムもGitHub上で公開されています

Androidとの違い

 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アプリが使用するフレームワークを取り込みます。

  1. GoogleCast.framework
  2. SystemConfiguration.framework
  3. MediaAccessibility.framework(for iOS 7 and later)
  4. CoreText.framework(for iOS 7 and later)

 XcodeプロジェクトのLinked Frameworks and Librariesに上記フレームワークを登録します。1.はGoogleが提供するフレームワークで、公式サイトからダウンロードする必要があります。それ以外はiOS SDKに同梱されているフレームワークなので、特にダウンロードする必要はありません。

Chromecastデバイスの検出と接続先選択

 次に実装を見ていきましょう。Chromecastデバイスの検出はGCKDeviceScannerクラスを使います(コード19)。

  1. self.deviceScanner = [[GCKDeviceScanner alloc] init];
  2. GCKFilterCriteria *filterCriteria = [[GCKFilterCriteria alloc] init];
  3. filterCriteria = [GCKFilterCriteria criteriaForAvailableApplicationWithID:@"APPLICATION_ID"];
  4. self.deviceScanner.filterCriteria = filterCriteria;
  5. [self.deviceScanner addListener:self];
  6. [self.deviceScanner startScan];
コード19 Chromecastデバイスの検出開始

 startScan:メソッドでデバイスの検出を開始し、addListener:メソッドで指定したリスナークラスに検出結果が通知されます。

 CGKFilterCriteriaはなくてもデバイス検出はできますが、CGKFilterCriteriaを設定することで、指定したApplication IDのReceiverアプリを起動できるChromecastデバイスだけを検出させることが可能です。

 例えば、iOSアプリはReceiverアプリに先立って公開するものの、後からReceiverアプリをCast Console上で公開するまでの間、iOSアプリのユーザーにCastアイコンを見せたくないケースがあったとします。このようなときにCGKFilterCriteriaを使うと、ReceiverアプリがCast Console上で公開に設定されるまではGCKDeviceScannerはデバイスを検出できないので、Castアイコンも表示されないといった制御が可能になります。

 addListener:メソッドで指定したリスナークラスはGCKDeviceScannerListenerプロトコルが定義するメソッドを実装します(コード20)。

  1. #pragma mark - GCKDeviceScannerListener
  2. - (void)deviceDidComeOnline:(GCKDevice *)device {
  3. // Chromecastデバイスが見つかったときに呼び出される
  4. NSLog(@"device found!!!");
  5. }
  6. - (void)deviceDidGoOffline:(GCKDevice *)device {
  7. // 見つかっていたChromecastデバイスを見失ったときに呼び出される
  8. NSLog(@"device disappeared!!!");
  9. }
コード20  GCKDeviceScannerListenerのメソッド実装

 deviceDidComeOnline:メソッドが呼び出されると、引数のdeviceに検出したデバイスが渡されてきます。初めてこのメソッドが呼ばれたタイミングで、Castアイコンの表示などの制御を行うといいでしょう。

 なお、ここで引数に渡されたdeviceの値をアプリが保持しなくても、GCKDeviceScanner のdevicesプロパティで全ての検出済みデバイスを取得できます。

Chromecastデバイスとの接続とReceiverアプリの起動

 ユーザーはCastメニューで接続先デバイスを選択できますが、そのUIはアプリ開発者が作成します。Castメニュー上で接続先デバイスが選択されたら、Chromecastデバイスとの接続を行います(コード21)。

  1. GCKDevice *selectedDevice = ...; //ユーザーが選択したデバイス
  2. deviceManager = [[GCKDeviceManager alloc] initWithDevice:selectedDevice
  3. clientPackageName:[info objectForKey:@"CFBundleIdentifier"]];
  4. deviceManager.delegate = self;
  5. [self.deviceManager connect];
コード21 Chromecastデバイスとの接続

 Chromecastデバイスとの接続やReceiverアプリの起動に関する処理は、GCKDeviceManagerクラスを使います。GCKDeviceManagerのconnectメソッドで接続を行い、delegateプロパティに指定したデリゲートに接続の結果が通知されます。デリゲートではGCKDeviceManagerDelegateプロパティが定義するメソッドを実装します(コード22)。

  1. #pragma mark - GCKDeviceManagerDelegate
  2. - (void)deviceManagerDidConnect:(GCKDeviceManager *)deviceManager {
  3. // 接続が成功したときに呼び出される
  4. NSLog(@"connected!!");
  5. // Receiverアプリを起動
  6. [self.deviceManager launchApplication:@"APPLICATION_ID"];
  7. }
  8. - (void)deviceManager:(GCKDeviceManager *)deviceManager
  9. didFailToConnectWithError:(NSError *)error{
  10. // 接続が失敗したときに呼び出される
  11. NSLog(@"Connection failed!!");
  12. }
  13. - (void)deviceManager:(GCKDeviceManager *)deviceManager
  14. didConnectToCastApplication:(GCKApplicationMetadata *)applicationMetadata
  15. sessionID:(NSString *)sessionID
  16. launchedApplication:(BOOL)launchedApp {
  17. // Receiverアプリと接続したときに呼び出される
  18. NSLog(@"Application connected!!");
  19. }
コード22 GCKDeviceManagerDelegateのメソッド実装

 接続が成功したら、Receiverアプリを起動します。上記のようにGCKDeviceManager のlaunchApplication:メソッドにReceiverアプリのApplication IDを指定して呼び出します。

 Receiverアプリが無事に起動するとGCKDeviceManagerDelegateのdeviceManager:didConnectToCastApplication:sessionID:launchedApplication:メソッドが呼ばれます。

チャンネル確立と動画再生

 次にReceiverアプリとの通信と動画再生について解説します。Receiverアプリとの通信は、Androidと同様にメディアチャンネル・カスタムチャンネルを使います。ここではメディアチャンネルを使った動画再生方法について実装を見てみます。

 メディアチャンネルの確立は以下の通りです。GCKMediaControlChannel がメディアチャンネルを表すクラスです(コード23)。

  1. self.mediaControlChannel = [[GCKMediaControlChannel alloc] init];
  2. self.mediaControlChannel.delegate = self;
  3. [self.deviceManager addChannel:self.mediaControlChannel];
コード23 メディアチャンネルの確立

 GCKMediaControlChannelのdelegateプロパティにはGCKMediaControlChannelDelegateプロトコルを実装したクラスのインスタンスを設定します。GCKMediaControlChannelDelegateプロトコルのメソッドは、メディアプレーヤーの状態が更新されたときに呼び出されます。GCKMediaControlChannelDelegateプロトコルのメソッドは全てoptionalですが、一部の実装例を示します(コード24)。

  1. #pragma mark - GCKMediaControlChannelDelegate
  2. - (void)mediaControlChannelDidUpdateStatus:(GCKMediaControlChannel *)mediaControlChannel
  3. {
  4. // メディアプレーヤーの状態が更新されたときに呼び出される
  5. switch (mediaControlChannel.mediaStatus.playerState) {
  6. case GCKMediaPlayerStatePlaying:
  7. NSLog(@"Status : Playing");
  8. break;
  9. case GCKMediaPlayerStatePaused:
  10. NSLog(@"Status : Plaused");
  11. break;
  12. case GCKMediaPlayerStateIdle:
  13. NSLog(@"Status : Idle");
  14. break;
  15. case GCKMediaPlayerStateBuffering:
  16. NSLog(@"Status : Buffering");
  17. break;
  18. case GCKMediaPlayerStateUnknown:
  19. default:
  20. NSLog(@"Status : Unknown");
  21. break;
  22. }
  23. }
  24. - (void)mediaControlChannelDidUpdateMetadata:(GCKMediaControlChannel *)mediaControlChannel
  25. {
  26. // メディアプレーヤーのメタデータが更新されたときに呼び出される
  27. NSLog(@"metadata updated.");
  28. }
コード24 GCKMediaControlChannelDelegateプロトコルのメソッド実装例

 メディアチャンネルを確立すると、動画をロードして再生できるようになります。次のように実装します(コード25)。

  1. // メタデータの作成
  2. GCKMediaMetadata *metadata = [[GCKMediaMetadata alloc] init];
  3. [metadata setString:title forKey:kGCKMetadataKeyTitle];
  4. [metadata setString:subtitle forKey:kGCKMetadataKeySubtitle];
  5. [metadata addImage:[[GCKImage alloc] initWithURL:thumbnailURL width:200 height:100]];
  6. // GCKMediaInformationインスタンス作成
  7. GCKMediaInformation *mediaInformation =
  8. [[GCKMediaInformation alloc] initWithContentID:@"http://your.server.com/video.mp4"
  9. streamType:GCKMediaStreamTypeNone
  10. contentType:mimeType
  11. metadata:metaData
  12. streamDuration:123
  13. customData:customDataJson];
  14. // 動画のロードと再生開始
  15. [_mediaControlChannel loadMedia:mediaInformation autoplay:autoPlay playPosition:startTime];
コード25 動画のロード・再生開始

 Androidと同様に、ロードするコンテンツに関する情報をセットしたGCKMediaInformationのインスタンスを作り、GCKMediaControlChannelのloadMedia:autoplay:playPosition:メソッドを呼び出します。メタデータはGCKMediaMetadataのインスタンスで表します。コンテンツのURLはGCKMediaInformationのインスタンス生成ときに指定します。

 メディアプレーヤーの一時停止やシークなどの操作はMediaControlChannelを使って次のようにします(コード26)。

  1. // 一時停止
  2. [_mediaControlChannel pause];
  3. //シーク
  4. NSTimeInterval position = 60.0;
  5. [_mediaControlChannel seekToTimeInterval:position];
コード26 一時停止とシーク

 一時停止などの操作によりメディアプレーヤーの状態が変わると、コード24のmediaControlChannelDidUpdateStatus:メソッドが呼び出されます。また、Androidと同様、他のSenderアプリの操作や、Receiver自身の制御によりメディアプレーヤーの状態が変わった時もmediaControlChannelDidUpdateStatus:メソッドは呼び出されます。

 以上でiOSの実装についての解説を終わります。