Hướng dẫn dành cho nhà phát triển này mô tả cách thêm tính năng hỗ trợ Google Cast vào ứng dụng trình phát iOS bằng SDK Trình phát iOS.
Thiết bị di động hoặc máy tính xách tay là thiết bị gửi (sender) có chức năng kiểm soát việc phát và thiết bị Google Cast là thiết bị nhận (receiver) hiển thị nội dung trên TV.
Khung gửi đề cập đến tệp nhị phân thư viện lớp Cast và các tài nguyên liên kết có trong thời gian chạy trên trình gửi. Ứng dụng gửi hoặc Ứng dụng Cast là một ứng dụng cũng chạy trên thiết bị gửi. Ứng dụng Web Receiver (Trình thu sóng web) đề cập đến ứng dụng HTML chạy trên Web Receiver.
Khung trình gửi sử dụng thiết kế lệnh gọi lại không đồng bộ để thông báo cho ứng dụng trình gửi về các sự kiện và chuyển đổi giữa nhiều trạng thái của vòng đời ứng dụng Cast.
Luồng ứng dụng
Các bước sau đây mô tả quy trình thực thi cấp cao thông thường cho ứng dụng iOS của trình gửi:
- Khung truyền bắt đầu
GCKDiscoveryManager
dựa trên các thuộc tính được cung cấp trongGCKCastOptions
để bắt đầu quét tìm thiết bị. - Khi người dùng nhấp vào nút Truyền, khung sẽ hiển thị hộp thoại Truyền với danh sách các thiết bị Truyền đã phát hiện.
- Khi người dùng chọn một thiết bị Truyền, khung sẽ cố gắng chạy ứng dụng Web Receiver trên thiết bị Truyền.
- Khung này gọi lệnh gọi lại trong ứng dụng gửi để xác nhận rằng ứng dụng Trình thu web đã được khởi chạy.
- Khung này tạo một kênh giao tiếp giữa ứng dụng người gửi và ứng dụng Trình nhận web.
- Khung này sử dụng kênh giao tiếp để tải và kiểm soát việc phát nội dung nghe nhìn trên Web Receiver.
- Khung này đồng bộ hoá trạng thái phát nội dung nghe nhìn giữa trình gửi và Web Receiver: khi người dùng thực hiện các thao tác trên giao diện người dùng của trình gửi, khung này sẽ chuyển các yêu cầu điều khiển nội dung nghe nhìn đó đến Web Receiver và khi Web Receiver gửi thông tin cập nhật trạng thái nội dung nghe nhìn, khung này sẽ cập nhật trạng thái của giao diện người dùng của trình gửi.
- Khi người dùng nhấp vào nút Truyền để ngắt kết nối với thiết bị Truyền, khung sẽ ngắt kết nối ứng dụng gửi với Trình nhận web.
Để khắc phục sự cố của trình gửi, bạn cần bật tính năng ghi nhật ký.
Để biết danh sách đầy đủ tất cả các lớp, phương thức và sự kiện trong khung Google Cast cho iOS, hãy xem Tài liệu tham khảo API Google Cast cho iOS. Các phần sau đây trình bày các bước tích hợp tính năng Truyền vào ứng dụng iOS.
Gọi phương thức từ luồng chính
Khởi chạy ngữ cảnh Truyền
Khung Cast có một đối tượng singleton toàn cục, GCKCastContext
, điều phối tất cả hoạt động của khung. Bạn phải khởi tạo đối tượng này sớm trong vòng đời của ứng dụng, thường là trong phương thức -[application:didFinishLaunchingWithOptions:]
của ứng dụng uỷ quyền, để quá trình tiếp tục phiên tự động khi khởi động lại ứng dụng của người gửi có thể kích hoạt đúng cách.
Bạn phải cung cấp đối tượng GCKCastOptions
khi khởi tạo GCKCastContext
.
Lớp này chứa các tuỳ chọn ảnh hưởng đến hành vi của khung. Quan trọng nhất trong số này là mã nhận dạng ứng dụng Web Receiver. Mã này được dùng để lọc kết quả khám phá và chạy ứng dụng Web Receiver khi bắt đầu một phiên truyền.
Phương thức -[application:didFinishLaunchingWithOptions:]
cũng là một nơi phù hợp để thiết lập một trình uỷ quyền ghi nhật ký để nhận thông điệp ghi nhật ký từ khung.
Các thông tin này có thể hữu ích khi gỡ lỗi và khắc phục sự cố.
@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
Các tiện ích UX Cast
SDK Cast cho iOS cung cấp các tiện ích tuân thủ Danh sách kiểm tra thiết kế Cast:
Lớp phủ giới thiệu: Lớp
GCKCastContext
có một phương thức,presentCastInstructionsViewControllerOnceWithCastButton
, có thể dùng để làm nổi bật nút Truyền trong lần đầu tiên có Trình thu phát web. Ứng dụng gửi có thể tuỳ chỉnh văn bản, vị trí của văn bản tiêu đề và nút Bỏ qua.Nút truyền: Bắt đầu từ SDK trình truyền iOS 4.6.0 của Cast, nút truyền sẽ luôn hiển thị khi thiết bị gửi được kết nối với Wi-Fi. Lần đầu tiên người dùng nhấn vào nút Truyền sau khi khởi động ứng dụng, một hộp thoại cấp quyền sẽ xuất hiện để người dùng có thể cấp cho ứng dụng quyền truy cập mạng cục bộ vào các thiết bị trên mạng. Sau đó, khi người dùng nhấn vào nút truyền, một hộp thoại truyền sẽ xuất hiện, trong đó liệt kê các thiết bị đã phát hiện. Khi người dùng nhấn vào nút truyền khi thiết bị đã kết nối, nút này sẽ hiển thị siêu dữ liệu nội dung nghe nhìn hiện tại (chẳng hạn như tiêu đề, tên của hãng thu âm và hình thu nhỏ) hoặc cho phép người dùng ngắt kết nối với thiết bị truyền. Khi người dùng nhấn vào nút truyền khi không có thiết bị nào, một màn hình sẽ hiển thị cho người dùng biết lý do không tìm thấy thiết bị và cách khắc phục sự cố.
Bộ điều khiển thu gọn: Khi người dùng đang truyền nội dung và đã rời khỏi trang nội dung hiện tại hoặc mở rộng bộ điều khiển sang một màn hình khác trong ứng dụng gửi, bộ điều khiển thu gọn sẽ hiển thị ở cuối màn hình để cho phép người dùng xem siêu dữ liệu nội dung nghe nhìn đang truyền và điều khiển chế độ phát.
Bộ điều khiển mở rộng: Khi người dùng đang truyền nội dung, nếu họ nhấp vào thông báo nội dung nghe nhìn hoặc bộ điều khiển mini, thì bộ điều khiển mở rộng sẽ khởi chạy, hiển thị siêu dữ liệu nội dung nghe nhìn đang phát và cung cấp một số nút để điều khiển việc phát nội dung nghe nhìn.
Thêm nút Truyền
Khung này cung cấp thành phần nút Truyền dưới dạng lớp con UIButton
. Bạn có thể thêm tệp này vào thanh tiêu đề của ứng dụng bằng cách gói tệp này trong UIBarButtonItem
. Một lớp con UIViewController
thông thường có thể cài đặt nút Truyền như sau:
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];
Theo mặc định, thao tác nhấn vào nút này sẽ mở hộp thoại Truyền do khung cung cấp.
Bạn cũng có thể thêm trực tiếp GCKUICastButton
vào bảng phân cảnh.
Định cấu hình tính năng khám phá thiết bị
Trong khung này, quá trình phát hiện thiết bị sẽ diễn ra tự động. Bạn không cần phải bắt đầu hoặc dừng quy trình khám phá một cách rõ ràng, trừ phi bạn triển khai giao diện người dùng tuỳ chỉnh.
Tính năng Khám phá trong khung này do lớp GCKDiscoveryManager
quản lý. Đây là một thuộc tính của GCKCastContext
. Khung này cung cấp thành phần hộp thoại Truyền mặc định để chọn và điều khiển thiết bị. Danh sách thiết bị được sắp xếp theo thứ tự bảng chữ cái theo tên thân thiện của thiết bị.
Cách hoạt động của tính năng quản lý phiên
SDK truyền phát giới thiệu khái niệm về phiên truyền phát, trong đó việc thiết lập kết hợp các bước kết nối với thiết bị, khởi chạy (hoặc tham gia) ứng dụng Web Receiver, kết nối với ứng dụng đó và khởi chạy kênh điều khiển nội dung đa phương tiện. Hãy xem Hướng dẫn về vòng đời ứng dụng của Trình thu phát trên web để biết thêm thông tin về các phiên truyền và vòng đời của Trình thu phát trên web.
Các phiên được quản lý bởi lớp GCKSessionManager
, là một thuộc tính của GCKCastContext
.
Các phiên riêng lẻ được biểu thị bằng các lớp con của lớp GCKSession
: ví dụ: GCKCastSession
biểu thị các phiên có thiết bị Cast. Bạn có thể truy cập vào phiên Truyền đang hoạt động (nếu có) dưới dạng thuộc tính currentCastSession
của GCKSessionManager
.
Bạn có thể sử dụng giao diện GCKSessionManagerListener
để theo dõi các sự kiện phiên, chẳng hạn như tạo phiên, tạm ngưng, tiếp tục và chấm dứt. Khung này tự động tạm ngưng các phiên khi ứng dụng gửi chuyển sang chế độ nền và cố gắng tiếp tục các phiên đó khi ứng dụng quay lại nền trước (hoặc được chạy lại sau khi ứng dụng bị chấm dứt bất thường/đột ngột trong khi phiên đang hoạt động).
Nếu bạn đang sử dụng hộp thoại Truyền, thì các phiên sẽ được tạo và huỷ tự động để phản hồi các cử chỉ của người dùng. Nếu không, ứng dụng có thể bắt đầu và kết thúc phiên một cách rõ ràng thông qua các phương thức trên GCKSessionManager
.
Nếu cần xử lý đặc biệt để phản hồi các sự kiện trong vòng đời phiên, ứng dụng có thể đăng ký một hoặc nhiều thực thể GCKSessionManagerListener
bằng GCKSessionManager
. GCKSessionManagerListener
là một giao thức xác định các lệnh gọi lại cho các sự kiện như bắt đầu phiên, kết thúc phiên, v.v.
Chuyển đổi luồng
Việc duy trì trạng thái phiên là cơ sở của quá trình chuyển luồng, trong đó người dùng có thể di chuyển các luồng âm thanh và video hiện có trên các thiết bị bằng cách sử dụng lệnh thoại, Ứng dụng Google Home hoặc màn hình thông minh. Nội dung nghe nhìn sẽ ngừng phát trên một thiết bị (nguồn) và tiếp tục phát trên một thiết bị khác (đích). Mọi thiết bị Cast có phần mềm cơ sở mới nhất đều có thể đóng vai trò là nguồn hoặc đích trong quá trình truyền trực tuyến.
Để lấy thiết bị đích mới trong quá trình chuyển luồng, hãy sử dụng thuộc tính GCKCastSession#device
trong lệnh gọi lại [sessionManager:didResumeCastSession:]
.
Hãy xem phần Chuyển luồng trên Web Receiver để biết thêm thông tin.
Tự động kết nối lại
Khung Cast thêm logic kết nối lại để tự động xử lý việc kết nối lại trong nhiều trường hợp khó xử, chẳng hạn như:
- Khôi phục sau khi tạm thời mất kết nối Wi-Fi
- Khôi phục sau khi thiết bị chuyển sang chế độ ngủ
- Khôi phục từ trạng thái chạy ứng dụng ở chế độ nền
- Khôi phục nếu ứng dụng gặp sự cố
Cách hoạt động của chế độ điều khiển nội dung nghe nhìn
Nếu một phiên Cast được thiết lập bằng ứng dụng Web Receiver hỗ trợ không gian tên media, thì khung sẽ tự động tạo một thực thể của GCKRemoteMediaClient
. Bạn có thể truy cập thực thể này dưới dạng thuộc tính remoteMediaClient
của thực thể GCKCastSession
.
Tất cả phương thức trên GCKRemoteMediaClient
đưa ra yêu cầu cho Trình nhận web sẽ trả về một đối tượng GCKRequest
có thể dùng để theo dõi yêu cầu đó. Bạn có thể gán GCKRequestDelegate
cho đối tượng này để nhận thông báo về kết quả cuối cùng của thao tác.
Dự kiến là nhiều phần của ứng dụng có thể chia sẻ thực thể của GCKRemoteMediaClient
và thực sự một số thành phần nội bộ của khung, chẳng hạn như hộp thoại Truyền và các nút điều khiển nội dung nghe nhìn thu nhỏ, có thể chia sẻ thực thể này. Để đạt được mục tiêu đó, GCKRemoteMediaClient
hỗ trợ việc đăng ký nhiều GCKRemoteMediaClientListener
.
Thiết lập siêu dữ liệu nội dung nghe nhìn
Lớp GCKMediaMetadata
đại diện cho thông tin về một mục nội dung nghe nhìn mà bạn muốn truyền. Ví dụ sau đây tạo một thực thể GCKMediaMetadata
mới của một bộ phim và đặt tiêu đề, phụ đề, tên hãng phim và hai hình ảnh.
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]];
Hãy xem phần Chọn hình ảnh và lưu vào bộ nhớ đệm về cách sử dụng hình ảnh có siêu dữ liệu nội dung nghe nhìn.
Tải nội dung nghe nhìn
Để tải một mục nội dung nghe nhìn, hãy tạo một thực thể GCKMediaInformation
bằng siêu dữ liệu của nội dung nghe nhìn đó. Sau đó, hãy lấy GCKCastSession
hiện tại và sử dụng GCKRemoteMediaClient
của ứng dụng đó để tải nội dung nghe nhìn trên ứng dụng receiver. Sau đó, bạn có thể sử dụng GCKRemoteMediaClient
để điều khiển ứng dụng trình phát nội dung nghe nhìn đang chạy trên receiver, chẳng hạn như để phát, tạm dừng và dừng.
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; }
Ngoài ra, hãy xem phần sử dụng kênh nội dung nghe nhìn.
Định dạng video 4K
Để xác định định dạng video của nội dung nghe nhìn, hãy sử dụng thuộc tính videoInfo
của GCKMediaStatus
để lấy thực thể hiện tại của GCKVideoInfo
.
Thực thể này chứa loại định dạng TV HDR cũng như chiều cao và chiều rộng tính bằng pixel. Các biến thể của định dạng 4K được chỉ định trong thuộc tính hdrType
bằng các giá trị enum GCKVideoInfoHDRType
.
Thêm tay điều khiển mini
Theo Danh sách kiểm tra thiết kế Cast, ứng dụng gửi phải cung cấp một chế độ điều khiển liên tục được gọi là trình điều khiển mini. Chế độ này sẽ xuất hiện khi người dùng rời khỏi trang nội dung hiện tại. Tay điều khiển mini cung cấp quyền truy cập tức thì và lời nhắc rõ ràng cho phiên Truyền hiện tại.
Khung Cast cung cấp một thanh điều khiển, GCKUIMiniMediaControlsViewController
, có thể được thêm vào các cảnh mà bạn muốn hiển thị tay điều khiển mini.
Khi ứng dụng gửi của bạn đang phát sự kiện phát trực tiếp video hoặc âm thanh, SDK sẽ tự động hiển thị nút phát/dừng thay vì nút phát/tạm dừng trong tay điều khiển mini.
Hãy xem phần Tuỳ chỉnh giao diện người dùng của ứng dụng phát trên iOS để biết cách ứng dụng phát có thể định cấu hình giao diện của tiện ích Truyền.
Có hai cách để thêm tay điều khiển mini vào ứng dụng gửi:
- Cho phép khung Cast quản lý bố cục của tay điều khiển mini bằng cách gói trình điều khiển khung hiển thị hiện có của bạn bằng trình điều khiển khung hiển thị riêng.
- Tự quản lý bố cục của tiện ích tay điều khiển mini bằng cách thêm tiện ích đó vào trình điều khiển thành phần hiển thị hiện có bằng cách cung cấp thành phần hiển thị phụ trong bảng phân cảnh.
Gói bằng GCKUICastContainerViewController
Cách đầu tiên là sử dụng GCKUICastContainerViewController
để gói một trình điều khiển khung hiển thị khác và thêm GCKUIMiniMediaControlsViewController
ở dưới cùng. Phương pháp này bị hạn chế ở chỗ bạn không thể tuỳ chỉnh ảnh động và không thể định cấu hình hành vi của trình điều khiển thành phần hiển thị vùng chứa.
Cách đầu tiên này thường được thực hiện trong phương thức -[application:didFinishLaunchingWithOptions:]
của trình uỷ quyền ứng dụng:
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
Nhúng vào trình điều khiển thành phần hiển thị hiện có
Cách thứ hai là thêm trình điều khiển mini trực tiếp vào trình điều khiển khung hiển thị hiện có bằng cách sử dụng createMiniMediaControlsViewController
để tạo một thực thể GCKUIMiniMediaControlsViewController
, sau đó thêm thực thể đó vào trình điều khiển khung hiển thị vùng chứa dưới dạng khung hiển thị phụ.
Thiết lập trình điều khiển chế độ xem trong trình uỷ quyền ứng dụng:
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; }
Trong trình điều khiển thành phần hiển thị gốc, hãy tạo một thực thể GCKUIMiniMediaControlsViewController
và thêm thực thể đó vào trình điều khiển thành phần hiển thị vùng chứa dưới dạng thành phần hiển thị phụ:
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
sẽ cho trình điều khiển thành phần hiển thị lưu trữ biết thời điểm hiển thị trình điều khiển mini:
func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController, shouldAppear _: Bool) { updateControlBarsVisibility() }
- (void)miniMediaControlsViewController: (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController shouldAppear:(BOOL)shouldAppear { [self updateControlBarsVisibility]; }
Thêm bộ điều khiển mở rộng
Danh sách kiểm tra thiết kế Google Cast yêu cầu ứng dụng gửi phải cung cấp một trình điều khiển mở rộng cho nội dung đa phương tiện đang truyền. Tay điều khiển mở rộng là phiên bản toàn màn hình của tay điều khiển thu nhỏ.
Trình điều khiển mở rộng là một chế độ xem toàn màn hình, cung cấp toàn quyền kiểm soát việc phát nội dung đa phương tiện từ xa. Khung hiển thị này cho phép ứng dụng truyền phát quản lý mọi khía cạnh có thể quản lý của một phiên truyền phát, ngoại trừ tính năng điều khiển âm lượng của Web Receiver và vòng đời của phiên (kết nối/dừng truyền phát). Lớp này cũng cung cấp tất cả thông tin trạng thái về phiên phát nội dung nghe nhìn (hình minh hoạ, tiêu đề, phụ đề, v.v.).
Chức năng của thành phần hiển thị này được triển khai bằng lớp GCKUIExpandedMediaControlsViewController
.
Việc đầu tiên bạn cần làm là bật trình điều khiển mở rộng mặc định trong ngữ cảnh truyền. Sửa đổi thực thể uỷ quyền ứng dụng để bật bộ điều khiển mở rộng mặc định:
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; .. }
Thêm mã sau vào trình điều khiển thành phần hiển thị để tải trình điều khiển mở rộng khi người dùng bắt đầu truyền 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]; }
Tay điều khiển mở rộng cũng sẽ tự động khởi chạy khi người dùng nhấn vào tay điều khiển mini.
Khi ứng dụng gửi của bạn đang phát video hoặc luồng âm thanh trực tiếp, SDK sẽ tự động hiển thị nút phát/dừng thay vì nút phát/tạm dừng trong bộ điều khiển mở rộng.
Hãy xem phần Áp dụng kiểu tuỳ chỉnh cho ứng dụng iOS để biết cách ứng dụng của bạn có thể định cấu hình giao diện của tiện ích Cast.
Điều chỉnh âm lượng
Khung Cast tự động quản lý âm lượng cho ứng dụng gửi. Khung này tự động đồng bộ hoá với âm lượng của Web Receiver cho các tiện ích giao diện người dùng được cung cấp. Để đồng bộ hoá thanh trượt do ứng dụng cung cấp, hãy sử dụng GCKUIDeviceVolumeController
.
Điều khiển âm lượng bằng nút vật lý
Bạn có thể sử dụng các nút âm lượng thực trên thiết bị gửi để thay đổi âm lượng của phiên Truyền trên Trình thu web bằng cách sử dụng cờ physicalVolumeButtonsWillControlDeviceVolume
trên GCKCastOptions
được đặt trên 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];
Xử lý lỗi
Điều rất quan trọng là các ứng dụng gửi phải xử lý tất cả lệnh gọi lại lỗi và quyết định phản hồi tốt nhất cho từng giai đoạn của vòng đời Truyền. Ứng dụng có thể hiển thị hộp thoại lỗi cho người dùng hoặc quyết định kết thúc phiên Truyền.
Ghi nhật ký
GCKLogger
là một singleton dùng để ghi nhật ký bằng khung. Sử dụng GCKLoggerDelegate
để tuỳ chỉnh cách bạn xử lý thông điệp nhật ký.
Khi sử dụng GCKLogger
, SDK sẽ tạo ra đầu ra ghi nhật ký ở dạng thông báo gỡ lỗi, lỗi và cảnh báo. Các thông điệp nhật ký này hỗ trợ gỡ lỗi và hữu ích cho việc khắc phục sự cố cũng như xác định vấn đề. Theo mặc định, đầu ra nhật ký sẽ bị chặn, nhưng bằng cách chỉ định GCKLoggerDelegate
, ứng dụng gửi có thể nhận các thông báo này từ SDK và ghi lại vào bảng điều khiển hệ thống.
@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
Để bật thông báo gỡ lỗi và thông báo chi tiết, hãy thêm dòng này vào mã sau khi đặt trình uỷ quyền (đã hiển thị trước đó):
let filter = GCKLoggerFilter.init() filter.minimumLevel = GCKLoggerLevel.verbose GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setMinimumLevel:GCKLoggerLevelVerbose]; [GCKLogger sharedInstance].filter = filter;
Bạn cũng có thể lọc các thông điệp nhật ký do GCKLogger
tạo ra.
Đặt cấp độ ghi nhật ký tối thiểu cho mỗi lớp, ví dụ:
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;
Tên lớp có thể là tên cố định hoặc mẫu glob, ví dụ: GCKUI\*
và GCK\*Session
.