Intégrer Cast dans votre application iOS

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

Ce guide du développeur explique comment ajouter la compatibilité Google Cast à l'application émettrice iOS à l'aide du SDK iOS Sender.

L'appareil mobile ou l'ordinateur portable est l'expéditeur qui contrôle la lecture, et l'appareil Google Cast est le destinataire 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 expéditeur ou l'application Cast fait référence à une application qui s'exécute également sur l'expéditeur. L'application Web Récepteur fait référence à l'application HTML exécutée sur le récepteur Web.

Le framework de l'expéditeur utilise une conception de rappel asynchrone pour informer l'application émettrice des événements et effectuer une transition entre les différents états du cycle de vie de l'application Cast.

Déroulement des opérations de l'application

La procédure suivante décrit le flux d'exécution standard pour une application iOS d'expéditeur:

  • Le framework Cast commence à utiliser GCKDiscoveryManager en fonction des propriétés fournies dans GCKCastOptions pour commencer à rechercher des appareils.
  • Lorsque l'utilisateur clique sur l'icône Cast, le framework présente 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 Récepteur sur l'appareil Cast.
  • Le framework appelle des rappels dans l'application de l'expéditeur pour vérifier que l'application Web Receiver a été lancée.
  • Le framework crée un canal de communication entre les applications de l'expéditeur et du récepteur Web.
  • Le framework utilise le canal de communication pour charger et contrôler la lecture de contenus multimédias sur le récepteur Web.
  • Le framework synchronise l'état de la lecture de contenus multimédias entre l'expéditeur et le récepteur Web. Lorsque l'utilisateur effectue des actions dans l'interface utilisateur de l'expéditeur, il transmet ces requêtes de contrôle multimédia au récepteur Web. Lorsque le récepteur Web envoie des mises à jour de l'état des contenus multimédias, le framework met à jour l'état de l'interface utilisateur de l'expéditeur.
  • Lorsque l'utilisateur clique sur l'icône Cast pour se déconnecter de l'appareil Cast, le framework déconnecte l'application émettrice du récepteur Web.

Pour résoudre les problèmes liés à l'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 la procédure à suivre pour intégrer Cast à votre application iOS.

Méthodes d'appel du thread principal

Initialiser le contexte Cast

Le framework Cast possède un objet global singleton, 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 automatique de la session au redémarrage de l'application de l'expéditeur puisse se déclencher correctement.

Un objet GCKCastOptions doit être fourni lors de l'initialisation de GCKCastContext. Cette classe contient des options qui affectent le comportement du framework. Le plus important de ces paramètres est l'ID de l'application Web Receiver, qui permet de filtrer les résultats de la découverte et de lancer l'application Web Receiver lorsqu'une session Cast est lancée.

La méthode -[application:didFinishLaunchingWithOptions:] est également un bon endroit pour configurer un délégué à la journalisation qui recevra les messages de journalisation du framework. Cela peut être utile pour le débogage et le dépannage.

Swift
@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)
    }
  }
}
Objectif-C

Délégué à l'utilisation d'applications

@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 de l'expérience utilisateur Cast

Le SDK Cast pour iOS fournit ces widgets conformes à la checklist de conception Cast:

  • Superposition d'introduction : la classe GCKCastContext possède une méthode, presentCastInstructionsViewControllerOnceWithCastButton, qui peut être utilisée pour mettre en surbrillance l'icône Cast la première fois qu'un récepteur Web est disponible. L'application expéditeur peut personnaliser le texte, la position du texte du titre et le bouton "Ignorer".

  • Cast Cast : à partir de la version 4.6.0 du SDK pour expéditeur iOS Cast, l'icône Cast est toujours visible lorsque l'appareil émetteur est connecté au Wi-Fi. La première fois que l'utilisateur appuie sur l'icône Cast après avoir démarré l'application, une boîte de dialogue d'autorisations s'affiche pour lui permettre d'autoriser l'application à accéder au réseau local aux appareils du réseau. Par la suite, lorsque l'utilisateur appuie sur l'icône Cast, une boîte de dialogue de diffusion s'affiche et répertorie les appareils détectés. Lorsque l'utilisateur appuie sur l'icône Cast lorsque l'appareil est connecté, les métadonnées multimédias actuelles (telles que le titre, le nom du studio d'enregistrement et une vignette) s'affichent, ou l'utilisateur peut se déconnecter de l'appareil Cast. Lorsque l'utilisateur appuie sur l'icône Cast alors qu'aucun appareil n'est disponible, un écran s'affiche. Il explique pourquoi les appareils sont introuvables et comment résoudre les problèmes.

  • Mini-télécommande : lorsque l'utilisateur caste du contenu et a quitté la page de contenu active ou la télécommande agrandie vers un autre écran de l'application de l'expéditeur, la mini-télécommande s'affiche au bas de l'écran pour permettre à l'utilisateur de voir les métadonnées multimédias en cours de diffusion et de contrôler la lecture.

  • Manette agrandie : lorsque l'utilisateur caste du contenu, s'il clique sur la notification multimédia ou la mini-télécommande, la télécommande développée se lance, qui affiche les métadonnées multimédias en cours de lecture et fournit plusieurs boutons pour contrôler la lecture des contenus multimédias.

Ajouter un icône Cast

Le framework fournit un composant d'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 icône Cast comme suit:

Swift
let castButton = GCKUICastButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
castButton.tintColor = UIColor.gray
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)
Objectif-C
GCKUICastButton *castButton = [[GCKUICastButton alloc] initWithFrame:CGRectMake(0, 0, 24, 24)];
castButton.tintColor = [UIColor grayColor];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:castButton];

Par défaut, appuyez sur le bouton pour ouvrir la boîte de dialogue Cast fournie par le framework.

GCKUICastButton peut également être ajouté directement au storyboard.

Configurer la détection 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 mettez en œuvre une UI personnalisée.

La découverte dans le framework est gérée par la classe GCKDiscoveryManager, qui est une propriété de la propriété 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 triée de façon lexicographique en fonction de leur nom.

Fonctionnement de la gestion des sessions

Le SDK Cast introduit le concept de session Cast, qui consiste à combiner les étapes de connexion à un appareil, de lancement (ou de participation) d'une application récepteur Web, de connexion à cette application et d'initialisation d'un canal de contrôle multimédia. Pour en savoir plus sur les sessions Cast et le cycle de vie du récepteur Web, consultez le Guide de cycle de vie de l'application.

Les sessions sont gérées par la classe GCKSessionManager, qui est une propriété de la propriété 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 Cast active (le cas échéant) en tant que propriété currentCastSession de GCKSessionManager.

L'interface GCKSessionManagerListener permet de surveiller les événements de session, tels que la création, la suspension, la reprise et l'arrêt de sessions. Le framework suspend automatiquement les sessions lorsque l'application émettrice passe en arrière-plan et tente de les reprendre lorsque l'application revient au premier plan (ou lorsqu'elle est redémarrée après l'arrêt anormal ou brutal d'une application alors qu'une session était active).

Si la boîte de dialogue "Caster" est utilisée, des sessions sont créées et supprimées 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 de la session, 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. Les utilisateurs peuvent déplacer des flux audio et vidéo existants d'un appareil à l'autre à l'aide de commandes vocales, de l'application Google Home ou d'un écran connecté. Le contenu multimédia s'arrête sur un appareil (la source) et se poursuit sur un autre (la destination). Tout appareil Cast doté du dernier micrologiciel peut servir de source ou de destination pour un transfert de diffusion.

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 diffusion sur le récepteur Web.

Reconnexion automatique

Le framework Cast ajoute une logique de reconnexion pour gérer automatiquement la reconnexion dans de nombreux cas subtils, tels que les suivants:

  • Rétablir une connexion Wi-Fi
  • Récupérer de l'appareil en veille
  • Restaurer après la mise en arrière-plan de l'application
  • Restaurer si l'appli a planté

Fonctionnement des commandes multimédias

Si une session Cast est établie avec une application Récepteur Web compatible avec l'espace de noms multimédia, une instance de GCKRemoteMediaClient est créée automatiquement par le framework. Elle est 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 objet GCKRequestDelegate peut être attribué à cet objet pour recevoir des notifications sur le résultat final de l'opération.

Il est normal que l'instance de GCKRemoteMediaClient soit 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 est compatible avec l'enregistrement de plusieurs GCKRemoteMediaClientListener.

Définir les 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, le sous-titre, le nom du studio d'enregistrement et deux images.

Swift
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))
Objectif-C
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 d'images sur l'utilisation d'images avec des métadonnées multimédias.

Charger le contenu multimédia

Pour charger un élément multimédia, créez une instance GCKMediaInformation à l'aide des métadonnées du support. Obtenez ensuite le GCKCastSession actuel et utilisez son GCKRemoteMediaClient pour charger le contenu multimédia sur l'application réceptrice. Vous pouvez ensuite utiliser GCKRemoteMediaClient pour contrôler une application de lecteur multimédia exécutée sur le récepteur, par exemple pour la lecture, la mise en pause et l'arrêt.

Swift
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
}
Objectif-C
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 Utiliser 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 TV 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

Conformément à la checklist de conception de Cast, une application émettrice doit fournir une commande persistante appelée mini-contrôleur qui doit s'afficher lorsque l'utilisateur quitte la page de contenu actuelle. La mini-télécommande fournit un accès instantané et un rappel visible pour la session Cast en cours.

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 l'application émettrice diffuse un flux vidéo en direct ou audio, le SDK affiche automatiquement un bouton de lecture/arrêt à la place du bouton de lecture/pause de la mini-télécommande.

Consultez la section Personnaliser l'interface utilisateur de l'expéditeur iOS pour découvrir comment l'application émettrice peut configurer l'apparence des widgets Cast.

Il existe deux façons d'ajouter la mini-télécommande à l'application de l'expéditeur:

  • Laissez le framework Cast gérer la mise en page du mini-contrôleur de vue 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 mini-widget de contrôleur en l'ajoutant au 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 élément 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 de conteneur.

La première méthode s'effectue généralement dans la méthode -[application:didFinishLaunchingWithOptions:] du délégué de l'application:

Swift
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()

  ...
}
Objectif-C
- (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];
  ...

}
Swift
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
    }
  }
}
Objectif-C

Délégué à l'utilisation d'applications

@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 le contrôleur de vue existant

La seconde méthode consiste à ajouter le mini-contrôleur directement à votre contrôleur de vue existant en utilisant createMiniMediaControlsViewController pour créer une instance GCKUIMiniMediaControlsViewController, puis à l'ajouter au contrôleur de vue de conteneur en tant que sous-vue.

Configurez votre contrôleur de vue dans le délégué de l'application:

Swift
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
}
Objectif-C
- (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 de conteneur en tant que sous-vue:

Swift
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)
    }
  }

...
Objectif-C

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 le mini-contrôleur doit être visible:

Swift
  func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController,
                                       shouldAppear _: Bool) {
    updateControlBarsVisibility()
  }
Objectif-C
- (void)miniMediaControlsViewController:
            (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController
                           shouldAppear:(BOOL)shouldAppear {
  [self updateControlBarsVisibility];
}

Ajouter une manette agrandie

La checklist de conception Google Cast nécessite qu'une application émettrice fournisse un contrôleur étendu pour le contenu multimédia en cours de diffusion. Le contrôleur développé est une version plein écran du mini-contrôleur.

Le contrôleur développé s'affiche en plein écran et offre un contrôle total sur la lecture des contenus multimédias à distance. Cette vue doit permettre à une application de cast de gérer tous les aspects gérables d'une session Cast, à l'exception du contrôle du volume du récepteur Web et du cycle de vie de la session (connexion/arrêt de la diffusion). Il fournit également toutes les informations d'état de la session multimédia (affiche, titre, sous-titre, etc.).

La fonctionnalité de cette vue est implémentée par la classe GCKUIExpandedMediaControlsViewController.

La première chose à faire est d'activer le contrôleur développé par défaut dans le contexte de diffusion. Modifiez le délégué de l'application pour activer le contrôleur développé par défaut:

Swift
func applicationDidFinishLaunching(_ application: UIApplication) {
  ..

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

  ...
}
Objectif-C
- (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:

Swift
func playSelectedItemRemotely() {
  GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()

  ...

  // Load your media
  sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation)
}
Objectif-C
- (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 l'application émettrice diffuse un flux vidéo en direct ou audio, le SDK affiche automatiquement un bouton de lecture/d'arrêt au lieu du bouton de lecture/pause dans la commande agrandie.

Consultez la section Appliquer des styles personnalisés à votre application iOS pour découvrir comment votre application émettrice peut configurer l'apparence des widgets Cast.

Contrôle du volume

Le framework Cast gère automatiquement le volume de l'application émettrice. Il 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ôler le volume du bouton physique

Vous pouvez utiliser les boutons de volume physique de l'appareil expéditeur pour modifier le volume de la session Cast sur le récepteur Web à l'aide de l'option physicalVolumeButtonsWillControlDeviceVolume de GCKCastOptions, qui est défini sur GCKCastContext.

Swift
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
let options = GCKCastOptions(discoveryCriteria: criteria)
options.physicalVolumeButtonsWillControlDeviceVolume = true
GCKCastContext.setSharedInstanceWith(options)
Objectif-C
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 émettrices gèrent tous les rappels d'erreurs et décident de la meilleure réponse pour chaque étape du cycle de vie Cast. L'application peut afficher des boîtes de dialogue d'erreur ou décider de mettre fin à la session Cast.

Journalisation

GCKLogger est un singleton utilisé pour la journalisation par le framework. Utilisez GCKLoggerDelegate pour personnaliser la façon dont vous gérez les messages de journal.

À l'aide de GCKLogger, le SDK génère 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 résoudre les problèmes et identifier les problèmes. Par défaut, la sortie du journal est supprimée, mais en attribuant un GCKLoggerDelegate, l'application expéditeur peut recevoir ces messages du SDK et les consigner dans la console système.

Swift
@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)
    }
  }
}
Objectif-C

Délégué à l'utilisation d'applications

@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 le débogage et les messages détaillés, ajoutez la ligne suivante au code après avoir défini le délégué (affiché précédemment):

Swift
let filter = GCKLoggerFilter.init()
filter.minimumLevel = GCKLoggerLevel.verbose
GCKLogger.sharedInstance().filter = filter
Objectif-C
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:

Swift
let filter = GCKLoggerFilter.init()
filter.setLoggingLevel(GCKLoggerLevel.verbose, forClasses: ["GCKUICastButton",
                                                            "GCKUIImageCache",
                                                            "NSMutableDictionary"])
GCKLogger.sharedInstance().filter = filter
Objectif-C
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érals ou des modèles glob, par exemple GCKUI\* et GCK\*Session.