Zintegruj przesyłanie z aplikacją na iOS

Z tego przewodnika dla programistów dowiesz się, jak dodać obsługę Google Cast do aplikacji wysyłającej na iOS za pomocą pakietu SDK na iOS.

Urządzenie mobilne lub laptop jest nadawcą, które steruje odtwarzaniem, a urządzenie Google Cast jest odbiornikiem, 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 znajdujących się w czasie działania u nadawcy. Określenia aplikacja nadawcy lub aplikacja do przesyłania odnoszą się do aplikacji również uruchomionej u nadawcy. Aplikacja Web Receiver odnosi się do aplikacji HTML uruchomionej na odbiorniku internetowym.

Platforma nadawcy korzysta z asynchronicznego projektu wywołania zwrotnego, aby informować aplikację nadawcy o zdarzeniach i przechodzić między różnymi stanami cyklu życia aplikacji Cast.

Przepływ aplikacji

Poniższe kroki opisują typowy ogólny proces wykonywania w aplikacji na iOS nadawcy:

  • Platforma Cast uruchamia się GCKDiscoveryManager na podstawie właściwości podanych w GCKCastOptions, aby rozpocząć skanowanie urządzeń.
  • Gdy użytkownik kliknie przycisk Cast, platforma wyświetli okno Cast z listą wykrytych urządzeń przesyłających.
  • Gdy użytkownik wybierze urządzenie przesyłające, platforma próbuje na nim uruchomić aplikację odbiornika internetowego.
  • Platforma wywołuje wywołania zwrotne w aplikacji nadawcy, aby potwierdzić, że aplikacja Web Receiver została uruchomiona.
  • Platforma tworzy kanał komunikacji między wysyłającym a aplikacjami odbiornika.
  • Platforma używa kanału komunikacji do wczytywania i kontrolowania odtwarzania multimediów na odbiorniku internetowym.
  • Platforma synchronizuje stan odtwarzania multimediów między nadawcą a odbiornikiem internetowym: gdy użytkownik wykonuje działania w interfejsie nadawcy, platforma przekazuje żą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, by odłączyć urządzenie przesyłające, platforma odłączy aplikację wysyłającą od odbiornika internetowego.

Aby rozwiązać problemy z nadawcą, musisz włączyć rejestrowanie.

Pełną listę wszystkich klas, metod i zdarzeń występujących na platformie Google Cast na iOS znajdziesz w dokumentacji interfejsu Google Cast API na iOS. W kolejnych sekcjach znajdziesz instrukcje integrowania przesyłania z aplikacją na iOS.

Metody nawiązywania połączeń z wątku głównego

Inicjowanie kontekstu przesyłania

Platforma Cast ma globalny obiekt typu singleton – GCKCastContext, który koordynuje wszystkie działania platformy. Aby można było prawidłowo aktywować automatyczne wznawianie sesji przy ponownym uruchomieniu aplikacji nadawcy, ten obiekt musi zostać zainicjowany na wczesnym etapie cyklu życia aplikacji, zwykle w metodzie -[application:didFinishLaunchingWithOptions:] jej przedstawiciela.

Podczas inicjowania GCKCastContext należy podać obiekt GCKCastOptions. Ta klasa zawiera opcje, które mają wpływ na działanie platformy. Najważniejszy z nich jest identyfikator aplikacji odbiornika internetowego, który służy do filtrowania wyników wykrywania i uruchamiania aplikacji Web Receiver po uruchomieniu sesji przesyłania.

Metoda -[application:didFinishLaunchingWithOptions:] to także dobre miejsce do skonfigurowania przedstawiciela logowania, który będzie odbierał komunikaty logowania z platformy. Mogą one 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 Cast SDK na iOS zawiera te widżety zgodne z listą kontrolną projektowania przesyłania:

  • Nakładka wprowadzająca: klasa GCKCastContext ma metodę presentCastInstructionsViewControllerOnceWithCastButton, której można użyć do wyróżnienia przycisku Cast, gdy odbiornik internetowy będzie dostępny. Aplikacja nadawcy może dostosować tekst, położenie tekstu tytułu i przycisk Odrzuć.

  • Przycisk Cast: w przypadku pakietu SDK Cast na iOS 4.6.0 przycisk przesyłania jest zawsze widoczny, gdy urządzenie nadawcy jest połączone z Wi-Fi. Gdy użytkownik po raz pierwszy kliknie przycisk Cast po uruchomieniu aplikacji, pojawi się okno uprawnień, które umożliwia przyznanie aplikacji dostępu do urządzeń w sieci lokalnej przez sieć lokalną. Gdy użytkownik kliknie przycisk przesyłania, wyświetli się okno przesyłania z listą wykrytych urządzeń. Gdy użytkownik naciśnie przycisk przesyłania po podłączeniu urządzenia, wyświetlą się aktualne metadane multimediów (takie jak tytuł, nazwa studia nagraniowego i obraz miniatury) lub użytkownik może 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 przyczynach braku urządzeń i sposobach rozwiązania tego problemu.

  • Mini Kontroler: gdy użytkownik przesyła treści i opuścił bieżącą stronę treści lub rozwinął kontroler na inny ekran w aplikacji nadawcy, u dołu ekranu jest wyświetlany minikontroler, który pozwala zobaczyć metadane aktualnie przesyłanych multimediów i sterować odtwarzaniem.

  • Rozszerzony kontroler: gdy użytkownik przesyła treści, po kliknięciu powiadomienia o multimediach lub minikontrolerze uruchamia się rozwinięty kontroler, który wyświetla metadane aktualnie odtwarzanych multimediów i zawiera kilka przycisków do sterowania odtwarzaniem.

Dodaj przycisk Cast

Platforma udostępnia komponent przycisku Cast jako podklasę UIButton. Aby dodać go do paska tytułu aplikacji, umieść go w elemencie UIBarButtonItem. Typowa podklasa UIViewController może instalować 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 kliknięcie przycisku powoduje otwarcie okna przesyłania dostępnego w ramce.

Element GCKUICastButton można też dodać bezpośrednio do scenorysu.

Skonfiguruj wykrywanie urządzeń

W ramach platformy wykrywanie urządzeń odbywa się automatycznie. Nie musisz wyraźnie rozpoczynać ani zatrzymywać procesu wykrywania, chyba że wdrożysz niestandardowy interfejs.

Wykrywaniem w ramach platformy zarządza klasa GCKDiscoveryManager, która jest właściwością GCKCastContext. Platforma udostępnia domyślny komponent okna przesyłania do wyboru urządzenia i sterowania nim. Lista urządzeń jest uporządkowana leksykograficznie według nazwy przyjaznej dla urządzeń.

Jak działa zarządzanie sesjami

W ramach pakietu Cast SDK wprowadzamy koncepcję sesji przesyłania, która obejmuje etapy łączenia się z urządzeniem, uruchamiania (lub dołączania) aplikacji odbiornika internetowego oraz łą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.

Sesjami zarządza klasa GCKSessionManager, która jest właściwością GCKCastContext. Poszczególne sesje są reprezentowane przez podklasy klasy GCKSession: na przykład GCKCastSession reprezentuje sesje z urządzeniami przesyłającymi. Aktualnie aktywną sesję przesyłania (jeśli jest aktywna) możesz uzyskać za pomocą właściwości currentCastSession elementu GCKSessionManager.

Interfejs GCKSessionManagerListener może służyć do monitorowania zdarzeń sesji, takich jak utworzenie, zawieszenie, wznowienie i zakończenie sesji. Platforma automatycznie zawiesza sesje, gdy aplikacja nadawcy działa w tle, i próbuje je wznowić, gdy aplikacja wróci na pierwszy plan (lub zostanie ponownie uruchomiona po nieoczekiwanym lub nagłym zakończeniu sesji, gdy sesja była aktywna).

Jeśli używane jest okno Cast, sesje są tworzone i automatycznie przerywane w odpowiedzi na gesty użytkownika. W przeciwnym razie aplikacja może rozpoczynać i kończyć sesje za pomocą metod na stronie GCKSessionManager.

Jeśli aplikacja musi przeprowadzać specjalne przetwarzanie w odpowiedzi na zdarzenia cyklu życia sesji, może zarejestrować co najmniej 1 instancję GCKSessionManagerListener za pomocą GCKSessionManager. GCKSessionManagerListener to protokół, który definiuje wywołania zwrotne dla zdarzeń takich jak początek i koniec sesji.

Przenoszenie strumienia

Zachowanie stanu sesji jest podstawą przenoszenia strumienia, w ramach której 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 zatrzymują się na jednym urządzeniu (źródle) i na drugim (miejscu docelowym). Każde urządzenie przesyłające z najnowszym oprogramowaniem może służyć jako źródło lub miejsce docelowe podczas przesyłania strumienia.

Aby pobrać 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 Przesyłanie strumienia w internetowym odbiorniku.

Automatyczne ponowne łączenie

Platforma Cast dodaje logikę ponownego połączenia, aby automatycznie obsługiwać ponowne połączenia w wielu subtelnych przypadkach, takich jak:

  • Odzyskanie połączenia po tymczasowej utracie sieci Wi-Fi
  • Wypoczynek po uśpieniu urządzenia
  • Przywracanie aplikacji w tle
  • Przywracanie systemu w przypadku awarii aplikacji

Jak działa sterowanie multimediami

Jeśli sesja przesyłania została ustanowiona za pomocą aplikacji odbiornika internetowego, która obsługuje przestrzeń nazw multimediów, platforma automatycznie utworzy instancję GCKRemoteMediaClient, do której można 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órego można użyć do śledzenia tego żądania. Do tego obiektu można przypisać GCKRequestDelegate, aby otrzymywać powiadomienia o ostatecznym wyniku operacji.

Wystąpienie GCKRemoteMediaClient powinno być współdzielone przez wiele części aplikacji, a niektóre wewnętrzne komponenty platformy, takie jak okno Cast i ministerowanie multimediów, również mają tę instancję. Dlatego GCKRemoteMediaClient obsługuje rejestrację wielu obiektów GCKRemoteMediaClientListener.

Ustawianie metadanych multimediów

Klasa GCKMediaMetadata reprezentuje 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 obrazów 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 go w GCKRemoteMediaClient, aby wczytać multimedia do aplikacji odbiornika. Za pomocą GCKRemoteMediaClient możesz sterować aplikacją odtwarzacza na odbiorniku, np. odtwarzać, wstrzymywać i zatrzymywać odtwarzanie.

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;
}

Przeczytaj też sekcję o korzystaniu ze ścieżek multimedialnych.

Format filmu 4K

Aby określić format filmu, użyj właściwości videoInfo GCKMediaStatus do pobrania bieżącego wystąpienia GCKVideoInfo. Ta instancja zawiera typ formatu telewizora HDR oraz wysokość i szerokość w pikselach. Warianty formatu 4K są określane we właściwości hdrType za pomocą wartości wyliczeniowych GCKVideoInfoHDRType.

Dodaj minikontrolery

Zgodnie z listą kontrolną projektowania aplikacji na potrzeby przesyłania aplikacja wysyłająca powinna zapewniać trwałą funkcję kontrolną znaną jako minikontroler, która powinna być wyświetlana, gdy użytkownik opuści bieżącą stronę z treściami. 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 pokazać minikontroler.

Gdy aplikacja nadawcy odtwarza transmisję na żywo wideo lub audio, pakiet SDK automatycznie wyświetla na minikontrolerze przycisk odtwarzania/zatrzymania w miejscu przycisku odtwarzania/wstrzymania.

Aby dowiedzieć się, jak aplikacja nadawcy może konfigurować wygląd widżetów Cast, zobacz Dostosowywanie interfejsu nadawcy na iOS.

Minikontroler można dodać do aplikacji wysyłającej na 2 sposoby:

  • Pozwól platformie Cast zarządzać układem minikontrolera, dodając do niego własny kontroler widoku.
  • Możesz samodzielnie zarządzać układem widżetu minikontrolera, dodając go do istniejącego kontrolera widoku, udostępniając w scenorysie widok podrzędny.

Zawijaj za pomocą kontrolera GCKUICastContainerViewController

Pierwszym z nich jest użycie obiektu GCKUICastContainerViewController, który opakowuje inny kontroler widoku danych i dodaje GCKUIMiniMediaControlsViewController na dole. Takie podejście jest ograniczone, ponieważ nie możesz dostosowywać animacji ani konfigurować działania kontrolera widoku kontenera.

Pierwszy sposób wykonuje się zwykle w metodzie -[application:didFinishLaunchingWithOptions:] przedstawiciela 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 dotychczasowego kontrolera widoku za pomocą narzędzia createMiniMediaControlsViewController do utworzenia instancji GCKUIMiniMediaControlsViewController, a następnie dodania jej do kontrolera widoku kontenera jako widoku podrzędnego.

Skonfiguruj kontroler widoku w przekazywaniu dostępu do 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;
}

Utwórz w kontrolerze widoku głównego 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 rozszerzony kontroler

Lista kontrolna projektowania Google Cast wymaga, by aplikacja wysyłająca udostępniała rozwinięty kontroler do przesyłanych multimediów. Rozwinięty kontroler to pełnoekranowa wersja minikontrolera.

Rozwinięty kontroler umożliwia oglądanie na pełnym ekranie, co zapewnia pełną kontrolę nad zdalnym odtwarzaniem multimediów. Ten widok powinien umożliwiać aplikacji przesyłającej zarządzanie każdym aspektem sesji przesyłania, z wyjątkiem sterowania głośnością odbiornika internetowego i cyklu życia sesji (połączenie/zatrzymanie przesyłania). Podaje też wszystkie informacje o stanie sesji multimediów (grafika, tytuł, podtytuł itp.).

Funkcjonalność tego widoku jest implementowana przez klasę GCKUIExpandedMediaControlsViewController.

Najpierw musisz włączyć domyślny rozszerzony kontroler w kontekście przesyłania. Zmień uprawnienia dostępu do 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 do kontrolera widoku ten kod, aby go załadować, 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 uruchomi się też automatycznie, gdy użytkownik naciśnie minikontroler.

Gdy aplikacja nadawcy odtwarza transmisję na żywo wideo lub audio, pakiet SDK automatycznie wyświetla przycisk odtwarzania/zatrzymywania w miejscu przycisku odtwarzania/wstrzymywania na rozwiniętym kontrolerze.

Informacje o tym, jak aplikacja nadawcy może konfigurować wygląd widżetów Cast, znajdziesz w artykule Stosowanie stylów niestandardowych do aplikacji na iOS.

Sterowanie głośnością

Platforma Cast automatycznie zarządza głośnością aplikacji nadawcy. Ramka automatycznie synchronizuje się z głośnością odbiornika internetowego dla dołączonych widżetów interfejsu. Aby zsynchronizować suwak dostępny w aplikacji, użyj GCKUIDeviceVolumeController.

Regulacja głośności za pomocą przycisku fizycznego

Za pomocą fizycznych przycisków głośności na urządzeniu nadawcy możesz zmieniać głośność sesji przesyłania na odbiorniku internetowym za pomocą flagi physicalVolumeButtonsWillControlDeviceVolume na GCKCastOptions, która jest ustawiona w 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ą obsługiwać wszystkie błędne wywołania zwrotne i określać najlepszą odpowiedź dla każdego etapu cyklu życia przesyłania. Aplikacja może wyświetlić użytkownikowi okna z błędami lub zakończyć sesję przesyłania.

Logowanie

GCKLogger to pojedynczy pojedynczy element używany 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 logów w postaci komunikatów debugowania, błędów i ostrzeżeń. Ułatwiają one debugowanie i są przydatne podczas rozwiązywania problemów. Domyślnie dane wyjściowe logów są ukryte, ale jeśli przypiszesz GCKLoggerDelegate, aplikacja nadawcy może otrzymywać 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ć także komunikaty debugowania i komunikaty szczegółowe, dodaj ten wiersz do kodu po ustawieniu przedstawiciela (jak pokazano poniżej):

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 generowane przez GCKLogger. Ustaw minimalny poziom logowania poszczególnych zajęć, 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 glob, na przykład GCKUI\* i GCK\*Session.