В этом руководстве разработчика описывается, как добавить поддержку Google Cast в приложение iOS Sender с помощью iOS Sender SDK.
Мобильное устройство или ноутбук является отправителем , который управляет воспроизведением, а устройство Google Cast — получателем , который отображает контент на телевизоре.
Платформа отправителя относится к двоичному файлу библиотеки классов Cast и связанным с ним ресурсам, присутствующим во время выполнения на отправителе. Приложение-отправитель или приложение Cast относится к приложению, которое также работает на отправителе. Приложение веб-приемника — это HTML-приложение, работающее на веб-приемнике.
Платформа отправителя использует асинхронный обратный вызов для информирования приложения-отправителя о событиях и перехода между различными состояниями жизненного цикла приложения Cast.
Процесс приложения
Следующие шаги описывают типичный высокоуровневый процесс выполнения приложения iOS-отправителя:
- Платформа Cast запускает
GCKDiscoveryManager
на основе свойств, указанных вGCKCastOptions
, чтобы начать сканирование устройств. - Когда пользователь нажимает кнопку Cast, платформа отображает диалоговое окно Cast со списком обнаруженных устройств Cast.
- Когда пользователь выбирает устройство Cast, платформа пытается запустить приложение веб-приемника на устройстве Cast.
- Платформа вызывает обратные вызовы в приложении-отправителе, чтобы подтвердить запуск приложения веб-приемника.
- Платформа создает канал связи между приложениями отправителя и веб-получателя.
- Платформа использует канал связи для загрузки и управления воспроизведением мультимедиа на веб-приемнике.
- Платформа синхронизирует состояние воспроизведения мультимедиа между отправителем и веб-получателем: когда пользователь выполняет действия пользовательского интерфейса отправителя, платформа передает эти запросы управления мультимедиа веб-приемнику, а когда веб-приемник отправляет обновления статуса мультимедиа, платформа обновляет состояние Пользовательский интерфейс отправителя.
- Когда пользователь нажимает кнопку Cast, чтобы отключиться от устройства Cast, платформа отключит приложение-отправитель от веб-приемника.
Чтобы устранить неполадки отправителя, вам необходимо включить ведение журнала .
Полный список всех классов, методов и событий в платформе Google Cast для iOS см. в Справочнике по API Google Cast для iOS . В следующих разделах описаны шаги по интеграции Cast в ваше приложение для iOS.
Вызов методов из основного потока
Инициализируйте контекст Cast
Платформа Cast имеет глобальный одноэлементный объект 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) } } }
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 iOS sender SDK 4.6.0, кнопка трансляции всегда видна, когда устройство-отправитель подключено к Wi-Fi. Когда пользователь впервые нажимает кнопку 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)
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, создание которого объединяет этапы подключения к устройству, запуска (или присоединения) приложения веб-приемника, подключения к этому приложению и инициализации канала управления мультимедиа. Дополнительные сведения о сеансах Cast и жизненном цикле веб-приемника см. в руководстве по жизненному циклу приложения веб-приемника.
Сеансы управляются классом GCKSessionManager
, который является свойством GCKCastContext
. Отдельные сеансы представлены подклассами класса GCKSession
: например, GCKCastSession
представляет сеансы с устройствами Cast. Вы можете получить доступ к текущему активному сеансу трансляции (если таковой имеется) с помощью свойства currentCastSession
GCKSessionManager
.
Интерфейс GCKSessionManagerListener
можно использовать для мониторинга событий сеанса, таких как создание, приостановка, возобновление и завершение сеанса. Платформа автоматически приостанавливает сеансы, когда приложение-отправитель переходит в фоновый режим, и пытается возобновить их, когда приложение возвращается на передний план (или перезапускается после ненормального/внезапного завершения приложения во время активного сеанса).
Если используется диалоговое окно Cast, сеансы создаются и закрываются автоматически в ответ на жесты пользователя. В противном случае приложение может запускать и завершать сеансы явно с помощью методов GCKSessionManager
.
Если приложению необходимо выполнить специальную обработку в ответ на события жизненного цикла сеанса, оно может зарегистрировать один или несколько экземпляров GCKSessionManagerListener
с помощью GCKSessionManager
. GCKSessionManagerListener
— это протокол, который определяет обратные вызовы для таких событий, как начало сеанса, его завершение и т. д.
Потоковая передача
Сохранение состояния сеанса является основой передачи потока, при которой пользователи могут перемещать существующие аудио- и видеопотоки между устройствами с помощью голосовых команд, приложения Google Home или интеллектуальных дисплеев. Воспроизведение мультимедиа прекращается на одном устройстве (источнике) и продолжается на другом (назначении). Любое устройство Cast с последней версией прошивки может служить источником или пунктом назначения при потоковой передаче.
Чтобы получить новое целевое устройство во время потоковой передачи, используйте свойство GCKCastSession#device
во время обратного вызова [sessionManager:didResumeCastSession:]
.
Дополнительную информацию см. в разделе Передача потока в веб-приемнике .
Автоматическое переподключение
Платформа Cast добавляет логику повторного подключения для автоматической обработки повторного подключения во многих сложных случаях, таких как:
- Восстановление после временной потери Wi-Fi
- Выход из спящего режима устройства
- Восстановление из фонового режима приложения
- Восстановление в случае сбоя приложения
Как работает контроль СМИ
Если сеанс трансляции установлен с приложением веб-приемника, которое поддерживает пространство имен мультимедиа, экземпляр 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 предоставляет панель управления GCKUIMiniMediaControlsViewController
, которую можно добавить в сцены, в которых вы хотите отобразить мини-контроллер.
Когда ваше приложение-отправитель воспроизводит видео- или аудиопоток в реальном времени, SDK автоматически отображает кнопку воспроизведения/остановки вместо кнопки воспроизведения/паузы на мини-контроллере.
См. раздел «Настройка пользовательского интерфейса iOS Sender» , чтобы узнать, как ваше приложение-отправитель может настроить внешний вид виджетов 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) } } ...
Рутконтейнервиевконтроллер.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
Рутконтейнервиевконтроллер.м
@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
.
Физическая кнопка регулировки громкости
Кнопки физической громкости на устройстве-отправителе можно использовать для изменения громкости сеанса Cast на веб-приемнике с помощью флага 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. Приложение может отображать пользователю диалоговые окна об ошибках или может принять решение о завершении сеанса трансляции.
Ведение журнала
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;
Имена классов могут быть буквальными именами или шаблонами glob, например, GCKUI\*
и GCK\*Session
.