Zintegruj przesyłanie z aplikacją na iOS

Ten przewodnik dla programistów opisuje, jak dodać obsługę Google Cast do aplikacji wysyłającej na iOS za pomocą pakietu iOS Sender SDK.

Urządzenie mobilne lub laptop to nadawca, który steruje odtwarzaniem, a urządzenie Google Cast to odbiornik, który wyświetla treści na telewizorze.

Platforma nadawcy to binarna biblioteka klas Cast i powiązane z nią zasoby dostępne w czasie działania na urządzeniu wysyłającym. Aplikacja nadawcy lub aplikacja Cast to aplikacja działająca również na urządzeniu nadawcy. Aplikacja odbiornika internetowego to aplikacja HTML działająca na odbiorniku internetowym.

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

Przepływ w aplikacji

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

  • Platforma Cast rozpoczyna GCKDiscoveryManager na podstawie właściwości podanych w GCKCastOptions, aby rozpocząć skanowanie w poszukiwaniu urządzeń.
  • Gdy użytkownik kliknie przycisk przesyłania, platforma wyświetli okno przesyłania z listą wykrytych urządzeń obsługujących przesyłanie.
  • Gdy użytkownik wybierze urządzenie przesyłające, platforma spróbuje uruchomić na nim aplikację Web Receiver.
  • Platforma wywołuje wywołania zwrotne w aplikacji nadawcy, aby potwierdzić uruchomienie aplikacji Web Receiver.
  • Framework tworzy kanał komunikacji między nadawcą a aplikacjami Web Receiver.
  • Platforma korzysta z kanału komunikacji do wczytywania multimediów i sterowania ich odtwarzaniem 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 te żądania sterowania multimediami do odbiornika internetowego, a gdy odbiornik internetowy wysyła aktualizacje stanu multimediów, platforma aktualizuje stan interfejsu nadawcy.
  • Gdy użytkownik kliknie przycisk przesyłania, aby odłączyć się od urządzenia Cast, platforma odłączy aplikację wysyłającą od odbiornika internetowego.

Aby rozwiązać problem z nadawcą, musisz włączyć logowanie.

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 instrukcje integracji Cast z aplikacją na iOS.

Wywoływanie metod z wątku głównego

Inicjowanie kontekstu Cast

Platforma Cast ma globalny obiekt singleton, GCKCastContext, który koordynuje wszystkie działania platformy. Ten obiekt musi zostać zainicjowany na wczesnym etapie cyklu życia aplikacji, zwykle w metodzie -[application:didFinishLaunchingWithOptions:] delegata aplikacji, aby automatyczne wznawianie sesji po ponownym uruchomieniu aplikacji wysyłającej mogło się prawidłowo uruchamiać.

Podczas inicjowania GCKCastContext należy podać obiekt GCKCastOptions. Ta klasa zawiera opcje, które wpływają na działanie platformy. Najważniejszy z nich to identyfikator aplikacji odbiornika internetowego, który służy do filtrowania wyników wykrywania i uruchamiania aplikacji odbiornika internetowego po rozpoczęciu sesji Cast.

Metoda -[application:didFinishLaunchingWithOptions:] to również dobre miejsce na skonfigurowanie delegata logowania, który będzie odbierać wiadomości logowania z frameworka. 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 UX Cast

Pakiet Cast SDK na iOS udostępnia te widżety zgodne z listą kontrolną dotyczącą projektowania Cast:

  • Nakładka wprowadzająca: klasa GCKCastContext ma metodę presentCastInstructionsViewControllerOnceWithCastButton, której można użyć, aby wyróżnić przycisk przesyłania przy pierwszym udostępnieniu odbiornika internetowego. Aplikacja nadawcy może dostosować tekst, położenie tekstu tytułu i przycisku Zamknij.

  • Przycisk przesyłania: od wersji 4.6.0 pakietu SDK nadawcy Cast na iOS przycisk przesyłania jest zawsze widoczny, gdy urządzenie nadawcy jest połączone z Wi-Fi. Gdy użytkownik po raz pierwszy kliknie przycisk przesyłania po początkowym uruchomieniu aplikacji, pojawi się okno dialogowe z prośbą o przyznanie uprawnień. Umożliwi to użytkownikowi przyznanie aplikacji dostępu do urządzeń w sieci lokalnej. Gdy użytkownik naciśnie przycisk przesyłania, wyświetli się okno przesyłania, w którym będą widoczne wykryte urządzenia. Gdy użytkownik kliknie przycisk przesyłania, gdy urządzenie jest połączone, wyświetlą się bieżące metadane multimediów (np. tytuł, nazwa studia nagrań i miniatura) lub pojawi się możliwość odłączenia się od urządzenia przesyłającego. Gdy użytkownik kliknie przycisk przesyłania, a nie będzie dostępnych żadnych urządzeń, wyświetli się ekran z informacjami o tym, dlaczego nie można znaleźć urządzeń, oraz o tym, jak rozwiązać ten problem.

  • Mini Controller: gdy użytkownik przesyła treści i opuści bieżącą stronę z treściami lub rozszerzony kontroler, aby przejść do innego ekranu w aplikacji nadawcy, u dołu ekranu wyświetla się Mini Controller, który umożliwia użytkownikowi wyświetlanie metadanych przesyłanych obecnie multimediów i sterowanie odtwarzaniem.

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

Dodawanie przycisku Cast

Platforma udostępnia komponent przycisku Cast jako podklasę UIButton. Można go dodać do paska tytułu aplikacji, umieszczając go w tagu UIBarButtonItem. Typowa podklasaUIViewController 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 przycisku spowoduje otwarcie okna przesyłania dostarczonego przez platformę.

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

Konfigurowanie wykrywania 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.

Wykrywanie w ramach platformy jest zarządzane przez klasę GCKDiscoveryManager, która jest właściwością klasy GCKCastContext. Platforma udostępnia domyślny komponent okna Cast do wyboru i sterowania urządzeniami. Lista urządzeń jest uporządkowana leksykograficznie według przyjaznej nazwy urządzenia.

Jak działa zarządzanie sesjami

Pakiet Cast SDK wprowadza pojęcie sesji Cast, której utworzenie łączy kroki połączenia z urządzeniem, uruchomienia (lub dołączenia do) aplikacji Web Receiver, połączenia z tą aplikacją i inicjowania kanału sterowania multimediami. Więcej informacji o sesjach Cast 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, np. GCKCastSession reprezentuje sesje na urządzeniach przesyłających. Możesz uzyskać dostęp do aktualnie aktywnej sesji Cast (jeśli taka istnieje) jako właściwości currentCastSession obiektu GCKSessionManager.

Interfejs GCKSessionManagerListener umożliwia monitorowanie zdarzeń sesji, takich jak tworzenie, zawieszanie, wznawianie i zakończenie sesji. Platforma automatycznie wstrzymuje sesje, gdy aplikacja wysyłająca przechodzi do działania w tle, i próbuje je wznowić, gdy aplikacja wraca na pierwszy plan (lub jest ponownie uruchamiana po nieprawidłowym/nagłym zakończeniu działania, gdy sesja była aktywna).

Jeśli używane jest okno przesyłania, sesje są tworzone i zamykane automatycznie w odpowiedzi na gesty użytkownika. W przeciwnym razie aplikacja może rozpoczynać i kończyć sesje w sposób jawny za pomocą metod w GCKSessionManager.

Jeśli aplikacja musi wykonać specjalne przetwarzanie w odpowiedzi na zdarzenia związane z cyklem życia sesji, może zarejestrować co najmniej 1 instancję GCKSessionManagerListenerGCKSessionManager. GCKSessionManagerListener to protokół, który definiuje wywołania zwrotne dla zdarzeń takich jak rozpoczęcie i zakończenie sesji itp.

Przeniesienie odtwarzania

Zachowanie stanu sesji jest podstawą przenoszenia strumieni, dzięki czemu użytkownicy mogą przenosić istniejące strumienie audio i wideo między urządzeniami za pomocą poleceń głosowych, aplikacji Google Home lub inteligentnych ekranów. Odtwarzanie multimediów zatrzymuje się na jednym urządzeniu (źródłowym) i jest kontynuowane na innym (docelowym). Każde urządzenie Cast z najnowszym oprogramowaniem może być źródłem lub miejscem docelowym podczas przesyłania strumieniowego.

Aby uzyskać nowe urządzenie docelowe podczas przenoszenia strumienia, użyj właściwości GCKCastSession#device w wywołaniu zwrotnym [sessionManager:didResumeCastSession:].

Więcej informacji znajdziesz w artykule Przesyłanie strumieniowe na odbiorniku internetowym.

Automatyczne ponowne łączenie

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

  • Przywracanie działania po tymczasowej utracie połączenia Wi-Fi
  • Przywracanie działania po przejściu urządzenia w tryb uśpienia
  • Przywracanie aplikacji po przejściu do tła
  • Przywracanie po awarii aplikacji

Jak działa sterowanie multimediami

Jeśli sesja Cast zostanie nawiązana z aplikacją odbiornika internetowego, która obsługuje przestrzeń nazw multimediów, platforma automatycznie utworzy instancję GCKRemoteMediaClient. Można uzyskać do niej dostęp jako do właściwości 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.

Oczekuje się, że instancja GCKRemoteMediaClient może być udostępniana przez wiele części aplikacji, a niektóre wewnętrzne komponenty platformy, takie jak okno Cast i mini odtwarzacz multimediów, rzeczywiście udostępniają tę instancję. W tym celu usługa GCKRemoteMediaClient umożliwia rejestrację wielu GCKRemoteMediaClientListener.

Ustawianie metadanych multimediów

Klasa GCKMediaMetadata reprezentuje informacje o elemencie multimedialnym, który chcesz przesłać. Poniższy przykład tworzy nową instancję GCKMediaMetadata filmu i ustawia tytuł, podtytuł, nazwę studia nagrań 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]];

Więcej informacji o używaniu obrazów z metadanymi multimediów znajdziesz w sekcji Wybieranie obrazów i ich buforowanie.

Wczytaj multimedia

Aby wczytać element multimedialny, utwórz instancję GCKMediaInformation na podstawie metadanych multimediów. Następnie pobierz bieżący GCKCastSession i użyj jego GCKRemoteMediaClient, aby załadować multimedia w aplikacji odbiornika. Możesz wtedy użyć GCKRemoteMediaClient do sterowania aplikacją odtwarzacza multimediów działającą na odbiorniku, np. do odtwarzania, wstrzymywania i zatrzymywania.

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ą dotyczącą używania ścieżek multimedialnych.

Format wideo 4K

Aby określić format wideo, użyj właściwości videoInfo elementu GCKMediaStatus, aby uzyskać bieżącą instancję elementu GCKVideoInfo. Ten obiekt zawiera typ formatu HDR TV oraz wysokość i szerokość w pikselach. Warianty formatu 4K są wskazywane we właściwości hdrType przez wartości wyliczeniowe GCKVideoInfoHDRType.

Dodawanie mini kontrolerów

Zgodnie z listą kontrolną dotyczącą projektowania Cast aplikacja wysyłająca powinna udostępniać stały element sterujący, czyli minikontroler, który powinien pojawiać się, gdy użytkownik opuści stronę z bieżącymi treściami. Mini kontroler zapewnia natychmiastowy dostęp i widoczne przypomnienie o bieżącej sesji Cast.

Platforma Cast udostępnia pasek sterowaniaGCKUIMiniMediaControlsViewController, który można dodać do scen, w których chcesz wyświetlać minikontroler.

Gdy aplikacja wysyłająca odtwarza transmisję na żywo wideo lub audio, pakiet SDK automatycznie wyświetla przycisk odtwarzania/zatrzymywania zamiast przycisku odtwarzania/pauzy w minikontrolerze.

Więcej informacji o tym, jak aplikacja wysyłająca może konfigurować wygląd widżetów Cast, znajdziesz w sekcji Dostosowywanie interfejsu aplikacji wysyłającej na iOS.

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

  • Pozwól platformie Cast zarządzać układem minikontrolera, owijając istniejący kontroler widoku własnym kontrolerem widoku.
  • Samodzielnie zarządzaj układem widżetu minikontrolera, dodając go do istniejącego kontrolera widoku, podając widok podrzędny w pliku storyboard.

Zawijanie za pomocą GCKUICastContainerViewController

Pierwszy sposób polega na użyciu GCKUICastContainerViewController który otacza inny kontroler widoku i dodaje GCKUIMiniMediaControlsViewController u dołu. To podejście ma jednak ograniczenia, ponieważ nie można dostosować animacji ani skonfigurować działania kontrolera widoku kontenera.

Pierwszy sposób jest zwykle realizowany 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

Osadzanie w istniejącym kontrolerze widoku

Drugi sposób polega na dodaniu minikontrolera bezpośrednio do istniejącego kontrolera widoku za pomocą funkcji createMiniMediaControlsViewController, aby utworzyć instancję GCKUIMiniMediaControlsViewController, a następnie dodać ją do kontrolera widoku kontenera jako podwidok.

Skonfiguruj kontroler widoku w delegacie 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 głównym kontrolerze widoku utwórz instancję GCKUIMiniMediaControlsViewController i dodaj ją do kontrolera widoku kontenera jako podrzędny widok:

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

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

Swift
  func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController,
                                       shouldAppear _: Bool) {
    updateControlBarsVisibility()
  }
Objective-C
- (void)miniMediaControlsViewController:
            (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController
                           shouldAppear:(BOOL)shouldAppear {
  [self updateControlBarsVisibility];
}

Dodawanie rozszerzonego kontrolera

Lista kontrolna projektu Google Cast wymaga, aby aplikacja wysyłająca udostępniała rozszerzony kontroler do przesyłanych multimediów. Rozwinięty kontroler to wersja mini kontrolera na pełnym ekranie.

Rozwinięty kontroler to widok pełnoekranowy, który zapewnia pełną kontrolę nad odtwarzaniem multimediów na urządzeniu zdalnym. Ten widok powinien umożliwiać aplikacji do przesyłania zarządzanie każdym aspektem sesji przesyłania, z wyjątkiem sterowania głośnością odbiornika internetowego i cyklem życia sesji (łączenie i zatrzymywanie przesyłania). Zawiera też wszystkie informacje o stanie sesji multimedialnej (okładka, tytuł, podtytuł itp.).

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

Najpierw musisz włączyć domyślny rozszerzony kontroler w kontekście przesyłania. Zmodyfikuj delegata aplikacji, aby włączyć domyślny rozszerzony 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 wczytać 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 zostanie też automatycznie uruchomiony, gdy użytkownik kliknie minikontroler.

Gdy aplikacja wysyłająca odtwarza transmisję na żywo wideo lub audio, pakiet SDK automatycznie wyświetla przycisk odtwarzania/zatrzymywania zamiast przycisku odtwarzania/wstrzymywania na rozwiniętym kontrolerze.

Więcej informacji o tym, jak aplikacja wysyłająca może skonfigurować wygląd widżetów Cast, znajdziesz w artykule Stosowanie niestandardowych stylów w aplikacji na iOS.

Sterowanie głośnością

Platforma Cast automatycznie zarządza głośnością aplikacji wysyłającej. Platforma automatycznie synchronizuje się z głośnością odbiornika internetowego w przypadku dostarczonych widżetów interfejsu. Aby zsynchronizować suwak udostępniony przez aplikację, użyj GCKUIDeviceVolumeController.

Sterowanie głośnością za pomocą przycisków fizycznych

Fizyczne przyciski głośności na urządzeniu wysyłającym mogą służyć do zmiany głośności sesji Cast na odbiorniku internetowym za pomocą flagi physicalVolumeButtonsWillControlDeviceVolumeGCKCastOptions, 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ługuj błędy

Aplikacje wysyłające muszą obsługiwać wszystkie wywołania zwrotne błędów i określać najlepszą odpowiedź na każdym etapie cyklu życia Cast. Aplikacja może wyświetlać użytkownikowi okna dialogowe z błędami lub zakończyć sesję Cast.

Logowanie

GCKLogger to pojedynczy obiekt używany przez platformę do logowania. Użyj GCKLoggerDelegate, aby dostosować sposób obsługi wiadomości dziennika.

Za pomocą GCKLogger pakiet SDK generuje dane logowania w postaci komunikatów debugowania, błędów i ostrzeżeń. Te komunikaty dziennika ułatwiają debugowanie i są przydatne podczas rozwiązywania problemów i ich identyfikowania. Domyślnie dane wyjściowe dziennika są pomijane, ale przypisując wartość GCKLoggerDelegate, aplikacja wysyłająca może otrzymywać te komunikaty 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ć też komunikaty debugowania i szczegółowe, dodaj ten wiersz do kodu po ustawieniu delegata (pokazanego wcześniej):

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 dla każdej klasy, np.:

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 dosłownymi lub wzorcami glob, np. GCKUI\*GCK\*Session.