В этом руководстве для разработчиков описывается, как добавить поддержку Google Cast в приложение iOS Sender с помощью iOS Sender SDK.
Мобильное устройство или ноутбук — это отправитель , который управляет воспроизведением, а устройство Google Cast — это получатель , который отображает контент на телевизоре.
Платформа отправителя относится к двоичному файлу библиотеки классов Cast и связанным ресурсам, присутствующим во время выполнения на отправителе. Приложение-отправитель или приложение Cast относится к приложению, которое также работает на отправителе. Приложение веб-приемника относится к приложению HTML, работающему на веб-приемнике.
Платформа отправителя использует дизайн асинхронного обратного вызова для информирования приложения-отправителя о событиях и для перехода между различными состояниями жизненного цикла приложения Cast.
Поток приложений
Следующие шаги описывают типичный высокоуровневый поток выполнения для отправляющего приложения iOS:
- Платформа Cast запускает
GCKDiscoveryManager
на основе свойств, предоставленных вGCKCastOptions
, чтобы начать сканирование устройств. - Когда пользователь нажимает кнопку Cast, платформа представляет диалоговое окно Cast со списком обнаруженных устройств Cast.
- Когда пользователь выбирает устройство Cast, платформа пытается запустить приложение Web Receiver на устройстве Cast.
- Платформа вызывает обратные вызовы в приложении-отправителе, чтобы подтвердить запуск приложения веб-приемника.
- Платформа создает канал связи между приложениями-отправителями и веб-приемниками.
- Платформа использует канал связи для загрузки и управления воспроизведением мультимедиа на веб-приемнике.
- Платформа синхронизирует состояние воспроизведения мультимедиа между отправителем и веб-приемником: когда пользователь выполняет действия пользовательского интерфейса отправителя, платформа передает эти запросы управления мультимедиа веб-приемнику, а когда веб-приемник отправляет обновления статуса мультимедиа, платформа обновляет состояние интерфейс отправителя.
- Когда пользователь нажимает кнопку Cast, чтобы отключиться от устройства Cast, платформа отключит приложение-отправитель от веб-приемника.
Чтобы устранить неполадки с отправителем, необходимо включить ведение журнала .
Полный список всех классов, методов и событий платформы Google Cast для iOS см. в Справочнике по API Google Cast для iOS . В следующих разделах описаны шаги по интеграции 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) } } }
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 Design:
Вводное наложение : класс
GCKCastContext
имеет методpresentCastInstructionsViewControllerOnceWithCastButton
, который можно использовать для выделения кнопки трансляции при первом доступе к веб-приемнику. Приложение-отправитель может настроить текст, положение текста заголовка и кнопку «Отклонить».Кнопка трансляции : начиная с версии SDK 4.6.0 для отправителя iOS, кнопка трансляции всегда видна, когда устройство-отправитель подключено к сети Wi-Fi. Когда пользователь впервые нажимает кнопку 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)
GCKUICastButton *castButton = [[GCKUICastButton alloc] initWithFrame:CGRectMake(0, 0, 24, 24)]; castButton.tintColor = [UIColor grayColor]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:castButton];
По умолчанию при нажатии на кнопку открывается диалоговое окно Cast, предоставляемое платформой.
GCKUICastButton
также можно добавить непосредственно в раскадровку.
Настройка обнаружения устройств
В фреймворке обнаружение устройств происходит автоматически. Нет необходимости явно запускать или останавливать процесс обнаружения, если вы не реализуете настраиваемый пользовательский интерфейс.
Обнаружение в фреймворке управляется классом GCKDiscoveryManager
, который является свойством GCKCastContext
. Платформа предоставляет диалоговый компонент Cast по умолчанию для выбора устройства и управления им. Список устройств упорядочен лексикографически по понятному имени устройства.
Как работает управление сессиями
Cast SDK представляет концепцию сеанса Cast, создание которого сочетает в себе этапы подключения к устройству, запуска (или присоединения) приложения Web Receiver, подключения к этому приложению и инициализации канала управления мультимедиа. Дополнительную информацию о сеансах Cast и жизненном цикле веб-приемника см. в руководстве по жизненному циклу приложения веб-приемника.
Сессии управляются классом 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
, отправляющие запросы к веб-приемнику, будут возвращать объект 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))
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 }
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; }
См. также раздел об использовании дорожек мультимедиа .
Формат видео 4К
Чтобы определить, какой видеоформат у вашего мультимедиа, используйте свойство videoInfo
GCKMediaStatus
, чтобы получить текущий экземпляр GCKVideoInfo
. Этот экземпляр содержит тип формата HDR TV, а также высоту и ширину в пикселях. Варианты формата 4K указываются в свойстве hdrType
значениями перечисления GCKVideoInfoHDRType
.
Добавьте мини-контроллеры
Согласно контрольному списку Cast Design , приложение-отправитель должно предоставлять постоянный элемент управления, известный как мини-контроллер , который должен появляться, когда пользователь уходит с текущей страницы содержимого. Мини-контроллер обеспечивает мгновенный доступ и визуальное напоминание о текущем сеансе Cast.
Инфраструктура Cast предоставляет панель управления GCKUIMiniMediaControlsViewController
, которую можно добавить к сценам, в которых вы хотите отобразить мини-контроллер.
Когда ваше приложение-отправитель воспроизводит видео- или аудиопоток в прямом эфире, SDK автоматически отображает кнопку воспроизведения/остановки вместо кнопки воспроизведения/паузы на мини-контроллере.
См. раздел Настройка пользовательского интерфейса отправителя iOS , чтобы узнать, как ваше приложение-отправитель может настроить внешний вид виджетов Cast.
Есть два способа добавить мини-контроллер в приложение-отправитель:
- Позвольте платформе 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() ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... // Wrap main view in the GCKUICastContainerViewController and display the mini controller. UIStoryboard *appStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; UINavigationController *navigationController = [appStoryboard instantiateViewControllerWithIdentifier:@"MainNavigation"]; GCKUICastContainerViewController *castContainerVC = [[GCKCastContext sharedInstance] createCastContainerControllerForViewController:navigationController]; castContainerVC.miniMediaControlsItemEnabled = YES; self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; self.window.rootViewController = castContainerVC; [self.window makeKeyAndVisible]; ... }
var castControlBarsEnabled: Bool { set(enabled) { if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController { castContainerVC.miniMediaControlsItemEnabled = enabled } else { print("GCKUICastContainerViewController is not correctly configured") } } get { if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController { return castContainerVC.miniMediaControlsItemEnabled } else { print("GCKUICastContainerViewController is not correctly configured") return false } } }
AppDelegate.h
@interface AppDelegate : UIResponder <UIApplicationDelegate> @property (nonatomic, strong) UIWindow *window; @property (nonatomic, assign) BOOL castControlBarsEnabled; @end
AppDelegate.m
@implementation AppDelegate ... - (void)setCastControlBarsEnabled:(BOOL)notificationsEnabled { GCKUICastContainerViewController *castContainerVC; castContainerVC = (GCKUICastContainerViewController *)self.window.rootViewController; castContainerVC.miniMediaControlsItemEnabled = notificationsEnabled; } - (BOOL)castControlBarsEnabled { GCKUICastContainerViewController *castContainerVC; castContainerVC = (GCKUICastContainerViewController *)self.window.rootViewController; return castContainerVC.miniMediaControlsItemEnabled; } ... @end
Встроить в существующий контроллер представления
Второй способ — добавить мини-контроллер непосредственно в существующий контроллер представления, используя 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 }
- (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) } } ...
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() }
- (void)miniMediaControlsViewController: (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController shouldAppear:(BOOL)shouldAppear { [self updateControlBarsVisibility]; }
Добавить расширенный контроллер
Контрольный список Google Cast Design требует, чтобы приложение-отправитель предоставляло расширенный контроллер для транслируемого мультимедиа. Расширенный контроллер представляет собой полноэкранную версию мини-контроллера.
Расширенный контроллер представляет собой полноэкранный режим, обеспечивающий полный контроль над удаленным воспроизведением мультимедиа. Это представление должно позволять приложению трансляции управлять всеми управляемыми аспектами сеанса трансляции, за исключением управления громкостью веб-приемника и жизненного цикла сеанса (подключение/остановка трансляции). Он также предоставляет всю информацию о состоянии сеанса мультимедиа (обложка, заголовок, подзаголовок и т. д.).
Функциональность этого представления реализуется классом GCKUIExpandedMediaControlsViewController
.
Первое, что вам нужно сделать, это включить расширенный контроллер по умолчанию в контексте приведения. Измените делегат приложения, чтобы включить расширенный контроллер по умолчанию:
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; .. }
Добавьте следующий код в свой контроллер представления, чтобы загружать расширенный контроллер, когда пользователь начинает транслировать видео:
func playSelectedItemRemotely() { GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls() ... // Load your media sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation) }
- (void)playSelectedItemRemotely { [[GCKCastContext sharedInstance] presentDefaultExpandedMediaControls]; ... // Load your media [self.sessionManager.currentSession.remoteMediaClient loadMedia:mediaInformation]; }
Расширенный контроллер также будет запускаться автоматически, когда пользователь коснется мини-контроллера.
Когда ваше приложение-отправитель воспроизводит видео- или аудиопоток в прямом эфире, SDK автоматически отображает кнопку воспроизведения/остановки вместо кнопки воспроизведения/паузы в расширенном контроллере.
См. Применение пользовательских стилей к вашему приложению iOS, чтобы узнать, как ваше приложение-отправитель может настроить внешний вид виджетов Cast.
Контроль громкости
Платформа Cast автоматически управляет объемом для приложения-отправителя. Платформа автоматически синхронизируется с томом веб-приемника для предоставленных виджетов пользовательского интерфейса. Чтобы синхронизировать слайдер, предоставленный приложением, используйте GCKUIDeviceVolumeController
.
Физическая кнопка регулировки громкости
Кнопки физической громкости на устройстве-отправителе можно использовать для изменения громкости сеанса трансляции на веб-приемнике с помощью флага physicalVolumeButtonsWillControlDeviceVolume
в GCKCastOptions
, который установлен в GCKCastContext
.
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID) let options = GCKCastOptions(discoveryCriteria: criteria) options.physicalVolumeButtonsWillControlDeviceVolume = true GCKCastContext.setSharedInstanceWith(options)
GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc] initWithApplicationID:kReceiverAppID]; GCKCastOptions *options = [[GCKCastOptions alloc] initWithDiscoveryCriteria :criteria]; options.physicalVolumeButtonsWillControlDeviceVolume = YES; [GCKCastContext setSharedInstanceWithOptions:options];
Обработка ошибок
Для приложений-отправителей очень важно обрабатывать все обратные вызовы ошибок и выбирать наилучший ответ для каждого этапа жизненного цикла 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) } } }
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
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
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setLoggingLevel:GCKLoggerLevelVerbose forClasses:@[@"GCKUICastButton", @"GCKUIImageCache", @"NSMutableDictionary" ]]; [GCKLogger sharedInstance].filter = filter;
Имена классов могут быть буквальными именами или шаблонами подстановок, например, GCKUI\*
и GCK\*Session
.