Panduan developer ini menjelaskan cara menambahkan dukungan Google Cast ke aplikasi pengirim iOS menggunakan iOS Sender SDK.
Perangkat seluler atau laptop adalah pengirim yang mengontrol pemutaran, dan perangkat Google Cast adalah penerima yang menampilkan konten di TV.
Framework pengirim mengacu pada biner library class Cast dan resource terkait yang ada saat runtime di pengirim. Aplikasi pengirim atau aplikasi Cast mengacu pada aplikasi yang juga berjalan di pengirim. Aplikasi Penerima Web mengacu pada aplikasi HTML yang berjalan di Penerima Web.
Framework pengirim menggunakan desain callback asinkron untuk memberi tahu aplikasi pengirim tentang peristiwa dan untuk bertransisi di antara berbagai status siklus proses aplikasi Cast.
Alur aplikasi
Langkah-langkah berikut menjelaskan alur eksekusi tingkat tinggi yang umum untuk aplikasi iOS pengirim:
- Framework Cast memulai
GCKDiscoveryManager
berdasarkan properti yang disediakan diGCKCastOptions
untuk mulai memindai perangkat. - Saat pengguna mengklik tombol Cast, framework akan menampilkan dialog Cast dengan daftar perangkat Cast yang ditemukan.
- Saat pengguna memilih perangkat Cast, framework akan mencoba meluncurkan aplikasi Penerima Web di perangkat Cast.
- Framework memanggil callback di aplikasi pengirim untuk mengonfirmasi bahwa aplikasi Penerima Web diluncurkan.
- Framework ini membuat saluran komunikasi antara aplikasi pengirim dan Penerima Web.
- Framework ini menggunakan saluran komunikasi untuk memuat dan mengontrol pemutaran media di Penerima Web.
- Framework menyinkronkan status pemutaran media antara pengirim dan Penerima Web: saat pengguna melakukan tindakan UI pengirim, framework akan meneruskan permintaan kontrol media tersebut ke Penerima Web, dan saat Penerima Web mengirim pembaruan status media, framework akan memperbarui status UI pengirim.
- Saat pengguna mengklik tombol Cast untuk memutuskan koneksi dari perangkat Cast, framework akan memutuskan koneksi aplikasi pengirim dari Penerima Web.
Untuk memecahkan masalah pengirim, Anda harus mengaktifkan logging.
Untuk mengetahui daftar lengkap semua class, metode, dan peristiwa dalam framework iOS Google Cast, lihat Referensi Google Cast iOS API. Bagian berikut membahas langkah-langkah untuk mengintegrasikan Transmisi ke aplikasi iOS Anda.
Memanggil metode dari thread utama
Melakukan inisialisasi konteks Cast
Framework Cast memiliki objek singleton global, GCKCastContext
, yang
mengkoordinasikan semua aktivitas framework. Objek ini harus diinisialisasi
di awal siklus proses aplikasi, biasanya di
metode -[application:didFinishLaunchingWithOptions:]
delegasi aplikasi, sehingga
dimulainya kembali sesi otomatis pada aplikasi pengirim yang dimulai ulang dapat dipicu dengan benar.
Objek GCKCastOptions
harus disediakan saat melakukan inisialisasi GCKCastContext
.
Class ini berisi opsi yang memengaruhi perilaku framework. Yang paling penting adalah ID aplikasi Penerima Web, yang digunakan untuk memfilter hasil penemuan dan meluncurkan aplikasi Penerima Web saat sesi Cast dimulai.
Metode -[application:didFinishLaunchingWithOptions:]
juga merupakan tempat yang baik
untuk menyiapkan delegasi logging guna menerima pesan logging dari framework.
Ini dapat berguna untuk proses debug dan pemecahan masalah.
@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
Widget UX Transmisi
Cast iOS SDK menyediakan widget berikut yang mematuhi Checklist Desain Cast:
Overlay Pengantar: Class
GCKCastContext
memiliki metode,presentCastInstructionsViewControllerOnceWithCastButton
, yang dapat digunakan untuk menyoroti tombol Cast saat pertama kali Penerima Web tersedia. Aplikasi pengirim dapat menyesuaikan teks, posisi teks judul, dan tombol Tutup.Tombol Transmisi: Mulai dari Cast iOS sender SDK 4.6.0, tombol transmisi selalu terlihat saat perangkat pengirim terhubung ke Wi-Fi. Saat pertama kali pengguna mengetuk tombol Transmisikan setelah memulai aplikasi, dialog izin akan muncul sehingga pengguna dapat memberikan akses jaringan lokal aplikasi ke perangkat di jaringan. Selanjutnya, saat pengguna mengetuk tombol transmisi, dialog transmisi akan ditampilkan yang mencantumkan perangkat yang ditemukan. Saat pengguna mengetuk tombol transmisi saat perangkat terhubung, tombol tersebut akan menampilkan metadata media saat ini (seperti judul, nama studio rekaman, dan gambar thumbnail) atau memungkinkan pengguna memutuskan koneksi dari perangkat transmisi. Saat pengguna mengetuk tombol transmisi saat tidak ada perangkat yang tersedia, layar akan ditampilkan yang memberi pengguna informasi tentang alasan perangkat tidak ditemukan dan cara memecahkan masalah.
Pengontrol Mini: Saat pengguna mentransmisikan konten dan telah keluar dari halaman konten saat ini atau memperluas pengontrol ke layar lain di aplikasi pengirim, pengontrol mini akan ditampilkan di bagian bawah layar agar pengguna dapat melihat metadata media yang sedang ditransmisikan dan mengontrol pemutaran.
Pengontrol yang Diperluas: Saat pengguna mentransmisikan konten, jika mereka mengklik notifikasi media atau pengontrol mini, pengontrol yang diperluas akan diluncurkan, yang menampilkan metadata media yang sedang diputar dan menyediakan beberapa tombol untuk mengontrol pemutaran media.
Menambahkan tombol Transmisikan
Framework ini menyediakan komponen tombol Cast sebagai subclass UIButton
. Ini dapat
ditambahkan ke kolom judul aplikasi dengan menggabungkannya di UIBarButtonItem
. Subclass
UIViewController
standar dapat menginstal tombol Transmisikan sebagai berikut:
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];
Secara default, mengetuk tombol akan membuka dialog Transmisikan yang disediakan oleh framework.
GCKUICastButton
juga dapat ditambahkan langsung ke storyboard.
Mengonfigurasi penemuan perangkat
Dalam framework, penemuan perangkat terjadi secara otomatis. Anda tidak perlu memulai atau menghentikan proses penemuan secara eksplisit, kecuali jika Anda menerapkan UI kustom.
Penemuan dalam framework dikelola oleh class
GCKDiscoveryManager
,
yang merupakan properti dari
GCKCastContext
. Framework
ini menyediakan komponen dialog Transmisi default untuk kontrol dan
pemilihan perangkat. Daftar perangkat diurutkan secara leksikografis berdasarkan nama perangkat yang mudah digunakan.
Cara kerja pengelolaan sesi
Cast SDK memperkenalkan konsep sesi Cast, yang pembuatannya menggabungkan langkah-langkah untuk menghubungkan ke perangkat, meluncurkan (atau bergabung) aplikasi Penerima Web, menghubungkan ke aplikasi tersebut, dan melakukan inisialisasi saluran kontrol media. Lihat Panduan siklus proses aplikasi Penerima Web untuk mengetahui informasi selengkapnya tentang sesi Transmisi dan siklus proses Penerima Web.
Sesi dikelola oleh class
GCKSessionManager
,
yang merupakan properti dari
GCKCastContext
.
Setiap sesi diwakili oleh subclass class
GCKSession
: misalnya,
GCKCastSession
mewakili sesi dengan perangkat Transmisi. Anda dapat mengakses sesi Cast
yang saat ini aktif (jika ada), sebagai properti currentCastSession
dari GCKSessionManager
.
Antarmuka
GCKSessionManagerListener
dapat digunakan untuk memantau peristiwa sesi, seperti pembuatan sesi,
penangguhan, dimulainya kembali, dan penghentian. Framework secara otomatis menangguhkan
sesi saat aplikasi pengirim beralih ke latar belakang dan mencoba melanjutkannya
saat aplikasi kembali ke latar depan (atau diluncurkan kembali setelah
penghentian aplikasi yang tidak normal/tiba-tiba saat sesi aktif).
Jika dialog Cast sedang digunakan, sesi akan dibuat dan dihapus secara otomatis sebagai respons terhadap gestur pengguna. Jika tidak, aplikasi dapat memulai dan mengakhiri sesi secara eksplisit melalui metode di GCKSessionManager
.
Jika aplikasi perlu melakukan pemrosesan khusus sebagai respons terhadap peristiwa siklus proses
sesi, aplikasi dapat mendaftarkan satu atau beberapa instance GCKSessionManagerListener
dengan
GCKSessionManager
. GCKSessionManagerListener
adalah protokol yang menentukan callback untuk peristiwa seperti awal sesi, akhir sesi, dan sebagainya.
Transfer streaming
Mempertahankan status sesi adalah dasar transfer streaming, tempat pengguna dapat memindahkan streaming audio dan video yang ada di seluruh perangkat menggunakan perintah suara, Aplikasi Google Home, atau layar smart. Media berhenti diputar di satu perangkat (sumber) dan berlanjut di perangkat lain (tujuan). Semua perangkat Cast dengan firmware terbaru dapat berfungsi sebagai sumber atau tujuan dalam transfer streaming.
Untuk mendapatkan perangkat tujuan baru selama transfer streaming, gunakan properti
GCKCastSession#device
selama callback
[sessionManager:didResumeCastSession:]
.
Lihat Transfer streaming di Penerima Web untuk mengetahui informasi selengkapnya.
Koneksi ulang otomatis
Framework Cast menambahkan logika penyambungan ulang untuk menangani penyambungan ulang secara otomatis dalam banyak kasus sudut halus, seperti:
- Memulihkan dari hilangnya Wi-Fi sementara
- Memulihkan dari mode tidur perangkat
- Memulihkan dari aplikasi yang berjalan di latar belakang
- Memulihkan jika aplikasi mengalami error
Cara kerja kontrol media
Jika sesi Cast dibuat dengan aplikasi Penerima Web yang mendukung namespace
media, instance
GCKRemoteMediaClient
akan dibuat secara otomatis oleh framework; instance ini dapat diakses sebagai
properti remoteMediaClient
dari
instance
GCKCastSession
.
Semua metode di GCKRemoteMediaClient
yang mengeluarkan permintaan ke Penerima Web
akan menampilkan
objek GCKRequest
yang
dapat digunakan untuk melacak permintaan tersebut. GCKRequestDelegate
dapat ditetapkan ke objek ini untuk menerima notifikasi tentang hasil
akhir operasi.
Diharapkan instance GCKRemoteMediaClient
dapat dibagikan oleh beberapa bagian aplikasi, dan memang beberapa komponen internal
framework seperti dialog Cast dan kontrol media mini berbagi
instance. Untuk itu, GCKRemoteMediaClient
mendukung pendaftaran beberapa
GCKRemoteMediaClientListener
.
Menetapkan metadata media
Class
GCKMediaMetadata
mewakili informasi tentang item media yang ingin Anda transmisikan. Contoh
berikut membuat instance GCKMediaMetadata
baru dari film dan menetapkan judul,
subtitel, nama studio rekaman, dan dua gambar.
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]];
Lihat bagian Pemilihan dan Pemcachean Gambar tentang penggunaan gambar dengan metadata media.
Memuat media
Untuk memuat item media, buat instance
GCKMediaInformation
menggunakan metadata media. Kemudian, dapatkan
GCKCastSession
saat ini dan
gunakan
GCKRemoteMediaClient
untuk memuat media di aplikasi penerima. Anda kemudian dapat menggunakan GCKRemoteMediaClient
untuk mengontrol aplikasi pemutar media yang berjalan di penerima, seperti untuk memutar,
menjeda, dan menghentikan.
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; }
Lihat juga bagian tentang menggunakan trek media.
Format video 4K
Untuk menentukan format video media Anda, gunakan properti videoInfo
dari
GCKMediaStatus
untuk mendapatkan instance
GCKVideoInfo
saat ini.
Instance ini berisi jenis format TV HDR serta tinggi dan lebar dalam
piksel. Varian format 4K ditunjukkan dalam properti hdrType
dengan nilai
enum GCKVideoInfoHDRType
.
Menambahkan pengontrol mini
Menurut Checklist Desain Cast, aplikasi pengirim harus menyediakan kontrol persisten yang dikenal sebagai pengontrol mini yang akan muncul saat pengguna keluar dari halaman konten saat ini. Pengontrol mini menyediakan akses instan dan pengingat yang terlihat untuk sesi Cast saat ini.
Framework Cast menyediakan panel kontrol,
GCKUIMiniMediaControlsViewController
,
yang dapat ditambahkan ke adegan tempat Anda ingin menampilkan pengontrol mini.
Saat aplikasi pengirim memutar live stream video atau audio, SDK akan otomatis menampilkan tombol putar/berhenti sebagai pengganti tombol putar/jeda di pengontrol mini.
Lihat Menyesuaikan UI Pengirim iOS untuk mengetahui cara aplikasi pengirim Anda mengonfigurasi tampilan widget Cast.
Ada dua cara untuk menambahkan pengontrol mini ke aplikasi pengirim:
- Izinkan framework Cast mengelola tata letak pengontrol mini dengan menggabungkan pengontrol tampilan yang ada dengan pengontrol tampilannya sendiri.
- Kelola tata letak widget pengontrol mini sendiri dengan menambahkannya ke pengontrol tampilan yang ada dengan menyediakan subtampilan di storyboard.
Menggabungkan menggunakan GCKUICastContainerViewController
Cara pertama adalah menggunakan
GCKUICastContainerViewController
yang menggabungkan pengontrol tampilan lain dan menambahkan
GCKUIMiniMediaControlsViewController
di bagian bawah. Pendekatan ini terbatas karena Anda tidak dapat menyesuaikan
animasi dan tidak dapat mengonfigurasi perilaku pengontrol tampilan penampung.
Cara pertama ini biasanya dilakukan dalam
metode -[application:didFinishLaunchingWithOptions:]
delegasi aplikasi:
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
Menyematkan di pengontrol tampilan yang ada
Cara kedua adalah menambahkan pengontrol mini langsung ke pengontrol tampilan
yang ada dengan menggunakan
createMiniMediaControlsViewController
untuk membuat
instance
GCKUIMiniMediaControlsViewController
, lalu menambahkannya ke pengontrol tampilan penampung sebagai subtampilan.
Siapkan pengontrol tampilan di delegasi aplikasi:
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; }
Di pengontrol tampilan root, buat instance GCKUIMiniMediaControlsViewController
dan tambahkan ke pengontrol tampilan penampung sebagai subtampilan:
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
memberi tahu pengontrol tampilan host kapan pengontrol mini harus terlihat:
func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController, shouldAppear _: Bool) { updateControlBarsVisibility() }
- (void)miniMediaControlsViewController: (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController shouldAppear:(BOOL)shouldAppear { [self updateControlBarsVisibility]; }
Menambahkan pengontrol yang diperluas
Checklist Desain Google Cast mengharuskan aplikasi pengirim untuk menyediakan pengontrol yang diperluas untuk media yang ditransmisi. Pengontrol yang diperluas adalah versi layar penuh dari pengontrol mini.
Pengontrol yang diperluas adalah tampilan layar penuh yang menawarkan kontrol penuh atas pemutaran media jarak jauh. Tampilan ini harus memungkinkan aplikasi transmisi mengelola setiap aspek yang dapat dikelola dari sesi transmisi, kecuali kontrol volume Penerima Web dan siklus proses sesi (menghubungkan/menghentikan transmisi). Fungsi ini juga memberikan semua informasi status tentang sesi media (karya seni, judul, subtitel, dan sebagainya ).
Fungsi tampilan ini diimplementasikan oleh class
GCKUIExpandedMediaControlsViewController
.
Hal pertama yang harus Anda lakukan adalah mengaktifkan pengontrol yang diperluas secara default dalam konteks transmisi. Ubah delegasi aplikasi untuk mengaktifkan pengontrol default yang diperluas:
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; .. }
Tambahkan kode berikut ke pengontrol tampilan untuk memuat pengontrol yang diperluas saat pengguna mulai mentransmisi video:
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]; }
Pengontrol yang diperluas juga akan diluncurkan secara otomatis saat pengguna mengetuk pengontrol mini.
Saat aplikasi pengirim memutar live stream video atau audio, SDK akan otomatis menampilkan tombol putar/berhenti sebagai pengganti tombol putar/jeda di pengontrol yang diperluas.
Lihat Menerapkan Gaya Kustom ke Aplikasi iOS untuk mengetahui cara aplikasi pengirim Anda dapat mengonfigurasi tampilan widget Cast.
Kontrol volume
Framework Cast secara otomatis mengelola volume untuk aplikasi pengirim. Framework
akan otomatis disinkronkan dengan volume Penerima Web untuk
widget UI yang disediakan. Untuk menyinkronkan penggeser yang disediakan oleh aplikasi, gunakan
GCKUIDeviceVolumeController
.
Kontrol volume tombol fisik
Tombol volume fisik di perangkat pengirim dapat digunakan untuk mengubah
volume sesi Transmisi di Penerima Web menggunakan
tanda physicalVolumeButtonsWillControlDeviceVolume
di
GCKCastOptions
,
yang ditetapkan di
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];
Menangani error
Sangat penting bagi aplikasi pengirim untuk menangani semua callback error dan menentukan respons terbaik untuk setiap tahap siklus proses Transmisi. Aplikasi dapat menampilkan dialog error kepada pengguna atau dapat memutuskan untuk mengakhiri sesi Transmisi.
Logging
GCKLogger
adalah singleton yang digunakan untuk logging oleh framework. Gunakan
GCKLoggerDelegate
untuk menyesuaikan cara Anda menangani pesan log.
Dengan GCKLogger
, SDK menghasilkan output logging dalam bentuk pesan, error, dan peringatan
debug. Pesan log ini membantu proses debug dan berguna
untuk memecahkan masalah dan mengidentifikasi masalah. Secara default, output log
dihilangkan, tetapi dengan menetapkan GCKLoggerDelegate
, aplikasi pengirim dapat menerima
pesan ini dari SDK dan mencatatnya ke konsol sistem.
@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
Untuk mengaktifkan pesan debug dan panjang juga, tambahkan baris ini ke kode setelah menetapkan delegasi (ditampilkan sebelumnya):
let filter = GCKLoggerFilter.init() filter.minimumLevel = GCKLoggerLevel.verbose GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setMinimumLevel:GCKLoggerLevelVerbose]; [GCKLogger sharedInstance].filter = filter;
Anda juga dapat memfilter pesan log yang dihasilkan oleh
GCKLogger
.
Tetapkan level logging minimum per class, misalnya:
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;
Nama class dapat berupa nama literal atau pola glob, misalnya,
GCKUI\*
dan GCK\*Session
.