Intégrer Cast à votre application iOS

Ce guide du développeur explique comment activer la compatibilité avec Google Cast sur un appareil iOS. l'application émettrice à l'aide du SDK Sender d'iOS.

L'appareil mobile ou l'ordinateur portable est l'expéditeur qui contrôle la lecture. L'appareil Google Cast est le récepteur qui affiche le contenu sur le téléviseur.

Le framework expéditeur fait référence au binaire de la bibliothèque de classes Cast et ressources présentes au moment de l'exécution sur l'émetteur. L'application émettrice ou l'application Cast fait référence à une application en cours d’exécution sur l’expéditeur. L'application Web Receiver 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'expéditeur application d'événements et de transition entre différents états de l'application Cast d'un cycle.

Déroulement des opérations de l'application

Les étapes suivantes décrivent le flux d'exécution de haut niveau typique pour un expéditeur Application iOS:

  • Le framework Cast commence GCKDiscoveryManager en fonction des propriétés fournies dans GCKCastOptions jusqu'à commencer à rechercher des appareils.
  • Lorsque l'utilisateur clique sur l'icône Cast, le framework affiche l'icône Cast. contenant la liste des appareils Cast détectés.
  • Lorsque l'utilisateur sélectionne un appareil Cast, le framework tente de lancer l' Web Receiver sur l'appareil Cast.
  • Le framework appelle des rappels dans l'application émettrice pour vérifier que le L'application Web Receiver a été lancée.
  • Le framework crée un canal de communication entre l'expéditeur et Applications Web Receiver.
  • Le framework utilise le canal de communication pour charger et contrôler les contenus multimédias la lecture sur le Web Receiver.
  • Le framework synchronise l'état de lecture des contenus multimédias entre l'émetteur et Web Receiver: lorsque l'utilisateur effectue des actions dans l'interface utilisateur de l'expéditeur, le framework transmet ces requêtes de contrôle multimédia au Web Receiver, et lorsque ce récepteur Web envoie des mises à jour de l'état du contenu multimédia, le framework met à jour l'état de l'UI de l'émetteur.
  • Lorsque l'utilisateur clique sur l'icône Cast pour se déconnecter de l'appareil Cast, le framework déconnectera 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 des classes, méthodes et événements de Google Cast, framework iOS, consultez la page sur l'API Google Cast iOS Documentation de référence. Les sections suivantes décrivent les étapes pour intégrer Cast à votre application iOS.

Méthodes d'appel à partir du thread principal

Initialiser le contexte Cast

Le framework Cast comporte un objet Singleton global, GCKCastContext, qui coordonne toutes les activités du framework. Cet objet doit être initialisé au début du cycle de vie de l'application, généralement -[application:didFinishLaunchingWithOptions:] du délégué d'application. que la reprise de session automatique au redémarrage de l'application de l'émetteur peut se déclencher correctement.

Un GCKCastOptions doit être fourni lors de l'initialisation de GCKCastContext. Cette classe contient des options qui affectent le comportement du framework. Les plus le plus important est l'ID d'application Web Receiver, qui sert à filtrer et de lancer l'application Web Receiver lorsqu'une session Cast est pour commencer.

Vous pouvez également utiliser la méthode -[application:didFinishLaunchingWithOptions:] pour configurer un délégué de journalisation afin de recevoir les messages de journalisation du framework. Ils peuvent être utiles 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)
    }
  }
}
Objective-C

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

Le SDK Cast pour iOS fournit des widgets qui sont conformes à la conception Cast. Checklist:

  • Superposition d'introduction: La classe GCKCastContext comporte une méthode, presentCastInstructionsViewControllerOnceWithCastButton qui permet de mettre en avant l'icône Cast la première fois qu'un récepteur Web est disponible. L'application émettrice peut personnaliser le texte et la position du titre. et sur le bouton Ignorer.

  • Icône Cast: À partir de la version 4.6.0 du SDK émetteur Cast pour iOS, 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 s'affiche afin que l'utilisateur puisse accorder à l'application l'accès au réseau local aux appareils le réseau. Par la suite, lorsque l'utilisateur appuie sur l'icône Cast, qui liste les appareils détectés. Lorsque l'utilisateur appuie sur sur l'icône Cast lorsque l'appareil est connecté, l'écran affiche Des métadonnées multimédias (comme le titre, le nom du studio d'enregistrement et une miniature) ) ou de se déconnecter de l'appareil Cast. Lorsque l'utilisateur appuie sur l'icône Cast lorsqu'aucun appareil n'est disponible, un écran s'affichera, indiquant à l'utilisateur pourquoi l'appareil n'a pas été détecté. et comment résoudre les problèmes.

  • Mini-télécommande: Lorsque l'utilisateur caste du contenu et a quitté la page ou la télécommande agrandie sur un autre écran de l'application émettrice, le la mini-télécommande est affichée en bas de l'écran pour permettre à l'utilisateur de pour voir les métadonnées de contenus multimédias en cours de diffusion et contrôler la lecture.

  • Contrôleur étendu: Lorsque l'utilisateur caste du contenu, s'il clique sur la notification multimédia ou la télécommande agrandie est lancée et affiche la métadonnées multimédias en cours de lecture et propose plusieurs boutons pour contrôler la lecture des contenus multimédias.

Ajouter une icône 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 configuration type La sous-classe UIViewController peut installer une 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)
Objective-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, lorsque vous appuyez sur le bouton, la boîte de dialogue Cast fournie par l'application d'infrastructure.

GCKUICastButton peuvent également être ajoutées directement au storyboard.

Configurer la détection d'appareils

Dans le framework, la détection d'appareils s'effectue automatiquement. Il n'est pas nécessaire démarrer ou arrêter explicitement le processus de découverte, sauf si vous implémentez une UI personnalisée.

Dans le framework, la découverte est gérée par la classe GCKDiscoveryManager, qui est une propriété GCKCastContext La propose un composant de boîte de dialogue Cast par défaut pour la sélection des appareils. le contrôle. La liste des appareils est triée dans l'ordre lexicographique en fonction du nom convivial de l'appareil.

Fonctionnement de la gestion des sessions

Le SDK Cast introduit le concept de session Cast, établissement qui combine les étapes de connexion à un appareil, de lancement (ou de connexion) à un Web l'application réceptrice, la connexion à cette application et l'initialisation d'un canal de commande multimédia. Voir le récepteur Web Guide sur le cycle de vie des applications pour en savoir plus sur les sessions Cast et le cycle de vie du récepteur Web.

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

La GCKSessionManagerListener interface peut être utilisée pour surveiller les événements de session, tels que la création de session, la suspension, la reprise et la résiliation. Le framework est automatiquement suspendu lorsque l'application émettrice passe en arrière-plan et tente de reprendre lorsque l'application revient au premier plan (ou qu'elle est relancée après une arrêt anormal ou abrupt de l'application alors qu'une session était active).

Si la boîte de dialogue "Caster" est utilisée, les sessions sont créées et supprimées automatiquement en réponse aux gestes de l'utilisateur. Sinon, l'application peut démarrer et se terminer les sessions explicitement via des méthodes GCKSessionManager

Si l'application doit effectuer un traitement spécial en réponse au cycle de vie de la session aux événements, il peut enregistrer une ou plusieurs instances GCKSessionManagerListener avec la GCKSessionManager. GCKSessionManagerListener est un protocole qui définit des rappels pour des événements tels que le début ou 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, où les utilisateurs peuvent déplacer des flux audio et vidéo existants d'un appareil à un autre à l'aide de commandes vocales, de Google Home des applications ou des écrans connectés. La lecture du contenu multimédia s'arrête sur un appareil (la source) et continue sur un autre (le destination). N'importe quel appareil Cast équipé du dernier micrologiciel peut servir de source ou de destination dans un le transfert de diffusion.

Pour récupérer le nouvel appareil de destination pendant le transfert de diffusion, utilisez le GCKCastSession#device pendant le [sessionManager:didResumeCastSession:] .

Voir Transfert de diffusion sur Web Receiver pour en savoir plus.

Reconnexion automatique

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

  • Se remettre d'une perte temporaire de connexion Wi-Fi
  • Récupérer après une mise en veille de l'appareil
  • Récupérer après la mise en arrière-plan de l'application
  • Récupérer en cas de plantage de l'application

Fonctionnement des commandes multimédias

Si une session Cast est établie avec une application Web Receiver compatible avec le contenu multimédia d'espace de noms, une instance de GCKRemoteMediaClient sont créées automatiquement par le framework. il est accessible propriété remoteMediaClient de GCKCastSession Compute Engine.

Toutes les méthodes sur GCKRemoteMediaClient qui envoient des requêtes au Web Receiver renvoie une un objet GCKRequest qui pour suivre la requête. A GCKRequestDelegate peut être attribué à cet objet pour recevoir des notifications sur résultat de l'opération.

Il est attendu que l'instance de GCKRemoteMediaClient peut être partagé par plusieurs parties de l'application, et certains composants internes du framework, comme la boîte de dialogue "Caster" et les mini-commandes multimédias partagent le Compute Engine. C'est pourquoi GCKRemoteMediaClient permet l'enregistrement de plusieurs GCKRemoteMediaClientListener

Définir les métadonnées multimédias

La GCKMediaMetadata représente des informations sur l'élément multimédia que vous souhaitez caster. Les éléments suivants : "example" crée une instance GCKMediaMetadata d'un film et définit le titre, un 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))
Objective-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]];

Reportez-vous à la section Sélection d'images et Mise en cache sur l'utilisation d'images avec des métadonnées multimédias.

Charger le média

Pour charger un élément multimédia, créez un GCKMediaInformation à l'aide des métadonnées du média. Ensuite, obtenez la valeur actuelle GCKCastSession et utilisez son GCKRemoteMediaClient pour charger le contenu multimédia dans 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, pause et arrêter.

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
}
Objective-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 sur à l'aide de 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 GCKVideoInfo Cette instance contient le type de format de téléviseur HDR, ainsi que la hauteur et la largeur en de pixels. Les variantes du format 4K sont indiquées dans la propriété hdrType par une énumération. Valeurs GCKVideoInfoHDRType.

Ajouter des mini-télécommandes

Selon l'architecture Cast, Checklist, une application émettrice doit fournir un contrôle persistant, appelé Mini manette qui doit s'afficher lorsque l'utilisateur quitte la page de contenu actuelle. La mini-télécommande offre un accès instantané et un rappel visible la session Cast en cours.

Le framework Cast fournit une barre de contrôle, GCKUIMiniMediaControlsViewController, que vous pouvez ajouter aux scènes dans lesquelles vous souhaitez afficher la mini-télécommande.

Lorsque l'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 la rubrique Personnaliser l'interface utilisateur iOS Sender pour découvrir comment votre l'application émettrice peut configurer l'apparence des widgets Cast.

Il existe deux façons d'ajouter la mini-télécommande à une application émettrice:

  • Laissez le framework Cast gérer la disposition 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 disposition du widget de la mini-télécommande en l'ajoutant à votre le 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 une GCKUIMiniMediaControlsViewController en bas. Cette approche est limitée dans la mesure où vous ne pouvez pas personnaliser et ne peut pas configurer le comportement du contrôleur de vue du conteneur.

Cette première méthode s'effectue généralement Méthode -[application:didFinishLaunchingWithOptions:] du délégué d'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()

  ...
}
Objective-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
    }
  }
}
Objective-C

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 la mini-télécommande directement à la vue existante. à l'aide du contrôleur createMiniMediaControlsViewController pour créer GCKUIMiniMediaControlsViewController puis en l'ajoutant 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:

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
}
Objective-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 un 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)
    }
  }

...
Objective-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

La GCKUIMiniMediaControlsViewControllerDelegate indique au contrôleur de vue de l'hôte à quel moment le mini-télécommande doit être visible:

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

Ajouter une télécommande agrandie

La checklist de conception de Google Cast exige qu'une application émettrice fournisse un manette pour le contenu multimédia casté. La télécommande agrandie est une version plein écran de la mini-télécommande.

La télécommande agrandie est une vue en plein écran qui offre un contrôle total sur la lecture de contenus multimédias à distance. Cette vue devrait permettre à une application de diffusion de gérer toutes aspect gérable d'une session Cast, à l'exception du volume Web Receiver et le cycle de vie de la session (connexion/arrêt de la diffusion). Il fournit également toutes les informations relatives à l'état de la session multimédia (illustration, titre, sous-titre, etc.), .

La fonctionnalité de cette vue est mise en œuvre par GCKUIExpandedMediaControlsViewController .

La première chose à faire est d'activer la télécommande agrandie par défaut dans le caste du contexte. Modifiez le délégué d'application pour activer la télécommande agrandie par défaut:

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

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

  ...
}
Objective-C
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  ..
}

Ajoutez le code suivant à votre contrôleur de vue pour charger le contrôleur étendu Lorsque l'utilisateur commence à caster une vidéo:

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

  ...

  // Load your media
  sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation)
}
Objective-C
- (void)playSelectedItemRemotely {
  [[GCKCastContext sharedInstance] presentDefaultExpandedMediaControls];

  ...

  // Load your media
  [self.sessionManager.currentSession.remoteMediaClient loadMedia:mediaInformation];
}

La télécommande agrandie se lancera également automatiquement lorsque l'utilisateur appuie sur la mini-télécommande.

Lorsque l'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 télécommande agrandie.

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

Réglage du volume

Le framework Cast gère automatiquement le volume pour l'application émettrice. La se synchronise automatiquement avec le volume Web Receiver pour les widgets d'interface utilisateur fournis. Pour synchroniser un curseur fourni par l'application, utilisez GCKUIDeviceVolumeController

Bouton physique de contrôle du volume

Les boutons de volume physiques de l'appareil émetteur peuvent être utilisés pour modifier le de la session Cast sur Web Receiver à l'aide de la l'indicateur physicalVolumeButtonsWillControlDeviceVolume sur la GCKCastOptions, qui est défini sur GCKCastContext

Swift
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
let options = GCKCastOptions(discoveryCriteria: criteria)
options.physicalVolumeButtonsWillControlDeviceVolume = true
GCKCastContext.setSharedInstanceWith(options)
Objective-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'erreur et décident la meilleure réponse à chaque étape du cycle de vie de Cast. L'application peut afficher d'erreur à l'utilisateur ou il peut décider de mettre fin à la session Cast.

Journalisation

GCKLogger est un singleton utilisé pour la journalisation par le framework. Utilisez le 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 forme de débogage. messages, erreurs et 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é, mais en attribuant un GCKLoggerDelegate, l'application émettrice peut recevoir ces messages à partir du SDK et les enregistrer 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)
    }
  }
}
Objective-C

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 les messages de débogage et les messages détaillés, ajoutez cette ligne au code après en configurant le délégué (comme indiqué précédemment):

Swift
let filter = GCKLoggerFilter.init()
filter.minimumLevel = GCKLoggerLevel.verbose
GCKLogger.sharedInstance().filter = filter
Objective-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
Objective-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éraux ou des modèles glob, par exemple : GCKUI\* et GCK\*Session.