本开发者指南介绍了如何为 iOS 设备添加 Google Cast 支持 发送应用。
移动设备或笔记本电脑是控制播放的发送方,并且 Google Cast 设备是在电视上显示内容的接收器。
发送器框架是指 Cast 类库二进制文件和相关联的 发送器上存在的资源发送方应用或投屏应用 指同时在发送端运行的应用。Web 接收器应用 是指在网络接收器上运行的 HTML 应用。
发送者框架使用异步回调设计来告知发送者 事件应用,以及在 Cast 应用生命周期的各种状态之间转换 循环。
应用流程
以下步骤描述了发送者的典型概要执行流程 iOS 应用:
- Cast 框架启动
GCKDiscoveryManager
其中提供的属性GCKCastOptions
至 开始扫描设备。 - 当用户点击“投射”按钮时,框架会显示“投射”按钮 对话框,其中包含发现的 Cast 设备列表。
- 当用户选择 Cast 设备时,框架会尝试启动 Cast 设备上的 Web 接收器应用。
- 框架调用发送者应用中的回调来确认 Web 接收器应用已启动。
- 该框架在发送者和 Web 接收器应用。
- 该框架使用通信渠道加载和控制媒体 在网络接收器上播放。
- 该框架会在发送器和 Web 接收器:当用户执行发送器界面操作时,框架会传递 向网络接收器发送这些媒体控制请求,以及 发送媒体状态更新时,框架会更新发送者界面的状态。
- 当用户点击“投射”按钮以断开与投射设备的连接时, 框架将断开发送器应用与网络接收器的连接。
要对发件人进行问题排查,您需要启用日志记录。
有关 Google Cast 中所有类、方法和事件的完整列表 iOS 框架,请参阅 Google Cast iOS API 参考文档。以下各部分介绍了相关步骤 了解如何将 Cast 集成到 iOS 应用中。
从主线程调用方法
初始化 Cast 上下文
Cast 框架有一个全局单例对象,即
GCKCastContext
,
协调框架的所有活动此对象必须初始化
在应用程序生命周期的早期阶段,
-[application:didFinishLaunchingWithOptions:]
方法,因此
确保发件人应用重启时可以正确触发自动会话恢复功能。
GCKCastOptions
对象时,必须提供对象。GCKCastContext
此类包含会影响框架行为的选项。最
其中重要的是 Web 接收器应用 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 用户体验 widget
Cast iOS SDK 提供了这些符合 Cast 设计的微件 核对清单:
入门叠加层:
GCKCastContext
类有一个方法,presentCastInstructionsViewControllerOnceWithCastButton
、 可用于在网络接收器首次运行时使“投射”按钮突出显示 可用。发送者应用可以自定义标题的文本和位置 文字和“关闭”按钮。Cast Button: 从 Cast iOS 发送器 SDK 4.6.0 开始,“投放”按钮始终可见 当发送方设备连接到 Wi-Fi 时。用户首次点按时 后,在“投放”按钮上,即会显示一个权限对话框 这样用户就可以向此应用授予对本地设备的访问权限 网络。随后,当用户点按投放按钮时,系统会投放一次 对话框,其中会列出发现的设备。当用户点按 投射按钮上会显示当前的 媒体元数据(例如标题、录音室名称和缩略图) 图片)或允许用户断开与投放设备的连接。当用户 在没有任何可用设备时点按投放按钮,屏幕会显示 会显示相关信息,让用户了解找不到设备的原因 以及如何排查问题。
迷你控制器: 当用户在投放内容时离开了当前 内容页面或展开的控制器转移到发送应用中的其他屏幕时, 迷你控制器显示在屏幕底部 查看当前投放的媒体元数据并控制播放。
展开后的控制器: 用户投放内容时,如果他们点击媒体通知或 迷你控制器时,展开的控制器会启动,显示 并提供了多个按钮来控制 媒体播放。
添加“投放”按钮
该框架提供了一个“投射”按钮组件作为 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
也可以直接添加到故事板中
配置设备发现
在该框架中,设备发现会自动进行。无需 明确启动或停止发现流程,除非您实现自定义界面。
框架中的发现由 类管理
GCKDiscoveryManager
、
它是
GCKCastContext
。通过
框架提供了一个默认的“投射”对话框组件,用于选择设备和
控制。设备列表按设备易记名称的字典顺序排序。
会话管理的运作方式
Cast SDK 引入了 Cast 会话的概念,即 它的创建过程中,涉及到连接设备、启动(或加入)Web 的步骤 接收方应用、连接到该应用并初始化媒体控制渠道。查看网络接收器 应用生命周期指南 详细了解 Cast 会话和网络接收器生命周期。
会话由 类管理
GCKSessionManager
、
它是
GCKCastContext
。
各个会话由类的子类表示。
GCKSession
:例如
GCKCastSession
表示与投放设备的会话。您可以访问当前正在投放的 Cast 内容
会话(如果有)设置为 GCKSessionManager
的 currentCastSession
属性。
通过
GCKSessionManagerListener
界面可用于监控会话事件,例如会话创建、
暂停、恢复和终止。框架自动挂起
当发送器应用进入后台并尝试恢复时,系统启动的会话数量
当应用返回前台(或在
会话处于活动状态时异常/突然终止应用)。
如果使用“投射”对话框,系统会创建并销毁会话
自动响应用户手势。否则,应用可能会启动和结束
来明确指定这些对象,
GCKSessionManager
。
如果应用需要执行特殊处理以响应会话生命周期
事件,它可以将一个或多个 GCKSessionManagerListener
实例注册到
GCKSessionManager
。GCKSessionManagerListener
是一种协议,定义了
会话开始、会话结束等事件的回调。
流式传输
保留会话状态是数据流传输的基础, 用户可以使用语音指令、Google Home 跨设备移动现有音频和视频流 应用或智能显示屏媒体在一台设备(源设备)上停止播放,然后在另一台设备(源设备)上继续播放 目标)。任何具有最新固件的投放设备都可以在 流式传输。
如需在流式传输期间获取新的目标设备,请使用
GCKCastSession#device
属性
[sessionManager:didResumeCastSession:]
回调。
请参阅 通过网络接收器进行流式传输 。
自动重新连接
Cast 框架添加了重新连接逻辑,以自动处理重新连接 例如:
- 在 Wi-Fi 暂时丢失的情况下恢复
- 从设备休眠状态中恢复
- 从应用后台恢复
- 在应用崩溃时恢复
媒体控件的工作原理
如果通过支持媒体的 Web 接收器应用建立 Cast 会话
命名空间中
GCKRemoteMediaClient
由框架自动创建它可作为
的 remoteMediaClient
属性
GCKCastSession
实例。
GCKRemoteMediaClient
上向网络接收器发出请求的所有方法
将返回
GCKRequest
对象,
来跟踪相应请求答
GCKRequestDelegate
可以分配给该对象,以接收有关
操作的结果。
GCKRemoteMediaClient
的实例应该
可能会由应用的多个部分共享,而实际上某些内部组件
框架(例如 Cast 对话框和迷你媒体控件)确实会共享
实例。为此,GCKRemoteMediaClient
支持在 YAML 文件中
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 发送者界面以了解您的 发送器应用可以配置 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
嵌入到现有视图控制器中
第二种方法是将迷你控制器直接添加到现有视图中
通过使用
createMiniMediaControlsViewController
来创建一个
GCKUIMiniMediaControlsViewController
实例,然后将其作为子视图添加到容器视图控制器。
在应用委托中设置视图控制器:
<ph type="x-smartling-placeholder">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
类。
首先,您需要在 投射上下文。修改应用委托以启用默认的展开控制器:
<ph type="x-smartling-placeholder">func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; .. }
将以下代码添加到您的视图控制器,以加载展开的控制器 当用户开始投射视频时触发:
<ph type="x-smartling-placeholder">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 widget 的外观。
音量控制
Cast 框架会自动管理发送器应用的音量。通过
框架会自动与 Web Receiver 卷同步,以便
提供的 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
以自定义处理日志消息的方式。
使用 GCKLogger
,SDK 以调试的形式生成日志记录输出
消息、错误和警告。这些日志消息有助于调试,
进行问题排查和发现问题的方法。默认情况下,日志输出为
但通过分配 GCKLoggerDelegate
,发送方应用可以接收
并将这些消息从 SDK 中记录下来,并记录到系统控制台。
@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
要同时启用调试消息和详细消息,请将下面这行代码添加到 设置委托(如前所述):
<ph type="x-smartling-placeholder">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
。