本開發人員指南說明如何為您的 iOS 新增 Google Cast 支援 傳送者應用程式。
行動裝置或筆記型電腦是控製播放的傳送者, Google Cast 裝置是指用於在電視上顯示內容的接收器。
傳送者架構是指 Cast 類別程式庫二進位檔,以及相關聯的 向傳送端發出的資源配置要求寄件者應用程式或投放應用程式 代表在傳送方上執行的應用程式。網路接收器應用程式 是指在 Web Receiver 上執行的 HTML 應用程式。
傳送方架構採用非同步回呼設計來通知傳送者 事件的應用程式中,以及轉換應用程式在不同狀態的切換
應用程式流程
下列步驟說明傳送者的一般高階執行流程 iOS 應用程式:
- Cast 架構開始
GCKDiscoveryManager
敬上 建立 DeploymentGCKCastOptions
變更為 即可開始掃描裝置。 - 當使用者按一下「投放」按鈕時,架構就會顯示「投放」 對話方塊中顯示偵測到的投放裝置清單。
- 使用者選取投放裝置時,架構會嘗試啟動 Cast 裝置上的 Web Receiver 應用程式
- 該架構會在傳送端應用程式中叫用回呼,以確認 已啟動 Web Receiver 應用程式。
- 這個架構可在傳送者和傳送者之間建立通訊管道 網路接收器應用程式。
- 該架構使用通訊管道來載入及控制媒體 並透過 Web Receiver 播放
- 架構會同步處理傳送者和傳送端的媒體播放狀態 網路接收端:當使用者進行傳送者 UI 動作時,架構會通過 向 Web 接收器發出的媒體控制要求 傳送媒體狀態更新,此架構會更新傳送者 UI 的狀態。
- 當使用者點選「投放」按鈕中斷與投放裝置的連線時, 這個架構會中斷傳送端應用程式與 Web Receiver 的連結。
如要排解寄件者的問題,你必須啟用記錄功能。
查看 Google Cast 的所有課程、方法和活動完整清單 iOS 架構,請參閱 Google Cast iOS API 參考資料。下列各節將說明 將 Cast 整合至 iOS 應用程式
主執行緒的呼叫方法
初始化 Cast 環境
Cast 架構有一個全域單例模式物件
GCKCastContext
,
協調架構所有的活動這個物件必須初始化
通常處於應用程式的生命週期中
應用程式委派的 -[application:didFinishLaunchingWithOptions:]
方法,因此
在傳送方應用程式重新啟動時,可正確觸發工作階段恢復功能。
GCKCastOptions
初始化 GCKCastContext
時必須提供物件。
此類別包含會影響架構行為的選項。最常出現
重要的是網路接收端應用程式 ID
探索結果,並在投放工作階段
已開始。
-[application:didFinishLaunchingWithOptions:]
方法也是個好地方
設定記錄委派來接收架構中的記錄訊息。
很適合用於偵錯和疑難排解。
@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate { let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID let kDebugLoggingEnabled = true var window: UIWindow? func applicationDidFinishLaunching(_ application: UIApplication) { let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID) let options = GCKCastOptions(discoveryCriteria: criteria) GCKCastContext.setSharedInstanceWith(options) // Enable logger. GCKLogger.sharedInstance().delegate = self ... } // MARK: - GCKLoggerDelegate func logMessage(_ message: String, at level: GCKLoggerLevel, fromFunction function: String, location: String) { if (kDebugLoggingEnabled) { print(function + " - " + message) } } }
AppDelegate.h
@interface AppDelegate () <GCKLoggerDelegate> @end
AppDelegate.m
@implementation AppDelegate static NSString *const kReceiverAppID = @"AABBCCDD"; static const BOOL kDebugLoggingEnabled = YES; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc] initWithApplicationID:kReceiverAppID]; GCKCastOptions *options = [[GCKCastOptions alloc] initWithDiscoveryCriteria:criteria]; [GCKCastContext setSharedInstanceWithOptions:options]; // Enable logger. [GCKLogger sharedInstance].delegate = self; ... return YES; } ... #pragma mark - GCKLoggerDelegate - (void)logMessage:(NSString *)message atLevel:(GCKLoggerLevel)level fromFunction:(NSString *)function location:(NSString *)location { if (kDebugLoggingEnabled) { NSLog(@"%@ - %@, %@", function, message, location); } } @end
Cast 使用者體驗小工具
Cast iOS SDK 提供這些符合 Cast 設計需求的小工具 檢查清單:
簡介重疊:
GCKCastContext
類別有方法presentCastInstructionsViewControllerOnceWithCastButton
、 這個按鈕可用來在網路接收端第一次鎖定「投放」按鈕 可以使用。傳送者應用程式可以自訂文字、標題的位置 文字和「關閉」按鈕投放按鈕: 從 Cast iOS 傳送端 SDK 4.6.0 開始,「投放」按鈕一律會顯示 傳送者裝置連上 Wi-Fi 時。使用者初次輕觸 (初次啟動應用程式後按下「投放」按鈕上會顯示權限對話方塊) 可讓使用者將應用程式區域網路存取權授予 網路。之後使用者輕觸「投放」按鈕時,系統就會進行投放 對話方塊會列出找到的裝置。使用者輕觸 裝置連線時,投放按鈕上就會顯示目前的 媒體中繼資料,例如標題、錄音室的名稱和縮圖 圖片),或讓使用者中斷與投放裝置的連線。當使用者 沒有可用裝置時輕觸投放按鈕,也就是畫面 會顯示「找不到裝置的原因」 以及如何排解問題
Mini Controller: 使用者投放內容且離開目前畫面時 內容頁面,或展開控制器連往傳送者應用程式的其他畫面, 螢幕底部顯示迷你控制器 查看目前投放的媒體中繼資料,並控製播放作業。
展開控制器: 當使用者投放內容時,點選媒體通知或 會啟動展開的控制器,並顯示 目前播放媒體中繼資料,並提供多個按鈕來控制 媒體播放。
新增「投放」按鈕
這個架構提供投放按鈕元件做為 UIButton
子類別。這項服務可以
加入至應用程式標題列的 UIBarButtonItem
中。一般
UIViewController
子類別可安裝「投放」按鈕,如下所示:
let castButton = GCKUICastButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24)) castButton.tintColor = UIColor.gray navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)
GCKUICastButton *castButton = [[GCKUICastButton alloc] initWithFrame:CGRectMake(0, 0, 24, 24)]; castButton.tintColor = [UIColor grayColor]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:castButton];
根據預設,輕觸按鈕即可開啟 這個架構的重點在於
GCKUICastButton
敬上
可以直接加入分鏡腳本
設定裝置探索功能
在這個架構中,系統會自動探索裝置。不需要 明確啟動或停止探索程序,除非您實作自訂 UI。
此架構中的探索作業是由類別管理
GCKDiscoveryManager
、
這是
GCKCastContext
。
架構會提供預設的 Cast 對話方塊元件,用來選取裝置
控管功能裝置清單會依裝置易記名稱的字母順序排列。
工作階段管理的運作方式
Cast SDK 引進投放工作階段的概念 結合了連線至裝置、啟動 (或加入) 網路的步驟 接收端應用程式,連結至該應用程式,以及初始化媒體控制管道。查看網路接收端 應用程式生命週期指南 ,進一步瞭解投放工作階段和網路接收端的生命週期。
課程由課程管理
GCKSessionManager
、
這是
GCKCastContext
。
個別工作階段是以類別的子類別表示
GCKSession
:例如
GCKCastSession
則代表有投放裝置的工作階段。你可以存取目前播放中的投放內容
工作階段 (如有),做為 GCKSessionManager
的 currentCastSession
屬性。
GCKSessionManagerListener
敬上
介面可用來監控工作階段事件,例如建立工作階段
停權、恢復和終止架構會自動暫停
工作階段期間,傳送者應用程式進入背景並嘗試繼續執行。
當應用程式返回前景時 (或是在
工作階段執行時,應用程式異常終止/突然終止)。
如果使用「投放」對話方塊,系統會建立工作階段並卸除
來自動回應使用者的手勢否則,應用程式可以啟動及結束
明確方法是
GCKSessionManager
。
應用程式是否需要進行特殊處理,才能回應工作階段生命週期
就可以透過以下指令註冊一或多個 GCKSessionManagerListener
例項
GCKSessionManager
。GCKSessionManagerListener
是一種通訊協定,會定義
工作階段開始和工作階段結束等事件的回呼。
變更串流裝置
保留工作階段狀態是串流傳輸的基礎, 使用者可以使用 Google Home 語音指令,切換不同裝置中的現有音訊和影片串流 應用程式或智慧螢幕。在一部裝置 (來源) 上停止播放媒體,然後於另一部裝置 ( 目的地)。凡是裝有最新韌體的投放裝置,都可以做為來源或目的地 變更串流裝置。
如要在串流傳輸期間取得新的目標裝置,請使用
GCKCastSession#device
敬上
資源期間
[sessionManager:didResumeCastSession:]
回呼。
詳情請見 透過網路接收器轉移串流裝置 瞭解詳情
自動重新連線
Cast 架構會新增重新連線邏輯,自動處理重新連線 許多細緻的案例,例如:
- 在 Wi-Fi 連線暫時中斷的情況下復原
- 從裝置睡眠狀態復原
- 從背景執行復原程序
- 在應用程式當機時復原
媒體控制選項的運作方式
如果您使用支援媒體的 Web 接收器應用程式建立投放工作階段
命名空間
GCKRemoteMediaClient
敬上
會自動建立這個架構;也能做為
remoteMediaClient
屬性
GCKCastSession
執行個體。
向網路接收端發出要求的所有 GCKRemoteMediaClient
方法
將傳回
GCKRequest
物件,
以便追蹤該要求A 罩杯
GCKRequestDelegate
敬上
指派給這個物件,以接收最終通知
作業結果。
系統應該會顯示 GCKRemoteMediaClient
的例項
可能會由應用程式的多個部分共用,而且確實有一些內部元件
「投放」對話方塊和小型媒體控制項等架構的機構
執行個體。在這邊,GCKRemoteMediaClient
可讓您註冊多個
GCKRemoteMediaClientListener
。
設定媒體中繼資料
GCKMediaMetadata
敬上
類別代表您要投放的媒體項目相關資訊。下列
範例會建立新的 GCKMediaMetadata
例項,並設定標題。
副標題、錄音室名稱,和兩張圖片。
let metadata = GCKMediaMetadata() metadata.setString("Big Buck Bunny (2008)", forKey: kGCKMetadataKeyTitle) metadata.setString("Big Buck Bunny tells the story of a giant rabbit with a heart bigger than " + "himself. When one sunny day three rodents rudely harass him, something " + "snaps... and the rabbit ain't no bunny anymore! In the typical cartoon " + "tradition he prepares the nasty rodents a comical revenge.", forKey: kGCKMetadataKeySubtitle) metadata.addImage(GCKImage(url: URL(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg")!, width: 480, height: 360))
GCKMediaMetadata *metadata = [[GCKMediaMetadata alloc] initWithMetadataType:GCKMediaMetadataTypeMovie]; [metadata setString:@"Big Buck Bunny (2008)" forKey:kGCKMetadataKeyTitle]; [metadata setString:@"Big Buck Bunny tells the story of a giant rabbit with a heart bigger than " "himself. When one sunny day three rodents rudely harass him, something " "snaps... and the rabbit ain't no bunny anymore! In the typical cartoon " "tradition he prepares the nasty rodents a comical revenge." forKey:kGCKMetadataKeySubtitle]; [metadata addImage:[[GCKImage alloc] initWithURL:[[NSURL alloc] initWithString:@"https://commondatastorage.googleapis.com/" "gtv-videos-bucket/sample/images/BigBuckBunny.jpg"] width:480 height:360]];
請參閱圖片選取與 快取 一節。
載入媒體
如要載入媒體項目,請建立
GCKMediaInformation
敬上
執行個體。然後取得最新的
GCKCastSession
和
使用
GCKRemoteMediaClient
要在接收器應用程式上載入媒體。接著,您可以使用 GCKRemoteMediaClient
用於控制接收器上執行的媒體播放器應用程式,例如播放媒體
暫停及停止播放
let url = URL.init(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4") guard let mediaURL = url else { print("invalid mediaURL") return } let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: mediaURL) mediaInfoBuilder.streamType = GCKMediaStreamType.none; mediaInfoBuilder.contentType = "video/mp4" mediaInfoBuilder.metadata = metadata; mediaInformation = mediaInfoBuilder.build() guard let mediaInfo = mediaInformation else { print("invalid mediaInformation") return } if let request = sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInfo) { request.delegate = self }
GCKMediaInformationBuilder *mediaInfoBuilder = [[GCKMediaInformationBuilder alloc] initWithContentURL: [NSURL URLWithString:@"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"]]; mediaInfoBuilder.streamType = GCKMediaStreamTypeNone; mediaInfoBuilder.contentType = @"video/mp4"; mediaInfoBuilder.metadata = metadata; self.mediaInformation = [mediaInfoBuilder build]; GCKRequest *request = [self.sessionManager.currentSession.remoteMediaClient loadMedia:self.mediaInformation]; if (request != nil) { request.delegate = self; }
另請參閱 使用媒體曲目。
4K 影片格式
如要判斷您的媒體影片格式,請使用 videoInfo
屬性
GCKMediaStatus
來取得目前的
GCKVideoInfo
。
這個執行個體包含 HDR TV 格式的類型,以及
像素。4K 格式的變化版本會以列舉方式在 hdrType
屬性中指定
值為 GCKVideoInfoHDRType
。
新增迷你控制器
根據 Cast Design 檢查清單、 傳送者應用程式應提供永久性控制項,稱為 mini 控制器 應該就能在使用者離開目前內容網頁時顯示。 迷你控制器可即時存取 目前的投放工作階段。
Cast 架構提供了控制列
GCKUIMiniMediaControlsViewController
、
您可在要顯示迷你控制器的場景中加入這個圖示。
當傳送方應用程式播放影片或音訊直播時,SDK 會自動在播放/暫停按鈕旁顯示播放/停止按鈕 小控制器中
請參閱自訂 iOS 寄件者 UI,瞭解 傳送端應用程式可以設定 Cast 小工具的外觀。
你可以透過下列兩種方式在傳送端應用程式中新增迷你控制器:
- 透過包裝的方式,讓 Cast 架構管理迷你控制器的版面配置 同時與您現有的檢視控制器搭配使用
- 將迷你控制器小工具配置到 在分鏡腳本中提供子檢視畫面
使用 GCKUICastContainerViewController
第一種方法是使用
GCKUICastContainerViewController
敬上
其中包含另一個檢視控制器,並新增了
GCKUIMiniMediaControlsViewController
。這種做法受到限制,導致您無法自訂
而且無法設定容器檢視控制器的行為。
第一種方法是在
應用程式委派的 -[application:didFinishLaunchingWithOptions:]
方法:
func applicationDidFinishLaunching(_ application: UIApplication) { ... // Wrap main view in the GCKUICastContainerViewController and display the mini controller. let appStoryboard = UIStoryboard(name: "Main", bundle: nil) let navigationController = appStoryboard.instantiateViewController(withIdentifier: "MainNavigation") let castContainerVC = GCKCastContext.sharedInstance().createCastContainerController(for: navigationController) castContainerVC.miniMediaControlsItemEnabled = true window = UIWindow(frame: UIScreen.main.bounds) window!.rootViewController = castContainerVC window!.makeKeyAndVisible() ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... // Wrap main view in the GCKUICastContainerViewController and display the mini controller. UIStoryboard *appStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; UINavigationController *navigationController = [appStoryboard instantiateViewControllerWithIdentifier:@"MainNavigation"]; GCKUICastContainerViewController *castContainerVC = [[GCKCastContext sharedInstance] createCastContainerControllerForViewController:navigationController]; castContainerVC.miniMediaControlsItemEnabled = YES; self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; self.window.rootViewController = castContainerVC; [self.window makeKeyAndVisible]; ... }
var castControlBarsEnabled: Bool { set(enabled) { if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController { castContainerVC.miniMediaControlsItemEnabled = enabled } else { print("GCKUICastContainerViewController is not correctly configured") } } get { if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController { return castContainerVC.miniMediaControlsItemEnabled } else { print("GCKUICastContainerViewController is not correctly configured") return false } } }
AppDelegate.h
@interface AppDelegate : UIResponder <UIApplicationDelegate> @property (nonatomic, strong) UIWindow *window; @property (nonatomic, assign) BOOL castControlBarsEnabled; @end
AppDelegate.m
@implementation AppDelegate ... - (void)setCastControlBarsEnabled:(BOOL)notificationsEnabled { GCKUICastContainerViewController *castContainerVC; castContainerVC = (GCKUICastContainerViewController *)self.window.rootViewController; castContainerVC.miniMediaControlsItemEnabled = notificationsEnabled; } - (BOOL)castControlBarsEnabled { GCKUICastContainerViewController *castContainerVC; castContainerVC = (GCKUICastContainerViewController *)self.window.rootViewController; return castContainerVC.miniMediaControlsItemEnabled; } ... @end
嵌入現有檢視控制器
第二種做法是直接將迷你控制器新增到現有檢視畫面
透過 Kubernetes API
createMiniMediaControlsViewController
敬上
來建立
GCKUIMiniMediaControlsViewController
然後將該執行個體新增為容器檢視控制器的子檢視畫面。
在應用程式委派項目中設定檢視控制器:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { ... GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true window?.clipsToBounds = true let rootContainerVC = (window?.rootViewController as? RootContainerViewController) rootContainerVC?.miniMediaControlsViewEnabled = true ... return true }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; self.window.clipsToBounds = YES; RootContainerViewController *rootContainerVC; rootContainerVC = (RootContainerViewController *)self.window.rootViewController; rootContainerVC.miniMediaControlsViewEnabled = YES; ... return YES; }
在根檢視區塊控制器中建立 GCKUIMiniMediaControlsViewController
並將其加入容器檢視控制器,做為子檢視:
let kCastControlBarsAnimationDuration: TimeInterval = 0.20 @objc(RootContainerViewController) class RootContainerViewController: UIViewController, GCKUIMiniMediaControlsViewControllerDelegate { @IBOutlet weak private var _miniMediaControlsContainerView: UIView! @IBOutlet weak private var _miniMediaControlsHeightConstraint: NSLayoutConstraint! private var miniMediaControlsViewController: GCKUIMiniMediaControlsViewController! var miniMediaControlsViewEnabled = false { didSet { if self.isViewLoaded { self.updateControlBarsVisibility() } } } var overriddenNavigationController: UINavigationController? override var navigationController: UINavigationController? { get { return overriddenNavigationController } set { overriddenNavigationController = newValue } } var miniMediaControlsItemEnabled = false override func viewDidLoad() { super.viewDidLoad() let castContext = GCKCastContext.sharedInstance() self.miniMediaControlsViewController = castContext.createMiniMediaControlsViewController() self.miniMediaControlsViewController.delegate = self self.updateControlBarsVisibility() self.installViewController(self.miniMediaControlsViewController, inContainerView: self._miniMediaControlsContainerView) } func updateControlBarsVisibility() { if self.miniMediaControlsViewEnabled && self.miniMediaControlsViewController.active { self._miniMediaControlsHeightConstraint.constant = self.miniMediaControlsViewController.minHeight self.view.bringSubview(toFront: self._miniMediaControlsContainerView) } else { self._miniMediaControlsHeightConstraint.constant = 0 } UIView.animate(withDuration: kCastControlBarsAnimationDuration, animations: {() -> Void in self.view.layoutIfNeeded() }) self.view.setNeedsLayout() } func installViewController(_ viewController: UIViewController?, inContainerView containerView: UIView) { if let viewController = viewController { self.addChildViewController(viewController) viewController.view.frame = containerView.bounds containerView.addSubview(viewController.view) viewController.didMove(toParentViewController: self) } } func uninstallViewController(_ viewController: UIViewController) { viewController.willMove(toParentViewController: nil) viewController.view.removeFromSuperview() viewController.removeFromParentViewController() } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "NavigationVCEmbedSegue" { self.navigationController = (segue.destination as? UINavigationController) } } ...
RootContainerViewController.h
static const NSTimeInterval kCastControlBarsAnimationDuration = 0.20; @interface RootContainerViewController () <GCKUIMiniMediaControlsViewControllerDelegate> { __weak IBOutlet UIView *_miniMediaControlsContainerView; __weak IBOutlet NSLayoutConstraint *_miniMediaControlsHeightConstraint; GCKUIMiniMediaControlsViewController *_miniMediaControlsViewController; } @property(nonatomic, weak, readwrite) UINavigationController *navigationController; @property(nonatomic, assign, readwrite) BOOL miniMediaControlsViewEnabled; @property(nonatomic, assign, readwrite) BOOL miniMediaControlsItemEnabled; @end
RootContainerViewController.m
@implementation RootContainerViewController - (void)viewDidLoad { [super viewDidLoad]; GCKCastContext *castContext = [GCKCastContext sharedInstance]; _miniMediaControlsViewController = [castContext createMiniMediaControlsViewController]; _miniMediaControlsViewController.delegate = self; [self updateControlBarsVisibility]; [self installViewController:_miniMediaControlsViewController inContainerView:_miniMediaControlsContainerView]; } - (void)setMiniMediaControlsViewEnabled:(BOOL)miniMediaControlsViewEnabled { _miniMediaControlsViewEnabled = miniMediaControlsViewEnabled; if (self.isViewLoaded) { [self updateControlBarsVisibility]; } } - (void)updateControlBarsVisibility { if (self.miniMediaControlsViewEnabled && _miniMediaControlsViewController.active) { _miniMediaControlsHeightConstraint.constant = _miniMediaControlsViewController.minHeight; [self.view bringSubviewToFront:_miniMediaControlsContainerView]; } else { _miniMediaControlsHeightConstraint.constant = 0; } [UIView animateWithDuration:kCastControlBarsAnimationDuration animations:^{ [self.view layoutIfNeeded]; }]; [self.view setNeedsLayout]; } - (void)installViewController:(UIViewController *)viewController inContainerView:(UIView *)containerView { if (viewController) { [self addChildViewController:viewController]; viewController.view.frame = containerView.bounds; [containerView addSubview:viewController.view]; [viewController didMoveToParentViewController:self]; } } - (void)uninstallViewController:(UIViewController *)viewController { [viewController willMoveToParentViewController:nil]; [viewController.view removeFromSuperview]; [viewController removeFromParentViewController]; } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"NavigationVCEmbedSegue"]) { self.navigationController = (UINavigationController *)segue.destinationViewController; } } ... @end
GCKUIMiniMediaControlsViewControllerDelegate
敬上
會在應顯示迷你控制器時通知主機檢視控制器:
func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController, shouldAppear _: Bool) { updateControlBarsVisibility() }
- (void)miniMediaControlsViewController: (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController shouldAppear:(BOOL)shouldAppear { [self updateControlBarsVisibility]; }
新增展開控制器
根據 Google Cast 設計檢查清單要求傳送者應用程式必須提供已展開的 控制器 不同媒體展開的控制器是全螢幕版本 小控制器
展開的控制器為全螢幕檢視畫面,可讓您完全掌控 遠端媒體播放。這個檢視畫面應可讓投放應用程式管理 可管理的投放工作階段部分 (網路接收器磁碟區除外) 控制和工作階段生命週期 (連線/停止投放)。這個平台也提供 媒體工作階段的狀態資訊 (圖片、標題、字幕等) )。
這個檢視畫面的功能是由
GCKUIExpandedMediaControlsViewController
敬上
類別
首先,您必須在使用者介面中啟用 投放內容。修改應用程式委派,啟用預設展開控制器:
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; .. }
將下列程式碼加入檢視控制器,載入展開控制器 使用者開始投放影片時:
func playSelectedItemRemotely() { GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls() ... // Load your media sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation) }
- (void)playSelectedItemRemotely { [[GCKCastContext sharedInstance] presentDefaultExpandedMediaControls]; ... // Load your media [self.sessionManager.currentSession.remoteMediaClient loadMedia:mediaInformation]; }
當使用者 就會輕觸迷你控制器
當傳送方應用程式播放影片或音訊直播時,SDK 會自動在播放/暫停按鈕旁顯示播放/停止按鈕 。
請參閱將自訂樣式套用至 iOS 裝置 應用程式 ,瞭解傳送者應用程式如何設定 Cast 小工具的外觀。
音量控制項
Cast 架構會自動管理傳送者應用程式的音量。
架構會自動與網路接收端磁碟區同步
提供的 UI 小工具如要同步處理應用程式提供的滑桿,請使用
GCKUIDeviceVolumeController
。
實體按鈕音量控制
寄件者裝置上的實體音量按鈕可用來變更
從網路接收端取得投放工作階段的音量
有 physicalVolumeButtonsWillControlDeviceVolume
旗標
GCKCastOptions
,
設為
GCKCastContext
。
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID) let options = GCKCastOptions(discoveryCriteria: criteria) options.physicalVolumeButtonsWillControlDeviceVolume = true GCKCastContext.setSharedInstanceWith(options)
GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc] initWithApplicationID:kReceiverAppID]; GCKCastOptions *options = [[GCKCastOptions alloc] initWithDiscoveryCriteria :criteria]; options.physicalVolumeButtonsWillControlDeviceVolume = YES; [GCKCastContext setSharedInstanceWithOptions:options];
處理錯誤
傳送方應用程式必須處理所有錯誤回呼並判斷 Cast 生命週期各階段的最佳回應。應用程式可顯示 錯誤訊息或指示結束「投放」工作階段。
記錄
GCKLogger
敬上
是架構用來記錄的單例模式。使用
GCKLoggerDelegate
敬上
自訂記錄訊息的處理方式。
SDK 會使用 GCKLogger
產生偵錯形式的記錄輸出內容
訊息、錯誤和警告這些記錄訊息可協助偵錯,
,用於疑難排解及找出問題。預設情況下,記錄輸出為
未封鎖,但如果指派 GCKLoggerDelegate
,傳送端應用程式即可接收
並將這些訊息記錄到系統主控台。
@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate { let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID let kDebugLoggingEnabled = true var window: UIWindow? func applicationDidFinishLaunching(_ application: UIApplication) { ... // Enable logger. GCKLogger.sharedInstance().delegate = self ... } // MARK: - GCKLoggerDelegate func logMessage(_ message: String, at level: GCKLoggerLevel, fromFunction function: String, location: String) { if (kDebugLoggingEnabled) { print(function + " - " + message) } } }
AppDelegate.h
@interface AppDelegate () <GCKLoggerDelegate> @end
AppDelegate.m
@implementation AppDelegate static NSString *const kReceiverAppID = @"AABBCCDD"; static const BOOL kDebugLoggingEnabled = YES; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... // Enable logger. [GCKLogger sharedInstance].delegate = self; ... return YES; } ... #pragma mark - GCKLoggerDelegate - (void)logMessage:(NSString *)message atLevel:(GCKLoggerLevel)level fromFunction:(NSString *)function location:(NSString *)location { if (kDebugLoggingEnabled) { NSLog(@"%@ - %@, %@", function, message, location); } } @end
如果也要啟用偵錯和詳細訊息,請在程式碼的後方 設定委派項目 (如上所示):
let filter = GCKLoggerFilter.init() filter.minimumLevel = GCKLoggerLevel.verbose GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setMinimumLevel:GCKLoggerLevelVerbose]; [GCKLogger sharedInstance].filter = filter;
另外,您也可以篩選
GCKLogger
。
為各類別設定最低記錄等級,例如:
let filter = GCKLoggerFilter.init() filter.setLoggingLevel(GCKLoggerLevel.verbose, forClasses: ["GCKUICastButton", "GCKUIImageCache", "NSMutableDictionary"]) GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setLoggingLevel:GCKLoggerLevelVerbose forClasses:@[@"GCKUICastButton", @"GCKUIImageCache", @"NSMutableDictionary" ]]; [GCKLogger sharedInstance].filter = filter;
類別名稱可以是常值名稱或 glob 模式,例如
《GCKUI\*
》和《GCK\*Session
》。