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ên thiết bị phát iOS bằng SDK thiết bị phát iOS.
Thiết bị di động hoặc máy tính xách tay là thiết bị phát kiểm soát việc phát, còn thiết bị Google Cast là thiết bị nhận hiển thị nội dung trên TV.
Khung thiết bị phát đề 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 thiết bị phát. Ứng dụng trên thiết bị phát hoặc ứng dụng Cast đề cập đến một ứng dụng cũng đang chạy trên thiết bị phát. Ứng dụng Web Receiver đề cập đến ứng dụng HTML đang chạy trên Web Receiver.
Khung thiết bị phát 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ên thiết bị phát 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.
Quy trình ứng dụng
Các bước sau đây mô tả quy trình thực thi cấp cao điển hình cho ứng dụng iOS trên thiết bị phát:
- Khung Cast bắt đầu
GCKDiscoveryManagerdựa trên các thuộc tính được cung cấp trongGCKCastOptionsđể bắt đầu quét 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ị Cast đã phát hiện.
- Khi người dùng chọn một thiết bị truyền, khung sẽ cố gắng khởi chạy ứng dụng Web Receiver trên thiết bị truyền.
- Khung sẽ gọi các lệnh gọi lại trong ứng dụng trên thiết bị phát để xác nhận rằng ứng dụng Web Receiver đã được khởi chạy.
- Khung sẽ tạo một kênh giao tiếp giữa ứng dụng trên thiết bị phát và ứng dụng Web Receiver.
- Khung sẽ 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 sẽ đồng bộ hoá trạng thái phát nội dung nghe nhìn giữa thiết bị phát 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 thiết bị phát, khung sẽ chuyển các yêu cầu kiểm soát nội dung nghe nhìn đó đến Web Receiver và khi Web Receiver gửi thông tin cập nhật về trạng thái nội dung nghe nhìn, khung sẽ cập nhật trạng thái của giao diện người dùng trên thiết bị phát.
- Khi người dùng nhấp vào nút Truyền để ngắt kết nối khỏi thiết bị truyền, khung sẽ ngắt kết nối ứng dụng trên thiết bị phát khỏi Web Receiver.
Để khắc phục sự cố với thiết bị phát, bạn cần bật tính năng ghi nhật ký.
Để xem danh sách đầy đủ tất cả các lớp, phương thức và sự kiện trong khung Google Cast iOS, hãy xem Tài liệu tham khảo API Google Cast iOS. Các phần sau đây trình bày các bước tích hợp Cast vào ứng dụng iOS.
Gọi phương thức từ luồng chính
Khởi chạy ngữ cảnh Cast
Khung Cast có một đối tượng singleton chung, the
GCKCastContext, which
điều phối tất cả các hoạt động của khung. Đối tượng này phải được khởi chạy sớm trong vòng đời của ứng dụng, thường là trong phương thức -[application:didFinishLaunchingWithOptions:] của thực thể đại diện của ứng dụng, để quá trình tiếp tục phiên tự động khi khởi động lại ứng dụng trên thiết bị phát có thể kích hoạt đúng cách.
Bạn phải cung cấp đối tượng GCKCastOptions
khi khởi chạy 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ã ứng dụng Web Receiver, được dùng để lọc kết quả phát hiện và khởi chạy ứng dụng Web Receiver khi bắt đầu phiên Cast.
Phương thức -[application:didFinishLaunchingWithOptions:] cũng là nơi thích hợp để thiết lập một thực thể đại diện ghi nhật ký nhằm nhận các thông báo ghi nhật ký từ khung.
Các thông báo 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
Tiện ích UX Cast
SDK Cast iOS cung cấp các tiện ích này tuân thủ Danh sách kiểm tra thiết kế Cast:
Lớp phủ giới thiệu: Lớp
GCKCastContextcó một phương thức,presentCastInstructionsViewControllerOnceWithCastButton, Bạn có thể sử dụng phương thức này để làm nổi bật nút Truyền vào lần đầu tiên có Web Receiver khả dụng. Ứng dụng trên thiết bị phát có thể tuỳ chỉnh văn bản, vị trí của văn bản tiêu đề và nút Đóng.Nút Truyền: Bắt đầu từ SDK thiết bị phát Cast iOS 4.6.0, nút truyền luôn xuất hiện khi thiết bị phát được kết nối với Wi-Fi. Vào 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, hộp thoại cấp quyền sẽ xuất hiện để người dùng có thể cấp quyền truy cập vào mạng cục bộ cho các thiết bị trên mạng. Sau đó, khi người dùng nhấn vào nút truyền, hộp thoại truyền sẽ xuất hiện, 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 trong khi thiết bị được 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 xưởng thu âm và hình thu nhỏ) hoặc cho phép người dùng ngắt kết nối khỏi thiết bị truyền. Khi người dùng nhấn vào nút truyền trong khi không có thiết bị nào, màn hình sẽ xuất hiện, cung cấp cho người dùng thông tin về 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 nhỏ: 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 bộ điều khiển mở rộng để chuyển sang màn hình khác trong ứng dụng trên thiết bị phát, bộ điều khiển thu nhỏ sẽ xuất hiện ở 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à kiểm soát việc 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 thu nhỏ, 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 để kiểm soát việc phát nội dung nghe nhìn.
Thêm nút Truyền
Khung 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 thành phần này vào thanh tiêu đề của ứng dụng bằng cách gói thành phần này trong UIBarButtonItem. Bạn có thể cài đặt nút Truyền như sau trong lớp con UIViewController thông thường:
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, khi bạn nhấn vào nút này, hộp thoại Truyền do khung cung cấp sẽ mở ra.
GCKUICastButton
cũng có thể thêm trực tiếp vào bảng phân cảnh.
Định cấu hình tính năng phát hiện thiết bị
Trong khung, tính năng phát hiện thiết bị sẽ tự động diễn ra. Bạn không cần phải bắt đầu hoặc dừng rõ ràng quy trình phát hiện trừ phi bạn triển khai giao diện người dùng tuỳ chỉnh.
Tính năng phát hiện trong khung được quản lý bởi lớp
GCKDiscoveryManager,
là một thuộc tính của
GCKCastContext. Khung cung cấp thành phần hộp thoại Truyền mặc định để chọn và kiểm soát thiết bị. Danh sách thiết bị được sắp xếp theo tên dễ nhớ của thiết bị theo thứ tự từ điển.
Cách hoạt động của tính năng quản lý phiên
SDK Cast giới thiệu khái niệm về phiên Cast. Việc thiết lập phiên này 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 kiểm soát nội dung nghe nhìn. Hãy xem hướng dẫn về Vòng đời của ứng dụng Web Receiver để biết thêm thông tin về các phiên Cast và vòng đời của Web Receiver.
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 Cast hiệ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 của phiên, chẳng hạn như tạo,
tạm ngưng, tiếp tục và chấm dứt phiên. Khung sẽ tự động tạm ngưng các phiên khi ứng dụng trên thiết bị phát chuyển sang chạy trong nền và cố gắng tiếp tục các phiên đó khi ứng dụng quay lại chạy ở nền trước (hoặc khởi chạy lại sau khi ứng dụng bị chấm dứt đột ngột/bất thường 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
các phiên một cách rõ ràng thông qua các phương thức trên
GCKSessionManager.
Nếu ứng dụng cần thực hiện quá trình xử lý đặc biệt để phản hồi các sự kiện trong vòng đời của phiên, thì ứng dụng đó có thể đăng ký một hoặc nhiều thực thể GCKSessionManagerListener với
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 phiên phát trực tuyến
Việc duy trì trạng thái phiên là cơ sở của quá trình chuyển đổi phiên phát trực tuyến. Tại đây, người dùng có thể di chuyển các phiên phát trực tuyến âm thanh và video hiện có trên các thiết bị bằ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 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ị truyền có chương trình cơ sở mới nhất đều có thể đóng vai trò là nguồn hoặc đích trong quá trình chuyển đổi phiên phát trực tuyến.
Để nhận thiết bị đích mới trong quá trình chuyển đổi phiên phát trực tuyến, hãy sử dụng thuộc tính
GCKCastSession#device
trong lệnh gọi lại
[sessionManager:didResumeCastSession:].
Hãy xem bài viết Chuyển đổi phiên phát trực tuyến 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 nhỏ, chẳng hạn như:
- Khôi phục sau khi mất Wi-Fi tạm thời
- Khôi phục sau khi thiết bị chuyển sang chế độ ngủ
- Khôi phục sau khi ứng dụng chuyển sang chạy trong nền
- Khôi phục nếu ứng dụng gặp sự cố
Cách hoạt động của tính năng kiểm soát nội dung nghe nhìn
Nếu phiên Cast được thiết lập bằng ứng dụng Web Receiver hỗ trợ không gian tên nội dung nghe nhìn, thì khung sẽ tự động tạo một thực thể
GCKRemoteMediaClient
; bạn có thể truy cập vào thực thể này dưới dạng thuộc tính
remoteMediaClient của thực thể
GCKCastSession.
Tất cả các phương thức trên GCKRemoteMediaClient đưa ra yêu cầu đến Web Receiver
sẽ trả về
GCKRequest đối tượng mà
bạn có thể dùng để theo dõi yêu cầu đó. Bạn có thể chỉ định
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.
Bạn có thể chia sẻ thực thể GCKRemoteMediaClient với nhiều phần của ứng dụng. Trên thực tế, 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ỏ, sẽ chia sẻ thực thể này. Để kết thúc, GCKRemoteMediaClient
hỗ trợ việc đăng ký nhiều
GCKRemoteMediaClientListeners.
Đặt siêu dữ liệu nội dung nghe nhìn
Lớp
GCKMediaMetadata
biểu thị 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 của xưởng thu âm 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 và lưu vào bộ nhớ đệm hình ảnh 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 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 thực thể này để tải nội dung nghe nhìn trên ứng dụng nhận. Sau đó, bạn có thể sử dụng GCKRemoteMediaClient
để kiểm soát ứng dụng trình phát nội dung nghe nhìn đang chạy trên thiết bị nhận, 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 về cách sử dụng bản nhạc.
Đị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ể
GCKVideoInfo hiện tại.
Thực thể này chứa loại định dạng TV HDR, 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 theo giá trị enum GCKVideoInfoHDRType.
Thêm bộ điều khiển thu nhỏ
Theo Danh sách kiểm tra thiết kế Cast, ứng dụng trên thiết bị phát phải cung cấp một nút điều khiển liên tục, được gọi là bộ điều khiển thu nhỏ , sẽ xuất hiện khi người dùng rời khỏi trang nội dung hiện tại. Bộ điều khiển thu nhỏ cung cấp quyền truy cập tức thì và lời nhắc hữu hình cho phiên Cast hiện tại.
Khung Cast cung cấp một thanh điều khiển,
GCKUIMiniMediaControlsViewController,
mà bạn có thể thêm vào các cảnh mà bạn muốn hiển thị bộ điều khiển thu nhỏ.
Khi ứng dụng trên thiết bị phát đang phát video hoặc phiên phát trực tiếp âm thanh, SDK sẽ tự động hiển thị nút phát/dừng thay cho nút phát/tạm dừng trong bộ điều khiển thu nhỏ.
Hãy xem bài viết Tuỳ chỉnh giao diện người dùng trên thiết bị phát iOS để biết cách ứng dụng trên thiết bị phát có thể định cấu hình giao diện của các tiện ích Cast.
Có 2 cách để thêm bộ điều khiển thu nhỏ vào ứng dụng trên thiết bị phát:
- Cho phép khung Cast quản lý bố cục của bộ điều khiển thu nhỏ bằng cách gói bộ điều khiển khung hiển thị hiện có bằng bộ điều khiển khung hiển thị riêng.
- Tự quản lý bố cục của tiện ích bộ điều khiển thu nhỏ bằng cách thêm tiện ích này vào bộ điều khiển khung hiển thị hiện có bằng cách cung cấp khung 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 bộ đ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 bộ điều khiển khung 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 thực thể đại diện của ứ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 bộ điều khiển khung hiển thị hiện có
Cách thứ hai là thêm trực tiếp bộ điều khiển thu nhỏ vào bộ điều khiển khung hiển thị hiện có bằng cách sử dụng
createMiniMediaControlsViewController
để tạo thực thể
GCKUIMiniMediaControlsViewController
, sau đó thêm thực thể này vào bộ đ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 bộ điều khiển khung hiển thị trong thực thể đại diện của ứ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 bộ điều khiển khung hiển thị gốc, hãy tạo thực thể GCKUIMiniMediaControlsViewController và thêm thực thể này vào bộ điều khiển khung hiển thị vùng chứa dưới dạng khung 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
The
GCKUIMiniMediaControlsViewControllerDelegate
cho biết bộ điều khiển khung hiển thị máy chủ khi bộ điều khiển thu nhỏ sẽ xuất hiện:
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 trên thiết bị phát cung cấp bộ điều khiển mở rộng cho nội dung nghe nhìn đang truyền. Bộ điều khiển mở rộng là phiên bản toàn màn hình của bộ điều khiển thu nhỏ.
Bộ điều khiển mở rộng là khung hiển thị toàn màn hình cung cấp khả năng kiểm soát đầy đủ việc phát nội dung nghe nhìn từ xa. Khung hiển thị này sẽ cho phép ứng dụng truyền quản lý mọi khía cạnh có thể quản lý của phiên truyền, ngoại trừ tính năng kiểm soát âm lượng của Web Receiver và vòng đời của phiên (kết nối/dừng truyền). Khung hiển thị này cũng cung cấp tất cả thông tin về trạng thái của phiên nội dung nghe nhìn (ảnh minh hoạ, tiêu đề, phụ đề, v.v.).
Chức năng của khung hiển thị này được triển khai bởi lớp
GCKUIExpandedMediaControlsViewController.
Việc đầu tiên bạn phải làm là bật bộ điều khiển mở rộng mặc định trong ngữ cảnh truyền. Sửa đổi thực thể đại diện của ứ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 bộ điều khiển khung hiển thị để tải bộ đ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]; }
Bộ đ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 bộ điều khiển thu nhỏ.
Khi ứng dụng trên thiết bị phát đang phát video hoặc phiên phát trực tiếp âm thanh, SDK sẽ tự động hiển thị nút phát/dừng thay cho nút phát/tạm dừng trong bộ điều khiển mở rộng.
Hãy xem bài viết Áp dụng kiểu tuỳ chỉnh cho ứng dụng iOS App để biết cách ứng dụng trên thiết bị phát có thể định cấu hình giao diện của các 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 trên thiết bị phát. Khung sẽ 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 chỉnh âm lượng bằng nút vật lý
Bạn có thể sử dụng các nút âm lượng vật lý trên thiết bị phát để thay đổi
âm lượng của phiên Cast trên Web Receiver 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
Các ứng dụng trên thiết bị phát cần xử lý tất cả cá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 Cast. Ứng dụng có thể hiển thị hộp thoại lỗi cho người dùng hoặc có thể quyết định kết thúc phiên Cast.
Xin lưu ý rằng một số lỗi, bao gồm cả
GCKErrorCode
GCKErrorCodeCancelled, là hành vi dự kiến.
Đừng cố gắng thử lại kết nối không thành công với GCKErrorCodeCancelled.
Nếu bạn làm vậy, hành vi không mong muốn có thể xảy ra.
Ghi nhật ký
GCKLogger
là một singleton được khung dùng để ghi nhật ký. Sử dụng
GCKLoggerDelegate
để tuỳ chỉnh cách bạn xử lý thông báo nhật ký.
Khi sử dụng GCKLogger, SDK sẽ tạo đầ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 báo nhật ký này hỗ trợ việc gỡ lỗi và hữu ích cho việc khắc phục sự cố và 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 trên thiết bị phát có thể nhận các thông báo này từ SDK và ghi nhật ký 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 cả 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 thực thể đại diện (đã trình bày 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 báo nhật ký do
GCKLogger tạo.
Đặt mức 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 theo nghĩa đen hoặc mẫu glob, ví dụ: GCKUI\* và GCK\*Session.