Este guia do desenvolvedor descreve como adicionar compatibilidade com o Google Cast ao seu iOS app remetente usando o SDK do remetente do iOS.
O dispositivo móvel ou laptop é o remetente que controla a reprodução, e o O dispositivo com Google Cast é o receptor que exibe o conteúdo na TV.
O framework do remetente se refere ao binário da biblioteca de classes do Cast e associado recursos presentes no tempo de execução no remetente. O app remetente ou o app Google Cast refere-se a um app que também é executado no remetente. O app receptor da Web refere-se ao aplicativo HTML executado no receptor da Web.
A estrutura do remetente usa um design de callback assíncrono para informar o remetente de eventos e fazer a transição entre os vários estados da vida útil do app Cast ciclo.
Fluxo de aplicativos
As etapas a seguir descrevem o fluxo de execução de alto nível típico de um remetente Aplicativo para iOS:
- O framework do Cast é iniciado
GCKDiscoveryManager
com base nas propriedadesGCKCastOptions
para começar a procurar dispositivos. - Quando o usuário clica no botão Transmitir, o framework apresenta o Caixa de diálogo com a lista de dispositivos de transmissão descobertos.
- Quando o usuário seleciona um dispositivo de transmissão, o framework tenta iniciar a App Receptor da Web no dispositivo de transmissão.
- O framework invoca callbacks no app remetente para confirmar que o O app receptor da Web foi iniciado.
- O framework cria um canal de comunicação entre o remetente Apps receptores da Web.
- O framework usa o canal de comunicação para carregar e controlar mídias a reprodução no receptor da Web.
- O framework sincroniza o estado de reprodução de mídia entre o remetente e o Receptor da Web: quando o usuário realiza ações na interface do remetente, o framework transmite essas solicitações de controle de mídia para o receptor da Web e quando ele envia atualizações de status de mídia, o framework atualiza o estado da interface do remetente.
- Quando o usuário clicar no botão Transmitir para se desconectar do dispositivo de transmissão, O framework desconectará o app remetente do receptor da Web.
Para resolver problemas do remetente, é necessário ativar o registro.
Para acessar uma lista abrangente de todas as classes, métodos e eventos no Google Cast Framework do iOS, consulte a API Google Cast para iOS Referência. As seções a seguir abrangem as etapas para integrar o Google Cast ao seu app iOS.
Chamar métodos da linha de execução principal
Inicializar o contexto de transmissão
O framework do Google Cast tem um objeto Singleton global, o
GCKCastContext
, que
coordena todas as atividades da estrutura. Esse objeto precisa ser inicializado
no início do ciclo de vida do aplicativo, normalmente
-[application:didFinishLaunchingWithOptions:]
do delegado do app.
que a retomada automática da sessão na reinicialização do app do remetente seja acionada corretamente.
Uma GCKCastOptions
O objeto precisa ser fornecido ao inicializar o GCKCastContext
.
Essa classe contém opções que afetam o comportamento do framework. A maior
mais importante é o ID do aplicativo do receptor da Web, usado para filtrar
os resultados da descoberta e iniciar o app Receptor da Web quando uma sessão de transmissão for
começar.
O método -[application:didFinishLaunchingWithOptions:]
também é um bom lugar
para configurar um delegado de geração de registros para receber as mensagens de registro do framework.
Elas podem ser úteis para depuração e solução de problemas.
@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
Os widgets de UX do Google Cast
O SDK do Cast para iOS fornece esses widgets que obedecem ao design do Cast Lista de verificação:
Sobreposição introdutória: A classe
GCKCastContext
tem um método,presentCastInstructionsViewControllerOnceWithCastButton
, que pode ser usado para destacar o botão Transmitir na primeira vez que um receptor da Web está disponível. O app remetente pode personalizar o texto e a posição do título e o botão "Dispensar".Botão Transmitir: A partir do SDK remetente do Cast para iOS 4.6.0, o botão Transmitir está sempre visível quando o dispositivo remetente estiver conectado ao Wi-Fi. Na primeira vez que o usuário toca no botão Transmitir depois de iniciar o app, uma caixa de diálogo de permissões aparece para que o usuário possa conceder ao app acesso à rede local aos dispositivos na na rede. Em seguida, quando o usuário toca no botão Transmitir, uma uma caixa de diálogo que lista os dispositivos descobertos é exibida. Quando o usuário toca no botão Transmitir enquanto o dispositivo estiver conectado, será mostrada metadados de mídia (como título, nome do estúdio de gravação e uma miniatura) imagem) ou permite que o usuário se desconecte do dispositivo de transmissão. Quando o usuário tocar no botão transmitir enquanto não houver dispositivos disponíveis, uma tela será exibido fornecendo informações ao usuário sobre por que os dispositivos não foram encontrados e como resolver problemas.
Minicontrole: Quando o usuário está transmitindo conteúdo e saiu da configuração página de conteúdo ou controlador expandido para outra tela no aplicativo remetente, o é exibido na parte inferior da tela para permitir que o usuário ver os metadados de mídia transmitidos no momento e controlar a reprodução.
Controlador expandido: Quando o usuário estiver transmitindo conteúdo, se clicar na notificação de mídia ou minicontrole, o controle expandido é iniciado, mostrando a metadados de mídia em reprodução no momento e fornece vários botões para controlar o a reprodução de mídia.
Adicionar um botão Transmitir
O framework fornece um componente do botão Transmitir como uma subclasse UIButton
. Ela pode
ser adicionado à barra de título do app colocando-o em uma UIBarButtonItem
. Uma configuração típica
A subclasse UIViewController
pode instalar um botão Transmitir da seguinte maneira:
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];
Por padrão, tocar no botão abre a caixa de diálogo "Transmitir" fornecida pelo de análise de dados em nuvem.
GCKUICastButton
também pode ser adicionado diretamente ao storyboard.
Configurar a descoberta de dispositivos
No framework, a descoberta de dispositivos acontece automaticamente. Não é preciso iniciar ou interromper explicitamente o processo de descoberta, a menos que você implemente uma interface personalizada.
A descoberta no framework é gerenciada pela classe
GCKDiscoveryManager
,
que é uma propriedade do
GCKCastContext
A
oferece um componente padrão da caixa de diálogo do Google Cast para seleção de dispositivos e
controle A lista de dispositivos é ordenada lexicograficamente pelo nome compatível com o dispositivo.
Como funciona o gerenciamento de sessões
O SDK do Cast apresenta o conceito de uma sessão do Google Cast, a que combina as etapas de conexão a um dispositivo, inicialização (ou entrada) de um App receptor, conectando-se a ele e inicializando um canal de controle de mídia. Consulte o receptor da Web Guia do ciclo de vida do aplicativo para mais informações sobre as sessões de transmissão e o ciclo de vida do receptor da Web.
As sessões são gerenciadas pela turma
GCKSessionManager
,
que é uma propriedade do
GCKCastContext
As sessões individuais são representadas por subclasses da classe
GCKSession
: por exemplo,
GCKCastSession
representa sessões com dispositivos de transmissão. É possível acessar o conteúdo do Google Cast ativo no momento
sessão (se houver), como a propriedade currentCastSession
de GCKSessionManager
.
A
GCKSessionManagerListener
pode ser usada para monitorar eventos de sessão, como criação de sessão,
suspensão, retomada e rescisão. O framework suspende automaticamente
sessões em que o app remetente entra em segundo plano e tenta retomar
quando o aplicativo retorna para o primeiro plano (ou é reiniciado após um
encerramento anormal/abrupto do app enquanto uma sessão estava ativa).
Se a caixa de diálogo "Transmitir" estiver sendo usada, as sessões serão criadas e removidas
automaticamente em resposta aos gestos do usuário. Caso contrário, o app pode iniciar e encerrar
sessões explicitamente usando métodos
GCKSessionManager
Se o app precisar fazer processamento especial em resposta ao ciclo de vida da sessão
ele pode registrar uma ou mais instâncias GCKSessionManagerListener
com
o GCKSessionManager
. O GCKSessionManagerListener
é um protocolo que define
callbacks para eventos como início e fim de sessão, entre outros.
Transferência de stream
A preservação do estado da sessão é a base da transferência de stream, em que Os usuários podem mover streams de áudio e vídeo entre dispositivos usando comandos de voz, o Google Home no app ou em smart displays. A mídia é interrompida em um dispositivo (a fonte) e continua em outro (a destino). Qualquer dispositivo de transmissão com o firmware mais recente pode servir como origem ou destino em um transferência de stream.
Para receber o novo dispositivo de destino durante a transferência da transmissão, use o
GCKCastSession#device
durante a
[sessionManager:didResumeCastSession:]
o retorno de chamada.
Consulte Transferência de stream no receptor da Web para mais informações.
Reconexão automática
O framework do Cast adiciona a lógica de reconexão para gerenciar automaticamente a reconexão. em muitos casos isolados sutis, como:
- Recuperar-se de uma perda temporária de Wi-Fi
- Recuperar-se do modo de inatividade do dispositivo
- Recuperar-se do app em segundo plano
- Fazer a recuperação em caso de falha do app
Como funcionam os controles de mídia
Se uma sessão de transmissão for estabelecida com um app receptor da Web compatível com a
namespace, uma instância de
GCKRemoteMediaClient
serão criadas automaticamente pela estrutura; ele pode ser acessado
remoteMediaClient
do
GCKCastSession
instância.
Todos os métodos em GCKRemoteMediaClient
que emitem solicitações para o receptor da Web
vai retornar
objeto GCKRequest
que
pode ser usada para acompanhar essa solicitação. Um
GCKRequestDelegate
pode ser atribuído a esse objeto para receber notificações sobre o evento
resultado da operação.
Espera-se que a instância de GCKRemoteMediaClient
pode ser compartilhado por várias partes do aplicativo e, de fato, por alguns componentes internos
do framework, como a caixa de diálogo "Transmitir" e os controles de minimídia, compartilham a
instância. Para isso, GCKRemoteMediaClient
permite o registro de vários
GCKRemoteMediaClientListener
s.
Definir metadados de mídia
A
GCKMediaMetadata
representa informações sobre um item de mídia que você deseja transmitir. O seguinte
exemplo cria uma nova instância GCKMediaMetadata
de um filme e define o título,
subtítulo, nome do estúdio de gravação e duas imagens.
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]];
Consulte as seções Seleção de imagem e Armazenamento em cache sobre o uso de imagens com metadados de mídia.
Carregar mídia
Para carregar um item de mídia, crie um
GCKMediaInformation
usando os metadados da mídia. Em seguida, obtenha a imagem
GCKCastSession
e
usar seu
GCKRemoteMediaClient
para carregar a mídia no app receptor. É possível usar GCKRemoteMediaClient
para controlar um app de player de mídia em execução no receptor, como para abrir,
pausar e parar.
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; }
Consulte também a seção sobre usando faixas de mídia.
Formato de vídeo 4K
Para determinar qual é o formato de vídeo da sua mídia, use a propriedade videoInfo
de
GCKMediaStatus
para conseguir a instância atual
GCKVideoInfo
Esta instância contém o tipo de formato de TV HDR e a altura e largura em
pixels. As variantes do formato 4K são indicadas na propriedade hdrType
pelo tipo enumerado
de valores GCKVideoInfoHDRType
.
Adicionar minicontroles
De acordo com o Design do elenco lista de verificação, um app remetente deve fornecer um controle persistente conhecido como mini controlador que deve aparecer quando o usuário sair da página de conteúdo atual. O minicontrole oferece acesso instantâneo e um lembrete visível para o sessão de transmissão atual.
O framework do Google Cast oferece uma barra de controle,
GCKUIMiniMediaControlsViewController
,
que pode ser adicionado às cenas em que você quer mostrar o minicontrole.
Quando o app remetente estiver reproduzindo uma transmissão ao vivo de vídeo ou áudio, o SDK exibe automaticamente um botão reproduzir/parar no lugar do botão reproduzir/pausar no minicontrole.
Consulte Personalizar a interface do remetente do iOS para saber como app remetente pode configurar a aparência dos widgets do Google Cast.
Há duas maneiras de adicionar o minicontrole a um app remetente:
- Permita que o framework do Google Cast gerencie o layout do minicontrole encapsulando seu controlador de visualização existente com seu próprio controlador de visualização.
- Gerencie o layout do widget do minicontrole adicionando-o ao seu controlador de visualização existente fornecendo uma subvisualização no storyboard.
Unir usando o GCKUICastContainerViewController
A primeira é usar
GCKUICastContainerViewController
que envolve outro controlador de visualizações e adiciona um
GCKUIMiniMediaControlsViewController
na parte de baixo. Essa abordagem é limitada, porque não é possível personalizar
animação e não pode configurar o comportamento do controlador de visualização do contêiner.
A primeira forma normalmente é feita
Método -[application:didFinishLaunchingWithOptions:]
do delegado do app:
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
Incorporar em um controlador de visualização existente
A segunda maneira é adicionar o minicontrole diretamente à sua visualização existente
controlador usando
createMiniMediaControlsViewController
para criar
GCKUIMiniMediaControlsViewController
e adicioná-la ao controlador de visualizações do contêiner como uma subvisualização.
Configure seu controlador de visualização no delegado do app:
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; }
No controlador de visualização raiz, crie um GCKUIMiniMediaControlsViewController
.
e adicioná-la ao controlador de visualizações do contêiner como uma subvisualização:
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
A
GCKUIMiniMediaControlsViewControllerDelegate
informa ao controlador de visualização do host quando o minicontrolador estará visível:
func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController, shouldAppear _: Bool) { updateControlBarsVisibility() }
- (void)miniMediaControlsViewController: (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController shouldAppear:(BOOL)shouldAppear { [self updateControlBarsVisibility]; }
Adicionar controle expandido
A Lista de verificação de design do Google Cast exige que um app remetente forneça uma lista de verificação controlador para a mídia transmitida. O controle expandido é uma versão em tela cheia do o minicontrole.
O controle expandido é uma exibição em tela cheia que oferece controle total do a reprodução de mídia remota. Essa visualização permite que um app de transmissão gerencie todos aspecto gerenciável de uma sessão de transmissão, exceto o volume do receptor da Web controle e ciclo de vida da sessão (conectar/parar transmissão). Ele também fornece todos os informações de status sobre a sessão de mídia (arte, título, subtítulo etc. adiante).
A funcionalidade dessa visualização é implementada pela função
GCKUIExpandedMediaControlsViewController
.
A primeira coisa que você precisa fazer é ativar o controle expandido padrão na contexto de transmissão. Modifique o delegado do app para ativar o controle expandido padrão:
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; .. }
Adicione o código a seguir ao seu controlador de visualização para carregar o controle expandido quando o usuário começa a transmitir um vídeo:
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]; }
O controle expandido também será iniciado automaticamente quando o usuário toca no minicontrole.
Quando o app remetente estiver reproduzindo uma transmissão ao vivo de vídeo ou áudio, o SDK exibe automaticamente um botão reproduzir/parar no lugar do botão reproduzir/pausar no controle expandido.
Consulte Aplicar estilos personalizados ao seu iOS App para saber como o app remetente pode configurar a aparência dos widgets do Google Cast.
Controle do volume
O framework do Cast gerencia automaticamente o volume do app remetente. A
sincroniza automaticamente com o volume do receptor da Web para o
widgets de IU fornecidos. Para sincronizar um controle deslizante fornecido pelo app, use
GCKUIDeviceVolumeController
Controle de volume do botão físico
Os botões de volume físico no dispositivo remetente podem ser usados para mudar a
da sessão de transmissão no receptor da Web usando o
a sinalização physicalVolumeButtonsWillControlDeviceVolume
na
GCKCastOptions
,
que é definido no
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];
Solucionar erros
É muito importante que os apps remetentes lidem com todos os callbacks de erro e decidam a melhor resposta para cada estágio do ciclo de vida do Google Cast. O app pode mostrar caixas de diálogo de erro para o usuário ou encerrar a sessão de transmissão.
Logging
GCKLogger
é um Singleton usado para geração de registros pelo framework. Use o
GCKLoggerDelegate
para personalizar o gerenciamento das mensagens de registro.
Ao usar o GCKLogger
, o SDK produz a saída de geração de registros na forma de depuração.
mensagens, erros e avisos. Essas mensagens de registro
ajudam na depuração e são úteis
para solucionar e identificar problemas. Por padrão, a saída do registro é
suprimidos, mas ao atribuir um GCKLoggerDelegate
, o app remetente pode receber
essas mensagens do SDK e registrá-las no console do sistema.
@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
Para ativar também as mensagens de depuração e detalhadas, adicione esta linha ao código após definindo o delegado (mostrado anteriormente):
let filter = GCKLoggerFilter.init() filter.minimumLevel = GCKLoggerLevel.verbose GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setMinimumLevel:GCKLoggerLevelVerbose]; [GCKLogger sharedInstance].filter = filter;
Também é possível filtrar as mensagens de registro geradas
GCKLogger
Defina o nível mínimo de geração de registros por classe, por exemplo:
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;
Os nomes das classes podem ser nomes literais ou padrões glob, por exemplo,
GCKUI\*
e GCK\*Session
.