Cast in iOS-App einbinden

In diesem Entwicklerhandbuch wird beschrieben, wie du deiner iOS-Sender-App mit dem iOS Sender SDK Google Cast-Unterstützung hinzufügen kannst.

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 Sender-Framework bezieht sich auf das Binärprogramm der Cast-Klassenbibliothek und die zugehörigen Ressourcen, die zur Laufzeit auf dem Sender vorhanden sind. Die Sender-App oder Cast-App bezieht sich auf eine App, die auch auf dem Sender ausgeführt wird. Die Web-Empfänger-App bezieht sich auf die HTML-Anwendung, die auf dem Web-Empfänger ausgeführt wird.

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

Anwendungsfluss

In den folgenden Schritten wird der typische Ablauf für die Ausführung einer iOS-App beschrieben:

  • Das Cast-Framework startet GCKDiscoveryManager basierend auf den in GCKCastOptions angegebenen Properties, um nach Geräten zu suchen.
  • Wenn der Nutzer auf die Schaltfläche „Streamen“ klickt, wird der Cast-Dialog mit der Liste der erkannten Cast-Geräte angezeigt.
  • Wenn der Nutzer ein Übertragungsgerät auswählt, versucht das Framework, die Web Receiver App auf dem Übertragungsgerät zu starten.
  • Das Framework ruft Rückrufe in der Sender-App auf, um zu bestätigen, dass die Web-Empfänger-App gestartet wurde.
  • Das Framework erstellt einen Kommunikationskanal zwischen den Apps „Sender“ und „Web Receiver“.
  • Das Framework verwendet den Kommunikationskanal, um die Medienwiedergabe auf dem Webreceiver zu laden und zu steuern.
  • Das Framework synchronisiert den Status der Medienwiedergabe zwischen Sender und Webreceiver: Wenn der Nutzer Aktionen auf der Sender-Benutzeroberfläche ausführt, leitet das Framework diese Anfragen zur Mediensteuerung an den Webreceiver weiter. Wenn der Webreceiver Aktualisierungen des Medienstatus sendet, aktualisiert das Framework den Status der Sender-Benutzeroberfläche.
  • Wenn der Nutzer auf die Schaltfläche „Streamen“ klickt, um die Verbindung zum Streaminggerät zu trennen, trennt das Framework die Verbindung der Sender-App zum Webempfänger.

Wenn Sie Probleme mit dem Absender beheben möchten, müssen Sie das Logging aktivieren.

Eine vollständige Liste aller Klassen, Methoden und Ereignisse im Google Cast iOS-Framework findest du in der Google Cast iOS API-Referenz. In den folgenden Abschnitten wird beschrieben, wie Sie Cast in Ihre iOS-App einbinden.

Methoden aus dem Hauptthread aufrufen

Cast-Kontext initialisieren

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

Bei der Initialisierung von GCKCastContext muss ein GCKCastOptions-Objekt angegeben werden. Diese Klasse enthält Optionen, die sich auf das Verhalten des Frameworks auswirken. Die wichtigste davon ist die Web Receiver-Anwendungs-ID, mit der Suchergebnisse gefiltert und die Web Receiver-App gestartet wird, wenn eine Übertragungssitzung gestartet wird.

Die Methode -[application:didFinishLaunchingWithOptions:] eignet sich auch gut, um einen Logging-Delegierten einzurichten, der die Logging-Nachrichten vom Framework empfängt. Sie können für die Fehlerbehebung und Fehlersuche nützlich 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)
    }
  }
}
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

Die UX-Widgets für die Übertragung

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

  • Einführungs-Overlay: Die GCKCastContext-Klasse hat eine Methode, presentCastInstructionsViewControllerOnceWithCastButton, mit der die Schaltfläche „Streamen“ hervorgehoben werden kann, wenn ein Webreceiver zum ersten Mal verfügbar ist. Die App des Absenders kann den Text, die Position des Titeltexts und die Schaltfläche „Schließen“ anpassen.

  • Übertragungsschaltfläche: Ab Version 4.6.0 des iOS-Sender-SDKs für Google Cast ist die Übertragungsschaltfläche immer sichtbar, wenn das Sendergerät mit dem WLAN verbunden ist. Wenn der Nutzer nach dem Starten der App zum ersten Mal auf die Schaltfläche „Streamen“ tippt, wird ein Berechtigungsdialogfeld angezeigt, in dem er der App den lokalen Netzwerkzugriff auf Geräte im Netzwerk gewähren kann. Wenn der Nutzer anschließend auf die Schaltfläche „Streamen“ tippt, wird ein Dialogfeld für die Übertragung angezeigt, in dem die gefundenen Geräte aufgeführt sind. Wenn der Nutzer auf die Übertragungsschaltfläche tippt, während das Gerät verbunden ist, werden die aktuellen Medienmetadaten (z. B. Titel, Name des Aufnahmestudios und ein Thumbnail-Bild) angezeigt oder der Nutzer kann die Verbindung zum Übertragungsgerät trennen. Wenn der Nutzer auf die Übertragungsschaltfläche tippt, während keine Geräte verfügbar sind, wird ein Bildschirm angezeigt, auf dem er Informationen dazu erhält, warum keine Geräte gefunden werden und wie er das Problem beheben kann.

  • Mini-Steuerelement: Wenn der Nutzer Inhalte streamt und die aktuelle Inhaltsseite oder den maximierten Controller verlassen hat, um einen anderen Bildschirm in der Sender-App aufzurufen, wird unten auf dem Bildschirm das Mini-Steuerelement angezeigt. So kann der Nutzer die Metadaten der gerade gestreamten Medien sehen und die Wiedergabe steuern.

  • Maximierte Steuerleiste: Wenn der Nutzer Inhalte streamt und auf die Medienbenachrichtigung oder die Ministeuerleiste klickt, wird die maximierte Steuerleiste geöffnet. Dort werden die Metadaten der gerade wiedergegebenen Medien angezeigt und es gibt mehrere Schaltflächen zur Steuerung der Medienwiedergabe.

Schaltfläche „Streamen“ hinzufügen

Das Framework bietet eine Schaltfläche „Streamen“ als UIButton-Unterklasse. Sie können sie der Titelleiste der App hinzufügen, indem Sie sie in UIBarButtonItem einschließen. Eine typische UIViewController-Unterklasse kann eine Wiedergabeschaltfläche so installieren:

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

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

GCKUICastButton kann auch direkt dem Storyboard hinzugefügt werden.

Geräteerkennung konfigurieren

Im Framework erfolgt die Geräteerkennung automatisch. Sie müssen den Erkennungsprozess nicht explizit starten oder beenden, es sei denn, Sie implementieren eine benutzerdefinierte Benutzeroberfläche.

Die Discovery im Framework wird von der Klasse GCKDiscoveryManager verwaltet, die eine Eigenschaft von GCKCastContext ist. Das Framework bietet eine Standardkomponente für das Übertragungsdialogfeld zur Geräteauswahl und ‑steuerung. Die Geräteliste ist alphabetisch nach dem gerätefreundlichen Namen sortiert.

So funktioniert die Sitzungsverwaltung

Das Cast SDK führt das Konzept einer Cast-Sitzung ein. Bei der Einrichtung werden die Schritte zum Herstellen einer Verbindung zu einem Gerät, zum Starten (oder Beitreten) einer Webreceiver-App, zum Herstellen einer Verbindung zu dieser App und zum Initialisieren eines Mediensteuerungskanals kombiniert. Weitere Informationen zu Übertragungssitzungen und zum Lebenszyklus des Webreceivers findest du im Leitfaden zum Anwendungslebenszyklus.

Sitzungen werden von der Klasse GCKSessionManager verwaltet, die ein Attribut der GCKCastContext ist. Einzelne Sitzungen werden durch Unterklassen der Klasse GCKSession dargestellt: GCKCastSession steht beispielsweise für Sitzungen mit Übertragungsgeräten. Du kannst auf die aktuell aktive Übertragungssitzung (falls vorhanden) als currentCastSession-Property von GCKSessionManager zugreifen.

Über die Benutzeroberfläche GCKSessionManagerListener können Sie Sitzungsereignisse wie die Sitzungserstellung, -aussetzung, -fortsetzung und -beendigung überwachen. Das Framework unterbricht Sitzungen automatisch, wenn die App des Absenders in den Hintergrund wechselt, und versucht, sie fortzusetzen, wenn die App wieder in den Vordergrund wechselt oder nach einer abnormalen/plötzlichen App-Beendigung fortgesetzt wird, während eine Sitzung aktiv war.

Wenn das Übertragungsdialogfeld verwendet wird, werden Sitzungen automatisch als Reaktion auf Nutzergesten erstellt und beendet. Andernfalls kann die App Sitzungen explizit über Methoden auf GCKSessionManager starten und beenden.

Wenn die App auf Ereignisse des Sitzungszyklus eine spezielle Verarbeitung ausfü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 Sitzungsende definiert.

Stream-Übertragung

Die Aufrechterhaltung des Sitzungsstatus ist die Grundlage der Streamübertragung, bei der Nutzer vorhandene Audio- und Videostreams per Sprachbefehl, über die Google Home App oder über Smart Displays zwischen Geräten verschieben können. Die Medienwiedergabe wird auf einem Gerät (der Quelle) beendet und auf einem anderen (dem Ziel) fortgesetzt. Jedes Cast-Gerät mit der neuesten Firmware kann als Quelle oder Ziel in einer Streamübertragung dienen.

Wenn du das neue Zielgerät während der Streamübertragung abrufen möchtest, verwende das Attribut GCKCastSession#device im Callback [sessionManager:didResumeCastSession:].

Weitere Informationen finden Sie unter Streamübertragung auf Webempfänger.

Automatische Wiederverbindung

Das Cast-Framework fügt eine Logik zur Wiederverbindung hinzu, um die Wiederverbindung in vielen subtilen Sonderfällen automatisch zu verarbeiten, z. B.:

  • Nach einem vorübergehenden WLAN-Ausfall wiederherstellen
  • Wiederherstellen nach Ruhezustand des Geräts
  • Wiederherstellen nach dem Auslagern der App
  • Wiederherstellen, wenn die App abgestürzt ist

Funktionsweise der Mediensteuerung

Wenn eine Cast-Sitzung mit einer Web-Empfänger-App hergestellt wird, die den Medien-Namespace unterstützt, wird vom Framework automatisch eine Instanz von GCKRemoteMediaClient erstellt. Auf diese kann über die Property remoteMediaClient der Instanz GCKCastSession zugegriffen werden.

Alle Methoden in GCKRemoteMediaClient, die Anfragen an den Webreceiver senden, geben ein GCKRequest-Objekt zurück, mit dem sich diese Anfrage verfolgen lässt. Diesem Objekt kann ein GCKRequestDelegate zugewiesen werden, um Benachrichtigungen über das Ergebnis des Vorgangs zu erhalten.

Es ist davon auszugehen, dass die Instanz von GCKRemoteMediaClient von mehreren Teilen der App gemeinsam genutzt wird. Tatsächlich teilen sich einige interne Komponenten des Frameworks wie das Übertragungsdialogfeld und die Mini-Mediensteuerungen die Instanz. Dazu unterstützt GCKRemoteMediaClient die Registrierung mehrerer GCKRemoteMediaClientListeners.

Medienmetadaten festlegen

Die Klasse GCKMediaMetadata enthält Informationen zu einem Medienelement, das du streamen möchtest. Im folgenden Beispiel wird eine neue GCKMediaMetadata-Instanz eines Films erstellt und der Titel, die Untertitel, der 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))
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]];

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

Medien laden

Wenn du ein Medienelement laden möchtest, erstelle eine GCKMediaInformation-Instanz mit den Metadaten des Mediums. Rufe dann die aktuelle GCKCastSession ab und verwende die GCKRemoteMediaClient, um die Medien in der Empfänger-App zu laden. Du kannst GCKRemoteMediaClient dann zum Steuern einer Mediaplayer-App verwenden, die auf dem Empfänger ausgeführt wird, z. B. zum Abspielen, Pausieren und Anhalten.

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

Weitere Informationen finden Sie auch im Abschnitt zur Verwendung von Medientracks.

4K-Videoformat

Um das Videoformat deiner Medien zu ermitteln, verwende die Property 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 in der Property hdrType durch die enum-Werte GCKVideoInfoHDRType angegeben.

Mini-Controller hinzufügen

Gemäß der Checkliste für das Design von Sender-Apps sollte eine Sender-App ein dauerhaftes Steuerelement namens Mini-Controller bereitstellen, das angezeigt werden sollte, wenn der Nutzer die aktuelle Inhaltsseite verlässt. Der Mini-Controller bietet sofortigen Zugriff und eine sichtbare Erinnerung an die aktuelle Übertragungssitzung.

Das Cast-Framework bietet eine Steuerleiste, GCKUIMiniMediaControlsViewController, die den Szenen hinzugefügt werden kann, in denen die Minifernbedienung angezeigt werden soll.

Wenn in Ihrer Sender-App ein Video- oder Audio-Livestream wiedergegeben wird, zeigt das SDK automatisch eine Wiedergabe-/Stopp-Schaltfläche anstelle der Wiedergabe-/Pause-Schaltfläche auf dem Mini-Controller an.

Unter iOS-Sender-UI anpassen erfährst du, wie du in deiner Sender-App das Aussehen der Übertragungs-Widgets konfigurieren kannst.

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

  • Lassen Sie das Cast-Framework das Layout des Mini-Controllers verwalten, indem Sie Ihren vorhandenen View Controller in einen eigenen View Controller einbetten.
  • Sie können das Layout des Mini-Controller-Widgets selbst verwalten, indem Sie es Ihrem vorhandenen View Controller hinzufügen. Dazu müssen Sie im Storyboard eine untergeordnete Ansicht angeben.

Mit dem GCKUICastContainerViewController einschließen

Die erste Möglichkeit ist die Verwendung von GCKUICastContainerViewController, um einen anderen View Controller einzubinden und unten ein GCKUIMiniMediaControlsViewController hinzuzufügen. Dieser Ansatz ist jedoch eingeschränkt, da Sie die Animation nicht anpassen und das Verhalten des Container-View-Controllers nicht konfigurieren können.

Diese erste Möglichkeit wird in der Regel in der Methode -[application:didFinishLaunchingWithOptions:] des App-Delegierten ausgeführt:

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

In vorhandenen View Controller einbetten

Die zweite Möglichkeit besteht darin, den Minicontroller direkt dem vorhandenen View-Controller hinzuzufügen. Verwenden Sie dazu createMiniMediaControlsViewController, um eine GCKUIMiniMediaControlsViewController-Instanz zu erstellen, und fügen Sie sie dann dem Container-View-Controller als untergeordnete Ansicht hinzu.

Richten Sie den Ansichtscontroller im App-Delegate 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
}
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;
}

Erstellen Sie in Ihrem Stamm-Viewcontroller eine GCKUIMiniMediaControlsViewController-Instanz und fügen Sie sie dem Container-Viewcontroller 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)
    }
  }

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

Mit dem GCKUIMiniMediaControlsViewControllerDelegate wird dem Host-View-Controller mitgeteilt, wann der Mini-Controller sichtbar sein soll:

Swift
  func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController,
                                       shouldAppear _: Bool) {
    updateControlBarsVisibility()
  }
Objective-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 Steuerbildschirm für die gestreamten Medien bereitstellen. Der erweiterte Controller ist eine Vollbildversion des Mini-Controllers.

Der erweiterte Controller ist eine Vollbildansicht, die die vollständige Kontrolle über die Remote-Medienwiedergabe bietet. Über diese Ansicht sollte eine Streaming-App alle verwaltebaren Aspekte einer Streamingsitzung verwalten können, mit Ausnahme der Lautstärkeregelung des Webempfängers und des Sitzungslebenszyklus (Verbinden/Streaming beenden). Außerdem enthält sie alle Statusinformationen zur Mediensitzung (Artwork, Titel, Untertitel usw.).

Die Funktionen dieser Ansicht werden von der Klasse GCKUIExpandedMediaControlsViewController implementiert.

Aktiviere zuerst den standardmäßigen erweiterten Controller im Streamingkontext. Ändern Sie den App-Delegierten, um den standardmäßigen maximierten Controller zu aktivieren:

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

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

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

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  ..
}

Füge deinem View Controller den folgenden Code hinzu, um den erweiterten Controller zu laden, wenn der Nutzer ein Video streamt:

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

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

Wenn in deiner Sender-App ein Video- oder Audio-Livestream wiedergegeben wird, zeigt das SDK im maximierten Steuerfeld automatisch eine Wiedergabe-/Stopp-Schaltfläche anstelle der Wiedergabe-/Pause-Schaltfläche an.

Unter Benutzerdefinierte Stile auf Ihre iOS-App anwenden erfahren Sie, wie Sie das Aussehen der Cast-Widgets in Ihrer Sender-App konfigurieren können.

Lautstärkeregelung

Das Cast-Framework verwaltet die Lautstärke für die Sender-App automatisch. Es wird automatisch mit der Lautstärke des Webempfängers für die bereitgestellten UI-Widgets synchronisiert. Verwenden Sie GCKUIDeviceVolumeController, um einen von der App bereitgestellten Schieberegler zu synchronisieren.

Lautstärkeregelung über physische Taste

Mit den physischen Lautstärketasten auf dem Sendergerät kann die Lautstärke der Übertragungssitzung auf dem Webempfänger über das Flag physicalVolumeButtonsWillControlDeviceVolume auf dem GCKCastOptions geändert werden, das auf dem GCKCastContext festgelegt ist.

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

Fehler verarbeiten

Es ist sehr wichtig, dass Sender-Apps alle Fehler-Callbacks verarbeiten und für jede Phase des Cast-Lebenszyklus die beste Antwort festlegen. Die App kann dem Nutzer Fehlerdialogfelder anzeigen oder die Übertragungssitzung beenden.

Logging

GCKLogger ist ein Singleton, das vom Framework für das Logging verwendet wird. Mit dem Befehl GCKLoggerDelegate können Sie festlegen, wie Protokollmeldungen verarbeitet werden.

Mithilfe der GCKLogger generiert das SDK Protokollausgaben in Form von Debug-Nachrichten, Fehlern und Warnungen. Diese Protokollmeldungen unterstützen die Fehlerbehebung und sind nützlich, um Probleme zu beheben und zu identifizieren. Standardmäßig wird die Protokollausgabe unterdrückt. Wenn Sie jedoch eine GCKLoggerDelegate zuweisen, kann die Absender-App 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)
    }
  }
}
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

Wenn Sie auch Debug- und ausführliche Meldungen aktivieren möchten, fügen Sie dem Code nach dem Festlegen des Delegaten (siehe oben) diese Zeile hinzu:

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;

Sie können die von GCKLogger generierten Logmeldungen auch filtern. Legen Sie die Mindestprotokollierungsebene pro Klasse fest, z. B.:

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;

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