1. Tổng quan

Lớp học lập trình này sẽ hướng dẫn bạn cách sửa đổi một ứng dụng video hiện có trên iOS để truyền nội dung trên một thiết bị hỗ trợ Google Cast.
Google Cast là gì?
Google Cast cho phép người dùng truyền nội dung từ thiết bị di động lên TV. Sau đó, người dùng có thể dùng thiết bị di động làm điều khiển từ xa để phát nội dung nghe nhìn trên TV.
Google Cast SDK cho phép bạn mở rộng ứng dụng để điều khiển các thiết bị hỗ trợ Google Cast (chẳng hạn như TV hoặc hệ thống âm thanh). Cast SDK cho phép bạn thêm các thành phần giao diện người dùng cần thiết dựa trên Danh sách kiểm tra thiết kế của Google Cast.
Danh sách kiểm tra thiết kế Google Cast được cung cấp để giúp trải nghiệm người dùng Cast trở nên đơn giản và dễ đoán trên tất cả các nền tảng được hỗ trợ.
Chúng ta sẽ xây dựng những gì?
Khi hoàn tất lớp học lập trình này, bạn sẽ có một ứng dụng video dành cho iOS có thể truyền video đến một thiết bị truyền Google Cast.
Kiến thức bạn sẽ học được
- Cách thêm Google Cast SDK vào một ứng dụng video mẫu.
- Cách thêm nút Truyền để chọn một thiết bị truyền.
- Cách kết nối với thiết bị truyền và chạy trình nhận nội dung nghe nhìn.
- Cách truyền video.
- Cách thêm bộ điều khiển thu nhỏ của Cast vào ứng dụng.
- Cách thêm bộ điều khiển mở rộng.
- Cách cung cấp lớp phủ giới thiệu.
- Cách tuỳ chỉnh tiện ích Cast.
- Cách tích hợp Cast Connect
Bạn cần có
- Xcode mới nhất.
- Một thiết bị di động chạy iOS 9 trở lên (hoặc Trình mô phỏng Xcode).
- Cáp dữ liệu USB để kết nối thiết bị di động với máy tính bạn dùng để phát triển (nếu bạn đang dùng thiết bị).
- Một thiết bị Google Cast, chẳng hạn như Chromecast hoặc Android TV được thiết lập để có quyền truy cập vào Internet.
- TV hoặc màn hình có cổng đầu vào HDMI.
- Bạn cần có Chromecast có Google TV để kiểm thử việc tích hợp Cast Connect, nhưng không bắt buộc đối với phần còn lại của Lớp học lập trình. Nếu không có, bạn có thể bỏ qua bước Thêm tính năng hỗ trợ Cast Connect ở gần cuối hướng dẫn này.
Trải nghiệm
- Bạn cần có kiến thức về phát triển iOS.
- Bạn cũng cần có kiến thức về cách xem TV :)
Bạn sẽ sử dụng hướng dẫn này như thế nào?
Bạn đánh giá thế nào về trải nghiệm của mình khi tạo ứng dụng iOS?
Bạn đánh giá thế nào về trải nghiệm xem truyền hình?
2. Nhận mã mẫu
Bạn có thể tải tất cả mã mẫu xuống máy tính...
và giải nén tệp zip đã tải xuống.
3. Chạy ứng dụng mẫu

Trước tiên, hãy xem ứng dụng mẫu hoàn chỉnh trông như thế nào. Ứng dụng này là một trình phát video cơ bản. Người dùng có thể chọn một video trong danh sách rồi phát video đó trên thiết bị hoặc truyền video đó đến một thiết bị truyền Google Cast.
Sau khi tải mã xuống, các hướng dẫn sau đây mô tả cách mở và chạy ứng dụng mẫu đã hoàn tất trong Xcode:
Câu hỏi thường gặp
Thiết lập CocoaPods
Để thiết lập CocoaPods, hãy chuyển đến bảng điều khiển và cài đặt bằng Ruby mặc định có trên macOS:
sudo gem install cocoapods
Nếu bạn gặp vấn đề, hãy tham khảo tài liệu chính thức để tải xuống và cài đặt trình quản lý phần phụ thuộc.
Thiết lập dự án
- Chuyển đến cửa sổ dòng lệnh rồi chuyển đến thư mục codelab.
- Cài đặt các phần phụ thuộc từ Podfile.
cd app-done pod update pod install
- Mở Xcode rồi chọn Open another project... (Mở một dự án khác...)
- Chọn tệp
CastVideos-ios.xcworkspacetrong thư mục
app-donetrong thư mục mã mẫu.
Chạy ứng dụng
Chọn mục tiêu và trình mô phỏng, rồi chạy ứng dụng:

Sau vài giây, bạn sẽ thấy ứng dụng video xuất hiện.
Nhớ nhấp vào "Cho phép" khi thông báo xuất hiện về việc chấp nhận các kết nối mạng đến. Biểu tượng Truyền sẽ không xuất hiện nếu bạn không chấp nhận lựa chọn này.

Nhấp vào nút Truyền rồi chọn thiết bị truyền của bạn.
Chọn một video rồi nhấp vào nút phát.
Video sẽ bắt đầu phát trên thiết bị Google Cast.
Bộ điều khiển mở rộng sẽ xuất hiện. Bạn có thể dùng nút phát/tạm dừng để điều khiển quá trình phát.
Quay lại danh sách video.
Giờ đây, bạn sẽ thấy một bộ điều khiển thu nhỏ ở cuối màn hình.

Nhấp vào nút tạm dừng trong bộ điều khiển thu nhỏ để tạm dừng video trên thiết bị nhận. Nhấp vào nút phát trong bộ điều khiển thu nhỏ để tiếp tục phát lại video.
Nhấp vào nút Truyền để dừng truyền đến thiết bị truyền.
4. Chuẩn bị dự án khởi đầu

Bạn cần thêm tính năng hỗ trợ Google Cast vào ứng dụng khởi động mà bạn đã tải xuống. Sau đây là một số thuật ngữ về Google Cast mà chúng ta sẽ sử dụng trong lớp học lập trình này:
- ứng dụng người gửi chạy trên thiết bị di động hoặc máy tính xách tay,
- ứng dụng receiver chạy trên thiết bị truyền Google Cast.
Thiết lập dự án
Giờ đây, bạn đã sẵn sàng xây dựng dựa trên dự án khởi đầu bằng Xcode:
- Chuyển đến cửa sổ dòng lệnh rồi chuyển đến thư mục codelab.
- Cài đặt các phần phụ thuộc từ Podfile.
cd app-start pod update pod install
- Mở Xcode rồi chọn Open another project... (Mở một dự án khác...)
- Chọn tệp
CastVideos-ios.xcworkspacetrong thư mục
app-starttrong thư mục mã mẫu.
Thiết kế ứng dụng
Ứng dụng tìm nạp danh sách video từ một máy chủ web từ xa và cung cấp danh sách để người dùng duyệt xem. Người dùng có thể chọn một video để xem thông tin chi tiết hoặc phát video đó trên thiết bị di động.
Ứng dụng này bao gồm 2 trình điều khiển khung hiển thị chính: MediaTableViewController và MediaViewController.
MediaTableViewController
UITableViewController này hiển thị danh sách video từ một thực thể MediaListModel. Danh sách video và siêu dữ liệu liên quan được lưu trữ trên một máy chủ từ xa dưới dạng tệp JSON. MediaListModel tìm nạp JSON này và xử lý để tạo danh sách các đối tượng MediaItem.
Đối tượng MediaItem mô hình hoá một video và siêu dữ liệu liên kết của video đó, chẳng hạn như tiêu đề, nội dung mô tả, URL cho hình ảnh và URL cho luồng phát.
MediaTableViewController tạo một thực thể MediaListModel rồi tự đăng ký làm MediaListModelDelegate để được thông báo khi siêu dữ liệu nội dung nghe nhìn đã được tải xuống để có thể tải chế độ xem bảng.
Người dùng sẽ thấy một danh sách hình thu nhỏ của video kèm theo nội dung mô tả ngắn gọn cho từng video. Khi một mục được chọn, MediaItem tương ứng sẽ được chuyển đến MediaViewController.
MediaViewController
Trình điều khiển khung hiển thị này hiển thị siêu dữ liệu về một video cụ thể và cho phép người dùng phát video đó trên thiết bị di động.
Trình điều khiển khung hiển thị lưu trữ một LocalPlayerView, một số nút điều khiển nội dung nghe nhìn và một vùng văn bản để hiện nội dung mô tả của video đã chọn. Trình phát chiếm phần trên cùng của màn hình, chừa chỗ cho phần mô tả chi tiết về video bên dưới. Người dùng có thể phát/tạm dừng hoặc tua video cục bộ.
Câu hỏi thường gặp
5. Thêm nút Truyền

Ứng dụng hỗ trợ Cast sẽ hiển thị nút Truyền trong mỗi trình điều khiển khung hiển thị. Khi nhấp vào nút Truyền, người dùng sẽ thấy danh sách các thiết bị truyền mà họ có thể chọn. Nếu người dùng đang phát nội dung cục bộ trên thiết bị gửi, thì việc chọn một thiết bị truyền sẽ bắt đầu hoặc tiếp tục phát trên thiết bị truyền đó. Bất cứ lúc nào trong phiên truyền, người dùng có thể nhấp vào nút Truyền và dừng truyền ứng dụng của bạn đến thiết bị truyền. Người dùng phải có thể kết nối hoặc ngắt kết nối với thiết bị truyền trong khi ở bất kỳ màn hình nào của ứng dụng, như mô tả trong Danh sách kiểm tra thiết kế Google Cast.
Cấu hình
Dự án bắt đầu yêu cầu các phần phụ thuộc và chế độ thiết lập Xcode giống như bạn đã thực hiện cho ứng dụng mẫu hoàn chỉnh. Hãy quay lại phần đó và làm theo các bước tương tự để thêm GoogleCast.framework vào dự án ứng dụng bắt đầu.
Khởi chạy
Khung Cast có một đối tượng singleton chung là GCKCastContext, đối tượng này đ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 uỷ quyền ứng dụng, để quá trình tiếp tục phiên tự động khi khởi động lại ứng dụng người gửi có thể kích hoạt đúng cách và quá trình quét thiết bị có thể bắt đầu.
Bạn phải cung cấp một đối tượng GCKCastOptions khi khởi chạy GCKCastContext. Lớp này chứa các lựa 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 nhận. Mã này được dùng để lọc kết quả tìm kiếm thiết bị Cast và để chạy ứng dụng nhận khi một phiên Cast bắt đầu.
Phương thức application(_:didFinishLaunchingWithOptions:) cũng là một nơi phù hợp để thiết lập một uỷ quyền ghi nhật ký nhằm nhận các thông báo ghi nhật ký từ khung Cast. Những thông tin này có thể hữu ích khi gỡ lỗi và khắc phục sự cố.
Khi phát triển ứng dụng của riêng mình có hỗ trợ Cast, bạn phải đăng ký làm nhà phát triển Cast rồi lấy mã ứng dụng cho ứng dụng của mình. Trong lớp học lập trình này, chúng ta sẽ sử dụng một mã ứng dụng mẫu.
Thêm mã sau vào AppDelegate.swift để khởi động GCKCastContext bằng mã ứng dụng từ chế độ cài đặt mặc định của người dùng và thêm một trình ghi nhật ký cho khung Google Cast:
import GoogleCast
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
fileprivate var enableSDKLogging = true
...
func application(_: UIApplication,
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
let options = GCKCastOptions(discoveryCriteria: GCKDiscoveryCriteria(applicationID: kReceiverAppID))
options.physicalVolumeButtonsWillControlDeviceVolume = true
GCKCastContext.setSharedInstanceWith(options)
window?.clipsToBounds = true
setupCastLogging()
...
}
...
func setupCastLogging() {
let logFilter = GCKLoggerFilter()
let classesToLog = ["GCKDeviceScanner", "GCKDeviceProvider", "GCKDiscoveryManager", "GCKCastChannel",
"GCKMediaControlChannel", "GCKUICastButton", "GCKUIMediaController", "NSMutableDictionary"]
logFilter.setLoggingLevel(.verbose, forClasses: classesToLog)
GCKLogger.sharedInstance().filter = logFilter
GCKLogger.sharedInstance().delegate = self
}
}
...
// MARK: - GCKLoggerDelegate
extension AppDelegate: GCKLoggerDelegate {
func logMessage(_ message: String,
at _: GCKLoggerLevel,
fromFunction function: String,
location: String) {
if enableSDKLogging {
// Send SDK's log messages directly to the console.
print("\(location): \(function) - \(message)")
}
}
}
Nút truyền
Giờ đây, sau khi GCKCastContext được khởi chạy, chúng ta cần thêm nút Truyền để cho phép người dùng chọn một thiết bị truyền. Cast SDK cung cấp một thành phần nút truyền có tên là GCKUICastButton dưới dạng một lớp con UIButton. Bạn có thể thêm biểu tượng này vào thanh tiêu đề của ứng dụng bằng cách gói biểu tượng đó trong một UIBarButtonItem. Chúng ta cần thêm nút Truyền vào cả MediaTableViewController và MediaViewController.
Thêm mã sau vào MediaTableViewController.swift và MediaViewController.swift:
import GoogleCast
@objc(MediaTableViewController)
class MediaTableViewController: UITableViewController, GCKSessionManagerListener,
MediaListModelDelegate, GCKRequestDelegate {
private var castButton: GCKUICastButton!
...
override func viewDidLoad() {
print("MediaTableViewController - viewDidLoad")
super.viewDidLoad()
...
castButton = GCKUICastButton(frame: CGRect(x: CGFloat(0), y: CGFloat(0),
width: CGFloat(24), height: CGFloat(24)))
// Overwrite the UIAppearance theme in the AppDelegate.
castButton.tintColor = UIColor.white
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)
...
}
...
}
Tiếp theo, hãy thêm mã sau vào MediaViewController.swift của bạn:
import GoogleCast
@objc(MediaViewController)
class MediaViewController: UIViewController, GCKSessionManagerListener, GCKRemoteMediaClientListener,
LocalPlayerViewDelegate, GCKRequestDelegate {
private var castButton: GCKUICastButton!
...
override func viewDidLoad() {
super.viewDidLoad()
print("in MediaViewController viewDidLoad")
...
castButton = GCKUICastButton(frame: CGRect(x: CGFloat(0), y: CGFloat(0),
width: CGFloat(24), height: CGFloat(24)))
// Overwrite the UIAppearance theme in the AppDelegate.
castButton.tintColor = UIColor.white
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)
...
}
...
}
Bây giờ, hãy chạy ứng dụng. Bạn sẽ thấy nút Truyền trong thanh điều hướng của ứng dụng và khi bạn nhấp vào nút này, nút này sẽ liệt kê các thiết bị truyền trên mạng cục bộ. GCKCastContext sẽ tự động quản lý tính năng phát hiện thiết bị. Chọn thiết bị truyền và ứng dụng nhận mẫu sẽ tải trên thiết bị truyền. Bạn có thể di chuyển giữa hoạt động duyệt xem và hoạt động của trình phát cục bộ, đồng thời trạng thái của nút Truyền sẽ luôn được đồng bộ hoá.
Chúng tôi chưa thiết lập bất kỳ tính năng hỗ trợ nào cho việc phát nội dung nghe nhìn, vì vậy, bạn chưa thể phát video trên thiết bị truyền. Nhấp vào nút Truyền để dừng truyền.
6. Truyền nội dung video

Chúng ta sẽ mở rộng ứng dụng mẫu để phát video từ xa trên thiết bị truyền. Để làm như vậy, chúng ta cần theo dõi nhiều sự kiện do khung Cast tạo ra.
Truyền nội dung nghe nhìn
Nhìn chung, nếu bạn muốn phát nội dung nghe nhìn trên một thiết bị truyền, thì cần phải thực hiện những việc sau:
- Tạo một đối tượng
GCKMediaInformationtừ Cast SDK mô hình hoá một mục nội dung nghe nhìn. - Người dùng kết nối với thiết bị truyền để khởi chạy ứng dụng nhận.
- Tải đối tượng
GCKMediaInformationvào bộ nhận và phát nội dung. - Theo dõi trạng thái của nội dung nghe nhìn.
- Gửi lệnh phát đến bộ nhận dựa trên hoạt động tương tác của người dùng.
Bước 1 là ánh xạ một đối tượng sang một đối tượng khác; GCKMediaInformation là thứ mà Cast SDK hiểu được và MediaItem là quá trình đóng gói của ứng dụng cho một mục nội dung nghe nhìn; chúng ta có thể dễ dàng ánh xạ một MediaItem sang một GCKMediaInformation. Chúng ta đã thực hiện Bước 2 trong phần trước. Bạn có thể dễ dàng thực hiện bước 3 bằng Cast SDK.
Ứng dụng mẫu MediaViewController đã phân biệt giữa chế độ phát cục bộ và chế độ phát từ xa bằng cách sử dụng enum này:
enum PlaybackMode: Int {
case none = 0
case local
case remote
}
private var playbackMode = PlaybackMode.none
Trong lớp học lập trình này, bạn không cần phải hiểu chính xác cách hoạt động của tất cả logic trong trình phát mẫu. Điều quan trọng cần lưu ý là bạn sẽ phải sửa đổi trình phát nội dung nghe nhìn của ứng dụng để nhận biết hai vị trí phát theo cách tương tự.
Hiện tại, trình phát cục bộ luôn ở trạng thái phát cục bộ vì trình phát này chưa biết gì về trạng thái Truyền. Chúng ta cần cập nhật giao diện người dùng dựa trên các quá trình chuyển đổi trạng thái diễn ra trong khung Cast. Ví dụ: nếu bắt đầu truyền, chúng ta cần dừng phát cục bộ và tắt một số nút điều khiển. Tương tự, nếu dừng truyền khi đang ở trong trình điều khiển khung hiển thị này, chúng ta cần chuyển sang chế độ phát cục bộ. Để xử lý việc đó, chúng ta cần theo dõi nhiều sự kiện do khung Cast tạo ra.
Quản lý phiên truyền
Đối với khung truyền, một phiên truyền 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), kết nối với ứng dụng nhận và khởi tạo kênh điều khiển nội dung nghe nhìn (nếu thích hợp). Kênh điều khiển nội dung nghe nhìn là cách khung truyền gửi và nhận thông báo từ trình phát nội dung nghe nhìn của bộ nhận.
Phiên truyền sẽ tự động bắt đầu khi người dùng chọn một thiết bị thông qua nút Truyền và sẽ tự động dừng khi người dùng ngắt kết nối. Khung Cast cũng tự động xử lý việc kết nối lại với một phiên nhận do sự cố về mạng.
Các phiên truyền được quản lý bằng GCKSessionManager. Bạn có thể truy cập vào GCKSessionManager thông qua GCKCastContext.sharedInstance().sessionManager. Bạn có thể dùng các lệnh gọi lại 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à kết thúc.
Trước tiên, chúng ta cần đăng ký trình nghe phiên và khởi tạo một số biến:
class MediaViewController: UIViewController, GCKSessionManagerListener,
GCKRemoteMediaClientListener, LocalPlayerViewDelegate, GCKRequestDelegate {
...
private var sessionManager: GCKSessionManager!
...
required init?(coder: NSCoder) {
super.init(coder: coder)
sessionManager = GCKCastContext.sharedInstance().sessionManager
...
}
override func viewWillAppear(_ animated: Bool) {
...
let hasConnectedSession: Bool = (sessionManager.hasConnectedSession())
if hasConnectedSession, (playbackMode != .remote) {
populateMediaInfo(false, playPosition: 0)
switchToRemotePlayback()
} else if sessionManager.currentSession == nil, (playbackMode != .local) {
switchToLocalPlayback()
}
sessionManager.add(self)
...
}
override func viewWillDisappear(_ animated: Bool) {
...
sessionManager.remove(self)
sessionManager.currentCastSession?.remoteMediaClient?.remove(self)
...
super.viewWillDisappear(animated)
}
func switchToLocalPlayback() {
...
sessionManager.currentCastSession?.remoteMediaClient?.remove(self)
...
}
func switchToRemotePlayback() {
...
sessionManager.currentCastSession?.remoteMediaClient?.add(self)
...
}
// MARK: - GCKSessionManagerListener
func sessionManager(_: GCKSessionManager, didStart session: GCKSession) {
print("MediaViewController: sessionManager didStartSession \(session)")
setQueueButtonVisible(true)
switchToRemotePlayback()
}
func sessionManager(_: GCKSessionManager, didResumeSession session: GCKSession) {
print("MediaViewController: sessionManager didResumeSession \(session)")
setQueueButtonVisible(true)
switchToRemotePlayback()
}
func sessionManager(_: GCKSessionManager, didEnd _: GCKSession, withError error: Error?) {
print("session ended with error: \(String(describing: error))")
let message = "The Casting session has ended.\n\(String(describing: error))"
if let window = appDelegate?.window {
Toast.displayMessage(message, for: 3, in: window)
}
setQueueButtonVisible(false)
switchToLocalPlayback()
}
func sessionManager(_: GCKSessionManager, didFailToStartSessionWithError error: Error?) {
if let error = error {
showAlert(withTitle: "Failed to start a session", message: error.localizedDescription)
}
setQueueButtonVisible(false)
}
func sessionManager(_: GCKSessionManager,
didFailToResumeSession _: GCKSession, withError _: Error?) {
if let window = UIApplication.shared.delegate?.window {
Toast.displayMessage("The Casting session could not be resumed.",
for: 3, in: window)
}
setQueueButtonVisible(false)
switchToLocalPlayback()
}
...
}
Trong MediaViewController, chúng tôi muốn được thông báo khi kết nối hoặc ngắt kết nối với thiết bị truyền để có thể chuyển sang hoặc chuyển từ trình phát cục bộ. Xin lưu ý rằng không chỉ phiên bản ứng dụng đang chạy trên thiết bị di động của bạn mới có thể làm gián đoạn kết nối, mà một phiên bản khác của ứng dụng (hoặc một ứng dụng khác) đang chạy trên một thiết bị di động khác cũng có thể làm gián đoạn kết nối.
Bạn có thể truy cập vào phiên hiện đang hoạt động dưới dạng GCKCastContext.sharedInstance().sessionManager.currentCastSession. Các phiên được tạo và huỷ tự động để phản hồi cử chỉ của người dùng trong hộp thoại Truyền.
Đang tải nội dung nghe nhìn
Trong Cast SDK, GCKRemoteMediaClient cung cấp một tập hợp các API thuận tiện để quản lý việc phát nội dung nghe nhìn từ xa trên bộ nhận. Đối với GCKCastSession hỗ trợ phát nội dung nghe nhìn, SDK sẽ tự động tạo một thực thể GCKRemoteMediaClient. Bạn có thể truy cập vào thuộc tính này dưới dạng thuộc tính remoteMediaClient của thực thể GCKCastSession.
Thêm đoạn mã sau vào MediaViewController.swift để tải video hiện đang được chọn trên bộ nhận:
@objc(MediaViewController)
class MediaViewController: UIViewController, GCKSessionManagerListener,
GCKRemoteMediaClientListener, LocalPlayerViewDelegate, GCKRequestDelegate {
...
@objc func playSelectedItemRemotely() {
loadSelectedItem(byAppending: false)
}
/**
* Loads the currently selected item in the current cast media session.
* @param appending If YES, the item is appended to the current queue if there
* is one. If NO, or if
* there is no queue, a new queue containing only the selected item is created.
*/
func loadSelectedItem(byAppending appending: Bool) {
print("enqueue item \(String(describing: mediaInfo))")
if let remoteMediaClient = sessionManager.currentCastSession?.remoteMediaClient {
let mediaQueueItemBuilder = GCKMediaQueueItemBuilder()
mediaQueueItemBuilder.mediaInformation = mediaInfo
mediaQueueItemBuilder.autoplay = true
mediaQueueItemBuilder.preloadTime = TimeInterval(UserDefaults.standard.integer(forKey: kPrefPreloadTime))
let mediaQueueItem = mediaQueueItemBuilder.build()
if appending {
let request = remoteMediaClient.queueInsert(mediaQueueItem, beforeItemWithID: kGCKMediaQueueInvalidItemID)
request.delegate = self
} else {
let queueDataBuilder = GCKMediaQueueDataBuilder(queueType: .generic)
queueDataBuilder.items = [mediaQueueItem]
queueDataBuilder.repeatMode = remoteMediaClient.mediaStatus?.queueRepeatMode ?? .off
let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
mediaLoadRequestDataBuilder.mediaInformation = mediaInfo
mediaLoadRequestDataBuilder.queueData = queueDataBuilder.build()
let request = remoteMediaClient.loadMedia(with: mediaLoadRequestDataBuilder.build())
request.delegate = self
}
}
}
...
}
Giờ đây, hãy cập nhật nhiều phương thức hiện có để sử dụng logic Phiên Cast để hỗ trợ tính năng phát từ xa:
required init?(coder: NSCoder) {
super.init(coder: coder)
...
castMediaController = GCKUIMediaController()
...
}
func switchToLocalPlayback() {
print("switchToLocalPlayback")
if playbackMode == .local {
return
}
setQueueButtonVisible(false)
var playPosition: TimeInterval = 0
var paused: Bool = false
var ended: Bool = false
if playbackMode == .remote {
playPosition = castMediaController.lastKnownStreamPosition
paused = (castMediaController.lastKnownPlayerState == .paused)
ended = (castMediaController.lastKnownPlayerState == .idle)
print("last player state: \(castMediaController.lastKnownPlayerState), ended: \(ended)")
}
populateMediaInfo((!paused && !ended), playPosition: playPosition)
sessionManager.currentCastSession?.remoteMediaClient?.remove(self)
playbackMode = .local
}
func switchToRemotePlayback() {
print("switchToRemotePlayback; mediaInfo is \(String(describing: mediaInfo))")
if playbackMode == .remote {
return
}
// If we were playing locally, load the local media on the remote player
if playbackMode == .local, (_localPlayerView.playerState != .stopped), (mediaInfo != nil) {
print("loading media: \(String(describing: mediaInfo))")
let paused: Bool = (_localPlayerView.playerState == .paused)
let mediaQueueItemBuilder = GCKMediaQueueItemBuilder()
mediaQueueItemBuilder.mediaInformation = mediaInfo
mediaQueueItemBuilder.autoplay = !paused
mediaQueueItemBuilder.preloadTime = TimeInterval(UserDefaults.standard.integer(forKey: kPrefPreloadTime))
mediaQueueItemBuilder.startTime = _localPlayerView.streamPosition ?? 0
let mediaQueueItem = mediaQueueItemBuilder.build()
let queueDataBuilder = GCKMediaQueueDataBuilder(queueType: .generic)
queueDataBuilder.items = [mediaQueueItem]
queueDataBuilder.repeatMode = .off
let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
mediaLoadRequestDataBuilder.queueData = queueDataBuilder.build()
let request = sessionManager.currentCastSession?.remoteMediaClient?.loadMedia(with: mediaLoadRequestDataBuilder.build())
request?.delegate = self
}
_localPlayerView.stop()
_localPlayerView.showSplashScreen()
setQueueButtonVisible(true)
sessionManager.currentCastSession?.remoteMediaClient?.add(self)
playbackMode = .remote
}
/* Play has been pressed in the LocalPlayerView. */
func continueAfterPlayButtonClicked() -> Bool {
let hasConnectedCastSession = sessionManager.hasConnectedCastSession
if mediaInfo != nil, hasConnectedCastSession() {
// Display an alert box to allow the user to add to queue or play
// immediately.
if actionSheet == nil {
actionSheet = ActionSheet(title: "Play Item", message: "Select an action", cancelButtonText: "Cancel")
actionSheet?.addAction(withTitle: "Play Now", target: self,
selector: #selector(playSelectedItemRemotely))
}
actionSheet?.present(in: self, sourceView: _localPlayerView)
return false
}
return true
}
Giờ thì hãy chạy ứng dụng trên thiết bị di động. Kết nối với thiết bị truyền và bắt đầu phát video. Bạn sẽ thấy video phát trên thiết bị nhận.
7. Bộ điều khiển mini
Danh sách kiểm tra thiết kế Cast yêu cầu tất cả ứng dụng Cast cung cấp bộ điều khiển thu nhỏ 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ỏ giúp bạn truy cập tức thì và nhắc nhở bạn về phiên Cast hiện tại.

Cast SDK cung cấp một thanh điều khiển GCKUIMiniMediaControlsViewController mà bạn có thể thêm vào những cảnh mà bạn muốn hiển thị các chế độ điều khiển liên tục.
Đối với ứng dụng mẫu, chúng ta sẽ sử dụng GCKUICastContainerViewController. Thành phần này bao bọc một trình điều khiển khung hiển thị khác và thêm một GCKUIMiniMediaControlsViewController ở dưới cùng.
Sửa đổi tệp AppDelegate.swift và thêm đoạn mã sau cho điều kiện if useCastContainerViewController trong phương thức sau:
func application(_: UIApplication,
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
let appStoryboard = UIStoryboard(name: "Main", bundle: nil)
guard let navigationController = appStoryboard.instantiateViewController(withIdentifier: "MainNavigation")
as? UINavigationController else { return false }
let castContainerVC = GCKCastContext.sharedInstance().createCastContainerController(for: navigationController)
as GCKUICastContainerViewController
castContainerVC.miniMediaControlsItemEnabled = true
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = castContainerVC
window?.makeKeyAndVisible()
...
}
Thêm thuộc tính và setter/getter này để kiểm soát chế độ hiển thị của bộ điều khiển thu nhỏ (chúng ta sẽ sử dụng các thuộc tính này trong một phần sau):
var isCastControlBarsEnabled: Bool {
get {
if useCastContainerViewController {
let castContainerVC = (window?.rootViewController as? GCKUICastContainerViewController)
return castContainerVC!.miniMediaControlsItemEnabled
} else {
let rootContainerVC = (window?.rootViewController as? RootContainerViewController)
return rootContainerVC!.miniMediaControlsViewEnabled
}
}
set(notificationsEnabled) {
if useCastContainerViewController {
var castContainerVC: GCKUICastContainerViewController?
castContainerVC = (window?.rootViewController as? GCKUICastContainerViewController)
castContainerVC?.miniMediaControlsItemEnabled = notificationsEnabled
} else {
var rootContainerVC: RootContainerViewController?
rootContainerVC = (window?.rootViewController as? RootContainerViewController)
rootContainerVC?.miniMediaControlsViewEnabled = notificationsEnabled
}
}
}
Chạy ứng dụng và truyền video. Khi quá trình phát bắt đầu trên thiết bị nhận, bạn sẽ thấy bộ điều khiển thu nhỏ xuất hiện ở cuối mỗi cảnh. Bạn có thể điều khiển chế độ phát từ xa bằng bộ điều khiển thu nhỏ. Nếu bạn di chuyển giữa hoạt động duyệt xem và hoạt động trình phát cục bộ, trạng thái của bộ điều khiển thu nhỏ phải luôn đồng bộ với trạng thái phát nội dung nghe nhìn của thiết bị nhận.
8. Lớp phủ giới thiệu
Danh sách kiểm tra thiết kế Google Cast yêu cầu ứng dụng truyền giới thiệu nút Truyền cho người dùng hiện tại để cho họ biết rằng ứng dụng truyền hiện hỗ trợ tính năng Truyền và cũng giúp người dùng mới làm quen với Google Cast.

Lớp GCKCastContext có một phương thức presentCastInstructionsViewControllerOnce mà bạn có thể dùng để làm nổi bật nút Truyền khi nút này xuất hiện lần đầu tiên với người dùng. Thêm mã sau vào MediaViewController.swift và MediaTableViewController.swift:
override func viewDidLoad() {
...
NotificationCenter.default.addObserver(self, selector: #selector(castDeviceDidChange),
name: NSNotification.Name.gckCastStateDidChange,
object: GCKCastContext.sharedInstance())
}
@objc func castDeviceDidChange(_: Notification) {
if GCKCastContext.sharedInstance().castState != .noDevicesAvailable {
// You can present the instructions on how to use Google Cast on
// the first time the user uses you app
GCKCastContext.sharedInstance().presentCastInstructionsViewControllerOnce(with: castButton)
}
}
Chạy ứng dụng trên thiết bị di động và bạn sẽ thấy lớp phủ giới thiệu.
9. Bộ điều khiển mở rộng
Danh sách kiểm tra thiết kế của Google Cast yêu cầu ứng dụng truyền phải cung cấp bộ điều khiển mở rộng cho nội dung nghe nhìn đang được 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à chế độ xem toàn màn hình, cho phép bạn kiểm soát hoàn toàn việc phát nội dung nghe nhìn từ xa. Khung hiển thị này sẽ cho phép một ứng dụng truyền nội dung quản lý mọi khía cạnh có thể quản lý của một phiên truyền, ngoại trừ chế độ điều khiển âm lượng của bộ nhận và vòng đời của phiên (kết nối/dừng truyền). Thư viện này cũng cung cấp tất cả thông tin về trạng thái của phiên phát nội dung đa phương tiệ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ằng 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 AppDelegate.swift để bật bộ điều khiển mở rộng mặc định:
import GoogleCast
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
...
func application(_: UIApplication,
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
// Add after the setShareInstanceWith(options) is set.
GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true
...
}
...
}
Thêm mã sau vào MediaViewController.swift để tải bộ điều khiển mở rộng khi người dùng bắt đầu truyền video:
@objc func playSelectedItemRemotely() {
...
appDelegate?.isCastControlBarsEnabled = false
GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()
}
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ỏ.
Chạy ứng dụng và truyền video. Bạn sẽ thấy bộ điều khiển mở rộng. Chuyển đến danh sách video rồi nhấp vào bộ điều khiển thu nhỏ để tải lại bộ điều khiển mở rộng.
10. Thêm tính năng hỗ trợ Cast Connect
Thư viện Cast Connect cho phép các ứng dụng gửi hiện có giao tiếp với các ứng dụng Android TV thông qua giao thức Cast. Cast Connect được xây dựng dựa trên cơ sở hạ tầng Cast, trong đó ứng dụng cho Android TV của bạn đóng vai trò là một bộ nhận.
Phần phụ thuộc
Trong Podfile, hãy đảm bảo google-cast-sdk được trỏ đến 4.4.8 trở lên như được liệt kê bên dưới. Nếu bạn đã sửa đổi tệp, hãy chạy pod update từ bảng điều khiển để đồng bộ hoá thay đổi với dự án của bạn.
pod 'google-cast-sdk', '>=4.4.8'
GCKLaunchOptions
Để chạy ứng dụng Android TV (còn gọi là Trình nhận Android), chúng ta cần đặt cờ androidReceiverCompatible thành true trong đối tượng GCKLaunchOptions. Đối tượng GCKLaunchOptions này quy định cách khởi chạy receiver và được truyền đến GCKCastOptions được đặt trong phiên bản dùng chung bằng GCKCastContext.setSharedInstanceWith.
Thêm các dòng sau vào AppDelegate.swift:
let options = GCKCastOptions(discoveryCriteria:
GCKDiscoveryCriteria(applicationID: kReceiverAppID))
...
/** Following code enables CastConnect */
let launchOptions = GCKLaunchOptions()
launchOptions.androidReceiverCompatible = true
options.launchOptions = launchOptions
GCKCastContext.setSharedInstanceWith(options)
Đặt thông tin đăng nhập để khởi chạy
Về phía người gửi, bạn có thể chỉ định GCKCredentialsData để biểu thị người đang tham gia phiên. credentials là một chuỗi do người dùng xác định, miễn là ứng dụng ATV của bạn có thể hiểu được chuỗi đó. GCKCredentialsData chỉ được truyền đến ứng dụng cho Android TV của bạn trong thời gian khởi chạy hoặc tham gia. Nếu bạn đặt lại mật khẩu trong khi đang kết nối, mật khẩu đó sẽ không được chuyển đến ứng dụng Android TV.
Để đặt Launch Credentials, bạn cần xác định GCKCredentialsData bất cứ lúc nào sau khi đặt GCKLaunchOptions. Để minh hoạ điều này, hãy thêm logic cho nút Creds để đặt thông tin đăng nhập sẽ được truyền đi khi phiên được thiết lập. Thêm mã sau vào MediaTableViewController.swift:
class MediaTableViewController: UITableViewController, GCKSessionManagerListener, MediaListModelDelegate, GCKRequestDelegate {
...
private var credentials: String? = nil
...
override func viewDidLoad() {
...
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Creds", style: .plain,
target: self, action: #selector(toggleLaunchCreds))
...
setLaunchCreds()
}
...
@objc func toggleLaunchCreds(_: Any){
if (credentials == nil) {
credentials = "{\"userId\":\"id123\"}"
} else {
credentials = nil
}
Toast.displayMessage("Launch Credentials: "+(credentials ?? "Null"), for: 3, in: appDelegate?.window)
print("Credentials set: "+(credentials ?? "Null"))
setLaunchCreds()
}
...
func setLaunchCreds() {
GCKCastContext.sharedInstance()
.setLaunch(GCKCredentialsData(credentials: credentials))
}
}
Đặt thông tin đăng nhập cho yêu cầu tải
Để xử lý credentials trên cả ứng dụng Web và ứng dụng Receiver Android TV, hãy thêm mã sau vào lớp MediaTableViewController.swift trong hàm loadSelectedItem:
let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
...
mediaLoadRequestDataBuilder.credentials = credentials
...
Tuỳ thuộc vào ứng dụng nhận mà thiết bị gửi đang truyền đến, SDK sẽ tự động áp dụng các thông tin đăng nhập nêu trên cho phiên đang diễn ra.
Kiểm thử Cast Connect
Các bước cài đặt APK Android TV trên Chromecast có Google TV
- Tìm địa chỉ IP của thiết bị Android TV. Thông thường, bạn có thể xem thông tin này trong phần Cài đặt > Mạng và Internet > (Tên mạng mà thiết bị của bạn đang kết nối). Ở bên phải, thông tin chi tiết và địa chỉ IP của thiết bị trên mạng sẽ xuất hiện.
- Sử dụng địa chỉ IP của thiết bị để kết nối với thiết bị đó qua ADB bằng thiết bị đầu cuối:
$ adb connect <device_ip_address>:5555
- Trong cửa sổ thiết bị đầu cuối, hãy chuyển đến thư mục cấp cao nhất cho các mẫu của lớp học lập trình mà bạn đã tải xuống khi bắt đầu lớp học lập trình này. Ví dụ:
$ cd Desktop/ios_codelab_src
- Cài đặt tệp .apk trong thư mục này vào Android TV bằng cách chạy:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- Giờ đây, bạn sẽ thấy một ứng dụng có tên là Truyền video trong trình đơn Ứng dụng của bạn trên thiết bị Android TV.
- Sau khi hoàn tất, hãy tạo và chạy ứng dụng trên trình mô phỏng hoặc thiết bị di động. Khi bạn thiết lập một phiên truyền đến thiết bị Android TV, thiết bị này sẽ khởi chạy ứng dụng Android Receiver trên Android TV. Khi bạn phát một video từ thiết bị di động iOS (thiết bị truyền), video đó sẽ phát trong Bộ nhận Android và cho phép bạn điều khiển chế độ phát bằng điều khiển từ xa của thiết bị Android TV.
11. Tuỳ chỉnh tiện ích Truyền
Khởi chạy
Bắt đầu bằng thư mục App-Done. Thêm các dòng sau vào phương thức applicationDidFinishLaunchingWithOptions trong tệp AppDelegate.swift.
func application(_: UIApplication,
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
let styler = GCKUIStyle.sharedInstance()
...
}
Sau khi bạn hoàn tất việc áp dụng một hoặc nhiều chế độ tuỳ chỉnh như đề cập trong phần còn lại của lớp học lập trình này, hãy xác nhận các kiểu bằng cách gọi mã bên dưới
styler.apply()
Tuỳ chỉnh chế độ xem truyền
Bạn có thể tuỳ chỉnh tất cả khung hiển thị mà Cast Application Framework quản lý bằng cách áp dụng các nguyên tắc tạo kiểu mặc định trên các khung hiển thị. Ví dụ: hãy thay đổi màu sắc của biểu tượng.
styler.castViews.iconTintColor = .lightGray
Bạn có thể ghi đè các giá trị mặc định theo từng màn hình nếu cần. Ví dụ: để ghi đè lightGrayColor cho màu phủ biểu tượng chỉ cho trình điều khiển nội dung nghe nhìn mở rộng.
styler.castViews.mediaControl.expandedController.iconTintColor = .green
Thay đổi màu sắc
Bạn có thể tuỳ chỉnh màu nền cho tất cả các khung hiển thị (hoặc riêng cho từng khung hiển thị). Đoạn mã sau đây đặt màu nền thành màu xanh dương cho tất cả các khung hiển thị do Cast Application Framework cung cấp.
styler.castViews.backgroundColor = .blue
styler.castViews.mediaControl.miniController.backgroundColor = .yellow
Thay đổi phông chữ
Bạn có thể tuỳ chỉnh phông chữ cho nhiều nhãn xuất hiện trong chế độ xem truyền. Hãy đặt tất cả phông chữ thành "Courier-Oblique" cho mục đích minh hoạ.
styler.castViews.headingTextFont = UIFont.init(name: "Courier-Oblique", size: 16) ?? UIFont.systemFont(ofSize: 16)
styler.castViews.mediaControl.headingTextFont = UIFont.init(name: "Courier-Oblique", size: 6) ?? UIFont.systemFont(ofSize: 6)
Thay đổi hình ảnh nút mặc định
Thêm hình ảnh tuỳ chỉnh của riêng bạn vào dự án và chỉ định hình ảnh cho các nút để tạo kiểu cho chúng.
let muteOnImage = UIImage.init(named: "yourImage.png")
if let muteOnImage = muteOnImage {
styler.castViews.muteOnImage = muteOnImage
}
Thay đổi giao diện của nút Truyền
Bạn cũng có thể tạo giao diện cho Tiện ích truyền bằng Giao thức UIAppearance. Đoạn mã sau đây tạo giao diện cho GCKUICastButton trên tất cả các khung hiển thị mà nút này xuất hiện:
GCKUICastButton.appearance().tintColor = UIColor.gray
12. Xin chúc mừng
Giờ đây, bạn đã biết cách bật tính năng truyền cho một ứng dụng video bằng cách sử dụng các tiện ích Cast SDK trên iOS.
Để biết thêm thông tin chi tiết, hãy xem hướng dẫn dành cho nhà phát triển iOS Sender.