In diesem Entwicklerleitfaden wird beschrieben, wie Sie Ihrer iOS Sender-App mit dem iOS Sender SDK Google Cast-Unterstützung hinzufügen.
Das Mobilgerät oder der Laptop ist der Sender , der die Wiedergabe steuert, und das Google Cast-Gerät ist der Receiver , der die Inhalte auf dem Fernseher anzeigt.
Das Sender-Framework bezieht sich auf die binäre 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 ebenfalls auf dem Sender 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 Zuständen des Lebenszyklus der Cast-App zu wechseln.
Anwendungsfluss
Die folgenden Schritte beschreiben den typischen allgemeinen Ausführungsablauf für eine iOS Sender-App:
- Das Cast-Framework startet
GCKDiscoveryManagerbasierend auf den inGCKCastOptionsangegebenen Properties, um nach Geräten zu suchen. - Wenn der Nutzer auf die Cast-Schaltfläche klickt, wird im Framework das Cast-Dialogfeld mit der Liste der gefundenen Cast-Geräte angezeigt.
- Wenn der Nutzer ein Cast-Gerät auswählt, versucht das Framework, die Web Receiver App auf dem Cast-Gerät zu starten.
- Das Framework ruft Callbacks in der Sender-App auf, um zu bestätigen, dass die Web Receiver App gestartet wurde.
- Das Framework erstellt einen Kommunikationskanal zwischen der Sender- und der Web Receiver App.
- Das Framework verwendet den Kommunikationskanal, um die Medienwiedergabe auf dem Web Receiver zu laden und zu steuern.
- Das Framework synchronisiert den Status der Medienwiedergabe zwischen Sender und Web Receiver. Wenn der Nutzer Aktionen auf der Benutzeroberfläche des Senders ausführt, leitet das Framework diese Anfragen zur Mediensteuerung an den Web Receiver weiter. Wenn der Web Receiver Statusaktualisierungen für Medien sendet, aktualisiert das Framework den Status der Benutzeroberfläche des Senders.
- Wenn der Nutzer auf die Cast-Schaltfläche klickt, um die Verbindung zum Cast-Gerät zu trennen, trennt das Framework die Verbindung der Sender-App zum Web Receiver.
Zur Fehlerbehebung bei Ihrem Sender müssen Sie das Logging aktivieren.
Eine umfassende Liste aller Klassen, Methoden und Ereignisse im Google Cast iOS-Framework finden Sie in der Google Cast iOS API Referenz. In den folgenden Abschnitten werden die Schritte zur Integration von Cast in Ihre iOS-App beschrieben.
Methoden aus dem Hauptthread aufrufen
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 App-Delegaten, damit die automatische Wiederaufnahme der Sitzung beim Neustart der Sender-App ordnungsgemäß 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-Anwendungs-ID, die verwendet wird, um Suchergebnisse zu filtern und die Web Receiver App zu starten, wenn eine Cast-Sitzung gestartet wird.
Die Methode -[application:didFinishLaunchingWithOptions:] ist auch ein guter Ort, um einen Logging-Delegaten einzurichten, der die Logging-Meldungen vom Framework empfängt.
Diese können bei der Fehlerbehebung hilfreich sein.
@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) } } }
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 diese Widgets, die der Cast-Design-Checkliste entsprechen:
Einführungs-Overlay: Die Klasse
GCKCastContexthat eine Methode,presentCastInstructionsViewControllerOnceWithCastButton, mit der die Cast-Schaltfläche hervorgehoben werden kann, wenn ein Web Receiver zum ersten Mal verfügbar ist. Die Sender-App kann den Text, die Position des Titels und die Schaltfläche „Schließen“ anpassen.Cast-Schaltfläche: Ab Version 4.6.0 des Cast iOS Sender SDK ist die Cast-Schaltfläche immer sichtbar, wenn das Sendergerät mit WLAN verbunden ist. Wenn der Nutzer nach dem ersten Starten der App zum ersten Mal auf die Cast-Schaltfläche tippt, wird ein Berechtigungsdialogfeld angezeigt, in dem er der App Zugriff auf das lokale Netzwerk für Geräte im Netzwerk gewähren kann. Wenn der Nutzer anschließend auf die Cast-Schaltfläche tippt, wird ein Cast-Dialogfeld mit den gefundenen Geräten angezeigt. Wenn der Nutzer auf die Cast-Schaltfläche tippt, während das Gerät verbunden ist, werden die aktuellen Metadaten der Medien (z. B. Titel, Name des Aufnahmestudios und ein Miniaturbild) angezeigt. Der Nutzer kann auch die Verbindung zum Cast-Gerät trennen. Wenn der Nutzer auf die Cast-Schaltfläche tippt, während keine Geräte verfügbar sind, wird ein Bildschirm mit Informationen dazu angezeigt, warum keine Geräte gefunden wurden und wie das Problem behoben werden kann.
Mini-Controller: Wenn der Nutzer Inhalte streamt und die aktuelle Inhaltsseite oder den erweiterten Controller verlassen hat, um zu einem anderen Bildschirm in der Sender-App zu wechseln, wird der Mini-Controller unten auf dem Bildschirm angezeigt. So kann der Nutzer die Metadaten der aktuell gestreamten Medien sehen und die Wiedergabe steuern.
Erweiterter Controller: Wenn der Nutzer Inhalte streamt und auf die Medienbenachrichtigung oder den Mini-Controller klickt, wird der erweiterte Controller gestartet. Dort werden die Metadaten der aktuell wiedergegebenen Medien angezeigt und es gibt mehrere Schaltflächen zur Steuerung der Medienwiedergabe.
Cast-Schaltfläche hinzufügen
Das Framework bietet eine Cast-Schaltflächenkomponente als UIButton-Unterklasse. Sie kann der Titelleiste der App hinzugefügt werden, indem sie in ein UIBarButtonItem eingebettet wird. Eine typische UIViewController-Unterklasse kann eine Cast-Schaltfläche so installieren:
let castButton = GCKUICastButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24)) castButton.tintColor = UIColor.gray navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)
GCKUICastButton *castButton = [[GCKUICastButton alloc] initWithFrame:CGRectMake(0, 0, 24, 24)]; castButton.tintColor = [UIColor grayColor]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:castButton];
Standardmäßig wird durch Tippen auf die Schaltfläche das Cast-Dialogfeld geöffnet, das vom Framework bereitgestellt wird.
GCKUICastButton
kann auch direkt zum Storyboard hinzugefügt werden.
Gerätesuche konfigurieren
Im Framework erfolgt die Gerätesuche automatisch. Sie müssen den Suchvorgang nicht explizit starten oder beenden, es sei denn, Sie implementieren eine benutzerdefinierte Benutzeroberfläche.
Die Suche im Framework wird von der Klasse
GCKDiscoveryManager,
die eine Property von der
GCKCastContextist. Das Framework bietet eine Standardkomponente für das Cast-Dialogfeld zur Geräteauswahl und -steuerung. Die Geräteliste ist lexikografisch nach dem Anzeigenamen des Geräts sortiert.
Funktionsweise der Sitzungsverwaltung
Im Cast SDK wird das Konzept einer Cast-Sitzung eingeführt. Die Einrichtung einer solchen Sitzung umfasst die Schritte zum Herstellen einer Verbindung zu einem Gerät, zum Starten (oder Beitreten) einer Web Receiver-App, zum Herstellen einer Verbindung zu dieser App und zum Initialisieren eines Mediensteuerung-Channels. Weitere Informationen zu Cast-Sitzungen und zum Web Receiver-Lebenszyklus finden Sie im Leitfaden zum Anwendungslebenszyklus für Web Receiver.
Sitzungen werden von der Klasse
GCKSessionManager,
verwaltet, die eine Property von der
GCKCastContextist.
Einzelne Sitzungen werden durch Unterklassen der Klasse
GCKSession dargestellt.
GCKCastSession
steht beispielsweise für Sitzungen mit Cast-Geräten. Sie können auf die aktuell aktive Cast-Sitzung (falls vorhanden) als currentCastSession-Property von GCKSessionManager zugreifen.
Mit der
GCKSessionManagerListener
Schnittstelle können Sie Sitzungsereignisse wie das Erstellen, Anhalten, Fortsetzen und Beenden von Sitzungen überwachen. Das Framework hält Sitzungen automatisch an, wenn die Sender-App in den Hintergrund wechselt, und versucht, sie fortzusetzen, wenn die App wieder in den Vordergrund wechselt oder nach einem abnormalen/abrupten Beenden der App neu gestartet wird, während eine Sitzung aktiv war.
Wenn das Cast-Dialogfeld verwendet wird, werden Sitzungen automatisch als Reaktion auf Nutzeraktionen erstellt und beendet. Andernfalls kann die App Sitzungen explizit über Methoden in
GCKSessionManagerstarten und beenden.
Wenn die App als Reaktion auf Ereignisse im Lebenszyklus der Sitzung 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
Das Beibehalten des Sitzungsstatus ist die Grundlage für die Stream-Übertragung, bei der Nutzer vorhandene Audio- und Videostreams per Sprachbefehl, über die Google Home App oder über Smart Displays auf andere Geräte übertragen können. Die Medienwiedergabe wird auf einem Gerät (der Quelle) beendet und auf einem anderen Gerät (dem Ziel) fortgesetzt. Jedes Cast-Gerät mit der neuesten Firmware kann als Quelle oder Ziel bei einer Stream-Übertragung dienen.
Wenn Sie das neue Zielgerät während der Stream-Übertragung erhalten möchten, verwenden Sie die
GCKCastSession#device
Property während des
[sessionManager:didResumeCastSession:]
Callbacks.
Weitere Informationen finden Sie unter Stream-Übertragung auf Web Receiver.
Automatische Wiederverbindung
Das Cast-Framework fügt eine Logik für die Wiederverbindung hinzu, um die Wiederverbindung in vielen subtilen Grenzfällen automatisch zu verarbeiten, z. B.:
- Wiederherstellung nach einem vorübergehenden WLAN-Verlust
- Wiederherstellung nach dem Wechsel in den Gerätesleep-Modus
- Wiederherstellung nach dem Wechsel der App in den Hintergrund
- Wiederherstellung nach einem App-Absturz
Funktionsweise der Mediensteuerung
Wenn eine Cast-Sitzung mit einer Web Receiver App eingerichtet wird, die den Medien
Namespace unterstützt, wird automatisch eine Instanz von
GCKRemoteMediaClient
vom Framework erstellt. Sie kann als
remoteMediaClientProperty der
GCKCastSession
Instanz aufgerufen werden.
Alle Methoden in GCKRemoteMediaClient, die Anfragen an den Web Receiver
senden, geben ein
GCKRequest-Objekt zurück, mit dem die Anfrage
verfolgt werden kann. Ein
GCKRequestDelegate
kann diesem Objekt zugewiesen werden, um Benachrichtigungen über das Endergebnis des Vorgangs zu erhalten.
Es wird erwartet, dass die Instanz von GCKRemoteMediaClient von mehreren Teilen der App gemeinsam genutzt wird. Einige interne Komponenten des Frameworks wie das Cast-Dialogfeld und die Mini-Mediensteuerung verwenden die Instanz tatsächlich gemeinsam. Zu diesem Zweck GCKRemoteMediaClient
unterstützt die Registrierung mehrerer
GCKRemoteMediaClientListeners.
Medienmetadaten festlegen
Die Klasse
GCKMediaMetadata
stellt Informationen zu einem Medienelement dar, das Sie streamen möchten. Im folgenden Beispiel wird eine neue GCKMediaMetadata-Instanz eines Films erstellt und der Titel, Untertitel, Name des Aufnahmestudios und zwei Bilder festgelegt.
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))
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
Um ein Medienelement zu laden, erstellen Sie eine
GCKMediaInformation
Instanz mit den Metadaten des Mediums. Rufen Sie dann die aktuelle
GCKCastSession ab und
verwenden Sie ihren
GCKRemoteMediaClient
, um die Medien in die Receiver-App zu laden. Sie können GCKRemoteMediaClient
dann verwenden, um eine Mediaplayer-App zu steuern, die auf dem Receiver ausgeführt wird, z. B. zum Abspielen,
Pausieren und Beenden.
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 }
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 zur Verwendung von Medientracks.
4K-Videoformat
Um zu ermitteln, welches Videoformat Ihre Medien haben, verwenden Sie die videoInfo Property 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 Cast Design Checkliste, sollte eine Sender-App eine dauerhafte Steuerung bieten, die als Mini Controller bezeichnet wird und angezeigt werden sollte, wenn der Nutzer die aktuelle Inhaltsseite verlässt. Der Mini-Controller bietet sofortigen Zugriff und eine sichtbare Erinnerung an die aktuelle Cast-Sitzung.
Das Cast-Framework bietet eine Steuerleiste,
GCKUIMiniMediaControlsViewController,
die den Szenen hinzugefügt werden kann, in denen der Mini-Controller angezeigt werden soll.
Wenn Ihre Sender-App einen Video- oder Audio-Livestream wiedergibt, zeigt das SDK automatisch eine Schaltfläche zum Abspielen/Beenden anstelle der Schaltfläche zum Abspielen/Pausieren im Mini-Controller an.
Unter Benutzeroberfläche des iOS-Senders anpassen erfahren Sie, wie Ihre Sender-App das Aussehen der Cast-Widgets konfigurieren kann.
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 Ansichtscontroller mit einem eigenen Ansichtscontroller umschließen.
- Verwalten Sie das Layout des Mini-Controller-Widgets selbst, indem Sie es Ihrem vorhandenen Ansichtscontroller hinzufügen und eine Unteransicht im Storyboard bereitstellen.
Mit GCKUICastContainerViewController umschließen
Die erste Möglichkeit ist die Verwendung von
GCKUICastContainerViewController
, das einen anderen Ansichtscontroller umschließt und unten ein
GCKUIMiniMediaControlsViewController
hinzufügt. Dieser Ansatz ist begrenzt, da Sie die Animation nicht anpassen und das Verhalten des Container-Ansichtscontrollers nicht konfigurieren können.
Diese erste Möglichkeit wird in der Regel in der Methode -[application:didFinishLaunchingWithOptions:] des App-Delegaten verwendet:
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() ... }
- (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]; ... }
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 } } }
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 Ansichtscontroller einbetten
Die zweite Möglichkeit besteht darin, den Mini-Controller direkt zu Ihrem vorhandenen Ansichts
controller hinzuzufügen. Verwenden Sie dazu
createMiniMediaControlsViewController
um eine
GCKUIMiniMediaControlsViewController
Instanz zu erstellen, und fügen Sie sie dann als Unteransicht zum Container-Ansichtscontroller hinzu.
Richten Sie Ihren Ansichtscontroller im App-Delegaten ein:
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 }
- (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 Stammansichtscontroller eine GCKUIMiniMediaControlsViewController-Instanz und fügen Sie sie als Unteransicht zum Container-Ansichtscontroller hinzu:
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) } } ...
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 Host-Ansichtscontroller mit, wann der Mini-Controller sichtbar sein soll:
func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController, shouldAppear _: Bool) { updateControlBarsVisibility() }
- (void)miniMediaControlsViewController: (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController shouldAppear:(BOOL)shouldAppear { [self updateControlBarsVisibility]; }
Erweiterten Controller hinzufügen
Die Google Cast-Design-Checkliste erfordert, dass eine Sender-App einen erweiterten Controller für die gestreamten Medien bietet. Der erweiterte Controller ist eine Vollbildversion des Mini-Controllers.
Der erweiterte Controller ist eine Vollbildansicht, die die vollständige Steuerung der Remote-Medienwiedergabe ermöglicht. Mit dieser Ansicht kann eine Streaming-App alle verwaltbaren Aspekte einer Cast-Sitzung verwalten, mit Ausnahme der Lautstärkeregelung des Web Receivers und des Sitzungslebenszyklus (Verbindung herstellen/Streaming beenden). Außerdem werden alle Statusinformationen zur Mediensitzung angezeigt (Cover, Titel, Untertitel usw.).
Die Funktionalität dieser Ansicht wird von der
GCKUIExpandedMediaControlsViewController
Klasse implementiert.
Zuerst müssen Sie den standardmäßigen erweiterten Controller im Cast-Kontext aktivieren. Ändern Sie den App-Delegaten, um den standardmäßigen erweiterten Controller zu aktivieren:
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; .. }
Fügen Sie den folgenden Code zu Ihrem Ansichtscontroller hinzu, um den erweiterten Controller zu laden, wenn der Nutzer ein Video streamt:
func playSelectedItemRemotely() { GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls() ... // Load your media sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation) }
- (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 Ihre Sender-App einen Video- oder Audio-Livestream wiedergibt, zeigt das SDK automatisch eine Schaltfläche zum Abspielen/Beenden anstelle der Schaltfläche zum Abspielen/Pausieren im erweiterten Controller an.
Unter Benutzerdefinierte Stile auf Ihre iOS App anwenden erfahren Sie, wie Ihre Sender-App das Aussehen der Cast-Widgets konfigurieren kann.
Lautstärkeregelung
Das Cast-Framework verwaltet die Lautstärke für die Sender-App automatisch. Das Framework synchronisiert die Lautstärke des Web Receivers automatisch für die bereitgestellten UI-Widgets. Verwenden Sie
GCKUIDeviceVolumeController, um einen von der App bereitgestellten Schieberegler zu synchronisieren.
Lautstärkeregelung über physische Tasten
Die physischen Lautstärketasten auf dem Sendergerät können verwendet werden, um die
Lautstärke der Cast-Sitzung auf dem Web Receiver zu ändern. Verwenden Sie dazu das
physicalVolumeButtonsWillControlDeviceVolume Flag in
GCKCastOptions,
das in
GCKCastContext festgelegt ist.
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID) let options = GCKCastOptions(discoveryCriteria: criteria) options.physicalVolumeButtonsWillControlDeviceVolume = true GCKCastContext.setSharedInstanceWith(options)
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 die beste Antwort für jede Phase des Cast-Lebenszyklus festlegen. Die App kann dem Nutzer Fehlerdialogfelder anzeigen oder die Cast-Sitzung beenden.
Einige Fehler, einschließlich der
GCKErrorCode
GCKErrorCodeCancelled, sind beabsichtigt.
Versuchen Sie nicht, eine Verbindung wiederherzustellen, die mit GCKErrorCodeCancelled fehlgeschlagen ist.
Andernfalls kann es zu unerwartetem Verhalten kommen.
Logging
GCKLogger
ist ein Singleton, das vom Framework für das Logging verwendet wird. Verwenden Sie die
GCKLoggerDelegate
, um die Verarbeitung von Logmeldungen anzupassen.
Mit GCKLogger erzeugt das SDK Logging-Ausgaben in Form von Debug-Meldungen, Fehlern und Warnungen. Diese Logmeldungen helfen bei der Fehlerbehebung und sind nützlich, um Probleme zu erkennen und zu beheben. Standardmäßig wird die Logausgabe unterdrückt. Wenn Sie jedoch einen GCKLoggerDelegate zuweisen, kann die Sender-App diese Meldungen vom SDK empfangen und in der Systemkonsole protokollieren.
@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) } } }
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 diese Zeile nach dem Festlegen des Delegaten (siehe oben) zum Code hinzu:
let filter = GCKLoggerFilter.init() filter.minimumLevel = GCKLoggerLevel.verbose GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setMinimumLevel:GCKLoggerLevelVerbose]; [GCKLogger sharedInstance].filter = filter;
Sie können die von
GCKLogger erzeugten Logmeldungen auch filtern.
Legen Sie die Mindestlogebene pro Klasse fest, z. B.:
let filter = GCKLoggerFilter.init() filter.setLoggingLevel(GCKLoggerLevel.verbose, forClasses: ["GCKUICastButton", "GCKUIImageCache", "NSMutableDictionary"]) GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setLoggingLevel:GCKLoggerLevelVerbose forClasses:@[@"GCKUICastButton", @"GCKUIImageCache", @"NSMutableDictionary" ]]; [GCKLogger sharedInstance].filter = filter;
Die Klassennamen können Literalnamen oder Glob-Muster sein, z. B. GCKUI\* und GCK\*Session.