Edited at

iOSでのChromCast対応 ② Cast端末にアクセスする

More than 3 years have passed since last update.

前回で準備が完了しましたので、実際に単純な情報をCastしてみようと思います。

完了していない方はこちら

今回は、押したボタンの色を送り、TVに側の背景色を変えるテストアプリを作ってみます。

言語: Swift2.0

環境: xCode7.3(bata)


関連記事はこちら。


GoogleCast端末にアクセスする


① まずはログをセット

class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
GCKLogger.sharedInstance().delegate = self
return true
}
}

extension AppDelegate: GCKLoggerDelegate {

func logFromFunction(function: UnsafePointer<Int8>, message: String!) {
let functionName = String.fromCString(function)
print(functionName! + " - " + message)
}
}


② 端末検索準備をする

今回は、Cast側の画面(Receiverアプリ)のカスタマイズは必要無いのでデフォルトのID(kGCKMediaDefaultReceiverApplicationID)を使用します

    override func viewDidLoad() {

super.viewDidLoad()
// Establish filter criteria.
let filterCriteria = GCKFilterCriteria(forAvailableApplicationWithID: kGCKMediaDefaultReceiverApplicationID)
// Initialize device scanner.
let scanner = GCKDeviceScanner(filterCriteria: filterCriteria)
scanner.addListener(self)
scanner.startScan()
deviceScanner = scanner
}


@トラブルシューティング

ここで不明なエラーが発生。起動時に落ちてしまう。

エラーはunrecognized selector sent to instanceと出てる。『そんな関数ないよ』?

どうやらここscanner.startScan()でかえってくるイベントが受信できていないようだ。

と、悩んでいたらこのサイトで解決策を入手。

Other Linker Flags-ObjCを追加で治りました。


③ 端末の接続状態を知る

GCKDeviceScannerListenerをセット

//

// MARK: - GCKDeviceScannerListener
//
extension ViewController: GCKDeviceScannerListener {

func deviceDidComeOnline(device: GCKDevice) {
print("Device found: \(device.friendlyName)")
setApeerCastButton()
}

func deviceDidGoOffline(device: GCKDevice) {
print("Device went away: \(device.friendlyName)")
setDisapeerCastButton()
}

func deviceDidChange(device: GCKDevice) {
print("deviceDidChange()")
}
}

ここでは、あらかじめ作成しておいたボタンを消したり出したりすることにした。

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

chromecastButton = UIButton(type: .Custom)
chromecastButton.addTarget(self, action: "didDownCastButton:", forControlEvents: .TouchDown)
chromecastButton.frame = CGRectMake(0, 20, 39, 34)
chromecastButton.setImage(nil, forState: .Normal)
view.addSubview(chromecastButton)
}

private func setApeerCastButton() {
chromecastButton.setImage(buttonImage, forState: .Normal)
}
private func setDisapeerCastButton() {
chromecastButton.setImage(buttonImage, forState: .Normal)
}
}

これで接続できるdeviceを検出した時(deviceDidComeOnline())と検出できなかった時(deviceDidGoOffline())の動作が決まった。


④ 接続可能deviceの一覧を出す

UIActionSheetを使って、接続可能端末の一覧を出します。


@IBAction func didDownCastButton(sender:AnyObject) {
if selectedDevice == nil {
showDeviceList()
} else {
showSelectedDevice()
}
}

private func showDeviceList() {
let sheet : UIActionSheet = UIActionSheet(title: "Connect to Device", delegate: self, cancelButtonTitle: nil, destructiveButtonTitle: nil)

if let devices = deviceScanner?.devices {
for device in devices {
sheet.addButtonWithTitle(device.friendlyName)
}
}
// Add the cancel button at the end so that indexes of the titles map to the array index.
sheet.addButtonWithTitle(cancelTitle)
sheet.cancelButtonIndex = sheet.numberOfButtons - 1
sheet.showInView(self.view)
}

private func showSelectedDevice() {
updateStatsFromDevice()
let friendlyName = "Casting to \(selectedDevice!.friendlyName)"

let sheet : UIActionSheet = UIActionSheet(title: friendlyName, delegate: self, cancelButtonTitle: nil, destructiveButtonTitle: nil)
var buttonIndex = 0

if let info = mediaInformation {
sheet.addButtonWithTitle(info.metadata.objectForKey(kGCKMetadataKeyTitle) as? String)
buttonIndex++
}

// Offer disconnect option.
sheet.addButtonWithTitle(disconnectTitle)
sheet.addButtonWithTitle(cancelTitle)
sheet.destructiveButtonIndex = buttonIndex++
sheet.cancelButtonIndex = buttonIndex

sheet.showInView(view)
}

UIActionSheetDelegateGCKDeviceManagerDelegateをセット


//
// MARK: UIActionSheetDelegate
//
extension ViewController: UIActionSheetDelegate {

func actionSheet(actionSheet: UIActionSheet, clickedButtonAtIndex buttonIndex: Int) {
print("actionSheet() buttonIndex = \(buttonIndex)")
if (buttonIndex == actionSheet.cancelButtonIndex) { return }

if (selectedDevice == nil) {
if (buttonIndex < deviceScanner?.devices.count) {
selectedDevice = deviceScanner?.devices[buttonIndex] as? GCKDevice
print("Selected device: \(selectedDevice!.friendlyName)")
connectToDevice()
}
} else if (actionSheet.buttonTitleAtIndex(buttonIndex) == disconnectTitle) {
// Disconnect button.
deviceManager?.leaveApplication()
deviceManager?.disconnect()
deviceDisconnected()
updateButtonStates()
}
}
}

//
// MARK: GCKDeviceManagerDelegate
//
extension ViewController: GCKDeviceManagerDelegate {

func deviceManagerDidConnect(deviceManager: GCKDeviceManager!) {
print("deviceManagerDidConnect()")

updateButtonStates()
deviceManager.launchApplication(receiverAppID)
}

func deviceManager(deviceManager: GCKDeviceManager,
didConnectToCastApplication applicationMetadata: GCKApplicationMetadata,
sessionID: String,
launchedApplication: Bool) {
print("Application has launched.")
mediaControlChannel = GCKMediaControlChannel()
mediaControlChannel?.delegate = self
deviceManager.addChannel(mediaControlChannel)
mediaControlChannel?.requestStatus()
}

func deviceManager(deviceManager: GCKDeviceManager!, didFailToConnectToApplicationWithError error: NSError) {
print("Received notification that device failed to connect to application.")
setDisConnecteView(error)
}

func deviceManager(deviceManager: GCKDeviceManager!, didFailToConnectWithError error: NSError!) {
print("Received notification that device failed to connect.")
setDisConnecteView(error)
}

func deviceManager(deviceManager: GCKDeviceManager!, didDisconnectWithError error: NSError!) {
print("Received notification that device disconnected.")
setDisConnecteView(error)
}

func deviceManager(deviceManager: GCKDeviceManager!, didReceiveApplicationMetadata metadata: GCKApplicationMetadata!) {
applicationMetadata = metadata
}

private func setDisConnecteView(error: NSError?) {
if let error = error { showError(error) }
deviceDisconnected()
updateButtonStates()
}

private func connectToDevice() {
if (selectedDevice == nil) { return }
let identifier = NSBundle.mainBundle().infoDictionary?["CFBundleIdentifier"] as! String
deviceManager = GCKDeviceManager(device: selectedDevice, clientPackageName: identifier)
print("connectToDevice() deviceManager = \(deviceManager)")
deviceManager?.delegate = self
deviceManager?.connect()
}

private func deviceDisconnected() {
print("deviceDisconnected()")
selectedDevice = nil
deviceManager = nil
}
}

これでボタン押下時にdeviceの選択ができるようになった

接続していない時(左)・すでに接続している時(右)

スクリーンショット 2015-09-11 20.06.18.png


これでCast端末への接続はできました。

次はiOSでのChromCast対応 ③ 接続先に映像を送ってみるをやります。



参考サイト