Ce guide du développeur explique comment ajouter la compatibilité avec Google Cast à votre application d'envoi iOS à l'aide du SDK d'envoi iOS.
L'appareil mobile ou l'ordinateur portable est l'expéditeur qui contrôle la lecture, et l'appareil Google Cast est le récepteur qui affiche le contenu sur le téléviseur.
Le framework d'envoi fait référence au binaire de la bibliothèque de classes Cast et aux ressources associées présentes au moment de l'exécution sur l'expéditeur. L'application émettrice ou application Cast désigne une application qui s'exécute également sur l'émetteur. L'application Web Receiver fait référence à l'application HTML exécutée sur le Web Receiver.
Le framework d'envoi utilise une conception de rappel asynchrone pour informer l'application d'envoi des événements et effectuer la transition entre les différents états du cycle de vie de l'application Cast.
Déroulement des opérations de l'application
Les étapes suivantes décrivent le flux d'exécution de haut niveau typique d'une application iOS d'envoi:
- Le framework Cast démarre
GCKDiscoveryManager
en fonction des propriétés fournies dansGCKCastOptions
pour commencer à rechercher des appareils. - Lorsque l'utilisateur clique sur le bouton Cast, le framework affiche la boîte de dialogue Cast avec la liste des appareils Cast détectés.
- Lorsque l'utilisateur sélectionne un appareil Cast, le framework tente de lancer l'application Web Receiver sur l'appareil Cast.
- Le framework appelle des rappels dans l'application d'envoi pour confirmer que l'application Web Receiver a été lancée.
- Le framework crée un canal de communication entre l'expéditeur et les applications Web Receiver.
- Le framework utilise le canal de communication pour charger et contrôler la lecture multimédia sur le récepteur Web.
- Le framework synchronise l'état de la lecture multimédia entre l'expéditeur et le récepteur Web: lorsque l'utilisateur effectue des actions dans l'UI de l'expéditeur, le framework transmet ces requêtes de contrôle multimédia au récepteur Web, et lorsque le récepteur Web envoie des mises à jour de l'état multimédia, le framework met à jour l'état de l'UI de l'expéditeur.
- Lorsque l'utilisateur clique sur le bouton Cast pour se déconnecter de l'appareil Cast, le framework dissocie l'application d'envoi du récepteur Web.
Pour résoudre les problèmes liés à votre expéditeur, vous devez activer la journalisation.
Pour obtenir la liste complète de toutes les classes, méthodes et événements du framework Google Cast pour iOS, consultez la documentation de référence de l'API Google Cast pour iOS. Les sections suivantes décrivent les étapes à suivre pour intégrer Cast à votre application iOS.
Appeler des méthodes à partir du thread principal
Initialiser le contexte Cast
Le framework Cast possède un objet singleton global, le GCKCastContext
, qui coordonne toutes les activités du framework. Cet objet doit être initialisé tôt dans le cycle de vie de l'application, généralement dans la méthode -[application:didFinishLaunchingWithOptions:]
du délégué de l'application, afin que la reprise de session automatique au redémarrage de l'application de l'émetteur puisse se déclencher correctement.
Un objet GCKCastOptions
doit être fourni lors de l'initialisation de GCKCastContext
.
Cette classe contient des options qui ont une incidence sur le comportement du framework. La plus importante de ces options est l'ID d'application du récepteur Web, utilisé pour filtrer les résultats de la détection et lancer l'application du récepteur Web lorsqu'une session Cast est lancée.
La méthode -[application:didFinishLaunchingWithOptions:]
est également utile pour configurer un délégué pour la journalisation, afin de recevoir les messages de journalisation depuis le framework.
Ceux-ci peuvent servir au débogage et au dépannage.
@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
Widgets d'expérience utilisateur Cast
Le SDK Cast pour iOS fournit les widgets suivants, qui respectent la checklist de conception Cast:
Superposition d'introduction : la classe
GCKCastContext
possède une méthode,presentCastInstructionsViewControllerOnceWithCastButton
, qui permet de mettre en surbrillance le bouton Cast la première fois qu'un récepteur Web est disponible. L'application de l'expéditeur peut personnaliser le texte, la position du texte du titre et le bouton "Ignorer".Bouton Cast : à partir du SDK de l'émetteur Cast pour iOS 4.6.0, le bouton Cast est toujours visible lorsque l'appareil émetteur est connecté au Wi-Fi. La première fois que l'utilisateur appuie sur le bouton Cast après avoir lancé l'application, une boîte de dialogue d'autorisations s'affiche pour que l'utilisateur puisse accorder à l'application un accès au réseau local aux appareils du réseau. Ensuite, lorsque l'utilisateur appuie sur le bouton de diffusion, une boîte de dialogue de diffusion s'affiche, qui liste les appareils détectés. Lorsque l'utilisateur appuie sur le bouton de diffusion lorsque l'appareil est connecté, les métadonnées multimédias actuelles (telles que le titre, le nom du studio d'enregistrement et une image miniature) s'affichent ou l'utilisateur peut se déconnecter de l'appareil de diffusion. Lorsque l'utilisateur appuie sur le bouton de diffusion alors qu'aucun appareil n'est disponible, un écran s'affiche pour lui indiquer pourquoi les appareils ne sont pas détectés et comment résoudre le problème.
Mini-télécommande : lorsque l'utilisateur cast un contenu et qu'il a quitté la page de contenu actuelle ou la télécommande agrandie pour accéder à un autre écran dans l'application d'envoi, la mini-télécommande s'affiche en bas de l'écran pour lui permettre de voir les métadonnées multimédias en cours de diffusion et de contrôler la lecture.
Commande étendue : lorsque l'utilisateur cast un contenu, s'il clique sur la notification multimédia ou sur la mini-télécommande, la commande étendue se lance. Elle affiche les métadonnées du contenu multimédia en cours de lecture et fournit plusieurs boutons pour contrôler la lecture.
Ajouter un bouton Cast
Le framework fournit un composant pour l'icône Cast en tant que sous-classe UIButton
. Il peut être ajouté à la barre de titre de l'application en l'encapsulant dans un UIBarButtonItem
. Une sous-classe UIViewController
type peut installer un bouton de diffusion en continu comme suit:
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];
Par défaut, appuyer sur le bouton ouvre la boîte de dialogue de diffusion fournie par le framework.
Vous pouvez également ajouter GCKUICastButton
directement au storyboard.
Configurer la découverte d'appareils
Dans le framework, la détection des appareils s'effectue automatiquement. Il n'est pas nécessaire de démarrer ou d'arrêter explicitement le processus de découverte, sauf si vous implémentez une UI personnalisée.
La découverte dans le framework est gérée par la classe GCKDiscoveryManager
, qui est une propriété de GCKCastContext
. Le framework fournit un composant de boîte de dialogue Cast par défaut pour la sélection et le contrôle des appareils. La liste des appareils est classée par ordre alphabétique en fonction du nom convivial de l'appareil.
Fonctionnement de la gestion des sessions
Le SDK Cast introduit le concept de session Cast, dont l'établissement combine les étapes de connexion à un appareil, de lancement (ou de reprise) d'une application Web Receiver, de connexion à cette application et d'initialisation d'un canal de commande multimédia. Pour en savoir plus sur les sessions de diffusion et le cycle de vie du Web Receiver, consultez le guide du cycle de vie des applications du Web Receiver.
Les sessions sont gérées par la classe GCKSessionManager
, qui est une propriété de GCKCastContext
.
Les sessions individuelles sont représentées par des sous-classes de la classe GCKSession
: par exemple, GCKCastSession
représente les sessions avec des appareils Cast. Vous pouvez accéder à la session de diffusion actuellement active (le cas échéant) en tant que propriété currentCastSession
de GCKSessionManager
.
L'interface GCKSessionManagerListener
peut être utilisée pour surveiller les événements de session, tels que la création, la suspension, la reprise et l'arrêt de la session. Le framework suspend automatiquement les sessions lorsque l'application d'envoi passe en arrière-plan et tente de les reprendre lorsque l'application revient au premier plan (ou est relancée après une fermeture anormale/brusque de l'application alors qu'une session était active).
Si la boîte de dialogue Cast est utilisée, les sessions sont créées et détruites automatiquement en réponse aux gestes des utilisateurs. Sinon, l'application peut démarrer et terminer des sessions explicitement via des méthodes sur GCKSessionManager
.
Si l'application doit effectuer un traitement spécial en réponse aux événements de cycle de vie de la session, elle peut enregistrer une ou plusieurs instances GCKSessionManagerListener
avec GCKSessionManager
. GCKSessionManagerListener
est un protocole qui définit des rappels pour des événements tels que le début et la fin de la session, etc.
Transfert de diffusion
La préservation de l'état de la session est la base du transfert de flux, qui permet aux utilisateurs de déplacer des flux audio et vidéo existants entre les appareils à l'aide de commandes vocales, de l'application Google Home ou d'écrans connectés. La lecture du contenu multimédia s'arrête sur un appareil (la source) et se poursuit sur un autre (la destination). N'importe quel appareil Cast équipé du dernier micrologiciel peut servir de source ou de destination dans un transfert de flux.
Pour obtenir le nouvel appareil de destination lors du transfert de flux, utilisez la propriété GCKCastSession#device
lors du rappel [sessionManager:didResumeCastSession:]
.
Pour en savoir plus, consultez la section Transfert de flux sur le Web Receiver.
Reconnexion automatique
Le framework Cast ajoute une logique de reconnexion pour gérer automatiquement la reconnexion dans de nombreux cas particuliers subtils, par exemple:
- Récupération après une perte temporaire du Wi-Fi
- Récupérer après la mise en veille de l'appareil
- Récupérer l'application après l'avoir mise en arrière-plan
- Récupérer l'application si elle a planté
Fonctionnement du contrôle des contenus multimédias
Si une session Cast est établie avec une application Web Receiver compatible avec l'espace de noms multimédia, une instance de GCKRemoteMediaClient
est créée automatiquement par le framework. Elle peut être accessible en tant que propriété remoteMediaClient
de l'instance GCKCastSession
.
Toutes les méthodes sur GCKRemoteMediaClient
qui envoient des requêtes au récepteur Web renvoient un objet GCKRequest
qui peut être utilisé pour suivre cette requête. Un GCKRequestDelegate
peut être attribué à cet objet pour recevoir des notifications sur le résultat final de l'opération.
Il est prévu que l'instance de GCKRemoteMediaClient
puisse être partagée par plusieurs parties de l'application. En effet, certains composants internes du framework, tels que la boîte de dialogue Cast et les mini commandes multimédias, partagent l'instance. À cette fin, GCKRemoteMediaClient
accepte l'enregistrement de plusieurs GCKRemoteMediaClientListener
.
Définir des métadonnées multimédias
La classe GCKMediaMetadata
représente les informations sur un élément multimédia que vous souhaitez caster. L'exemple suivant crée une instance GCKMediaMetadata
d'un film et définit le titre, les sous-titres, le nom du studio d'enregistrement et deux images.
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]];
Consultez la section Sélection et mise en cache des images pour découvrir comment utiliser des images avec des métadonnées multimédias.
Charger un contenu multimédia
Pour charger un élément multimédia, créez une instance GCKMediaInformation
à l'aide des métadonnées du contenu multimédia. Obtenez ensuite le GCKCastSession
actuel et utilisez son GCKRemoteMediaClient
pour charger le contenu multimédia dans l'application du récepteur. Vous pouvez ensuite utiliser GCKRemoteMediaClient
pour contrôler une application de lecteur multimédia exécutée sur le récepteur, par exemple pour lancer, mettre en pause et arrêter la lecture.
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; }
Consultez également la section sur l'utilisation des pistes multimédias.
Format vidéo 4K
Pour déterminer le format vidéo de votre contenu multimédia, utilisez la propriété videoInfo
de GCKMediaStatus
pour obtenir l'instance actuelle de GCKVideoInfo
.
Cette instance contient le type de format de téléviseur HDR, ainsi que la hauteur et la largeur en pixels. Les variantes du format 4K sont indiquées dans la propriété hdrType
par les valeurs d'énumération GCKVideoInfoHDRType
.
Ajouter des mini-manettes
Selon la checklist de conception de Google Cast, une application émettrice doit fournir une commande persistante appelée mini-télécommande qui doit s'afficher lorsque l'utilisateur quitte la page de contenu actuelle. Cette mini-télécommande permet à l'utilisateur d'accéder instantanément à sa session Cast en cours et de disposer d'un rappel visible.
Le framework Cast fournit une barre de contrôle, GCKUIMiniMediaControlsViewController
, qui peut être ajoutée aux scènes dans lesquelles vous souhaitez afficher la mini-télécommande.
Lorsque votre application émettrice lit un flux vidéo ou audio en direct, le SDK affiche automatiquement un bouton Lecture/Arrêt à la place du bouton Lecture/Pause dans la mini télécommande.
Consultez Personnaliser l'interface utilisateur de l'émetteur iOS pour découvrir comment votre application d'émetteur peut configurer l'apparence des widgets Cast.
Pour ajouter la mini-télécommande à une application d'envoi, vous avez deux possibilités:
- Laissez le framework Cast gérer la mise en page de la mini-télécommande en encapsulant votre contrôleur de vue existant avec son propre contrôleur de vue.
- Gérez vous-même la mise en page du widget de mini-télécommande en l'ajoutant à votre contrôleur de vue existant en fournissant une sous-vue dans le storyboard.
Encapsuler à l'aide de GCKUICastContainerViewController
La première consiste à utiliser GCKUICastContainerViewController
, qui encapsule un autre contrôleur de vue et ajoute un GCKUIMiniMediaControlsViewController
en bas. Cette approche est limitée, car vous ne pouvez pas personnaliser l'animation ni configurer le comportement du contrôleur de vue du conteneur.
Cette première méthode est généralement effectuée dans la méthode -[application:didFinishLaunchingWithOptions:]
du délégué de l'application:
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
Intégrer dans un contrôleur de vue existant
La deuxième méthode consiste à ajouter le mini-contrôleur directement à votre contrôleur de vue existant à l'aide de createMiniMediaControlsViewController
pour créer une instance GCKUIMiniMediaControlsViewController
, puis à l'ajouter au contrôleur de vue du conteneur en tant que sous-vue.
Configurez votre contrôleur de vue dans le délégué d'application:
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; }
Dans votre contrôleur de vue racine, créez une instance GCKUIMiniMediaControlsViewController
et ajoutez-la au contrôleur de vue du conteneur en tant que sous-vue:
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
indique au contrôleur de vue hôte quand la mini-télécommande doit être visible:
func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController, shouldAppear _: Bool) { updateControlBarsVisibility() }
- (void)miniMediaControlsViewController: (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController shouldAppear:(BOOL)shouldAppear { [self updateControlBarsVisibility]; }
Ajouter une télécommande agrandie
La checklist de conception de Google Cast exige que l'application émettrice soit dotée d'une télécommande agrandie associée au contenu multimédia en cours de diffusion. La télécommande agrandie est une version en plein écran de la mini-télécommande.
La télécommande agrandie est une version en plein écran qui permet de contrôler entièrement la lecture du contenu multimédia à distance. Cette vue devrait permettre à une application de diffusion de contrôler tous les aspects gérables d'une session Cast, à l'exception du volume du récepteur Web et du cycle de vie de la session (connexion/arrêt de la diffusion). Elle fournit également toutes les informations d'état sur la session multimédia (illustrations, titre, sous-titres, etc.).
La fonctionnalité de cette vue est implémentée par la classe GCKUIExpandedMediaControlsViewController
.
La première chose à faire est d'activer la télécommande agrandie par défaut dans le contexte de diffusion. Modifiez le délégué d'application pour activer la télécommande agrandie par défaut:
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; .. }
Ajoutez le code suivant à votre contrôleur de vue pour charger la télécommande agrandie lorsque l'utilisateur commence à caster une vidéo:
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]; }
Elle se lance automatiquement lorsque l'utilisateur appuie sur la mini-télécommande.
Lorsque votre application émettrice lit une diffusion vidéo ou audio en direct, le SDK affiche automatiquement un bouton Lecture/Arrêt à la place du bouton Lecture/Pause dans la télécommande étendue.
Pour découvrir comment votre application émettrice peut configurer l'apparence des widgets Cast, consultez Appliquer des styles personnalisés à votre application iOS.
Réglage du volume
Le framework Cast gère automatiquement le volume de l'application d'envoi. Le framework se synchronise automatiquement avec le volume du récepteur Web pour les widgets d'interface utilisateur fournis. Pour synchroniser un curseur fourni par l'application, utilisez GCKUIDeviceVolumeController
.
Contrôle du volume à l'aide d'un bouton physique
Les boutons de volume physiques de l'appareil émetteur peuvent être utilisés pour modifier le volume de la session Cast sur le récepteur Web à l'aide de l'indicateur physicalVolumeButtonsWillControlDeviceVolume
sur GCKCastOptions
, qui est défini sur 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];
Gérer les erreurs
Il est très important que les applications d'envoi gèrent tous les rappels d'erreur et décident de la meilleure réponse à chaque étape du cycle de vie de Cast. L'application peut afficher des boîtes de dialogue d'erreur à l'utilisateur ou décider de mettre fin à la session de diffusion.
Journalisation
GCKLogger
est un singleton utilisé pour la journalisation par le framework. Utilisez GCKLoggerDelegate
pour personnaliser la gestion des messages de journal.
À l'aide de GCKLogger
, le SDK produit une sortie de journalisation sous la forme de messages de débogage, d'erreurs et d'avertissements. Ces messages de journal facilitent le débogage et sont utiles pour le dépannage et l'identification des problèmes. Par défaut, la sortie du journal est supprimée, mais en attribuant un GCKLoggerDelegate
, l'application d'envoi peut recevoir ces messages du SDK et les consigner dans la console système.
@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
Pour activer également les messages de débogage et de type verbeux, ajoutez cette ligne au code après avoir défini le délégué (comme indiqué précédemment):
let filter = GCKLoggerFilter.init() filter.minimumLevel = GCKLoggerLevel.verbose GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setMinimumLevel:GCKLoggerLevelVerbose]; [GCKLogger sharedInstance].filter = filter;
Vous pouvez également filtrer les messages de journal générés par GCKLogger
.
Définissez le niveau de journalisation minimal par classe, par exemple:
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;
Les noms de classe peuvent être des noms littéraux ou des expressions génériques, par exemple GCKUI\*
et GCK\*Session
.