Cast in Ihre iOS-App einbinden

Mit Sammlungen den Überblick behalten Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.

In diesem Entwicklerleitfaden wird beschrieben, wie Sie der iOS-Absender-App mithilfe des iOS Sender SDK Google Cast-Unterstützung hinzufügen.

Das Mobilgerät oder der Laptop ist der Absender, 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 zugehörige Ressourcen, die zur Laufzeit auf dem Absender vorhanden sind. Die Absender-App oder die 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 dem Web Receiver 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

Die folgenden Schritte beschreiben den typischen allgemeinen Ablauf für eine iOS-Absenderanwendung:

  • Das Cast-Framework startet GCKDiscoveryManager auf der Grundlage der Attribute in GCKCastOptions, um mit dem Scannen nach Geräten zu beginnen.
  • Wenn der Nutzer auf das Cast-Symbol klickt, wird im Cast-Dialogfeld das Cast-Dialogfeld 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 Callbacks in der Absender-App auf, um zu bestätigen, dass die Web Receiver App gestartet wurde.
  • Das Framework erstellt einen Kommunikationskanal zwischen den Apps des Absenders und des Webempfängers.
  • Das Framework verwendet den Kommunikationskanal, um die Medienwiedergabe auf dem Webempfänger zu laden und zu steuern.
  • Das Framework synchronisiert den Medienwiedergabestatus zwischen dem Absender und dem Webempfänger: Wenn der Nutzer Aktionen auf der Benutzeroberfläche des Senders ausführt, übergibt das Framework diese Mediensteuerungsanfragen an den Webempfänger und wenn der Webempfänger Aktualisierungen des Medienstatus sendet, aktualisiert das Framework den Status der Benutzeroberfläche des Absenders.
  • Wenn der Nutzer auf das Cast-Symbol klickt, um die Verbindung zum Cast-Gerät zu trennen, trennt das Framework die Absender-App vom Webempfänger.

Um Fehler bei Ihrem Absender zu beheben, müssen Sie Logging aktivieren.

Eine umfassende Liste aller Klassen, Methoden und Ereignisse im Google Cast-iOS-Framework finden Sie in der Google Cast-API für iOS. In den folgenden Abschnitten wird beschrieben, wie Sie Cast in Ihre iOS-App einbinden.

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ühzeitig im Lebenszyklus der Anwendung initialisiert werden, normalerweise in der Methode -[application:didFinishLaunchingWithOptions:] des Anwendungsdelegats, sodass die automatische Sitzungswiederaufnahme beim Neustart der Absenderanwendung ordnungsgemäß ausgelöst werden kann.

Beim Initialisieren des 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, die zum Filtern von Erkennungsergebnissen und zum Starten der Web Receiver-App verwendet wird, wenn eine Cast-Sitzung gestartet wird.

Die Methode -[application:didFinishLaunchingWithOptions:] ist auch ein guter Ort, um einen Logging-Delegaten einzurichten, der die Logging-Nachrichten aus dem Framework empfängt. Diese können für die Fehlerbehebung und Fehlerbehebung nützlich sein.

Schnell
@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

UX-Widgets streamen

Das Cast iOS SDK bietet die folgenden Widgets, die der Cast Design-Checkliste entsprechen:

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

  • Cast-Schaltfläche: Ab dem Cast SDK für iOS 4.6.0 ist das Cast-Symbol immer sichtbar, wenn das Absendergerät mit einem WLAN verbunden ist. Wenn der Nutzer nach dem ersten Start der App zum ersten Mal auf das Cast-Symbol tippt, wird ein Dialogfeld mit Berechtigungen angezeigt, über das der Nutzer lokalen Geräten Zugriff auf die Geräte im Netzwerk gewähren kann. Wenn der Nutzer dann auf das Cast-Symbol tippt, wird ein Cast-Dialogfeld mit den erkannten Geräten angezeigt. Wenn der Nutzer auf das Cast-Symbol tippt, während das Gerät verbunden ist, werden die aktuellen Medienmetadaten wie Titel, Name des Aufnahmestudios und ein Thumbnail angezeigt. Außerdem kann der Nutzer die Verbindung zum Übertragungsgerät trennen. Wenn der Nutzer auf das Cast-Symbol tippt, aber keine Geräte verfügbar sind, wird ein Bildschirm angezeigt, auf dem der Nutzer erfahren kann, warum Geräte nicht gefunden wurden und wie die Fehlerbehebung funktioniert.

  • Mini-Controller: Wenn der Nutzer Inhalte streamt und die aktuelle Inhaltsseite oder den maximierten Controller zu einem anderen Bildschirm in der Absender-App verschoben hat, wird der Mini-Controller unten auf dem Bildschirm angezeigt, damit der Nutzer die aktuellen Streaming-Metadaten sehen und die Wiedergabe steuern kann.

  • Erweiterter Controller: Wenn der Nutzer Inhalte streamt, wird er beim Anklicken der Medienbenachrichtigung oder des Minicontrollers gestartet. Dieser zeigt die aktuell wiedergegebenen Medienmetadaten an und bietet mehrere Schaltflächen zum Steuern der Medienwiedergabe.

Cast-Symbol hinzufügen

Das Framework bietet eine Cast-Schaltfläche als Schaltfläche UIButton. Sie kann der Titelleiste der App hinzugefügt werden. Dazu wird sie in eine UIBarButtonItem-Datei umschlossen. In einer typischen UIViewController-Unterklasse kann das Cast-Symbol so installiert werden:

Schnell
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 Cast-Dialogfeld geöffnet, das vom Framework zur Verfügung gestellt wird.

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

Die Erkennung im Framework wird von der Klasse GCKDiscoveryManager verwaltet, einer Property von GCKCastContext. Das Framework bietet eine standardmäßige Cast-Dialogkomponente für die Geräteauswahl und -steuerung. Die Geräteliste wird lexikografisch nach dem Gerätenamen sortiert.

So funktioniert die Sitzungsverwaltung

Mit dem Cast SDK wird das Konzept einer Cast-Sitzung eingeführt. Dabei werden die Schritte zum Herstellen einer Verbindung mit einem Gerät, zum Starten (oder Teilnehmen an einer) Web-Receiver-App, zum Herstellen einer Verbindung zu dieser App und zum Initialisieren eines Medienkontrollkanals kombiniert. Weitere Informationen zu Cast-Sitzungen und dem Lebenszyklus von Web-Empfängern finden Sie im Leitfaden zum Lebenszyklus von Anwendungen.

Sitzungen werden von der Klasse GCKSessionManager verwaltet, einer Property von GCKCastContext. Einzelne Sitzungen werden durch abgeleitete Klassen der Klasse GCKSession dargestellt. Beispielsweise stellt GCKCastSession Sitzungen mit Cast-Geräten dar. Sie können auf die derzeit aktive Cast-Sitzung (falls vorhanden) als currentCastSession-Property von GCKSessionManager zugreifen.

Über die Schnittstelle GCKSessionManagerListener können Sie Sitzungsereignisse wie Sitzungserstellung, Sperrung, Wiederaufnahme und Beendigung überwachen. Das Framework sperrt automatisch Sitzungen, wenn die Absender-App in den Hintergrund geht, und versucht, sie fortzusetzen, wenn die App wieder in den Vordergrund wechselt (oder nach einem abnormalen/abrupten Anwendungs-Abschluss während einer aktiven Sitzung neu gestartet wird).

Wenn das Cast-Dialogfeld verwendet wird, werden Sitzungen automatisch als Reaktion auf Nutzergesten erstellt und gelöscht. Andernfalls kann die Anwendung Sitzungen explizit über Methoden in GCKSessionManager starten und beenden.

Wenn die Anwendung als Reaktion auf Sitzungslebenszyklus-Ereignisse 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 solche Ereignisse wie Sitzungsstart und Sitzungsende definiert.

Stream-Übertragung

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

Verwende 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 auf Web Receiver.

Automatische Verbindung

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

  • Wiederherstellung nach vorübergehendem WLAN-Verlust
  • Aus dem Ruhemodus des Geräts wiederherstellen
  • Wiederherstellung aus dem Hintergrund der App
  • Wiederherstellen, wenn die App abgestürzt ist

Funktionsweise der Mediensteuerung

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

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

Es wird erwartet, dass die Instanz von GCKRemoteMediaClient von mehreren Teilen der App gemeinsam genutzt werden kann. Tatsächlich teilen sich auch einige interne Komponenten des Frameworks wie das Cast-Dialogfeld und die Mini-Mediensteuerungen die Instanz. Zu diesem Zweck unterstützt GCKRemoteMediaClient die Registrierung mehrerer GCKRemoteMediaClientListeners.

Medienmetadaten festlegen

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

Schnell
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 Bildauswahl und Caching.

Medien laden

Erstellen Sie zum Laden eines Medienelements eine GCKMediaInformation-Instanz mit den Metadaten der Medien. Rufen Sie dann die aktuelle GCKCastSession ab und verwenden Sie deren GCKRemoteMediaClient, um die Medien in die Empfänger-App zu laden. Anschließend können Sie mit GCKRemoteMediaClient eine Medien-Player-App steuern, die auf dem Empfänger ausgeführt wird, z. B. zum Abspielen, Pausieren und Beenden.

Schnell
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 unter Medien-Tracks verwenden.

4K-Videoformat

Verwenden Sie zum Ermitteln des Videoformats Ihrer Medien das Attribut videoInfo von GCKMediaStatus, um die aktuelle Instanz von GCKVideoInfo abzurufen. Diese Instanz enthält den HDR-TV-Typ und die Höhe und Breite in Pixeln. Varianten des 4K-Formats werden im Attribut hdrType durch die Aufzählungswerte GCKVideoInfoHDRType angegeben.

Mini-Controller hinzufügen

Gemäß der Checkliste für das Cast-Design sollte eine Absender-App eine dauerhafte Steuerung bieten, die als Mini-Controller bezeichnet wird und angezeigt werden soll, wenn der Nutzer die aktuelle Inhaltsseite verlässt. Der Mini-Controller bietet sofortigen Zugriff und eine sichtbare Erinnerung für die aktuelle Streamingsitzung.

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

Wenn die Absender-App einen Video- oder Audio-Livestream wiedergibt, wird im SDK auf der Mini-Controller-Seite automatisch eine Wiedergabe-/Pause-Schaltfläche angezeigt.

Unter iOS-Sender-UI anpassen erfahren Sie, wie Ihre Absender-App das Aussehen der Cast-Widgets konfigurieren kann.

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

  • Sie können das Layout des Minicontrollers über das Cast-Framework verwalten lassen. Dazu wird Ihr vorhandener Ansicht-Controller mit einem eigenen Ansicht-Controller zusammengefasst.
  • Sie können das Layout des Mini-Controller-Widgets selbst verwalten, indem Sie es Ihrem vorhandenen Ansicht-Controller hinzufügen, indem Sie im Storyboard eine Unteransicht angeben.

Wrapping mit dem GCKUICastContainerViewController

Die erste Möglichkeit ist die Verwendung von GCKUICastContainerViewController, das einen anderen Ansichtscontroller umschließt und unten GCKUIMiniMediaControlsViewController hinzufügt. Dieser Ansatz ist eingeschränkt, da Sie die Animation nicht anpassen und das Verhalten des Container View Controllers nicht konfigurieren können.

In der Regel wird diese Methode zuerst in der Methode -[application:didFinishLaunchingWithOptions:] des Anwendungsdelegats ausgeführt:

Schnell
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 Ansicht-Controller einbetten

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

Richten Sie den View Controller im App-Delegaten ein:

Schnell
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 im Root-Ansicht-Controller eine GCKUIMiniMediaControlsViewController-Instanz und fügen Sie sie dem Container-Ansicht-Controller als Unteransicht hinzu:

Schnell
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

Der GCKUIMiniMediaControlsViewControllerDelegate teilt dem Hostansicht-Controller mit, wann der Minicontroller sichtbar sein soll:

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

Erweiterten Controller hinzufügen

In der Checkliste für das Design von Google Cast muss eine Absender-App einen maximierten Controller für die gestreamten Medien bereitstellen. Der maximierte Controller ist eine Vollbildversion des Mini-Controllers.

Der erweiterte Controller ist eine Vollbildansicht, die die vollständige Steuerung der Remote-Medienwiedergabe bietet. In dieser Ansicht sollte eine Streaming-App alle verwaltbaren Aspekte einer Streamingsitzung verwalten können, mit Ausnahme der Lautstärkeregelung des Webempfängers und des Sitzungslebenszyklus (Verbindung herstellen/beenden). Außerdem enthält es alle Statusinformationen zur Mediensitzung (Grafiken, Titel, Untertitel usw.).

Die Funktion dieser Ansicht wird durch die Klasse GCKUIExpandedMediaControlsViewController implementiert.

Als Erstes müssen Sie den standardmäßigen erweiterten Controller im Cast-Kontext aktivieren. Ändern Sie den App-Delegaten so, dass der standardmäßige erweiterte Controller aktiviert wird:

Schnell
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, damit der maximierte Controller geladen wird, wenn der Nutzer mit dem Streamen eines Videos beginnt:

Schnell
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 maximierte Controller wird auch automatisch gestartet, wenn der Nutzer auf den Minicontroller tippt.

Wenn deine Absender-App einen Video- oder Audio-Livestream wiedergibt, wird im SDK automatisch eine Wiedergabe-/Pause-Schaltfläche statt der Wiedergabe-/Pause-Schaltfläche angezeigt.

Unter Benutzerdefinierte Stile auf Ihre iOS-App anwenden erfahren Sie, wie Ihre Absender-App das Aussehen der Cast-Widgets konfigurieren kann.

Lautstärkeregelung

Das Cast-Framework verwaltet automatisch das Volumen 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 Schieberegler zu synchronisieren.

Lautstärkeregelung

Mit den physischen Lautstärketasten des Absendergeräts können Sie die Lautstärke der Streamingsitzung im Webempfänger mithilfe des Flags physicalVolumeButtonsWillControlDeviceVolume auf dem GCKCastOptions ändern, das auf GCKCastContext festgelegt ist.

Schnell
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

Es ist sehr wichtig, dass Absender-Apps alle Fehler-Callbacks handhaben und die beste Antwort für jede Phase des Cast-Lebenszyklus festlegen. Die App kann dem Nutzer Fehlerdialoge anzeigen oder die Cast-Sitzung beenden.

Logging

GCKLogger ist ein Singleton, das vom Framework für das Logging verwendet wird. Mit GCKLoggerDelegate können Sie den Umgang mit Lognachrichten anpassen.

Mit GCKLogger generiert das SDK eine Logging-Ausgabe in Form von Fehlerbehebungsmeldungen, Fehlern und Warnungen. Diese Logmeldungen erleichtern die Fehlerbehebung und sind hilfreich, um Probleme zu beheben und Probleme zu identifizieren. Standardmäßig wird die Logausgabe unterdrückt. Wenn Sie jedoch ein GCKLoggerDelegate zuweisen, kann die Absenderanwendung diese Nachrichten vom SDK empfangen und in der Systemkonsole protokollieren.

Schnell
@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

Fügen Sie dem Code die folgende Zeile hinzu, nachdem Sie den Bevollmächtigten festgelegt haben (siehe oben):

Schnell
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 Lognachrichten filtern. Legen Sie die minimale Logging-Ebene pro Klasse fest. Beispiel:

Schnell
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, beispielsweise GCKUI\* und GCK\*Session.