Cast in iOS-App einbinden

In diesem Entwicklerleitfaden wird beschrieben, wie du deiner iOS-Sender-App mit dem iOS Sender SDK die Google Cast-Unterstützung hinzufügst.

Das Mobilgerät oder der Laptop ist der Sender, der die Wiedergabe steuert, und das Google Cast-Gerät ist der Empfänger, der die Inhalte auf dem Fernseher anzeigt.

Das Absender-Framework bezieht sich auf das Binärprogramm der Cast-Klassenbibliothek und die zugehörigen Ressourcen, die zur Laufzeit auf dem Absender vorhanden sind. Die Absender-App oder Cast-App bezieht sich auf eine App, die auch auf dem Absender ausgeführt wird. Die Web Receiver-App bezieht sich auf die HTML-Anwendung, die auf Web Receiver ausgeführt wird.

Das Absender-Framework verwendet ein asynchrones Callback-Design, um die Sender-App über Ereignisse zu informieren und zwischen verschiedenen Zuständen des Lebenszyklus der Cast-App zu wechseln.

Anwendungsfluss

In den folgenden Schritten wird der typische allgemeine Ausführungsablauf für eine Absender-iOS-App beschrieben:

  • Das Cast-Framework startet GCKDiscoveryManager basierend auf den Attributen in GCKCastOptions, um mit dem Scannen nach Geräten zu beginnen.
  • Wenn der Nutzer auf das Cast-Symbol klickt, zeigt das Framework das Cast-Dialogfeld mit der Liste der erkannten Übertragungsgeräte an.
  • Wenn der Nutzer ein Übertragungsgerät auswählt, versucht das Framework, die Web Receiver App auf dem Übertragungsgerät zu starten.
  • Das Framework ruft in der Absenderanwendung Callbacks auf, um zu bestätigen, dass die Webempfänger-App gestartet wurde.
  • Das Framework erstellt einen Kommunikationskanal zwischen der Absender- und der Webempfängeranwendung.
  • Das Framework verwendet den Kommunikationskanal, um die Medienwiedergabe auf dem Webempfänger zu laden und zu steuern.
  • Das Framework synchronisiert den Status der Medienwiedergabe zwischen Sender und Web Receiver: Wenn der Nutzer UI-Aktionen des Absenders durchführt, übergibt das Framework diese Mediensteuerungsanfragen an den Web Receiver. Wenn der Webempfänger Aktualisierungen zum Medienstatus sendet, aktualisiert das Framework den Status der Absender-UI.
  • Wenn der Nutzer auf das Cast-Symbol klickt, um die Verbindung zum Übertragungsgerät zu trennen, trennt das Framework die Absender-App vom Web Receiver.

Um Fehler beim Absender zu beheben, müssen Sie die Protokollierung aktivieren.

Eine umfassende Liste aller Klassen, Methoden und Ereignisse im Google Cast iOS-Framework finden Sie in der Referenz zur Google Cast iOS API. In den folgenden Abschnitten werden die Schritte zur Integration von Cast in deine iOS-App beschrieben.

Aufrufmethoden aus dem Hauptthread

Cast-Kontext initialisieren

Das Cast-Framework hat ein globales Singleton-Objekt, das GCKCastContext, das alle Aktivitäten des Frameworks koordiniert. Dieses Objekt muss früh im Lebenszyklus der Anwendung initialisiert werden, in der Regel in der Methode -[application:didFinishLaunchingWithOptions:] des Anwendungsdelegats, damit die automatische Sitzungswiederaufnahme beim Neustart der Absender-App korrekt ausgelöst werden kann.

Beim Initialisieren von GCKCastContext muss ein GCKCastOptions-Objekt angegeben werden. Diese Klasse enthält Optionen, die das Verhalten des Frameworks beeinflussen. Die wichtigste davon ist die Web Receiver-App-ID, die zum Filtern von Erkennungsergebnissen und zum Starten der Web Receiver-App beim Start einer Cast-Sitzung verwendet wird.

Die Methode -[application:didFinishLaunchingWithOptions:] eignet sich auch gut, um einen Logging-Delegaten einzurichten, der die Logging-Nachrichten vom Framework empfangen soll. Diese können bei der Fehlersuche und Fehlerbehebung hilfreich sein.

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

Die Cast UX-Widgets

Das Cast iOS SDK bietet die folgenden Widgets, die der Checkliste für das Cast-Design entsprechen:

  • Einführendes Overlay: Die Klasse GCKCastContext verfügt über die Methode presentCastInstructionsViewControllerOnceWithCastButton, mit der das Cast-Symbol hervorgehoben werden kann, wenn ein Webempfänger zum ersten Mal verfügbar ist. Die Absender-App kann den Text, die Position des Titeltextes und die Schaltfläche „Schließen“ anpassen.

  • Cast-Symbol: Ab Version 4.6.0 des Cast iOS Sender SDK ist das Cast-Symbol immer sichtbar, wenn das Gerät des Absenders mit dem WLAN verbunden ist. Wenn der Nutzer nach dem ersten Start der App zum ersten Mal auf das Cast-Symbol tippt, wird ein Berechtigungsdialogfeld angezeigt, über das der Nutzer der App Zugriff auf das lokale Netzwerk auf Geräte im Netzwerk gewähren kann. Wenn der Nutzer dann auf das Cast-Symbol tippt, wird ein Dialogfeld angezeigt, in dem die erkannten Geräte aufgeführt sind. Wenn der Nutzer auf das Cast-Symbol tippt, während das Gerät verbunden ist, werden die aktuellen Metadaten der Medien (z. B. Titel, Name des Aufnahmestudios und eine Miniaturansicht) angezeigt oder der Nutzer kann die Verbindung zum Übertragungsgerät trennen. Wenn der Nutzer auf das Cast-Symbol tippt, während keine Geräte verfügbar sind, wird ein Bildschirm mit Informationen dazu angezeigt, warum Geräte nicht gefunden werden und wie die Fehler behoben werden können.

  • Mini-Controller: Wenn der Nutzer Inhalte überträgt und von der aktuellen Inhaltsseite oder dem erweiterten Controller zu einem anderen Bildschirm in der Sender-App navigiert ist, wird der Mini-Controller unten auf dem Bildschirm angezeigt, damit der Nutzer die aktuell gestreamten Medienmetadaten sehen und die Wiedergabe steuern kann.

  • Erweiterter Controller: Wenn der Nutzer beim Streamen von Inhalten auf die Medienbenachrichtigung oder den Mini-Controller klickt, wird der maximierte Controller gestartet. Dieser zeigt die aktuell wiedergegebenen Medienmetadaten an und bietet mehrere Schaltflächen zur Steuerung der Medienwiedergabe.

Cast-Symbol hinzufügen

Das Framework stellt eine Komponente für das Cast-Symbol als abgeleitete UIButton-Klasse bereit. Er kann der Titelleiste der App hinzugefügt werden, indem er in ein UIBarButtonItem-Element eingebunden wird. So lässt sich ein Cast-Symbol in einer typischen abgeleiteten UIViewController-Klasse installieren:

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

Wenn Sie auf die Schaltfläche tippen, wird standardmäßig das vom Framework bereitgestellte Cast-Dialogfeld geöffnet.

GCKUICastButton kann auch direkt dem Storyboard hinzugefügt werden.

Geräteerkennung konfigurieren

Im Framework erfolgt die Geräteerkennung automatisch. Der Erkennungsprozess muss nicht explizit gestartet oder beendet werden, es sei denn, Sie implementieren eine benutzerdefinierte UI.

Die Erkennung im Framework wird von der Klasse GCKDiscoveryManager verwaltet, die ein Attribut von GCKCastContext ist. Das Framework bietet eine standardmäßige Cast-Dialogkomponente für die Geräteauswahl und -steuerung. Die Geräteliste ist lexikografisch nach dem für das Gerät angezeigten Namen geordnet.

Funktionsweise der Sitzungsverwaltung

Mit dem Cast SDK wird das Konzept einer Cast-Sitzung eingeführt. Dabei werden alle Schritte kombiniert, um eine Verbindung zu einem Gerät herzustellen, eine Web-Receiver-App zu starten (oder der App beizutreten), eine Verbindung zu dieser App herzustellen und einen Mediensteuerungskanal zu initialisieren. Weitere Informationen zu Streamingsitzungen und zum Web Receiver-Lebenszyklus findest du im Leitfaden zum Lebenszyklus von Anwendungen für Web Receiver.

Sitzungen werden von der Klasse GCKSessionManager verwaltet, die ein Attribut von GCKCastContext ist. Einzelne Sitzungen werden durch abgeleitete Klassen der Klasse GCKSession dargestellt. Zum Beispiel steht GCKCastSession für Sitzungen mit Übertragungsgeräten. Du kannst über das Attribut currentCastSession von GCKSessionManager auf die aktuell aktive Umwandlungssitzung (falls vorhanden) zugreifen.

Über die Schnittstelle GCKSessionManagerListener können Sie Sitzungsereignisse wie Erstellung, Sperrung, Wiederaufnahme und Beendigung von Sitzungen überwachen. Das Framework hält Sitzungen automatisch an, wenn die Absender-App in den Hintergrund wechselt, und versucht, sie fortzusetzen, wenn die App in den Vordergrund zurückkehrt (oder neu gestartet wird, nachdem die App abnormal/abrupt beendet wurde, während eine Sitzung aktiv war).

Wenn das Dialogfeld „Streamen“ verwendet wird, werden Sitzungen als Reaktion auf Nutzergesten automatisch erstellt und beendet. Andernfalls kann die Anwendung Sitzungen explizit über Methoden in GCKSessionManager starten und beenden.

Wenn die Anwendung als Reaktion auf Ereignisse des Sitzungslebenszyklus eine spezielle Verarbeitung durchführen muss, kann sie eine oder mehrere GCKSessionManagerListener-Instanzen bei der GCKSessionManager registrieren. GCKSessionManagerListener ist ein Protokoll, das Callbacks für Ereignisse wie Sitzungsstart und ‐ende definiert.

Stream-Übertragung

Die Beibehaltung des Sitzungsstatus ist die Grundlage der Streamübertragung. Nutzer können vorhandene Audio- und Videostreams mithilfe von Sprachbefehlen, der Google Home App oder Smart Displays auf andere Geräte verschieben. Die Medienwiedergabe wird auf einem Gerät (Quelle) und auf einem anderen Gerät (dem Ziel) beendet. Jedes Übertragungsgerät mit der neuesten Firmware kann als Quellen oder Ziele in einer Streamübertragung dienen.

Verwenden Sie das Attribut GCKCastSession#device während des [sessionManager:didResumeCastSession:]-Callbacks, um das neue Zielgerät während der Streamübertragung abzurufen.

Weitere Informationen finden Sie unter Stream-Übertragung mit Web Receiver.

Automatische Wiederherstellung der Verbindung

Das Cast-Framework fügt Logik für die erneute Verbindung hinzu, um in vielen subtilen Grenzfällen automatisch die erneute Verbindung zu verarbeiten, z. B.:

  • Wiederherstellung nach einem vorübergehenden WLAN-Ausfall
  • Aus Geräte-Ruhemodus wiederherstellen
  • Nach Hintergrundwiedergabe wiederherstellen
  • Wiederherstellung nach einem Absturz der App

So funktioniert die Mediensteuerung

Wenn eine Cast-Sitzung mit einer Web Receiver-App eingerichtet wird, die den Medien-Namespace unterstützt, erstellt das Framework automatisch eine GCKRemoteMediaClient-Instanz. Sie kann als remoteMediaClient-Attribut der GCKCastSession-Instanz aufgerufen werden.

Alle Methoden unter GCKRemoteMediaClient, die Anfragen an Web Receiver senden, geben ein GCKRequest-Objekt zurück, mit dem diese Anfrage verfolgt werden kann. Diesem Objekt kann eine GCKRequestDelegate zugewiesen werden, um Benachrichtigungen über das letztendliche Ergebnis des Vorgangs zu erhalten.

Es ist zu erwarten, dass die Instanz von GCKRemoteMediaClient von mehreren Teilen der App gemeinsam genutzt wird. Einige interne Komponenten des Frameworks, z. B. das Dialogfeld „Streamen“ und die Mini-Mediensteuerung, teilen die Instanz jedoch. Zu diesem Zweck unterstützt GCKRemoteMediaClient die Registrierung mehrerer GCKRemoteMediaClientListeners.

Medienmetadaten festlegen

Die Klasse GCKMediaMetadata stellt Informationen zu einem Medienelement dar, das Sie umwandeln möchten. Im folgenden Beispiel wird eine neue GCKMediaMetadata-Instanz eines Films erstellt und Titel, Untertitel, Name des Aufnahmestudios und zwei Bilder festgelegt.

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))
Ziel-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]];

Weitere Informationen zur Verwendung von Bildern mit Medienmetadaten finden Sie im Abschnitt Bilderauswahl und Caching.

Medien laden

Zum Laden eines Medienelements erstellen Sie eine GCKMediaInformation-Instanz mit den Metadaten des Mediums. Rufen Sie dann das aktuelle GCKCastSession ab und verwenden Sie dessen GCKRemoteMediaClient, um die Medien in die Empfänger-App zu laden. Sie können dann GCKRemoteMediaClient verwenden, um eine auf dem Empfänger ausgeführte Mediaplayer-App zu steuern, z. B. zum Abspielen, Pausieren und Stoppen.

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
}
Ziel-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;
}

Weitere Informationen finden Sie im Abschnitt Medien-Tracks verwenden.

4K-Videoformat

Wenn Sie das Videoformat Ihrer Mediendatei bestimmen möchten, verwenden Sie das Attribut videoInfo von GCKMediaStatus, um die aktuelle Instanz von GCKVideoInfo abzurufen. Diese Instanz enthält den Typ des HDR TV-Formats sowie die Höhe und Breite in Pixeln. Varianten des 4K-Formats werden im Attribut hdrType durch die Enum-Werte GCKVideoInfoHDRType angegeben.

Mini-Controller hinzufügen

Gemäß der Checkliste für das Streamingdesign sollte eine Absender-App eine dauerhafte Steuerung – den sogenannten Mini-Controller – bereitstellen, der angezeigt werden sollte, wenn der Nutzer die aktuelle Inhaltsseite verlässt. Der Mini-Controller bietet sofortigen Zugriff und eine sichtbare Erinnerung für die aktuelle Cast-Sitzung.

Das Cast-Framework bietet eine Steuerleiste (GCKUIMiniMediaControlsViewController), die den Szenen hinzugefügt werden kann, in denen der Mini-Controller gezeigt werden soll.

Wenn die Sender-App einen Video- oder Audio-Livestream wiedergibt, zeigt das SDK automatisch eine Wiedergabe-/Stopp-Schaltfläche anstelle der Wiedergabe-/Pause-Schaltfläche auf dem Mini-Controller an.

Wie Ihre Absender-App die Darstellung der Cast-Widgets konfigurieren kann, erfahren Sie unter iOS-Sender-UI anpassen.

Es gibt zwei Möglichkeiten, den Mini-Controller einer Sender-App hinzuzufügen:

  • Lassen Sie das Cast-Framework das Layout des Mini-Controllers verwalten. Dazu verpacken Sie den vorhandenen Ansichts-Controller mit einem eigenen Ansicht-Controller.
  • Du kannst das Layout des Mini-Controller-Widgets selbst verwalten, indem du es deinem vorhandenen Ansicht-Controller hinzufügst, indem du im Storyboard eine Unteransicht bereitstellst.

Wrapping mit GCKUICastContainerViewController

Die erste Möglichkeit besteht darin, den GCKUICastContainerViewController zu verwenden, der einen weiteren Ansichts-Controller umschließt und am unteren Rand ein GCKUIMiniMediaControlsViewController-Element hinzufügt. Dieser Ansatz ist insofern eingeschränkt, als Sie die Animation nicht anpassen und das Verhalten des Container-Ansicht-Controllers nicht konfigurieren können.

Dies erfolgt in der Regel in der Methode -[application:didFinishLaunchingWithOptions:] des Anwendungsdelegats:

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

  ...
}
Ziel-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
    }
  }
}
Ziel-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

In vorhandenen Ansichts-Controller einbetten

Die zweite Möglichkeit besteht darin, den Mini-Controller direkt zu Ihrem vorhandenen View-Controller hinzuzufügen. Dazu verwenden Sie createMiniMediaControlsViewController, um eine GCKUIMiniMediaControlsViewController-Instanz zu erstellen und sie dem Container View-Controller als untergeordnete Ansicht hinzuzufügen.

Richten Sie den Ansichts-Controller im App-Delegaten ein:

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
}
Ziel-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;
}

Erstellen Sie in Ihrem Root-Ansicht-Controller eine GCKUIMiniMediaControlsViewController-Instanz und fügen Sie sie dem Container View-Controller als untergeordnete Ansicht hinzu:

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

...
Ziel-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 teilt dem Hostansicht-Controller mit, wann der Mini-Controller sichtbar sein soll:

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

Maximierten Controller hinzufügen

Gemäß der Google Cast-Design-Checkliste muss eine Sender-App einen erweiterten Controller für die gestreamten Medien bereitstellen. Der erweiterte Controller ist eine Vollbildversion des Mini-Controllers.

Der erweiterte Controller bietet eine Vollbildansicht, mit der Sie die Remote-Medienwiedergabe steuern können. In dieser Ansicht sollte eine Streaming-App alle verwaltbaren Aspekte einer Streamingsitzung verwalten können, mit Ausnahme der Lautstärkeregelung und des Sitzungslebenszyklus von Web Receiver (Streaming verbinden/beenden). Sie enthält auch alle Statusinformationen zur Mediensitzung (Artwork, Titel, Untertitel usw.).

Die Funktionalität dieser Ansicht wird durch die Klasse GCKUIExpandedMediaControlsViewController implementiert.

Zuerst müssen Sie den erweiterten Standard-Controller im Cast-Kontext aktivieren. Ändern Sie den App-Delegaten, um den standardmäßigen erweiterten Controller zu aktivieren:

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

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

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

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  ..
}

Fügen Sie dem Ansicht-Controller den folgenden Code hinzu, um den erweiterten Controller zu laden, wenn der Nutzer mit dem Streamen eines Videos beginnt:

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

  ...

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

  ...

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

Der erweiterte Controller wird auch automatisch gestartet, wenn der Nutzer auf den Mini-Controller tippt.

Wenn die Sender-App einen Video- oder Audio-Livestream wiedergibt, zeigt das SDK im erweiterten Controller automatisch eine Wiedergabe-/Stopp-Schaltfläche anstelle der Wiedergabe-/Pause-Schaltfläche an.

Wie die Absender-App die Darstellung der Cast-Widgets konfigurieren kann, erfährst du unter Benutzerdefinierte Stile auf iOS-App anwenden.

Lautstärkeregelung

Das Cast-Framework verwaltet automatisch das Volume für die Absender-App. Das Framework wird automatisch mit dem Web Receiver-Volume für die bereitgestellten UI-Widgets synchronisiert. Verwenden Sie GCKUIDeviceVolumeController, um einen von der App bereitgestellten Schieberegler zu synchronisieren.

Lautstärkeregelung mit physischer Taste

Mit den physischen Lautstärketasten auf dem Gerät des Absenders kannst du die Lautstärke der Übertragungssitzung auf dem Web Receiver ändern. Dazu wird das Flag physicalVolumeButtonsWillControlDeviceVolume auf dem GCKCastOptions verwendet, das in GCKCastContext festgelegt ist.

Swift
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
let options = GCKCastOptions(discoveryCriteria: criteria)
options.physicalVolumeButtonsWillControlDeviceVolume = true
GCKCastContext.setSharedInstanceWith(options)
Ziel-C
GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc]
                                          initWithApplicationID:kReceiverAppID];
GCKCastOptions *options = [[GCKCastOptions alloc]
                                          initWithDiscoveryCriteria :criteria];
options.physicalVolumeButtonsWillControlDeviceVolume = YES;
[GCKCastContext setSharedInstanceWithOptions:options];

Fehler verarbeiten

Absender-Apps müssen alle Fehlerrückrufe verarbeiten und für jede Phase des Cast-Lebenszyklus die beste Antwort festlegen. Die App kann dem Nutzer Fehlerdialogfelder anzeigen oder entscheiden, die Streaming-Sitzung zu beenden.

Logging

GCKLogger ist ein Singleton-Element, das vom Framework für das Logging verwendet wird. Mit GCKLoggerDelegate können Sie die Verarbeitung von Logeinträgen anpassen.

Mit dem GCKLogger generiert das SDK eine Logging-Ausgabe in Form von Debug-Meldungen, Fehlern und Warnungen. Diese Logeinträge unterstützen Sie bei der Fehlerbehebung und sind hilfreich bei der Fehlerbehebung und Identifizierung von Problemen. Standardmäßig wird die Logausgabe unterdrückt. Durch die Zuweisung einer GCKLoggerDelegate kann die Absenderanwendung diese Nachrichten vom SDK empfangen und in der Systemkonsole protokollieren.

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

Um auch Debug- und ausführliche Meldungen zu aktivieren, fügen Sie dem Code diese Zeile hinzu, nachdem Sie den Bevollmächtigten festgelegt haben (siehe oben):

Swift
let filter = GCKLoggerFilter.init()
filter.minimumLevel = GCKLoggerLevel.verbose
GCKLogger.sharedInstance().filter = filter
Ziel-C
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init];
[filter setMinimumLevel:GCKLoggerLevelVerbose];
[GCKLogger sharedInstance].filter = filter;

Sie können auch die von GCKLogger generierten Logeinträge filtern. Legen Sie die minimale Logging-Ebene pro Klasse fest. Beispiel:

Swift
let filter = GCKLoggerFilter.init()
filter.setLoggingLevel(GCKLoggerLevel.verbose, forClasses: ["GCKUICastButton",
                                                            "GCKUIImageCache",
                                                            "NSMutableDictionary"])
GCKLogger.sharedInstance().filter = filter
Ziel-C
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init];
[filter setLoggingLevel:GCKLoggerLevelVerbose
             forClasses:@[@"GCKUICastButton",
                          @"GCKUIImageCache",
                          @"NSMutableDictionary"
                          ]];
[GCKLogger sharedInstance].filter = filter;

Die Klassennamen können entweder Literalnamen oder glob-Muster sein, z. B. GCKUI\* und GCK\*Session.