Z tego przewodnika dla programistów dowiesz się, jak dodać obsługę Google Cast do aplikacji nadawcy na iOS za pomocą pakietu SDK nadawcy na iOS.
Urządzenie mobilne lub laptop jest nadawcą, który kontroluje odtwarzanie, a urządzenie Google Cast to odbiornik, który wyświetla treści na telewizorze.
Platforma nadawcy odnosi się do pliku binarnego biblioteki klas Cast i powiązanych z nim zasobów w czasie działania u nadawcy. Określenie aplikacja nadawcy lub aplikacja Cast odnosi się również do aplikacji uruchomionej u nadawcy. Aplikacja Web Receivedr odnosi się do aplikacji HTML uruchomionej w odbiorniku internetowym.
Platforma nadawcy korzysta z asynchronicznego wywołania zwrotnego, który informuje aplikację nadawcy o zdarzeniach i przechodzi między różnymi stanami cyklu życia aplikacji Cast.
Przepływ aplikacji
Podane niżej kroki opisują typowy ogólny proces wykonywania w przypadku aplikacji na iOS nadawcy:
- Platforma przesyłania uruchamia się
GCKDiscoveryManager
na podstawie właściwości określonych wGCKCastOptions
, aby rozpocząć skanowanie w poszukiwaniu urządzeń. - Gdy użytkownik kliknie przycisk Cast, platforma wyświetli okno przesyłania z listą wykrytych urządzeń przesyłających.
- Gdy użytkownik wybiera urządzenie przesyłające, platforma próbuje uruchomić na nim aplikację Web Odbiornik.
- Platforma wywołuje wywołania zwrotne w aplikacji nadawcy, aby potwierdzić, że aplikacja Web Receivedr została uruchomiona.
- Platforma tworzy kanał komunikacyjny między nadawcą a aplikacjami odbierającymi pocztę.
- Platforma wykorzystuje kanał komunikacyjny 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 działania w interfejsie użytkownika nadawcy, platforma przekazuje te żądania 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 odłączyć urządzenie przesyłające, platforma odłączy aplikację nadawcy od odbiornika internetowego.
Aby rozwiązywać problemy z nadawcą, musisz włączyć rejestrowanie.
Pełną listę wszystkich klas, metod i zdarzeń w platformie Google Cast na iOS znajdziesz w dokumentacji interfejsu Google Cast API na iOS. W kolejnych sekcjach znajdziesz informacje o integrowaniu Cast z aplikacją na iOS.
Metody wywoływania z wątku głównego
Zainicjuj kontekst przesyłania
Platforma Cast zawiera globalny obiekt typu singleton GCKCastContext
, który koordynuje wszystkie działania platformy. Aby automatyczne wznowienie sesji po ponownym uruchomieniu aplikacji nadawcy zostało prawidłowo aktywowane, musi zostać zainicjowany na początku cyklu życia aplikacji, zwykle za pomocą metody -[application:didFinishLaunchingWithOptions:]
delegata aplikacji.
Podczas inicjowania GCKCastContext
należy dostarczyć obiekt GCKCastOptions
.
Ta klasa zawiera opcje, które wpływają na działanie platformy. Najważniejszym z nich jest identyfikator aplikacji Web Receivedr, który służy do filtrowania wyników wykrywania i uruchamiania aplikacji Web Receivedr po rozpoczęciu sesji przesyłania.
Metoda -[application:didFinishLaunchingWithOptions:]
jest też dobrym miejscem do skonfigurowania przedstawiciela logowania, który będzie odbierać komunikaty logowania z platformy.
Takie informacje mogą 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
Widżety Cast UX
Pakiet SDK Cast na iOS udostępnia te widżety, które są zgodne z listą kontrolną projektowania przesyłania:
Nakładka z wprowadzeniem: 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, położenie tekstu tytułu i przycisku zamykania.Przycisk przesyłania: Od pakietu SDK obsługującego przesyłanie na iOS w wersji 4.6.0 przycisk przesyłania jest zawsze widoczny, gdy urządzenie wysyłające jest połączone z Wi-Fi. Gdy użytkownik po raz pierwszy kliknie przycisk Cast po uruchomieniu aplikacji, pojawi się okno uprawnień, w którym użytkownik może przyznać aplikacji dostęp do urządzeń w sieci przez sieć lokalną. Następnie, gdy użytkownik kliknie przycisk przesyłania, pojawi się okno przesyłania z listą wykrytych urządzeń. Gdy użytkownik kliknie przycisk przesyłania, gdy urządzenie jest podłączone, wyświetlone zostaną bieżące metadane multimediów (takie jak tytuł, nazwa studia 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 urządzenia nie można znaleźć i jak rozwiązać problem.
Minikontroler: gdy użytkownik przesyła treści i opuści bieżącą stronę treści lub rozwinie kontroler na inny ekran w aplikacji nadawcy, u dołu ekranu pojawi się minikontroler, który umożliwia użytkownikowi przeglądanie aktualnie przesyłanych metadanych multimediów i sterowanie odtwarzaniem.
Rozwinięty kontroler: gdy użytkownik przesyła treści, a potem kliknie powiadomienie o multimediach lub minikontroler, uruchomi się rozwinięty kontroler, który wyświetli metadane odtwarzanych multimediów oraz kilka przycisków do sterowania odtwarzaniem multimediów.
Dodaj przycisk Cast
Platforma udostępnia komponent przycisku Cast jako podklasę UIButton
. Można ją dodać do paska tytułu aplikacji, zawijając ją obiektem 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];
Domyślnie dotknięcie tego przycisku spowoduje otwarcie okna przesyłania dostarczanego przez platformę.
Materiał GCKUICastButton
można też dodać bezpośrednio do scenorysu.
Skonfiguruj wykrywanie urządzeń
W ramach tej platformy wykrywanie urządzeń odbywa się automatycznie. Nie musisz bezpośrednio rozpoczynać ani zatrzymywać procesu wykrywania, chyba że wdrożysz niestandardowy interfejs użytkownika.
Discovery w platformie zarządza klasa GCKDiscoveryManager
, która jest właściwością GCKCastContext
. Platforma zawiera domyślny komponent okna przesyłania, który umożliwia wybór urządzenia i sterowanie nim. Lista urządzeń jest uporządkowana leksykograficznie według nazwy przyjaznej dla urządzenia.
Jak działa zarządzanie sesjami
Pakiet SDK Cast przedstawia koncepcję sesji przesyłania, która łączy w sobie etapy łączenia się z urządzeniem, uruchamiania (lub dołączania) aplikacji internetowej odbiornika, łączenia się z nią i inicjowania kanału sterowania multimediami. Więcej informacji o sesjach przesyłania i cyklu życia odbiornika internetowego znajdziesz w przewodniku po cyklu życia aplikacji na odbiorniku internetowym.
Sesjami zarządza klasa GCKSessionManager
, która jest właściwością komponentu GCKCastContext
.
Poszczególne sesje są reprezentowane przez podklasy klasy GCKSession
, np. GCKCastSession
oznacza sesje z urządzeniami przesyłającymi. Możesz uzyskać dostęp do aktywnej sesji przesyłania (jeśli taka jest) w ramach właściwości currentCastSession
interfejsu GCKSessionManager
.
Interfejs GCKSessionManagerListener
może służyć do monitorowania zdarzeń sesji, takich jak tworzenie, zawieszenie, wznowienie i zakończenie sesji. Platforma automatycznie zawiesza sesje, gdy aplikacja nadawcy działa w tle i próbuje je wznowić, gdy aplikacja wraca na pierwszy plan (lub zostaje ponownie uruchomiona po nieoczekiwanym lub nagłym zamknięciu aplikacji, gdy sesja była aktywna).
Jeśli używane jest okno przesyłania, sesje są tworzone i usuwane automatycznie w odpowiedzi na gesty użytkownika. W przeciwnym razie aplikacja może bezpośrednio rozpoczynać i kończyć sesje za pomocą metod w GCKSessionManager
.
Jeśli w odpowiedzi na zdarzenia cyklu życia sesji aplikacja musi wykonać specjalne przetwarzanie, może zarejestrować co najmniej 1 instancję GCKSessionManagerListener
za pomocą GCKSessionManager
. GCKSessionManagerListener
to protokół, który definiuje wywołania zwrotne dla takich zdarzeń jak początek sesji, koniec sesji itd.
Przenoszenie strumienia
Zachowanie stanu sesji jest podstawą przesyłania strumienia, w ramach którego użytkownicy mogą przenosić strumienie audio i wideo między urządzeniami za pomocą poleceń głosowych, aplikacji Google Home lub inteligentnych ekranów. Odtwarzanie multimediów zatrzymuje się na jednym urządzeniu (źródło), a następnie jest kontynuowane na innym (miejscu docelowym). Każde urządzenie przesyłające z najnowszym oprogramowaniem może służyć jako źródło lub miejsca docelowe w przesyłaniu strumieniowym.
Aby uzyskać nowe urządzenie docelowe podczas przenoszenia strumienia, użyj właściwości GCKCastSession#device
podczas wywołania zwrotnego [sessionManager:didResumeCastSession:]
.
Więcej informacji znajdziesz w sekcji Przenoszenie strumienia w internetowym odbiorniku.
Automatyczne ponowne połączenie
Platforma Cast dodaje funkcje ponownego łączenia, które automatycznie obsługują ponowne łączenie w wielu subtelnych sytuacjach, takich jak:
- przywracanie działania po tymczasowej utracie połączenia z Wi-Fi,
- Regeneracja po uśpieniu urządzenia
- Przywróć działanie aplikacji po uruchomieniu w tle
- Przywracanie w przypadku awarii aplikacji
Jak działa sterowanie multimediami
Jeśli sesja przesyłania zostanie ustanowiona w aplikacji odbiornika internetowego, która obsługuje przestrzeń nazw multimediów, platforma automatycznie utworzy instancję GCKRemoteMediaClient
. Można do niej uzyskać dostęp jako właściwość remoteMediaClient
instancji GCKCastSession
.
Wszystkie metody w GCKRemoteMediaClient
, które wysyłają żądania do odbiornika internetowego, zwracają obiekt GCKRequest
, który może służyć do śledzenia tego żądania. Do tego obiektu można przypisać obiekt GCKRequestDelegate
, aby otrzymywać powiadomienia o ostatecznym wyniku tej operacji.
Instancja GCKRemoteMediaClient
może być współużytkowana przez wiele części aplikacji, a niektóre wewnętrzne komponenty platformy, takie jak okno przesyłania i elementy sterujące minimultimediami, są w jej przypadku współużytkowane. W tym celu GCKRemoteMediaClient
obsługuje rejestrację wielu GCKRemoteMediaClientListener
.
Ustaw metadane multimediów
Klasa GCKMediaMetadata
zawiera informacje o elemencie multimedialnym, który chcesz przesyłać. Poniższy przykład tworzy nowe wystąpienie filmu GCKMediaMetadata
oraz ustawia tytuł, podtytuł, 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]];
Informacje o używaniu obrazów z metadanymi multimediów znajdziesz w sekcji Wybór obrazu i pamięć podręczna.
Wczytaj multimedia
Aby wczytać element multimedialny, utwórz instancję GCKMediaInformation
, korzystając z metadanych multimediów. Następnie pobierz bieżący GCKCastSession
i użyj GCKRemoteMediaClient
do wczytania multimediów w aplikacji odbiornika. Następnie możesz użyć GCKRemoteMediaClient
do sterowania aplikacją odtwarzacza multimediów działającą na odbiorniku, np. odtwarzaniem, wstrzymywaniem i zatrzymywaniem.
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; }
Zapoznaj się też z sekcją o używaniu ścieżek multimedialnych.
Format wideo 4K
Aby określić format wideo, użyj właściwości videoInfo
GCKMediaStatus
, aby pobrać bieżące wystąpienie obiektu GCKVideoInfo
.
Ta instancja zawiera typ formatu HDR TV oraz wysokość i szerokość w pikselach. Warianty formatu 4K są wskazane we właściwości hdrType
za pomocą wartości wyliczeniowych GCKVideoInfoHDRType
.
Dodawanie minikontrolerów
Zgodnie z listą kontrolną projektu przesyłania aplikacja nadawcy powinna zapewniać stały element sterujący (nazywany minikontrolerem), który powinien się pojawiać, gdy użytkownik opuści bieżącą stronę treści. Minikontroler zapewnia natychmiastowy dostęp i widoczne przypomnienie o bieżącej sesji przesyłania.
Platforma przesyłania zawiera pasek sterowania GCKUIMiniMediaControlsViewController
, który można dodać do scen, w których chcesz pokazać minikontroler.
Gdy aplikacja nadawcy odtwarza transmisję wideo lub audio na żywo, pakiet SDK automatycznie wyświetla przycisk odtwarzania/wstrzymywania na minikontrolerze.
W artykule Dostosowywanie interfejsu nadawcy w iOS znajdziesz informacje o tym, jak aplikacja nadawcy może skonfigurować wygląd widżetów Cast.
Minikontroler można dodać do aplikacji nadawcy na 2 sposoby:
- Pozwól platformie Cast zarządzać układem minikontrolera, dodając własny kontroler widoku do istniejącego kontrolera.
- Układem minikontrolera możesz samodzielnie zarządzać, dodając go do istniejącego kontrolera widoków, udostępniając w scenorysie widok podrzędny.
Zawijanie za pomocą kontrolera GCKUICastContainerViewController
Pierwszym sposobem jest użycie obiektu GCKUICastContainerViewController
, który opakowuje inny kontroler widoku i dodaje na dole element GCKUIMiniMediaControlsViewController
. Takie podejście jest ograniczone przez to, że nie można dostosować animacji ani skonfigurować działania kontrolera widoku kontenera.
Pierwszy sposób wykonuje się zwykle w metodzie -[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
Umieść w istniejącym kontrolerze widoku
Drugim sposobem jest dodanie minikontrolera bezpośrednio do obecnego kontrolera widoku za pomocą narzędzia createMiniMediaControlsViewController
w celu utworzenia instancji GCKUIMiniMediaControlsViewController
, a następnie dodania go do kontrolera widoku kontenera jako widoku podrzędnego.
Skonfiguruj kontroler widoku w przekazywaniu 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 kontrolerze widoku głównego utwórz instancję GCKUIMiniMediaControlsViewController
i dodaj ją do kontrolera widoku kontenera jako widok podrzędny:
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 hosta, kiedy minikontroler powinien być widoczny:
func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController, shouldAppear _: Bool) { updateControlBarsVisibility() }
- (void)miniMediaControlsViewController: (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController shouldAppear:(BOOL)shouldAppear { [self updateControlBarsVisibility]; }
Dodaj rozwinięty kontroler
Lista kontrolna projektu Google Cast wymaga, aby aplikacja nadawcy udostępniała rozwinięty kontroler do przesyłania multimediów. Rozszerzony kontroler to pełnoekranowa wersja minikontrolera.
Rozwinięty kontroler to widok pełnoekranowy, który zapewnia pełną kontrolę nad zdalnym odtwarzaniem multimediów. Ten widok powinien umożliwiać aplikacji przesyłającej zarządzanie każdym możliwym do zarządzania aspektem sesji przesyłania z wyjątkiem regulacji głośności odbiornika internetowego i cyklu życia sesji (łączenie/zatrzymywanie przesyłania). Zawiera też wszystkie informacje o stanie sesji multimediów (grafika, tytuł, podtytuł itp.).
Funkcje tego widoku są implementowane przez klasę GCKUIExpandedMediaControlsViewController
.
Najpierw musisz włączyć domyślny rozwinięty kontroler w kontekście przesyłania. Zmodyfikuj delegata aplikacji, aby włączyć domyślny rozwinięty kontroler:
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; .. }
Dodaj ten kod do kontrolera widoku, aby wczytywać rozwinięty kontroler, gdy użytkownik rozpocznie przesyłanie 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 będzie się też uruchamiał automatycznie, gdy użytkownik kliknie minikontroler.
Gdy aplikacja nadawcy odtwarza transmisję wideo lub audio na żywo, pakiet SDK automatycznie wyświetla przycisk odtwarzania/wstrzymywania na rozwiniętym kontrolerze.
Informacje o tym, jak aplikacja nadawcy może skonfigurować wygląd widżetów przesyłania, znajdziesz w sekcji Stosowanie stylów niestandardowych do aplikacji na iOS.
Sterowanie głośnością
Platforma Cast automatycznie zarządza woluminem w aplikacji nadawcy. Platforma automatycznie synchronizuje się z woluminem odbiornika internetowego w przypadku dołączonych widżetów interfejsu. Aby zsynchronizować suwak aplikacji, użyj opcji GCKUIDeviceVolumeController
.
Sterowanie głośnością za pomocą fizycznego przycisku
Fizyczne przyciski głośności na urządzeniu nadawcy mogą zmieniać głośność sesji przesyłania w odbiorniku internetowym za pomocą flagi physicalVolumeButtonsWillControlDeviceVolume
na 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ługa błędów
Aplikacje nadawców muszą uwzględniać wszystkie wywołania zwrotne błędów i określać najlepszą odpowiedź na każdy etap cyklu życia przesyłania. Aplikacja może wyświetlić użytkownikowi okno z komunikatem o błędzie lub zakończyć sesję przesyłania.
Logowanie
GCKLogger
to pojedyncze polecenie używane do logowania przez platformę. Za pomocą GCKLoggerDelegate
możesz dostosować sposób obsługi komunikatów logu.
Za pomocą GCKLogger
pakiet SDK generuje dane wyjściowe dzienników w postaci komunikatów debugowania, błędów i ostrzeżeń. Komunikaty te ułatwiają debugowanie oraz pomagają
rozwiązywać i identyfikować problemy. Domyślnie dane wyjściowe logów są pomijane, ale gdy przypiszesz GCKLoggerDelegate
, aplikacja nadawcy może odbierać te wiadomości 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ć również komunikaty dotyczące debugowania i szczegółowe komunikaty, po ustawieniu przedstawiciela (przedstawionego wcześniej) dodaj do kodu ten wiersz do kodu:
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 logu wygenerowane przez GCKLogger
.
Ustaw minimalny poziom logowania dla każdej 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 literałowymi lub wzorcami globalnymi, np. GCKUI\*
i GCK\*Session
.