Интегрируйте Cast в свое приложение для iOS

В этом руководстве для разработчиков описывается, как добавить поддержку Google Cast в приложение iOS Sender с помощью iOS Sender SDK.

Мобильное устройство или ноутбук является отправителем , который управляет воспроизведением, а устройство Google Cast — приемником , которое отображает контент на телевизоре.

Фреймворк отправителя относится к двоичному файлу библиотеки классов Cast и связанным ресурсам, присутствующим во время выполнения на отправителе. Приложение отправителя или приложение Cast относится к приложению, также работающему на отправителе. Приложение Web Receiver относится к приложению HTML, работающему на Web Receiver.

Фреймворк отправителя использует асинхронную конструкцию обратного вызова для информирования приложения-отправителя о событиях и для перехода между различными состояниями жизненного цикла приложения Cast.

Поток приложений

Следующие шаги описывают типичный высокоуровневый поток выполнения для приложения-отправителя iOS:

  • Фреймворк Cast запускает GCKDiscoveryManager на основе свойств, предоставленных в GCKCastOptions , чтобы начать сканирование устройств.
  • Когда пользователь нажимает кнопку Cast, фреймворк отображает диалоговое окно Cast со списком обнаруженных устройств Cast.
  • Когда пользователь выбирает устройство Cast, фреймворк пытается запустить приложение Web Receiver на устройстве Cast.
  • Фреймворк вызывает обратные вызовы в приложении-отправителе, чтобы подтвердить запуск приложения Web Receiver.
  • Фреймворк создает канал связи между отправителем и приложениями Web Receiver.
  • Фреймворк использует канал связи для загрузки и управления воспроизведением мультимедиа на веб-приемнике.
  • Фреймворк синхронизирует состояние воспроизведения мультимедиа между отправителем и веб-получателем: когда пользователь выполняет действия пользовательского интерфейса отправителя, фреймворк передает эти запросы на управление мультимедиа веб-получателю, а когда веб-получатель отправляет обновления статуса мультимедиа, фреймворк обновляет состояние пользовательского интерфейса отправителя.
  • Когда пользователь нажимает кнопку Cast, чтобы отключиться от устройства Cast, фреймворк отключит приложение-отправитель от веб-приемника.

Для устранения неполадок отправителя вам необходимо включить ведение журнала .

Полный список всех классов, методов и событий в фреймворке Google Cast iOS см. в Справочнике Google Cast iOS API . В следующих разделах описываются шаги по интеграции Cast в ваше приложение iOS.

Вызов методов из основного потока

Инициализируйте контекст Cast

Фреймворк Cast имеет глобальный одноэлементный объект GCKCastContext , который координирует все действия фреймворка. Этот объект должен быть инициализирован на ранней стадии жизненного цикла приложения, обычно в методе -[application:didFinishLaunchingWithOptions:] делегата приложения, чтобы автоматическое возобновление сеанса при перезапуске приложения-отправителя могло сработать должным образом.

Объект GCKCastOptions должен быть предоставлен при инициализации GCKCastContext . Этот класс содержит параметры, которые влияют на поведение фреймворка. Наиболее важным из них является идентификатор приложения Web Receiver, который используется для фильтрации результатов обнаружения и запуска приложения Web Receiver при запуске сеанса Cast.

Метод -[application:didFinishLaunchingWithOptions:] также является хорошим местом для настройки делегата регистрации для получения сообщений регистрации от фреймворка. Они могут быть полезны для отладки и устранения неполадок.

Быстрый
@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

Виджеты Cast UX

Cast iOS SDK предоставляет следующие виджеты, которые соответствуют контрольному списку дизайна Cast:

  • Вводный оверлей : класс GCKCastContext имеет метод presentCastInstructionsViewControllerOnceWithCastButton , который можно использовать для подсветки кнопки Cast в первый раз, когда доступен Web Receiver. Приложение-отправитель может настроить текст, положение текста заголовка и кнопку Dismiss.

  • Кнопка Cast : Начиная с версии Cast iOS sender SDK 4.6.0, кнопка cast всегда видна, когда устройство-отправитель подключено к Wi-Fi. Когда пользователь впервые нажимает кнопку Cast после первоначального запуска приложения, появляется диалоговое окно разрешений, чтобы пользователь мог предоставить приложению доступ к локальной сети к устройствам в сети. Впоследствии, когда пользователь нажимает кнопку cast, отображается диалоговое окно cast, в котором перечислены обнаруженные устройства. Когда пользователь нажимает кнопку cast, когда устройство подключено, оно отображает текущие метаданные мультимедиа (такие как название, название студии звукозаписи и миниатюрное изображение) или позволяет пользователю отключиться от устройства cast. Когда пользователь нажимает кнопку cast, когда нет доступных устройств, отображается экран, предоставляющий пользователю информацию о том, почему устройства не найдены, и как устранить неполадки.

  • Мини-контроллер : когда пользователь транслирует контент и переходит со страницы текущего контента или расширенного контроллера на другой экран в приложении-отправителе, в нижней части экрана отображается мини-контроллер, позволяющий пользователю видеть метаданные текущего транслируемого медиаконтента и управлять воспроизведением.

  • Расширенный контроллер : если пользователь транслирует контент, нажав на уведомление о мультимедиа или мини-контроллер, запустится расширенный контроллер, который отображает метаданные воспроизводимого в данный момент мультимедиа и предоставляет несколько кнопок для управления воспроизведением мультимедиа.

Добавить кнопку трансляции

Фреймворк предоставляет компонент кнопки Cast как подкласс UIButton . Его можно добавить в заголовок приложения, обернув его в UIBarButtonItem . Типичный подкласс UIViewController может установить кнопку Cast следующим образом:

Быстрый
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];

По умолчанию нажатие кнопки открывает диалоговое окно Cast, предоставляемое фреймворком.

GCKUICastButton также можно добавить непосредственно в раскадровку.

Настроить обнаружение устройств

В рамках платформы обнаружение устройств происходит автоматически. Нет необходимости явно запускать или останавливать процесс обнаружения, если только вы не реализуете пользовательский интерфейс.

Discovery в фреймворке управляется классом GCKDiscoveryManager , который является свойством GCKCastContext . Фреймворк предоставляет компонент диалога Cast по умолчанию для выбора и управления устройством. Список устройств упорядочен лексикографически по понятному имени устройства.

Как работает управление сеансом

Cast SDK представляет концепцию сеанса Cast, установление которого объединяет шаги подключения к устройству, запуска (или присоединения) приложения Web Receiver, подключения к этому приложению и инициализации канала управления мультимедиа. См. руководство по жизненному циклу приложения Web Receiver для получения дополнительной информации о сеансах Cast и жизненном цикле Web Receiver.

Сеансы управляются классом GCKSessionManager , который является свойством GCKCastContext . Отдельные сеансы представлены подклассами класса GCKSession : например, GCKCastSession представляет сеансы с устройствами Cast. Вы можете получить доступ к текущему активному сеансу Cast (если таковой имеется) как к свойству currentCastSession GCKSessionManager .

Интерфейс GCKSessionManagerListener может использоваться для мониторинга событий сеанса, таких как создание сеанса, приостановка, возобновление и завершение. Фреймворк автоматически приостанавливает сеансы, когда приложение-отправитель переходит в фоновый режим, и пытается возобновить их, когда приложение возвращается на передний план (или перезапускается после ненормального/резкого завершения работы приложения, пока сеанс был активен).

Если используется диалог Cast, то сеансы создаются и автоматически завершаются в ответ на жесты пользователя. В противном случае приложение может запускать и завершать сеансы явно с помощью методов GCKSessionManager .

Если приложению необходимо выполнить специальную обработку в ответ на события жизненного цикла сеанса, оно может зарегистрировать один или несколько экземпляров GCKSessionManagerListener с помощью GCKSessionManager . GCKSessionManagerListener — это протокол, который определяет обратные вызовы для таких событий, как начало сеанса, конец сеанса и т. д.

Потоковая передача

Сохранение состояния сеанса является основой потоковой передачи, где пользователи могут перемещать существующие аудио- и видеопотоки между устройствами с помощью голосовых команд, приложения Google Home или интеллектуальных дисплеев. Медиа останавливается на одном устройстве (источнике) и продолжается на другом (адресате). Любое устройство Cast с последней версией прошивки может служить источником или адресатом потоковой передачи.

Чтобы получить новое целевое устройство во время передачи потока, используйте свойство GCKCastSession#device во время обратного вызова [sessionManager:didResumeCastSession:] .

Более подробную информацию см. в разделе Передача потока на веб-приемнике .

Автоматическое переподключение

Фреймворк Cast добавляет логику повторного подключения для автоматической обработки повторного подключения во многих сложных случаях, таких как:

  • Восстановление после временной потери WiFi
  • Выход из спящего режима устройства
  • Восстановление после перевода приложения в фоновый режим
  • Восстановление в случае сбоя приложения

Как работает контроль над СМИ

Если сеанс Cast установлен с приложением Web Receiver, которое поддерживает пространство имен мультимедиа, экземпляр GCKRemoteMediaClient будет создан фреймворком автоматически; к нему можно получить доступ как к свойству remoteMediaClient экземпляра GCKCastSession .

Все методы GCKRemoteMediaClient , которые отправляют запросы в Web Receiver, возвращают объект GCKRequest , который можно использовать для отслеживания этого запроса. GCKRequestDelegate можно назначить этому объекту для получения уведомлений о конечном результате операции.

Ожидается, что экземпляр GCKRemoteMediaClient может совместно использоваться несколькими частями приложения, и, действительно, некоторые внутренние компоненты фреймворка, такие как диалог Cast и мини-элементы управления мультимедиа, совместно используют экземпляр. С этой целью GCKRemoteMediaClient поддерживает регистрацию нескольких GCKRemoteMediaClientListener s.

Установить метаданные мультимедиа

Класс GCKMediaMetadata представляет информацию о медиа-элементе, который вы хотите транслировать. Следующий пример создает новый экземпляр GCKMediaMetadata фильма и задает заголовок, подзаголовок, название студии звукозаписи и два изображения.

Быстрый
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]];

Подробнее об использовании изображений с метаданными мультимедиа читайте в разделе «Выбор и кэширование изображений» .

Загрузить медиа

Чтобы загрузить элемент мультимедиа, создайте экземпляр GCKMediaInformation , используя метаданные мультимедиа. Затем получите текущий GCKCastSession и используйте его GCKRemoteMediaClient для загрузки мультимедиа в приложение-приемник. Затем вы можете использовать GCKRemoteMediaClient для управления приложением медиаплеера, запущенным на приемнике, например для воспроизведения, паузы и остановки.

Быстрый
let url = URL.init(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")
guard let mediaURL = url else {
  print("invalid mediaURL")
  return
}

let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: mediaURL)
mediaInfoBuilder.streamType = GCKMediaStreamType.none;
mediaInfoBuilder.contentType = "video/mp4"
mediaInfoBuilder.metadata = metadata;
mediaInformation = mediaInfoBuilder.build()

guard let mediaInfo = mediaInformation else {
  print("invalid mediaInformation")
  return
}

if let request = sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInfo) {
  request.delegate = self
}
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;
}

См. также раздел об использовании медиа-треков .

Формат видео 4K

Чтобы определить, какой видеоформат у ваших медиафайлов, используйте свойство videoInfo GCKMediaStatus для получения текущего экземпляра GCKVideoInfo . Этот экземпляр содержит тип формата HDR TV, а также высоту и ширину в пикселях. Варианты формата 4K указаны в свойстве hdrType с помощью значений перечисления GCKVideoInfoHDRType .

Добавить мини-контроллеры

Согласно контрольному списку дизайна Cast , приложение-отправитель должно предоставлять постоянный элемент управления, известный как мини-контроллер , который должен появляться, когда пользователь покидает текущую страницу контента. Мини-контроллер обеспечивает мгновенный доступ и видимое напоминание для текущего сеанса Cast.

Фреймворк Cast предоставляет панель управления GCKUIMiniMediaControlsViewController , которую можно добавлять в сцены, в которых вы хотите отображать мини-контроллер.

Когда приложение-отправитель воспроизводит видео- или аудиопоток в прямом эфире, SDK автоматически отображает кнопку воспроизведения/остановки вместо кнопки воспроизведения/паузы на мини-контроллере.

Информацию о том, как приложение отправителя может настраивать внешний вид виджетов Cast, см. в разделе Настройка пользовательского интерфейса iOS Sender .

Добавить мини-контроллер в приложение-отправитель можно двумя способами:

  • Позвольте платформе Cast управлять компоновкой мини-контроллера, обернув ваш существующий контроллер представления в свой собственный контроллер представления.
  • Управляйте макетом мини-виджета контроллера самостоятельно, добавляя его к существующему контроллеру представления, предоставляя подпредставление в раскадровке.

Обертка с использованием GCKUICastContainerViewController

Первый способ — использовать GCKUICastContainerViewController , который оборачивает другой контроллер представления и добавляет GCKUIMiniMediaControlsViewController внизу. Этот подход ограничен тем, что вы не можете настраивать анимацию и не можете настраивать поведение контроллера представления контейнера.

Первый способ обычно выполняется в методе -[application:didFinishLaunchingWithOptions:] делегата приложения:

Быстрый
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];
  ...

}
Быстрый
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

Встроить в существующий контроллер представления

Второй способ — добавить мини-контроллер непосредственно в существующий контроллер представления, используя createMiniMediaControlsViewController для создания экземпляра GCKUIMiniMediaControlsViewController , а затем добавить его в контроллер представления контейнера в качестве подпредставления.

Настройте контроллер представления в делегате приложения:

Быстрый
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;
}

В корневом контроллере представления создайте экземпляр GCKUIMiniMediaControlsViewController и добавьте его в контроллер представления контейнера как подпредставление:

Быстрый
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 сообщает контроллеру представления хоста, когда мини-контроллер должен быть видимым:

Быстрый
  func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController,
                                       shouldAppear _: Bool) {
    updateControlBarsVisibility()
  }
Objective-C
- (void)miniMediaControlsViewController:
            (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController
                           shouldAppear:(BOOL)shouldAppear {
  [self updateControlBarsVisibility];
}

Добавить расширенный контроллер

Контрольный список дизайна Google Cast требует, чтобы приложение-отправитель предоставляло расширенный контроллер для транслируемого медиа. Расширенный контроллер — это полноэкранная версия мини-контроллера.

Расширенный контроллер — это полноэкранный вид, который предлагает полный контроль над воспроизведением удаленного мультимедиа. Этот вид должен позволить приложению для трансляции управлять всеми управляемыми аспектами сеанса трансляции, за исключением регулировки громкости Web Receiver и жизненного цикла сеанса (подключение/остановка трансляции). Он также предоставляет всю информацию о состоянии сеанса мультимедиа (обложка, заголовок, субтитры и т. д.).

Функциональность этого представления реализована классом GCKUIExpandedMediaControlsViewController .

Первое, что вам нужно сделать, это включить расширенный контроллер по умолчанию в контексте приведения. Измените делегат приложения, чтобы включить расширенный контроллер по умолчанию:

Быстрый
func applicationDidFinishLaunching(_ application: UIApplication) {
  ..

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

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

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  ..
}

Добавьте следующий код в контроллер представления, чтобы загрузить расширенный контроллер, когда пользователь начинает трансляцию видео:

Быстрый
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];
}

Расширенный контроллер также будет запускаться автоматически, когда пользователь нажмет на мини-контроллер.

Когда приложение-отправитель воспроизводит видео- или аудиопоток в реальном времени, SDK автоматически отображает кнопку воспроизведения/остановки вместо кнопки воспроизведения/паузы в расширенном контроллере.

Подробнее о том, как приложение-отправитель может настроить внешний вид виджетов Cast, см. в разделе Применение пользовательских стилей к приложению iOS.

Регулировка громкости

Фреймворк Cast автоматически управляет громкостью для приложения-отправителя. Фреймворк автоматически синхронизируется с громкостью Web Receiver для предоставленных виджетов пользовательского интерфейса. Чтобы синхронизировать слайдер, предоставленный приложением, используйте GCKUIDeviceVolumeController .

Физическая кнопка регулировки громкости

Физические кнопки регулировки громкости на устройстве-отправителе можно использовать для изменения громкости сеанса трансляции на веб-приемнике с помощью флага physicalVolumeButtonsWillControlDeviceVolume в GCKCastOptions , который устанавливается в GCKCastContext .

Быстрый
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];

Обработка ошибок

Для приложений-отправителей очень важно обрабатывать все обратные вызовы ошибок и выбирать наилучший ответ для каждого этапа жизненного цикла Cast. Приложение может отображать пользователю диалоговые окна с ошибками или может решить завершить сеанс Cast.

Ведение журнала

GCKLogger — это синглтон, используемый фреймворком для ведения журнала. Используйте GCKLoggerDelegate для настройки способа обработки сообщений журнала.

Используя GCKLogger , SDK создает вывод журнала в виде отладочных сообщений, ошибок и предупреждений. Эти сообщения журнала помогают отладке и полезны для устранения неполадок и выявления проблем. По умолчанию вывод журнала подавлен, но, назначив GCKLoggerDelegate , приложение-отправитель может получать эти сообщения от SDK и регистрировать их в системной консоли.

Быстрый
@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

Чтобы включить отладочные и подробные сообщения, добавьте эту строку в код после установки делегата (показано ранее):

Быстрый
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;

Вы также можете фильтровать сообщения журнала, созданные GCKLogger . Установите минимальный уровень ведения журнала для каждого класса, например:

Быстрый
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;

Имена классов могут быть как буквальными именами, так и шаблонами глобальных переменных, например, GCKUI\* и GCK\*Session .

,

В этом руководстве для разработчиков описывается, как добавить поддержку Google Cast в приложение iOS Sender с помощью iOS Sender SDK.

Мобильное устройство или ноутбук является отправителем , который управляет воспроизведением, а устройство Google Cast — приемником , которое отображает контент на телевизоре.

Фреймворк отправителя относится к двоичному файлу библиотеки классов Cast и связанным ресурсам, присутствующим во время выполнения на отправителе. Приложение отправителя или приложение Cast относится к приложению, также работающему на отправителе. Приложение Web Receiver относится к приложению HTML, работающему на Web Receiver.

Фреймворк отправителя использует асинхронную конструкцию обратного вызова для информирования приложения-отправителя о событиях и для перехода между различными состояниями жизненного цикла приложения Cast.

Поток приложений

Следующие шаги описывают типичный высокоуровневый поток выполнения для приложения-отправителя iOS:

  • Фреймворк Cast запускает GCKDiscoveryManager на основе свойств, предоставленных в GCKCastOptions , чтобы начать сканирование устройств.
  • Когда пользователь нажимает кнопку Cast, фреймворк отображает диалоговое окно Cast со списком обнаруженных устройств Cast.
  • Когда пользователь выбирает устройство Cast, фреймворк пытается запустить приложение Web Receiver на устройстве Cast.
  • Фреймворк вызывает обратные вызовы в приложении-отправителе, чтобы подтвердить запуск приложения Web Receiver.
  • Фреймворк создает канал связи между отправителем и приложениями Web Receiver.
  • Фреймворк использует канал связи для загрузки и управления воспроизведением мультимедиа на веб-приемнике.
  • Фреймворк синхронизирует состояние воспроизведения мультимедиа между отправителем и веб-получателем: когда пользователь выполняет действия пользовательского интерфейса отправителя, фреймворк передает эти запросы на управление мультимедиа веб-получателю, а когда веб-получатель отправляет обновления статуса мультимедиа, фреймворк обновляет состояние пользовательского интерфейса отправителя.
  • Когда пользователь нажимает кнопку Cast, чтобы отключиться от устройства Cast, фреймворк отключит приложение-отправитель от веб-приемника.

Для устранения неполадок отправителя вам необходимо включить ведение журнала .

Полный список всех классов, методов и событий в фреймворке Google Cast iOS см. в Справочнике Google Cast iOS API . В следующих разделах описываются шаги по интеграции Cast в ваше приложение iOS.

Вызов методов из основного потока

Инициализируйте контекст Cast

Фреймворк Cast имеет глобальный одноэлементный объект GCKCastContext , который координирует все действия фреймворка. Этот объект должен быть инициализирован на ранней стадии жизненного цикла приложения, обычно в методе -[application:didFinishLaunchingWithOptions:] делегата приложения, чтобы автоматическое возобновление сеанса при перезапуске приложения-отправителя могло сработать должным образом.

Объект GCKCastOptions должен быть предоставлен при инициализации GCKCastContext . Этот класс содержит параметры, которые влияют на поведение фреймворка. Наиболее важным из них является идентификатор приложения Web Receiver, который используется для фильтрации результатов обнаружения и запуска приложения Web Receiver при запуске сеанса Cast.

Метод -[application:didFinishLaunchingWithOptions:] также является хорошим местом для настройки делегата регистрации для получения сообщений регистрации от фреймворка. Они могут быть полезны для отладки и устранения неполадок.

Быстрый
@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

Виджеты Cast UX

Cast iOS SDK предоставляет следующие виджеты, которые соответствуют контрольному списку дизайна Cast:

  • Вводный оверлей : класс GCKCastContext имеет метод presentCastInstructionsViewControllerOnceWithCastButton , который можно использовать для подсветки кнопки Cast в первый раз, когда доступен Web Receiver. Приложение-отправитель может настроить текст, положение текста заголовка и кнопку Dismiss.

  • Кнопка Cast : Начиная с версии Cast iOS sender SDK 4.6.0, кнопка cast всегда видна, когда устройство-отправитель подключено к Wi-Fi. Когда пользователь впервые нажимает кнопку Cast после первоначального запуска приложения, появляется диалоговое окно разрешений, чтобы пользователь мог предоставить приложению доступ к локальной сети к устройствам в сети. Впоследствии, когда пользователь нажимает кнопку cast, отображается диалоговое окно cast, в котором перечислены обнаруженные устройства. Когда пользователь нажимает кнопку cast, когда устройство подключено, оно отображает текущие метаданные мультимедиа (такие как название, название студии звукозаписи и миниатюрное изображение) или позволяет пользователю отключиться от устройства cast. Когда пользователь нажимает кнопку cast, когда нет доступных устройств, отображается экран, предоставляющий пользователю информацию о том, почему устройства не найдены, и как устранить неполадки.

  • Мини-контроллер : когда пользователь транслирует контент и переходит со страницы текущего контента или расширенного контроллера на другой экран в приложении-отправителе, в нижней части экрана отображается мини-контроллер, позволяющий пользователю видеть метаданные текущего транслируемого медиаконтента и управлять воспроизведением.

  • Расширенный контроллер : если пользователь транслирует контент, нажав на уведомление о мультимедиа или мини-контроллер, запустится расширенный контроллер, который отображает метаданные воспроизводимого в данный момент мультимедиа и предоставляет несколько кнопок для управления воспроизведением мультимедиа.

Добавить кнопку трансляции

Фреймворк предоставляет компонент кнопки Cast как подкласс UIButton . Его можно добавить в заголовок приложения, обернув его в UIBarButtonItem . Типичный подкласс UIViewController может установить кнопку Cast следующим образом:

Быстрый
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];

По умолчанию нажатие кнопки открывает диалоговое окно Cast, предоставляемое фреймворком.

GCKUICastButton также можно добавить непосредственно в раскадровку.

Настроить обнаружение устройств

В рамках платформы обнаружение устройств происходит автоматически. Нет необходимости явно запускать или останавливать процесс обнаружения, если только вы не реализуете пользовательский интерфейс.

Discovery в фреймворке управляется классом GCKDiscoveryManager , который является свойством GCKCastContext . Фреймворк предоставляет компонент диалога Cast по умолчанию для выбора и управления устройством. Список устройств упорядочен лексикографически по понятному имени устройства.

Как работает управление сеансом

Cast SDK представляет концепцию сеанса Cast, установление которого объединяет шаги подключения к устройству, запуска (или присоединения) приложения Web Receiver, подключения к этому приложению и инициализации канала управления мультимедиа. См. руководство по жизненному циклу приложения Web Receiver для получения дополнительной информации о сеансах Cast и жизненном цикле Web Receiver.

Сеансы управляются классом GCKSessionManager , который является свойством GCKCastContext . Отдельные сеансы представлены подклассами класса GCKSession : например, GCKCastSession представляет сеансы с устройствами Cast. Вы можете получить доступ к текущему активному сеансу Cast (если таковой имеется) как к свойству currentCastSession GCKSessionManager .

Интерфейс GCKSessionManagerListener может использоваться для мониторинга событий сеанса, таких как создание сеанса, приостановка, возобновление и завершение. Фреймворк автоматически приостанавливает сеансы, когда приложение-отправитель переходит в фоновый режим, и пытается возобновить их, когда приложение возвращается на передний план (или перезапускается после ненормального/резкого завершения работы приложения, пока сеанс был активен).

Если используется диалог Cast, то сеансы создаются и автоматически завершаются в ответ на жесты пользователя. В противном случае приложение может запускать и завершать сеансы явно с помощью методов GCKSessionManager .

Если приложению необходимо выполнить специальную обработку в ответ на события жизненного цикла сеанса, оно может зарегистрировать один или несколько экземпляров GCKSessionManagerListener с помощью GCKSessionManager . GCKSessionManagerListener — это протокол, который определяет обратные вызовы для таких событий, как начало сеанса, конец сеанса и т. д.

Потоковая передача

Сохранение состояния сеанса является основой потоковой передачи, где пользователи могут перемещать существующие аудио- и видеопотоки между устройствами с помощью голосовых команд, приложения Google Home или интеллектуальных дисплеев. Медиа останавливается на одном устройстве (источнике) и продолжается на другом (адресате). Любое устройство Cast с последней версией прошивки может служить источником или адресатом потоковой передачи.

Чтобы получить новое целевое устройство во время передачи потока, используйте свойство GCKCastSession#device во время обратного вызова [sessionManager:didResumeCastSession:] .

Более подробную информацию см. в разделе Передача потока на веб-приемнике .

Автоматическое переподключение

Фреймворк Cast добавляет логику повторного подключения для автоматической обработки повторного подключения во многих сложных случаях, таких как:

  • Восстановление после временной потери WiFi
  • Выход из спящего режима устройства
  • Восстановление после перевода приложения в фоновый режим
  • Восстановление в случае сбоя приложения

Как работает контроль над СМИ

Если сеанс Cast установлен с приложением Web Receiver, которое поддерживает пространство имен мультимедиа, экземпляр GCKRemoteMediaClient будет создан фреймворком автоматически; к нему можно получить доступ как к свойству remoteMediaClient экземпляра GCKCastSession .

Все методы GCKRemoteMediaClient , которые отправляют запросы в Web Receiver, возвращают объект GCKRequest , который можно использовать для отслеживания этого запроса. GCKRequestDelegate можно назначить этому объекту для получения уведомлений о конечном результате операции.

Ожидается, что экземпляр GCKRemoteMediaClient может совместно использоваться несколькими частями приложения, и, действительно, некоторые внутренние компоненты фреймворка, такие как диалог Cast и мини-элементы управления мультимедиа, совместно используют экземпляр. С этой целью GCKRemoteMediaClient поддерживает регистрацию нескольких GCKRemoteMediaClientListener s.

Установить метаданные мультимедиа

Класс GCKMediaMetadata представляет информацию о медиа-элементе, который вы хотите транслировать. Следующий пример создает новый экземпляр GCKMediaMetadata фильма и задает заголовок, подзаголовок, название студии звукозаписи и два изображения.

Быстрый
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]];

Подробнее об использовании изображений с метаданными мультимедиа читайте в разделе «Выбор и кэширование изображений» .

Загрузить медиа

Чтобы загрузить элемент мультимедиа, создайте экземпляр GCKMediaInformation , используя метаданные мультимедиа. Затем получите текущий GCKCastSession и используйте его GCKRemoteMediaClient для загрузки мультимедиа в приложение-приемник. Затем вы можете использовать GCKRemoteMediaClient для управления приложением медиаплеера, запущенным на приемнике, например для воспроизведения, паузы и остановки.

Быстрый
let url = URL.init(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")
guard let mediaURL = url else {
  print("invalid mediaURL")
  return
}

let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: mediaURL)
mediaInfoBuilder.streamType = GCKMediaStreamType.none;
mediaInfoBuilder.contentType = "video/mp4"
mediaInfoBuilder.metadata = metadata;
mediaInformation = mediaInfoBuilder.build()

guard let mediaInfo = mediaInformation else {
  print("invalid mediaInformation")
  return
}

if let request = sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInfo) {
  request.delegate = self
}
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;
}

См. также раздел об использовании медиа-треков .

Формат видео 4K

Чтобы определить, какой видеоформат у ваших медиафайлов, используйте свойство videoInfo GCKMediaStatus для получения текущего экземпляра GCKVideoInfo . Этот экземпляр содержит тип формата HDR TV, а также высоту и ширину в пикселях. Варианты формата 4K указаны в свойстве hdrType с помощью значений перечисления GCKVideoInfoHDRType .

Добавить мини-контроллеры

Согласно контрольному списку дизайна Cast , приложение-отправитель должно предоставлять постоянный элемент управления, известный как мини-контроллер , который должен появляться, когда пользователь покидает текущую страницу контента. Мини-контроллер обеспечивает мгновенный доступ и видимое напоминание для текущего сеанса Cast.

Фреймворк Cast предоставляет панель управления GCKUIMiniMediaControlsViewController , которую можно добавлять в сцены, в которых вы хотите отображать мини-контроллер.

Когда приложение-отправитель воспроизводит видео- или аудиопоток в прямом эфире, SDK автоматически отображает кнопку воспроизведения/остановки вместо кнопки воспроизведения/паузы на мини-контроллере.

Информацию о том, как приложение отправителя может настраивать внешний вид виджетов Cast, см. в разделе Настройка пользовательского интерфейса iOS Sender .

Добавить мини-контроллер в приложение-отправитель можно двумя способами:

  • Позвольте платформе Cast управлять компоновкой мини-контроллера, обернув ваш существующий контроллер представления в свой собственный контроллер представления.
  • Управляйте макетом мини-виджета контроллера самостоятельно, добавляя его к существующему контроллеру представления, предоставляя подпредставление в раскадровке.

Обертка с использованием GCKUICastContainerViewController

Первый способ — использовать GCKUICastContainerViewController , который оборачивает другой контроллер представления и добавляет GCKUIMiniMediaControlsViewController внизу. Этот подход ограничен тем, что вы не можете настраивать анимацию и не можете настраивать поведение контроллера представления контейнера.

Первый способ обычно выполняется в методе -[application:didFinishLaunchingWithOptions:] делегата приложения:

Быстрый
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];
  ...

}
Быстрый
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

Встроить в существующий контроллер представления

Второй способ — добавить мини-контроллер непосредственно в существующий контроллер представления, используя createMiniMediaControlsViewController для создания экземпляра GCKUIMiniMediaControlsViewController , а затем добавить его в контроллер представления контейнера в качестве подпредставления.

Настройте контроллер представления в делегате приложения:

Быстрый
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;
}

В корневом контроллере представления создайте экземпляр GCKUIMiniMediaControlsViewController и добавьте его в контроллер представления контейнера как подпредставление:

Быстрый
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 сообщает контроллеру представления хоста, когда мини-контроллер должен быть видимым:

Быстрый
  func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController,
                                       shouldAppear _: Bool) {
    updateControlBarsVisibility()
  }
Objective-C
- (void)miniMediaControlsViewController:
            (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController
                           shouldAppear:(BOOL)shouldAppear {
  [self updateControlBarsVisibility];
}

Добавить расширенный контроллер

Контрольный список дизайна Google Cast требует, чтобы приложение-отправитель предоставляло расширенный контроллер для транслируемого медиа. Расширенный контроллер — это полноэкранная версия мини-контроллера.

Расширенный контроллер — это полноэкранный вид, который предлагает полный контроль над воспроизведением удаленного мультимедиа. Этот вид должен позволить приложению для трансляции управлять всеми управляемыми аспектами сеанса трансляции, за исключением регулировки громкости Web Receiver и жизненного цикла сеанса (подключение/остановка трансляции). Он также предоставляет всю информацию о состоянии сеанса мультимедиа (обложка, заголовок, субтитры и т. д.).

Функциональность этого представления реализована классом GCKUIExpandedMediaControlsViewController .

Первое, что вам нужно сделать, это включить расширенный контроллер по умолчанию в контексте приведения. Измените делегат приложения, чтобы включить расширенный контроллер по умолчанию:

Быстрый
func applicationDidFinishLaunching(_ application: UIApplication) {
  ..

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

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

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  ..
}

Добавьте следующий код в контроллер представления, чтобы загрузить расширенный контроллер, когда пользователь начинает трансляцию видео:

Быстрый
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];
}

Расширенный контроллер также будет запускаться автоматически, когда пользователь нажмет на мини-контроллер.

Когда приложение-отправитель воспроизводит видео- или аудиопоток в реальном времени, SDK автоматически отображает кнопку воспроизведения/остановки вместо кнопки воспроизведения/паузы в расширенном контроллере.

Подробнее о том, как приложение-отправитель может настроить внешний вид виджетов Cast, см. в разделе Применение пользовательских стилей к приложению iOS.

Регулировка громкости

Фреймворк Cast автоматически управляет громкостью для приложения-отправителя. Фреймворк автоматически синхронизируется с громкостью Web Receiver для предоставленных виджетов пользовательского интерфейса. Чтобы синхронизировать слайдер, предоставленный приложением, используйте GCKUIDeviceVolumeController .

Физическая кнопка регулировки громкости

Физические кнопки регулировки громкости на устройстве-отправителе можно использовать для изменения громкости сеанса трансляции на веб-приемнике с помощью флага physicalVolumeButtonsWillControlDeviceVolume в GCKCastOptions , который устанавливается в GCKCastContext .

Быстрый
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];

Обработка ошибок

Для приложений-отправителей очень важно обрабатывать все обратные вызовы ошибок и выбирать наилучший ответ для каждого этапа жизненного цикла Cast. Приложение может отображать пользователю диалоговые окна с ошибками или может решить завершить сеанс Cast.

Ведение журнала

GCKLogger — это синглтон, используемый фреймворком для ведения журнала. Используйте GCKLoggerDelegate для настройки способа обработки сообщений журнала.

Используя GCKLogger , SDK создает вывод журнала в виде отладочных сообщений, ошибок и предупреждений. Эти сообщения журнала помогают отладке и полезны для устранения неполадок и выявления проблем. По умолчанию вывод журнала подавлен, но, назначив GCKLoggerDelegate , приложение-отправитель может получать эти сообщения от SDK и регистрировать их в системной консоли.

Быстрый
@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

Чтобы включить отладочные и подробные сообщения, добавьте эту строку в код после установки делегата (показано ранее):

Быстрый
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;

Вы также можете фильтровать сообщения журнала, созданные GCKLogger . Установите минимальный уровень ведения журнала для каждого класса, например:

Быстрый
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;

Имена классов могут быть как буквальными именами, так и шаблонами глобальных переменных, например, GCKUI\* и GCK\*Session .

,

В этом руководстве для разработчиков описывается, как добавить поддержку Google Cast в приложение iOS Sender с помощью iOS Sender SDK.

Мобильное устройство или ноутбук является отправителем , который управляет воспроизведением, а устройство Google Cast — приемником , которое отображает контент на телевизоре.

Структура отправителя относится к библиотеке актеров библиотеки и связанных с ними ресурсов, присутствующих во время выполнения отправителя. Приложение для отправителя или приложение CAST относится к приложению, также работающему на отправителе. Приложение Web Receiver относится к приложению HTML, работающему на веб -приемнике.

Фреймворк отправителя использует асинхронную конструкцию обратного вызова для информирования приложения-отправителя о событиях и для перехода между различными состояниями жизненного цикла приложения Cast.

Поток приложений

В следующих шагах описывается типичный поток выполнения высокого уровня для приложения для отправителя iOS:

  • Актерская структура начинает GCKDiscoveryManager на основе свойств, представленных в GCKCastOptions , чтобы начать сканирование на устройства.
  • Когда пользователь нажимает кнопку Cast, фреймворк отображает диалоговое окно Cast со списком обнаруженных устройств Cast.
  • Когда пользователь выбирает устройство Cast, фреймворк пытается запустить приложение Web Receiver на устройстве Cast.
  • Фреймворк вызывает обратные вызовы в приложении-отправителе, чтобы подтвердить запуск приложения Web Receiver.
  • Фреймворк создает канал связи между отправителем и приложениями Web Receiver.
  • Фреймворк использует канал связи для загрузки и управления воспроизведением мультимедиа на веб-приемнике.
  • Фреймворк синхронизирует состояние воспроизведения мультимедиа между отправителем и веб-получателем: когда пользователь выполняет действия пользовательского интерфейса отправителя, фреймворк передает эти запросы на управление мультимедиа веб-получателю, а когда веб-получатель отправляет обновления статуса мультимедиа, фреймворк обновляет состояние пользовательского интерфейса отправителя.
  • Когда пользователь нажимает кнопку Cast, чтобы отключиться от устройства Cast, фреймворк отключит приложение-отправитель от веб-приемника.

Чтобы устранение неполадок вашего отправителя, вам нужно включить журнал .

Для получения комплексного списка всех классов, методов и событий в рамках Google Cast IOS см. Справочник Google Cast IOS API . Следующие разделы охватывают шаги для интеграции актерского состава в приложение для iOS.

Методы вызова основного потока

Инициализировать контекст актерского состава

У Cast Framework есть глобальный объект Singleton, GCKCastContext , который координирует все действия фреймворка. Этот объект должен быть инициализирован на ранних этапах жизненного цикла приложения, как правило, в методе -[application:didFinishLaunchingWithOptions:] приложение делегата приложения, так что автоматическое возобновление сеанса при перезагрузке приложения для отправителя можно правильно запустить.

Объект GCKCastOptions должен быть поставлен при инициализации GCKCastContext . Этот класс содержит варианты, которые влияют на поведение структуры. Наиболее важным из них является идентификатор приложения веб -приемника, который используется для фильтрации результатов обнаружения и для запуска приложения веб -приемника при запуске актеров.

Метод -[application:didFinishLaunchingWithOptions:] также является хорошим местом для настройки делегата журнала для получения сообщений о ведении журнала из фреймворка. Они могут быть полезны для отладки и устранения неполадок.

Быстрый
@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

Актерские виджеты UX

Актерский iOS SDK предоставляет эти виджеты, которые соответствуют контрольному списку дизайна актеров:

  • Вводное наложение : класс GCKCastContext имеет метод, presentCastInstructionsViewControllerOnceWithCastButton , который можно использовать для освещения кнопки CAST при первом доступном веб -приемнике. Приложение Sender может настроить текст, положение текста заголовка и кнопку увольнения.

  • Кнопка отливки : начиная с литой iOS Sender SDK 4.6.0, кнопка отливки всегда видно, когда устройство отправителя подключено к Wi-Fi. В первый раз, когда пользователь нажимает на кнопку CAST после первоначального запуска приложения, появляется диалоговое окно разрешений, чтобы пользователь мог предоставить приложению локальную сеть доступ к устройствам в сети. Впоследствии, когда пользователь нажимает на кнопку отливки, отображается диалог отлив, в котором перечислены обнаруженные устройства. Когда пользователь нажимает на кнопку CAST во время подключения устройства, он отображает текущие метаданные носителя (например, заголовок, имя студии звукозаписи и миниатюрное изображение) или позволяет пользователю отключиться от устройства CAST. Когда пользователь нажимает на кнопку CAST, в то время как нет доступных устройств, будет отображаться экран, предоставляя пользователь информацию о том, почему устройства не найдены и как устранять устранение неполадок.

  • Mini Controller : Когда пользователь отбирает контент и отошел от текущей страницы содержимого или расширенного контроллера к другому экрану в приложении Sender, Mini Controller отображается в нижней части экрана, чтобы пользователь позволил пользователю видеть в настоящее время метаданные Castling Media и управлять воспроизведением.

  • Расширенный контроллер : Когда пользователь разыгрывает контент, если он нажимает на уведомление о носителях или мини -контроллер, расширенный контроллер запускает, который отображает в настоящее время игровые метаданные медиа и предоставляет несколько кнопок для управления воспроизведением носителя.

Добавить кнопку литой

Framework обеспечивает компонент литой кнопки в качестве подкласса UIButton . Он может быть добавлен в строку заголовка приложения, завернув его в UIBarButtonItem . Типичный подкласс UIViewController может установить кнопку литой следующим образом:

Быстрый
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];

По умолчанию, нажатие кнопки откроет диалог отлив, который обеспечивается Framework.

GCKUICastButton также можно добавить непосредственно в раскадровку.

Настроить обнаружение устройств

В структуре обнаружение устройства происходит автоматически. Нет необходимости явно начинать или останавливать процесс обнаружения, если вы не реализуете пользовательский пользовательский интерфейс.

Discovery in The Framework управляется классом GCKDiscoveryManager , который является свойством GCKCastContext . Framework обеспечивает диалоговый компонент по умолчанию для выбора устройства и управления. Список устройств упорядочен лексикографически по имени.

Как работает управление сеансом

Cast SDK представляет концепцию актеров, создание которой объединяет шаги подключения к устройству, запуск (или соединения) приложения для веб -получателя, подключения к этому приложению и инициализации канала управления носителем. См. Руководство по жизненному циклу приложения для веб -получателя для получения дополнительной информации о сессиях CAST и жизненном цикле веб -получателя.

Сессии управляются классом GCKSessionManager , который является свойством GCKCastContext . Отдельные сеансы представлены подклассами класса GCKSession : например, GCKCastSession представляет сеансы GCKSessionManager currentCastSession

Интерфейс GCKSessionManagerListener может использоваться для мониторинга событий сеанса, таких как создание сеанса, подвеска, возобновление и прекращение. Фреймворк автоматически приостанавливает сеансы, когда приложение отправителя входит в фон, и пытается возобновить их, когда приложение возвращается на передний план (или перезаписывается после аномального/резкого завершения приложения, когда сеанс был активным).

Если используется диалоговое окно CAST, то автоматически создаются сеансы и разбиты в ответ на жесты пользователей. В противном случае приложение может запускать и заканчивать сеансы явно с помощью методов на GCKSessionManager .

Если приложение необходимо выполнить специальную обработку в ответ на события жизненного цикла сеанса, оно может зарегистрировать один или несколько экземпляров GCKSessionManagerListener с GCKSessionManager . GCKSessionManagerListener - это протокол, который определяет обратные вызовы для таких событий, как запуск сеанса, конец сеанса и так далее.

Потоковая передача

Сохранение состояния сеанса является основой передачи потока, где пользователи могут перемещать существующие аудио и видеопотоки на разных устройствах, используя голосовые команды, приложение Google Home или Smart Displays. СМИ перестает играть на одном устройстве (источник) и продолжается на другом (пункт назначения). Любое отличное устройство с новейшей прошивкой может служить источниками или направлениями при передаче потока.

Чтобы получить новое назначение устройства во время передачи потока, используйте свойство GCKCastSession#device во время [sessionManager:didResumeCastSession:] обратный вызов.

См. Перевод потока на веб -приемнике для получения дополнительной информации.

Автоматическое переподключение

Структура литой добавляет логику переподключения, чтобы автоматически обрабатывать переподключение во многих тонких угловых случаях, таких как:

  • Восстановиться после временной потери Wi -Fi
  • Восстановиться после сна
  • Восстановиться после фонового приложения
  • Восстановиться, если приложение разбилось

Как работает управление медиа

Если сессия создана с помощью приложения для веб -приемника, которое поддерживает пространство имен медиа, экземпляр GCKRemoteMediaClient будет создан автоматически в рамках; Доступ к нему можно получить как свойство remoteMediaClient экземпляра GCKCastSession .

Все методы на GCKRemoteMediaClient , какие запросы на вопрос в веб -приемник возвращают объект GCKRequest , который можно использовать для отслеживания этого запроса. GCKRequestDelegate может быть назначен этому объекту для получения уведомлений о возможном результате операции.

Ожидается, что экземпляр GCKRemoteMediaClient может быть использован несколькими частями приложения, и, действительно, некоторые внутренние компоненты структуры, таких как диалог CAST и Mini Media Controls, делятся экземпляром. С этой целью GCKRemoteMediaClient поддерживает регистрацию множественных GCKRemoteMediaClientListener S.

Установить метаданные мультимедиа

Класс GCKMediaMetadata представляет информацию о предмете медиа, который вы хотите сыграть. Следующий пример создает новый экземпляр фильма GCKMediaMetadata и устанавливает название, подзаголовок, название студии звукозаписи и два изображения.

Быстрый
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]];

См. Раздел «Выбор изображений и кэширование» об использовании изображений с метаданными средствами мультимедиа.

Загрузить медиа

Чтобы загрузить элемент медиа, создайте экземпляр GCKMediaInformation , используя метаданные СМИ. Затем получите текущую GCKCastSession и используйте его GCKRemoteMediaClient для загрузки носителя в приложение для приемника. Затем вы можете использовать GCKRemoteMediaClient для управления приложением для медиаплеерного игрока, работающего на приемнике, например, для игры, пауза и остановки.

Быстрый
let url = URL.init(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")
guard let mediaURL = url else {
  print("invalid mediaURL")
  return
}

let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: mediaURL)
mediaInfoBuilder.streamType = GCKMediaStreamType.none;
mediaInfoBuilder.contentType = "video/mp4"
mediaInfoBuilder.metadata = metadata;
mediaInformation = mediaInfoBuilder.build()

guard let mediaInfo = mediaInformation else {
  print("invalid mediaInformation")
  return
}

if let request = sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInfo) {
  request.delegate = self
}
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;
}

Также см. Раздел об использовании медиа -треков .

Формат видео 4K

Чтобы определить, что такое видео -формат, используйте свойства videoInfo GCKMediaStatus , чтобы получить текущий экземпляр GCKVideoInfo . Этот экземпляр содержит тип формата HDR TV и высота и ширину в пикселях. Варианты формата 4K указаны в свойстве hdrType с помощью значений enum GCKVideoInfoHDRType .

Добавить мини -контроллеры

Согласно контрольному списку дизайна актеров , приложение для отправителя должно предоставить постоянный контроль, известный как мини -контроллер , который должен появляться, когда пользователь отступает от текущей страницы содержимого. Mini Controller обеспечивает мгновенный доступ и видимое напоминание для текущего сессии.

Cast Framework предоставляет панель управления, GCKUIMiniMediaControlsViewController , которая может быть добавлена ​​в сцены, в которых вы хотите показать мини -контроллер.

Когда ваше приложение для отправителя воспроизводит видео или аудио в прямом эфире, SDK автоматически отображает кнопку воспроизведения/остановки вместо кнопки Play/Pause в мини -контроллере.

См. Настройте пользовательский интерфейс отправителя iOS для того, как ваше приложение для отправителя может настроить внешний вид виджетов.

Есть два способа добавить мини -контроллер в приложение для отправителя:

  • Пусть кастрюля управляет макетом мини -контроллера, обернув ваш существующий контроллер представления своим собственным контроллером представления.
  • Управляйте макетом виджета Mini Controller, добавив его к существующему контроллеру представления, предоставив подвесок в раскадровке.

Обернуть с помощью gckuicastcontainerviewcontroller

Первым способом является использование GCKUICastContainerViewController , который завершает другой контроллер представления и добавляет GCKUIMiniMediaControlsViewController внизу. Этот подход ограничен тем, что вы не можете настроить анимацию и не можете настроить поведение контроллера представления контейнера.

Этот первый путь обычно выполняется в -[application:didFinishLaunchingWithOptions:] Метод делегата приложения:

Быстрый
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];
  ...

}
Быстрый
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

Встроенный в существующий контроллер представления

Второй способ состоит в том, чтобы добавить мини -контроллер непосредственно к вашему существующему контроллеру представления, используя createMiniMediaControlsViewController для создания экземпляра GCKUIMiniMediaControlsViewController , а затем добавить его в контроллер представления контейнера в качестве подвеса.

Установите контроллер вашего представления в делегате приложения:

Быстрый
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;
}

В вашем контроллере с корнем создайте экземпляр GCKUIMiniMediaControlsViewController и добавьте его в контроллер представления контейнера в качестве подвиги:

Быстрый
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 сообщает контроллеру представления хоста, когда мини -контроллер должен быть виден:

Быстрый
  func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController,
                                       shouldAppear _: Bool) {
    updateControlBarsVisibility()
  }
Objective-C
- (void)miniMediaControlsViewController:
            (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController
                           shouldAppear:(BOOL)shouldAppear {
  [self updateControlBarsVisibility];
}

Добавить расширенный контроллер

Контрольный список Design Design Google требует приложения для отправителя для предоставления расширенного контроллера для сети. Расширенный контроллер - это полная версия мини -контроллера.

Расширенный контроллер - это полноэкранный вид, который предлагает полное управление воспроизведением удаленного носителя. Эта точка зрения должна позволить приложению Casting управлять каждым управляемым аспектом актеров, за исключением управления объемом и жизненным циклом сеанса веб -приемника (Connect/Stop Casting). Он также предоставляет всю информацию о статусе о сессии СМИ (произведения искусства, название, подзаголовок и т. Д.).

Функциональность этой точки зрения реализована классом GCKUIExpandedMediaControlsViewController .

Первое, что вам нужно сделать, это включить расширенный контроллер по умолчанию в контексте актеров. Измените делегат приложения, чтобы включить расширенный контроллер по умолчанию:

Быстрый
func applicationDidFinishLaunching(_ application: UIApplication) {
  ..

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

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

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  ..
}

Добавьте следующий код в контроллер вашего представления для загрузки расширенного контроллера, когда пользователь начинает снимать видео:

Быстрый
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];
}

Расширенный контроллер также будет запущен автоматически, когда пользователь нажимает на мини -контроллер.

Когда ваше приложение для отправителя воспроизводит видео или аудио в прямом эфире, SDK автоматически отображает кнопку воспроизведения/остановки вместо кнопки Play/Pause в расширенном контроллере.

См. Применить пользовательские стили к приложению для iOS для того, как ваше приложение для отправителя может настроить внешний вид виджетов актеров.

Регулировка громкости

Актерная структура автоматически управляет томом для приложения отправителя. Фтруктура автоматически синхронизируется с объемом веб -приемника для предоставленных виджетов пользовательского интерфейса. Для синхронизации слайдера, предоставленного приложением, используйте GCKUIDeviceVolumeController .

Физическая кнопка регулировки громкости

Кнопки физического громкости на устройстве отправителя могут использоваться для изменения объема сеанса актеров на веб -приемнике, используя флаг physicalVolumeButtonsWillControlDeviceVolume на GCKCastOptions , который установлен на GCKCastContext .

Быстрый
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];

Обработка ошибок

Для приложений отправителя очень важно обрабатывать все обратные вызовы ошибки и определять лучший ответ для каждого этапа жизненного цикла актера. Приложение может отображать диалог ошибок пользователю, или оно может принять решение о прекращении сессии.

Ведение журнала

GCKLogger - это синглтон, используемый для регистрации за рамками. Используйте GCKLoggerDelegate , чтобы настроить, как вы обрабатываете сообщения журнала.

Используя GCKLogger , SDK производит вывод журнала в виде сообщений, ошибок и предупреждений отладки. Эти сообщения журнала помогают отладке и полезны для устранения неполадок и выявления проблем. По умолчанию вывод журнала подавляется, но путем назначения GCKLoggerDelegate приложение отправителя может получать эти сообщения от SDK и зарегистрировать их в системную консоли.

Быстрый
@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

Чтобы также включить сообщения отладки и словесных сообщений, добавьте эту строку в код после настройки делегата (показано ранее):

Быстрый
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;

Вы также можете отфильтровать сообщения журнала, созданные GCKLogger . Например, установите минимальный уровень регистрации в классе:

Быстрый
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;

Имена классов могут быть буквальными именами или шаблонами шаровика, например, сеанса GCKUI\* и GCK\*Session .