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 inGCKCastOptions
, 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.
@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
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 MethodepresentCastInstructionsViewControllerOnceWithCastButton
, 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:
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];
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 GCKRemoteMediaClientListener
s.
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.
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]];
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.
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 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:
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 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:
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 im Root-Ansicht-Controller eine GCKUIMiniMediaControlsViewController
-Instanz und fügen Sie sie dem Container-Ansicht-Controller als Unteransicht 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 Hostansicht-Controller mit, wann der Minicontroller sichtbar sein soll:
func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController, shouldAppear _: Bool) { updateControlBarsVisibility() }
- (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:
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (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:
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 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.
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 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.
@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
Fügen Sie dem Code die folgende Zeile hinzu, nachdem Sie den Bevollmächtigten festgelegt haben (siehe oben):
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 auch die von GCKLogger
generierten Lognachrichten filtern.
Legen Sie die minimale Logging-Ebene pro Klasse fest. Beispiel:
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 entweder Literalnamen oder Glob-Muster sein, beispielsweise GCKUI\*
und GCK\*Session
.