Bật ứng dụng iOS

1. Tổng quan

Biểu trưng Google Cast

Lớp học lập trình này sẽ hướng dẫn bạn cách chỉnh sửa 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 sang TV. Sau đó, người dùng có thể sử dụng thiết bị di động làm điều khiển từ xa để phát nội dung đa phương tiện trên TV.

Google Cast SDK cho phép bạn mở rộng ứng dụng của mình để đ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ế 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 đơn giản và có thể dự đoán được trên tất cả các nền tảng được hỗ trợ.

Chúng tôi 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 tới thiết bị Google Cast.

Kiến thức bạn sẽ học được

  • Cách thêm Google Cast SDK vào ứng dụng video mẫu.
  • Cách thêm nút Truyền để chọn thiết bị Google Cast.
  • Cách kết nối với thiết bị truyền và khởi chạy thiết bị nhận nội dung nghe nhìn.
  • Cách truyền video.
  • Cách thêm bộ điều khiển Cast Mini vào ứng dụng.
  • Cách thêm tay điều khiển mở rộng.
  • Cách cung cấp lớp phủ giới thiệu.
  • Cách tuỳ chỉnh các tiện ích Truyền.
  • 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 sử dụng thiết bị).
  • Thiết bị Google Cast như Chromecast hoặc Android TV được định cấu hình có quyền truy cập Internet.
  • TV hoặc màn hình có đầu vào HDMI.
  • Bạn cần phải có Chromecast có Google TV để kiểm thử việc tích hợp Cast Connect nhưng không bắt buộc phải cung cấp cho phần còn lại của Lớp học lập trình. Nếu bạn chưa có tài khoản này, hãy bỏ qua bước Thêm tính năng hỗ trợ Cast Connect ở 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 trước đó.
  • Bạn cũng cần có kiến thức trước về cách xem TV :)

Bạn sẽ sử dụng hướng dẫn này như thế nào?

Chỉ đọc qua Đọc và hoàn thành các bài tập

Bạn đánh giá thế nào về trải nghiệm của mình trong việc tạo ứng dụng iOS?

Người mới bắt đầu Trung cấp Thành thạo

Bạn đánh giá thế nào về trải nghiệm của mình khi xem TV?

Người mới bắt đầu Trung cấp Thành thạo

2. Nhận mã mẫu

Bạn có thể tải tất cả mã mẫu xuống máy tính của mình...

rồi giải nén tệp zip đã tải xuống.

3. Chạy ứng dụng mẫu

Biểu trưng Apple iOS

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 đó tới thiết bị Google Cast.

Sau khi tải mã xuống, bạn hãy làm theo hướng dẫn sau đây để biết cách mở và chạy ứng dụng mẫu đã hoàn chỉnh trong Xcode:

Câu hỏi thường gặp

Thiết lập CocoaPods

Để thiết lập CocoaPods, hãy truy cập vào 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 và cài đặt trình quản lý phần phụ thuộc.

Thiết lập dự án

  1. Chuyển đến dòng lệnh của bạn rồi chuyển đến thư mục của lớp học lập trình này.
  2. Cài đặt phần phụ thuộc từ Podfile.
cd app-done
pod update
pod install
  1. Mở Xcode rồi chọn Mở một dự án khác...
  2. Chọn tệp CastVideos-ios.xcworkspace từ thư mục biểu tượng thư mụcapp-done trong thư mục mã mẫu.

Chạy ứng dụng

Chọn mục tiêu và trình mô phỏng, sau đó chạy ứng dụng:

Thanh công cụ của trình mô phỏng ứng dụng XCode

Bạn sẽ thấy ứng dụng video xuất hiện sau vài giây.

Hãy nhớ nhấp vào "Cho phép" khi thông báo xuất hiện về việc chấp nhận kết nối mạng đến. Biểu tượng Truyền sẽ không xuất hiện nếu tuỳ chọn này không được chấp nhận.

Hộp thoại xác nhận yêu cầu quyền chấp nhận kết nối mạng đến

Nhấp vào nút Truyền và chọn thiết bị Google Cast của bạn.

Chọn một video, nhấp vào nút phát.

Video sẽ bắt đầu phát trên thiết bị Google Cast của bạn.

Bộ điều khiển mở rộng sẽ xuất hiện. Bạn có thể sử 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.

Một bộ điều khiển thu nhỏ đang xuất hiện ở cuối màn hình.

Hình minh hoạ iPhone đang chạy ứng dụng CastVideos với bộ điều khiển thu nhỏ xuất hiện ở dưới cùng

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 bộ thu. 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 tới thiết bị Google Cast.

4. Chuẩn bị dự án khởi động

Hình minh hoạ iPhone đang chạy ứng dụng CastVideos

Chúng tôi cần thêm tính năng hỗ trợ cho Google Cast vào ứng dụng ban đầu mà bạn tải xuống. Dưới đây là một số thuật ngữ của Google Cast mà chúng tôi sẽ sử dụng trong lớp học lập trình này:

  • một ứng dụng của người gửi chạy trên thiết bị di động hoặc máy tính xách tay,
  • một ứng dụng broadcast receiver chạy trên thiết bị Google Cast.

Thiết lập dự án

Bây giờ, bạn đã sẵn sàng tạo bản dựng trên đầu dự án khởi đầu bằng Xcode:

  1. Chuyển đến dòng lệnh của bạn rồi chuyển đến thư mục của lớp học lập trình này.
  2. Cài đặt phần phụ thuộc từ Podfile.
cd app-start
pod update
pod install
  1. Mở Xcode rồi chọn Mở một dự án khác...
  2. Chọn tệp CastVideos-ios.xcworkspace từ thư mục biểu tượng thư mụcapp-start trong thư mục mã mẫu.

Thiết kế ứng dụng

Ứng dụng này tìm nạp danh sách video qua một máy chủ web từ xa và cung cấp một danh sách để người dùng duyệt xem. Người dùng có thể chọn một video để xem chi tiết hoặc phát video trên thiết bị di động.

Ứng dụng bao gồm 2 trình kiểm soát khung hiển thị chính: MediaTableViewControllerMediaViewController.

MediaTableViewController

UITableViewController này hiển thị một danh sách các 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á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 đối tượng MediaItem.

Đối tượng MediaItem lập mô hình một video và siêu dữ liệu liên kết với video đó, chẳng hạn như tiêu đề, nội dung mô tả, URL của hình ảnh và URL của luồng phát.

MediaTableViewController tạo một thực thể MediaListModel rồi tự đăng ký là MediaListModelDelegate để nhận thông báo khi siêu dữ liệu đa phương tiện được tải xuống có thể tải chế độ xem theo bảng.

Người dùng sẽ thấy một danh sách hình thu nhỏ video cùng với nội dung mô tả ngắn cho mỗi video. Khi bạn chọn một mục, MediaItem tương ứng sẽ được truyền đến MediaViewController.

MediaViewController

Trình điều khiển chế độ xem 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 chế độ xem lưu trữ LocalPlayerView, một số chế độ điều khiển nội dung nghe nhìn và một vùng văn bản để hiển thị nội dung mô tả của video đã chọn. Trình phát bao phủ phần trên cùng của màn hình, chừa chỗ cho nội dung 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 tìm nút phát lại video trên thiết bị.

Câu hỏi thường gặp

5. Thêm nút Truyền

Hình minh hoạ một phần ba phía trên cùng của chiếc iPhone đang chạy ứng dụng CastVideos, cho thấy nút Truyền ở góc trên bên phải

Ứng dụng hỗ trợ Cast hiển thị nút Truyền trong mỗi bộ điều khiển khung hiển thị của ứng dụng đó. Khi nhấp vào nút Truyền, bạn sẽ thấy danh sách các Thiết bị truyền mà người dùng có thể chọn. Nếu người dùng đang phát nội dung trên thiết bị gửi, thì thao tá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 tới Thiết bị truyền. Người dùng phải có khả năng kết nối hoặc ngắt kết nối khỏi Thiết bị truyền khi đang ở 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ế của Google Cast.

Cấu hình

Dự án khởi động yêu cầu các phần phụ thuộc và cách thiết lập Xcode giống như với ứng dụng mẫu đã hoàn thà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 khởi động ứng dụng.

Khởi chạy

Khung Truyền có một đối tượng singleton toàn cầu là GCKCastContext, đối tượng này sẽ điều phối tất cả hoạt động của khung. Đối tượng này phải được khởi tạo 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ể uỷ quyền ứng dụng, để việc tự động tiếp tục phiên khi khởi động lại ứng dụng của người gửi có thể kích hoạt đúng cách và quá trình quét tìm thiết bị có thể bắt đầu.

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ã ứng dụng của thiết bị nhận. Mã này được dùng để lọc kết quả khám phá thiết bị truyền và chạy ứng dụng nhận khi phiên Truyền bắt đầu.

Phương thức application(_:didFinishLaunchingWithOptions:) cũng là nơi phù hợp để thiết lập uỷ quyền ghi nhật ký nhằm nhận thông báo nhật ký từ khung Truyền. 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ó hỗ trợ Cast, bạn phải đăng ký làm nhà phát triển Cast, sau đó lấy mã ứng dụng cho ứng dụng của mình. Đối với 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 chạy GCKCastContext bằng mã ứng dụng từ giá trị mặc định của người dùng và thêm 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, 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 Thiết bị truyền. SDK Truyền cung cấp thành phần nút Truyền có tên GCKUICastButton làm 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 một UIBarButtonItem. Chúng ta cần thêm nút Cast vào cả MediaTableViewControllerMediaViewController.

Thêm mã sau vào MediaTableViewController.swiftMediaViewController.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. Khi bạn nhấp vào nút đó, nút Truyền sẽ liệt kê các thiết bị Truyền trên mạng cục bộ của bạn. Tính năng khám phá thiết bị được GCKCastContext quản lý tự động. Chọn Thiết bị truyền của bạ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 web và hoạt động của người chơi trên máy tính cũng như trạng thái của nút Truyền được đồng bộ hoá.

Chúng tôi chưa kết nối bất kỳ tính năng hỗ trợ nào cho tính năng 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

Hình minh hoạ một chiếc iPhone đang chạy ứng dụng CastVideos, hiển thị thông tin chi tiết về một video cụ thể ("Tales of Steel"). Ở dưới cùng là trình phát thu nhỏ

Chúng tôi sẽ mở rộng ứng dụng mẫu để phát video từ xa trên Thiết bị truyền. Để làm được việc đó, chúng ta cần theo dõi các sự kiện do Khung Truyền tạo ra.

Truyền nội dung đa phương tiện

Nhìn chung, nếu bạn muốn phát nội dung nghe nhìn trên Thiết bị truyền, thì những việc sau đây cần xảy ra:

  1. Tạo đối tượng GCKMediaInformation từ Cast SDK để tạo mô hình cho một mục nội dung đa phương tiện.
  2. Người dùng kết nối với Thiết bị truyền để chạy ứng dụng nhận.
  3. Tải đối tượng GCKMediaInformation vào receiver (trình thu nhận) rồi phát nội dung đó.
  4. Theo dõi trạng thái nội dung nghe nhìn.
  5. Gửi lệnh phát đến thiết bị nhận dựa trên tương tác của người dùng.

Bước 1 giống như việc ánh xạ một đối tượng sang một đối tượng khác; GCKMediaInformation là điều mà SDK truyền hiểu được và MediaItem là gói đóng gói của ứng dụng cho một mục nội dung đa phương tiện; chúng ta có thể dễ dàng liên kết MediaItem với GCKMediaInformation. Chúng ta đã thực hiện Bước 2 trong phần trước. Bước 3 rất dễ thực hiện bằng Cast SDK.

Ứng dụng mẫu MediaViewController đã phân biệt giữa chế độ phát cục bộ và phát từ xa bằng cách sử dụng giá trị enum sau:

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, điều quan trọng là bạn phải hiểu chính xác cách thức hoạt động của tất cả logic của trình phát mẫu. Bạn cần hiểu rằng trình phát nội dung đa phương tiện của ứng dụng sẽ phải được sửa đổi để nhận biết được hai vị trí phát theo cách tương tự nhau.

Hiện tại, trình phát cục bộ luôn ở trạng thái phát cục bộ vì 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 quá trình chuyển đổi trạng thái diễn ra trong khung Truyền. Ví dụ: Nếu bắt đầu truyền, chúng ta cần dừng việc phát trên thiết bị và tắt một số chế độ điều khiển. Tương tự, nếu dừng truyền khi đang sử dụng 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 các sự kiện do khung Truyền tạo ra.

Quản lý phiên truyền

Đối với khung Truyền, phiên Truyền sẽ 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 bộ thu và khởi chạy 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ộ thu.

Phiên Truyền sẽ tự động bắt đầu khi người dùng chọn một thiết bị từ nút Truyền và sẽ tự động dừng khi người dùng ngắt kết nối. Việc kết nối lại với phiên bộ thu do các sự cố kết nối mạng cũng được Khung truyền tự động xử lý.

Các phiên truyền do GCKSessionManager quản lý và có thể truy cập 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à chấm dứt.

Trước tiên, chúng ta cần đăng ký trình nghe phiên và khởi chạy 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 chúng ta kết nối hoặc ngắt kết nối khỏi Thiết bị truyền để có thể chuyển sang hoặc từ trình phát cục bộ. Xin lưu ý rằng khả năng kết nối có thể bị gián đoạn không chỉ do phiên bản ứng dụng đang chạy trên thiết bị di động của bạn, mà có thể bị gián đoạn bởi phiên bản khác của ứng dụng (hoặc ứng dụng khác) chạy trên thiết bị di động khác.

Bạn có thể truy cập vào phiên đang hoạt động bằng địa chỉ GCKCastContext.sharedInstance().sessionManager.currentCastSession. Các phiên được tạo và tự động thu gọn theo cử chỉ của người dùng từ hộp thoại Truyền.

Đang tải nội dung nghe nhìn

Trong Cast SDK, GCKRemoteMediaClient cung cấp một bộ API thuận tiện để quản lý việc phát nội dung đa phương tiện từ xa trên bộ thu. Đối với GCKCastSession có hỗ trợ phát nội dung đa phương tiện, một thực thể của GCKRemoteMediaClient sẽ được SDK tạo tự động. Bạn có thể truy cập đối tượng này dưới dạng thuộc tính remoteMediaClient của thực thể GCKCastSession.

Thêm mã sau vào MediaViewController.swift để tải video hiện được chọn trên bộ thu:

@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
      }
    }
  }
  ...
}

Bây giờ, hãy cập nhật nhiều phương thức hiện có để sử dụng logic Phiên truyền nhằm hỗ trợ 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
}

Bây giờ, hãy chạy ứng dụng trên thiết bị di động của bạn. Kết nối với Thiết bị truyền và bắt đầu phát video. Bạn sẽ thấy video đang phát trên bộ thu.

7. Bộ điều khiển mini

Danh sách kiểm tra thiết kế truyền yêu cầu tất cả các ứng dụng Truyền phải cung cấp bộ điều khiển nhỏ để hiển thị khi người dùng rời khỏi trang nội dung hiện tại. Bộ điều khiển thu nhỏ cấp quyền truy cập tức thì và lời nhắc hiển thị cho phiên Truyền hiện tại.

Hình minh hoạ phần dưới cùng của chiếc iPhone đang chạy ứng dụng CastVideos, lấy nét vào bộ điều khiển thu nhỏ

SDK truyền cung cấp thanh điều khiển GCKUIMiniMediaControlsViewController, có thể được thêm vào những cảnh mà bạn muốn hiển thị các nút điều khiển cố định.

Đối với ứng dụng mẫu, chúng ta sẽ sử dụng GCKUICastContainerViewController để gói một bộ điều khiển chế độ xem khác và thêm GCKUIMiniMediaControlsViewController ở dưới cùng.

Sửa đổi tệp AppDelegate.swift và thêm 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 này và phương thức setter/getter để điều khiển chế độ hiển thị của bộ điều khiển mini (chúng ta sẽ sử dụng các phương thức này trong 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 một video. Khi quá trình phát bắt đầu trên bộ thu, 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 tính năng phát từ xa bằng tay điều khiển mini. Nếu bạn chuyển giữa hoạt động duyệt web và hoạt động của người chơi trên máy, thì trạng thái tay điều khiển thu nhỏ sẽ luôn đồng bộ với trạng thái phát nội dung nghe nhìn của trình thu 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 dành cho người gửi ra mắt nút Truyền cho người dùng hiện tại để cho họ biết rằng ứng dụng của người gửi hiện hỗ trợ tính năng Truyền và cũng giúp người dùng mới sử dụng Google Cast.

Hình minh hoạ một chiếc iPhone đang chạy ứng dụng CastVideos với lớp phủ nút Truyền, làm nổi bật nút Truyền và hiện thông báo "Chạm để truyền nội dung nghe nhìn đến TV và loa"

Lớp GCKCastContext có một phương thức presentCastInstructionsViewControllerOnce có thể dùng để làm nổi bật nút Truyền khi nút này hiển thị lần đầu tiên với người dùng. Thêm mã sau vào MediaViewController.swiftMediaTableViewController.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. Mở rộng bộ điều khiển

Danh sách kiểm tra thiết kế Google Cast yêu cầu ứng dụng người gửi cung cấp bộ điều khiển mở rộng cho nội dung nghe nhì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 mini.

Hình minh hoạ một chiếc iPhone đang chạy ứng dụng CastVideos đang phát video với bộ điều khiển mở rộng xuất hiện ở dưới cùng

Bộ điều khiển mở rộng là chế độ xem toàn màn hình cho phép kiểm soát toàn bộ việc phát nội dung đa phương tiện từ xa. Chế độ xem 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ừ kiểm soát âm lượng của trình thu nhận và vòng đời của phiên (kết nối/dừng truyền). Trang 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 đa phương tiệ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 lớp GCKUIExpandedMediaControlsViewController triển khai.

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 một video. Bạn sẽ thấy tay điều khiển mở rộng. Quay lại danh sách video và khi bạn nhấp vào bộ điều khiển thu nhỏ, bộ điều khiển mở rộng sẽ tải lại.

10. Thêm tính năng hỗ trợ Cast Connect

Thư viện Cast Connect cho phép các ứng dụng hiện có của người gửi 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 Android TV hoạt động như một bộ thu.

Phần phụ thuộc

Trong Podfile, hãy đảm bảo google-cast-sdk trỏ đến 4.4.8 trở lên như liệt kê dưới đây. Nếu bạn đã sửa đổi tệp, hãy chạy pod update trong bảng điều khiển để đồng bộ hoá nội dung 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 được gọi là Bộ thu Android, chúng ta cần đặt cờ androidReceiverCompatible thành true trong đối tượng GCKLaunchOptions. Đối tượng GCKLaunchOptions này chỉ ra cách khởi chạy receiver (trình thu nhận) và được truyền đến GCKCastOptions được đặt trong thực thể dùng chung bằng GCKCastContext.setSharedInstanceWith.

Thêm các dòng sau vào AppDelegate.swift của bạn:

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 xác thực khi chạy

Ở phía người gửi, bạn có thể chỉ định GCKCredentialsData để cho biết ai sẽ tham gia phiên này. credentials là một chuỗi có thể 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 Android TV trong thời gian khởi chạy hoặc tham gia. Nếu bạn thiết lập lại khi đang kết nối thì mã đó sẽ không được chuyển tới ứng dụng Android TV.

Để thiết lập Thông tin đăng nhập khi chạy, bạn cần phải xác định GCKCredentialsData bất cứ lúc nào sau khi đặt GCKLaunchOptions. Để chứng minh điều này, hãy thêm logic cho nút Creds để thiết lập thông tin xác thực sẽ được chuyển khi phiên được thiết lập. Thêm mã sau vào MediaTableViewController.swift của bạn:

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 xác thực khi yêu cầu tải

Để xử lý credentials trên cả ứng dụng Bộ thu web và ứng dụng 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à người gửi đang truyền nội dung đến, SDK sẽ tự động áp dụng thông tin đăng nhập ở trên vào 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

  1. Tìm địa chỉ IP của thiết bị Android TV. Thông thường, bạn có thể tìm thấy 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, màn hình sẽ hiển thị thông tin chi tiết và IP của thiết bị trên mạng.
  2. Sử dụng địa chỉ IP để thiết bị của bạn 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
  1. Trong cửa sổ dòng lệnh, hãy chuyển đến thư mục cấp cao nhất cho các mẫu 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
  1. 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
  1. Giờ đây, bạn có thể thấy một ứng dụng theo tên là Truyền video trong trình đơn Ứng dụng của bạn trên thiết bị Android TV.
  2. 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 thiết lập phiên truyền với thiết bị Android TV, giờ đây thiết bị sẽ khởi chạy ứng dụng Bộ thu Android trên Android TV của bạn. Phát video từ thiết bị gửi dành cho thiết bị di động trên iOS sẽ khởi chạy video trong Bộ thu của Android và cho phép bạn điều khiển quá trình phát bằng điều khiển từ xa của thiết bị Android TV.

11. Tùy chỉnh tiện ích Truyền

Khởi chạy

Bắt đầu với thư mục App-Done. Thêm phần 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 áp dụng một hoặc nhiều cá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 đoạn mã dưới đây

styler.apply()

Tuỳ chỉnh khung hiển thị Truyền

Bạn có thể tuỳ chỉnh tất cả các khung hiển thị mà Khung ứng dụng truyền quản lý bằng cách áp dụng nguyên tắc định kiểu mặc định trên các khung hiển thị. Ví dụ: hãy thay đổi màu phủ của biểu tượng.

styler.castViews.iconTintColor = .lightGray

Bạn có thể ghi đè các giá trị mặc định trên từng màn hình nếu cần. Ví dụ: để ghi đè màu lightGrayColor cho màu sắc thái biểu tượng chỉ dành cho trình điều khiển nội dung đa phương tiệ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ả chế độ xem (hoặc riêng lẻ cho từng chế độ xem). 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 Khung ứng dụng truyền của bạn 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 các nhãn khác nhau trong các chế độ xem nội dung 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à gán 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. Các giao diện mã sau đây cho GCKUICastButton trên tất cả các khung hiển thị mà mã 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 ứng dụng video bằng các tiện ích SDK Truyền trên iOS.

Để biết thêm chi tiết, hãy xem hướng dẫn cho nhà phát triển về Người gửi trên iOS.