Ten przewodnik dla programistów opisuje, jak dodać obsługę Google Cast do aplikacji nadawczej na iOS za pomocą pakietu SDK nadawcy na iOS.
Urządzenie mobilne lub laptop to nadawca, który kontroluje odtwarzanie, a urządzenie Google Cast to odbiornik, które wyświetla treści na telewizorze.
Framework nadawcy to binarne biblioteki klas Cast i powiązane zasoby obecne w czasie wykonywania w nadajniku. Aplikacja nadawcy lub aplikacja przesyłania odnosi się do aplikacji działającej również na urządzeniu nadawcy. Aplikacja Web Receiver to aplikacja HTML uruchamiana na Web Receiver.
Framework nadawcy używa asynchronicznego wywołania zwrotnego, aby informować aplikację nadawcy o zdarzeniach i przechodzeniu między różnymi stanami cyklu życia aplikacji przesyłania.
Przebieg działania aplikacji
Oto typowy ogólny przepływ danych w przypadku aplikacji na iOS:
- Platforma Cast rozpoczyna działanie
GCKDiscoveryManager
na podstawie właściwości podanych wGCKCastOptions
, aby rozpocząć skanowanie w poszukiwaniu urządzeń. - Gdy użytkownik kliknie przycisk przesyłania, framework wyświetli okno przesyłania z listą wykrytych urządzeń przesyłania.
- Gdy użytkownik wybierze urządzenie przesyłające, framework spróbuje uruchomić na tym urządzeniu aplikację Web Receiver.
- Platforma wywołuje funkcje zwrotne w aplikacji nadawcy, aby potwierdzić, że aplikacja Web Receiver została uruchomiona.
- Platforma tworzy kanał komunikacji między aplikacjami nadawcy i Web Receiver.
- Framework używa kanału komunikacji do wczytywania i kontrolowania odtwarzania multimediów w odbiorniku internetowym.
- Platforma synchronizuje stan odtwarzania multimediów między nadawcą a odbiornikiem internetowym: gdy użytkownik wykonuje czynności w interfejsie nadawcy, platforma przekazuje te żądania sterowania multimediami do odbiornika internetowego, a gdy odbiornik internetowy wysyła aktualizacje stanu multimediów, platforma aktualizuje stan interfejsu nadawcy.
- Gdy użytkownik kliknie przycisk Cast, aby rozłączyć się z urządzeniem Cast, framework odłączy aplikację przesyłającą od odbiornika internetowego.
Aby rozwiązać problem z nadawcą, musisz włączyć logowanie.
Pełną listę wszystkich klas, metod i zdarzeń w ramach interfejsu Google Cast na iOS znajdziesz w dokumentacji interfejsu Google Cast API na iOS. W następnych sekcjach znajdziesz instrukcje integracji Cast w aplikacji na iOS.
Wywoływanie metod z wątku głównego
Inicjowanie kontekstu przesyłania
Platforma Cast ma globalny obiekt pojedynczy GCKCastContext
, który koordynuje wszystkie jej działania. Ten obiekt musi zostać zainicjowany na początku cyklu życia aplikacji, zazwyczaj w metodzie -[application:didFinishLaunchingWithOptions:]
delegata aplikacji, aby można było prawidłowo uruchomić automatyczne wznawianie sesji po ponownym uruchomieniu aplikacji nadawcy.
Podczas inicjowania GCKCastContext
należy podać obiekt GCKCastOptions
.
Ta klasa zawiera opcje, które wpływają na działanie platformy. Najważniejszym z nich jest identyfikator aplikacji Web Receiver, który służy do filtrowania wyników wyszukiwania i uruchamiania aplikacji Web Receiver po rozpoczęciu sesji przesyłania.
Metoda -[application:didFinishLaunchingWithOptions:]
to też dobre miejsce do skonfigurowania delegata rejestrowania, który będzie odbierał komunikaty rejestrowania z ramy.
Mogą one być przydatne podczas debugowania i rozwiązywania problemów.
@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
Widgety UX przesyłania
Pakiet iOS SDK Cast udostępnia te widżety zgodne z wytycznymi dotyczącymi projektu Cast:
Overlay wprowadzający: klasa
GCKCastContext
ma metodępresentCastInstructionsViewControllerOnceWithCastButton
, której można użyć do wyróżnienia przycisku Cast przy pierwszym udostępnieniu odbiornika internetowego. Aplikacja nadawcy może dostosować tekst, pozycję tytułu i przycisku Zamknij.Przycisk przesyłania: od wersji pakietu SDK przesyłającego na iOS 4.6.0 przycisk przesyłania jest zawsze widoczny, gdy urządzenie przesyłające jest połączone z Wi-Fi. Gdy użytkownik po raz pierwszy kliknie przycisk Cast po uruchomieniu aplikacji, pojawi się okno z uprawnieniami, w którym będzie można zezwolić aplikacji na dostęp do sieci lokalnej na urządzeniach w sieci. Gdy użytkownik kliknie przycisk przesyłania, wyświetli się okno przesyłania z listą wykrytych urządzeń. Gdy użytkownik kliknie przycisk przesyłania, gdy urządzenie jest połączone, wyświetli się bieżące metadane multimediów (takie jak tytuł, nazwa studia nagrywającego i miniatura) lub użytkownik będzie mógł odłączyć się od urządzenia przesyłającego. Gdy użytkownik kliknie przycisk przesyłania, gdy nie ma dostępnych urządzeń, wyświetli się ekran z informacjami o tym, dlaczego nie można znaleźć urządzeń i jak rozwiązać ten problem.
Mini kontroler: gdy użytkownik przesyła treści i przechodzi z bieżącej strony treści lub rozwiniętego kontrolera na inny ekran w aplikacji nadawcy, na dole ekranu wyświetla się mini kontroler, aby użytkownik mógł zobaczyć metadane przesyłanych multimediów i sterować odtwarzaniem.
Rozwinięty kontroler: gdy użytkownik przesyła treści, klika powiadomienie o multimediach lub minikontroler, uruchamia rozwinięty kontroler, który wyświetla metadane aktualnie odtwarzanych multimediów i zawiera kilka przycisków do sterowania odtwarzaniem.
Dodawanie przycisku przesyłania
Framework udostępnia komponent przycisku Cast jako podklasę UIButton
. Możesz go dodać do paska tytułu aplikacji, umieszczając go w elementzie UIBarButtonItem
. Typowa podklasa UIViewController
może zainstalować przycisk Cast w ten sposób:
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];
Dotknięcie przycisku spowoduje otwarcie okna przesyłania, które jest dostarczane przez framework.
GCKUICastButton
można też dodać bezpośrednio do storyboardu.
Konfigurowanie wykrywania urządzeń
W ramach tej platformy wykrywanie urządzeń odbywa się automatycznie. Nie musisz ręcznie uruchamiać ani zatrzymywać procesu wykrywania, chyba że wdrożesz niestandardowy interfejs użytkownika.
Odkrywanie w ramach frameworku jest zarządzane przez klasę GCKDiscoveryManager
, która jest właściwością obiektu GCKCastContext
. Framework udostępnia domyślny komponent okna Cast do wyboru urządzenia i jego sterowania. Lista urządzeń jest uporządkowana alfabetycznie według nazwy przyjaznej dla użytkownika.
Jak działa zarządzanie sesjami
Pakiet SDK Cast wprowadza pojęcie sesji Cast, której utworzenie łączy w sobie czynności takie jak połączenie z urządzeniem, uruchomienie (lub dołączenie) aplikacji Web Receiver, połączenie z tą aplikacją i inicjowanie kanału sterowania multimediami. Więcej informacji o sesjach przesyłania i cyklu życia odbiornika internetowego znajdziesz w przewodniku po cyklu życia aplikacji.
Sesjami zarządza klasa GCKSessionManager
, która jest usługą w GCKCastContext
.
Poszczególne sesje są reprezentowane przez podklasy klasy GCKSession
: na przykład GCKCastSession
reprezentuje sesje z urządzeniami przesyłającymi. Możesz uzyskać dostęp do bieżącej sesji przesyłania (jeśli jest aktywna) jako elementu currentCastSession
w usługach GCKSessionManager
.
W interfejsie GCKSessionManagerListener
możesz sprawdzać zdarzenia sesji, takie jak tworzenie, zawieszanie, wznawianie i zakończenie sesji. Framework automatycznie zawiesza sesje, gdy aplikacja nadawcy przechodzi do działania w tle, i próbuje je wznowić, gdy aplikacja wróci na pierwszy plan (lub zostanie ponownie uruchomiona po nieprawidłowym lub nagłym zakończeniu sesji).
Jeśli używane jest okno przesyłania, sesje są tworzone i zamykane automatycznie w odpowiedzi na gesty użytkownika. W przeciwnym razie aplikacja może rozpoczynać i kończyć sesje za pomocą metod GCKSessionManager
.
Jeśli aplikacja musi przeprowadzić specjalne przetwarzanie w odpowiedzi na zdarzenia cyklu życia sesji, może zarejestrować co najmniej 1 instancję GCKSessionManagerListener
w komponencie GCKSessionManager
. GCKSessionManagerListener
to protokół, który definiuje funkcje wywoływane z powodu takich zdarzeń jak rozpoczęcie i zakończenie sesji.
Przeniesienie odtwarzania
Zachowanie stanu sesji stanowi podstawę przenoszenia strumienia, dzięki czemu użytkownicy mogą przenosić istniejące strumienie audio i wideo między urządzeniami za pomocą poleceń głosowych, aplikacji Google Home lub inteligentnych ekranów. Multimedia przestają być odtwarzane na jednym urządzeniu (źródło) i kontynuują na drugim (miejsce docelowe). Źródłem lub celem w ramach przesyłania strumieniowego może być dowolne urządzenie Cast z najnowszą wersją oprogramowania układowego.
Aby podczas przenoszenia strumienia uzyskać nowe urządzenie docelowe, użyj właściwości GCKCastSession#device
w wywołaniu zwrotnym [sessionManager:didResumeCastSession:]
.
Więcej informacji znajdziesz w artykule Przenoszenie strumienia na odbiornik internetowy.
Automatyczne ponowne łączenie
Platforma Cast dodaje logikę ponownego nawiązywania połączenia, aby automatycznie obsługiwać ponowne połączenie w wielu subtelnych przypadkach szczególnych, takich jak:
- Przywracanie działania po tymczasowej utracie połączenia z Wi-Fi
- Przywracanie po przejściu urządzenia w stan uśpienia
- Przywracanie aplikacji z tła
- Odzyskiwanie danych w przypadku awarii aplikacji
Jak działa kontrola multimediów
Jeśli sesja Cast zostanie nawiązana za pomocą aplikacji Web Receiver obsługującej przestrzeń nazw media, framework automatycznie utworzy instancję GCKRemoteMediaClient
. Można ją uzyskać jako właściwość remoteMediaClient
instancji GCKCastSession
.
Wszystkie metody w GCKRemoteMediaClient
, które wysyłają żądania do odbiornika internetowego, zwracają obiekt GCKRequest
, którego można użyć do śledzenia żądania. Do tego obiektu można przypisać obiekt GCKRequestDelegate
, aby otrzymywać powiadomienia o ewentualnym wyniku operacji.
Oczekuje się, że instancja GCKRemoteMediaClient
może być współdzielona przez wiele części aplikacji, a nawet niektóre komponenty wewnętrzne frameworku, takie jak okno Cast i ministerowania multimediami, mogą współdzielić instancję. W tym celu usługa GCKRemoteMediaClient
umożliwia rejestrację wielu GCKRemoteMediaClientListener
.
Ustawianie metadanych multimediów
Klasa GCKMediaMetadata
reprezentuje informacje o elemencie multimedialnym, który chcesz przesłać. W tym przykładzie tworzymy nową instancję filmu GCKMediaMetadata
, ustawiając tytuł, lektor, nazwę studia nagraniowego i 2 obrazy.
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]];
Więcej informacji o używaniu obrazów z metadanymi multimediów znajdziesz w sekcji Wybieranie obrazów i ich buforowanie.
Ładowanie multimediów
Aby wczytać element multimedialny, utwórz instancję GCKMediaInformation
, używając metadanych multimediów. Następnie pobierz bieżący GCKCastSession
i użyj go do załadowania multimediów w aplikacji odbiornika. Następnie możesz użyć GCKRemoteMediaClient
do sterowania aplikacją odtwarzacza multimediów uruchomioną na odbiorniku, np. do odtwarzania, wstrzymywania i zatrzymywania.GCKRemoteMediaClient
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; }
Zobacz też sekcję Korzystanie z ścieżek multimedialnych.
Format wideo 4K
Aby określić format filmu, użyj właściwości videoInfo
obiektu GCKMediaStatus
, aby uzyskać bieżący przykład GCKVideoInfo
.
Ta instancja zawiera typ formatu HDR TV oraz wysokość i szerokość w pikselach. Wersje formatu 4K są wskazywane we właściwości hdrType
za pomocą wartości typu enum GCKVideoInfoHDRType
.
Dodawanie minikontrolerów
Zgodnie z listą kontrolną dotyczącą projektowania Cast aplikacja wysyłająca powinna zapewniać trwały element sterujący, zwany minikontrolerem, który powinien pojawiać się, gdy użytkownik przejdzie z bieżącej strony treści. Minikontroler zapewnia natychmiastowy dostęp i widoczne przypomnienie o bieżącej sesji przesyłania.
Platforma Cast udostępnia pasek sterowania
GCKUIMiniMediaControlsViewController
,
który można dodać do scen, w których chcesz wyświetlić minikontroler.
Gdy aplikacja nadawcy odtwarza transmisję na żywo z dźwiękiem lub wideo, SDK automatycznie wyświetla przycisk odtwarzania/zatrzymania zamiast przycisku odtwarzania/pauzowania na mini-kontrolerze.
Aby dowiedzieć się, jak aplikacja nadawcza może skonfigurować wygląd widżetów przesyłania, przeczytaj sekcję Dostosowywanie interfejsu użytkownika aplikacji nadawcy na iOS.
Minikontroler można dodać do aplikacji nadawcy na 2 sposoby:
- Pozwól platformie Cast zarządzać układem minikontrolera, owijając dotychczasowy kontroler widoku własnym kontrolerem widoku.
- Możesz samodzielnie zarządzać układem widżetu minikontrolera, dodając go do dotychczasowego kontrolera widoku, podając widok podrzędny na storyboardzie.
Zakończ za pomocą klasy GCKUICastContainerViewController.
Pierwszym sposobem jest użycie klasy GCKUICastContainerViewController
, która otacza inny kontroler widoku i dodaje u dołu element GCKUIMiniMediaControlsViewController
. To podejście ma pewne ograniczenia, ponieważ nie można dostosowywać animacji ani konfigurować zachowania kontrolera widoku kontenera.
Pierwszy sposób jest zwykle wykonywany w metodie -[application:didFinishLaunchingWithOptions:]
delegata aplikacji:
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
Wstawianie w obecnym kontrolerze widoku
Drugim sposobem jest dodanie minikontrolera bezpośrednio do istniejącego kontrolera widoku, używając funkcji createMiniMediaControlsViewController
do utworzenia instancji GCKUIMiniMediaControlsViewController
, a następnie dodania jej do kontrolera widoku kontenera jako podwidoku.
Skonfiguruj kontroler widoku w delegacie aplikacji:
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; }
W kodzie głównego kontrolera widoku utwórz instancję klasy GCKUIMiniMediaControlsViewController
i dodaj ją do kontrolera widoku kontenera jako podwidok:
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
GCKUIMiniMediaControlsViewControllerDelegate
informuje kontroler widoku gospodarza, kiedy mini kontroler powinien być widoczny:
func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController, shouldAppear _: Bool) { updateControlBarsVisibility() }
- (void)miniMediaControlsViewController: (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController shouldAppear:(BOOL)shouldAppear { [self updateControlBarsVisibility]; }
Dodawanie rozszerzonego kontrolera
Lista kontrolna Google Cast Design wymaga, aby aplikacja wysyłająca udostępniała rozszerzony kontroler dla przesyłanych multimediów. Rozwinięty kontroler to wersja minikontrolera na pełnym ekranie.
Rozwinięty kontroler to widok pełnoekranowy, który zapewnia pełną kontrolę nad odtwarzaniem multimediów na urządzeniu zdalnym. Widok ten powinien umożliwiać aplikacji do przesyłania treści zarządzanie wszystkimi aspektami sesji przesyłania, z wyjątkiem sterowania głośnością odbiornika internetowego i cyklu życia sesji (łączenia i zatrzymywania przesyłania). Zawiera też wszystkie informacje o stanie sesji multimediów (np. informacje o obrazie, tytule, napisach itp.).
Funkcje tego widoku są realizowane przez klasę GCKUIExpandedMediaControlsViewController
.
Najpierw musisz włączyć domyślny rozszerzony kontroler w kontekście Chromecasta. Zmodyfikuj delegata aplikacji, aby włączyć domyślny rozszerzony kontroler:
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; .. }
Dodaj do kontrolera widoku ten kod, aby wczytywać rozszerzony kontroler, gdy użytkownik rozpocznie przesyłanie strumieniowe filmu:
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]; }
Rozwinięty kontroler zostanie też automatycznie uruchomiony, gdy użytkownik dotknie minikontrolera.
Gdy aplikacja nadawcy odtwarza transmisję na żywo z filmem lub dźwiękiem, SDK automatycznie wyświetli przycisk odtwarzania/zatrzymania zamiast przycisku odtwarzania/pauzowania w rozwiniętym kontrolerze.
Aby dowiedzieć się, jak aplikacja nadawcy może skonfigurować wygląd widżetów przesyłania, przeczytaj artykuł Stosowanie niestandardowych stylów w aplikacji na iOS.
Sterowanie głośnością
Platforma Cast automatycznie zarządza głośnością aplikacji przesyłającej. Platforma automatycznie synchronizuje się z głośnością odbiornika internetowego w dostarczonych widżetach interfejsu. Aby zsynchronizować suwak udostępniony przez aplikację, użyj GCKUIDeviceVolumeController
.
Sterowanie głośnością za pomocą przycisku fizycznego
Za pomocą fizycznych przycisków głośności na urządzeniu nadawczym można zmienić głośność sesji przesyłania na odbiornik internetowy za pomocą flagi physicalVolumeButtonsWillControlDeviceVolume
w GCKCastOptions
, która jest ustawiona na GCKCastContext
.
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];
Obsługuj błędy
Aplikacje nadawców muszą obsługiwać wszystkie wywołania zwrotne błędów i decydowac na podstawie każdego etapu cyklu życia Cast o najlepszej odpowiedzi. Aplikacja może wyświetlić użytkownikowi okno z błędem lub zakończyć sesję przesyłania.
Logowanie
GCKLogger
to obiekt singleton używany do logowania przez framework. Aby dostosować sposób obsługi komunikatów z dziennika, użyj opcji GCKLoggerDelegate
.
Korzystając z funkcji GCKLogger
, pakiet SDK generuje dane logowania w postaci komunikatów debugowania, błędów i ostrzeżeń. Te komunikaty dziennika ułatwiają debugowanie oraz są przydatne do rozwiązywania problemów i identyfikowania ich przyczyn. Domyślnie dane wyjściowe dziennika są pomijane, ale po przypisaniu wartości GCKLoggerDelegate
aplikacja nadawcza może odbierać te komunikaty z pakietu SDK i rejestrować je w konsoli systemowej.
@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
Aby włączyć debugowanie i wyświetlanie szczegółowych komunikatów, dodaj ten wiersz do kodu po ustawieniu delegata (jak pokazano wcześniej):
let filter = GCKLoggerFilter.init() filter.minimumLevel = GCKLoggerLevel.verbose GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setMinimumLevel:GCKLoggerLevelVerbose]; [GCKLogger sharedInstance].filter = filter;
Możesz też filtrować komunikaty dziennika generowane przez GCKLogger
.
Ustaw minimalny poziom rejestrowania na podstawie klasy, na przykład:
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;
Nazwy klas mogą być nazwami dosłownymi lub wzorcami glob, na przykład GCKUI\*
i GCK\*Session
.