Zintegruj przesyłanie z aplikacją na iOS

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 w GCKCastOptions, 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.

Swift
@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)
    }
  }
}
Objective-C

AppDelegate.h

@interface AppDelegate () <GCKLoggerDelegate>
@end

AppDelegate.m

@implementation AppDelegate

static NSString *const kReceiverAppID = @"AABBCCDD";
static const BOOL kDebugLoggingEnabled = YES;

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc]
                                    initWithApplicationID:kReceiverAppID];
  GCKCastOptions *options = [[GCKCastOptions alloc] initWithDiscoveryCriteria:criteria];
  [GCKCastContext setSharedInstanceWithOptions:options];

  // Enable logger.
  [GCKLogger sharedInstance].delegate = self;

  ...

  return YES;
}

...

#pragma mark - GCKLoggerDelegate

- (void)logMessage:(NSString *)message
           atLevel:(GCKLoggerLevel)level
      fromFunction:(NSString *)function
          location:(NSString *)location {
  if (kDebugLoggingEnabled) {
    NSLog(@"%@ - %@, %@", function, message, location);
  }
}

@end

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:

Swift
let castButton = GCKUICastButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
castButton.tintColor = UIColor.gray
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)
Objective-C
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.

Swift
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))
Objective-C
GCKMediaMetadata *metadata = [[GCKMediaMetadata alloc]
                                initWithMetadataType:GCKMediaMetadataTypeMovie];
[metadata setString:@"Big Buck Bunny (2008)" forKey:kGCKMetadataKeyTitle];
[metadata setString:@"Big Buck Bunny tells the story of a giant rabbit with a heart bigger than "
 "himself. When one sunny day three rodents rudely harass him, something "
 "snaps... and the rabbit ain't no bunny anymore! In the typical cartoon "
 "tradition he prepares the nasty rodents a comical revenge."
             forKey:kGCKMetadataKeySubtitle];
[metadata addImage:[[GCKImage alloc]
                    initWithURL:[[NSURL alloc] initWithString:@"https://commondatastorage.googleapis.com/"
                                 "gtv-videos-bucket/sample/images/BigBuckBunny.jpg"]
                    width:480
                    height:360]];

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.

Swift
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
}
Objective-C
GCKMediaInformationBuilder *mediaInfoBuilder =
  [[GCKMediaInformationBuilder alloc] initWithContentURL:
   [NSURL URLWithString:@"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"]];
mediaInfoBuilder.streamType = GCKMediaStreamTypeNone;
mediaInfoBuilder.contentType = @"video/mp4";
mediaInfoBuilder.metadata = metadata;
self.mediaInformation = [mediaInfoBuilder build];

GCKRequest *request = [self.sessionManager.currentSession.remoteMediaClient loadMedia:self.mediaInformation];
if (request != nil) {
  request.delegate = self;
}

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:

Swift
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()

  ...
}
Objective-C
- (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...

  // Wrap main view in the GCKUICastContainerViewController and display the mini controller.
  UIStoryboard *appStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
  UINavigationController *navigationController =
          [appStoryboard instantiateViewControllerWithIdentifier:@"MainNavigation"];
  GCKUICastContainerViewController *castContainerVC =
          [[GCKCastContext sharedInstance] createCastContainerControllerForViewController:navigationController];
  castContainerVC.miniMediaControlsItemEnabled = YES;
  self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
  self.window.rootViewController = castContainerVC;
  [self.window makeKeyAndVisible];
  ...

}
Swift
var castControlBarsEnabled: Bool {
  set(enabled) {
    if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController {
      castContainerVC.miniMediaControlsItemEnabled = enabled
    } else {
      print("GCKUICastContainerViewController is not correctly configured")
    }
  }
  get {
    if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController {
      return castContainerVC.miniMediaControlsItemEnabled
    } else {
      print("GCKUICastContainerViewController is not correctly configured")
      return false
    }
  }
}
Objective-C

AppDelegate.h

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, assign) BOOL castControlBarsEnabled;

@end

AppDelegate.m

@implementation AppDelegate

...

- (void)setCastControlBarsEnabled:(BOOL)notificationsEnabled {
  GCKUICastContainerViewController *castContainerVC;
  castContainerVC =
      (GCKUICastContainerViewController *)self.window.rootViewController;
  castContainerVC.miniMediaControlsItemEnabled = notificationsEnabled;
}

- (BOOL)castControlBarsEnabled {
  GCKUICastContainerViewController *castContainerVC;
  castContainerVC =
      (GCKUICastContainerViewController *)self.window.rootViewController;
  return castContainerVC.miniMediaControlsItemEnabled;
}

...

@end

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:

Swift
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
}
Objective-C
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  self.window.clipsToBounds = YES;

  RootContainerViewController *rootContainerVC;
  rootContainerVC =
      (RootContainerViewController *)self.window.rootViewController;
  rootContainerVC.miniMediaControlsViewEnabled = YES;

  ...

  return YES;
}

W kontrolerze widoku głównego utwórz instancję GCKUIMiniMediaControlsViewController i dodaj ją do kontrolera widoku kontenera jako widok podrzędny:

Swift
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)
    }
  }

...
Objective-C

RootContainerViewController.h

static const NSTimeInterval kCastControlBarsAnimationDuration = 0.20;

@interface RootContainerViewController () <GCKUIMiniMediaControlsViewControllerDelegate> {
  __weak IBOutlet UIView *_miniMediaControlsContainerView;
  __weak IBOutlet NSLayoutConstraint *_miniMediaControlsHeightConstraint;
  GCKUIMiniMediaControlsViewController *_miniMediaControlsViewController;
}

@property(nonatomic, weak, readwrite) UINavigationController *navigationController;

@property(nonatomic, assign, readwrite) BOOL miniMediaControlsViewEnabled;
@property(nonatomic, assign, readwrite) BOOL miniMediaControlsItemEnabled;

@end

RootContainerViewController.m

@implementation RootContainerViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  GCKCastContext *castContext = [GCKCastContext sharedInstance];
  _miniMediaControlsViewController =
      [castContext createMiniMediaControlsViewController];
  _miniMediaControlsViewController.delegate = self;

  [self updateControlBarsVisibility];
  [self installViewController:_miniMediaControlsViewController
              inContainerView:_miniMediaControlsContainerView];
}

- (void)setMiniMediaControlsViewEnabled:(BOOL)miniMediaControlsViewEnabled {
  _miniMediaControlsViewEnabled = miniMediaControlsViewEnabled;
  if (self.isViewLoaded) {
    [self updateControlBarsVisibility];
  }
}

- (void)updateControlBarsVisibility {
  if (self.miniMediaControlsViewEnabled &&
      _miniMediaControlsViewController.active) {
    _miniMediaControlsHeightConstraint.constant =
        _miniMediaControlsViewController.minHeight;
    [self.view bringSubviewToFront:_miniMediaControlsContainerView];
  } else {
    _miniMediaControlsHeightConstraint.constant = 0;
  }
  [UIView animateWithDuration:kCastControlBarsAnimationDuration
                   animations:^{
                     [self.view layoutIfNeeded];
                   }];
  [self.view setNeedsLayout];
}

- (void)installViewController:(UIViewController *)viewController
              inContainerView:(UIView *)containerView {
  if (viewController) {
    [self addChildViewController:viewController];
    viewController.view.frame = containerView.bounds;
    [containerView addSubview:viewController.view];
    [viewController didMoveToParentViewController:self];
  }
}

- (void)uninstallViewController:(UIViewController *)viewController {
  [viewController willMoveToParentViewController:nil];
  [viewController.view removeFromSuperview];
  [viewController removeFromParentViewController];
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  if ([segue.identifier isEqualToString:@"NavigationVCEmbedSegue"]) {
    self.navigationController =
        (UINavigationController *)segue.destinationViewController;
  }
}

...

@end

GCKUIMiniMediaControlsViewControllerDelegate informuje kontroler widoku hosta, kiedy minikontroler powinien być widoczny:

Swift
  func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController,
                                       shouldAppear _: Bool) {
    updateControlBarsVisibility()
  }
Objective-C
- (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:

Swift
func applicationDidFinishLaunching(_ application: UIApplication) {
  ..

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

  ...
}
Objective-C
- (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:

Swift
func playSelectedItemRemotely() {
  GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()

  ...

  // Load your media
  sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation)
}
Objective-C
- (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.

Swift
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
let options = GCKCastOptions(discoveryCriteria: criteria)
options.physicalVolumeButtonsWillControlDeviceVolume = true
GCKCastContext.setSharedInstanceWith(options)
Objective-C
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.

Swift
@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)
    }
  }
}
Objective-C

AppDelegate.h

@interface AppDelegate () <GCKLoggerDelegate>
@end

AppDelegate.m

@implementation AppDelegate

static NSString *const kReceiverAppID = @"AABBCCDD";
static const BOOL kDebugLoggingEnabled = YES;

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...

  // Enable logger.
  [GCKLogger sharedInstance].delegate = self;

  ...

  return YES;
}

...

#pragma mark - GCKLoggerDelegate

- (void)logMessage:(NSString *)message
           atLevel:(GCKLoggerLevel)level
      fromFunction:(NSString *)function
          location:(NSString *)location {
  if (kDebugLoggingEnabled) {
    NSLog(@"%@ - %@, %@", function, message, location);
  }
}

@end

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:

Swift
let filter = GCKLoggerFilter.init()
filter.minimumLevel = GCKLoggerLevel.verbose
GCKLogger.sharedInstance().filter = filter
Objective-C
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:

Swift
let filter = GCKLoggerFilter.init()
filter.setLoggingLevel(GCKLoggerLevel.verbose, forClasses: ["GCKUICastButton",
                                                            "GCKUIImageCache",
                                                            "NSMutableDictionary"])
GCKLogger.sharedInstance().filter = filter
Objective-C
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.