1. Opis
Dzięki tym ćwiczeniom z programowania dowiesz się, jak zmodyfikować istniejącą aplikację wideo na iOS, by przesyłać treści na urządzenie obsługujące Google Cast.
Co to jest Google Cast?
Google Cast pozwala użytkownikom przesyłać treści z urządzenia mobilnego na telewizor. Dzięki temu użytkownicy mogą używać swoich urządzeń mobilnych jako pilota do odtwarzania multimediów na telewizorze.
Pakiet SDK Google Cast umożliwia rozszerzenie aplikacji do sterowania urządzeniami obsługującymi Google Cast (np. telewizorem czy systemem audio). Pakiet SDK Cast pozwala dodać niezbędne komponenty interfejsu zgodnie z listą kontrolną projektowania Google Cast.
Dostępna jest lista kontrolna projektu Google Cast, dzięki której korzystanie z Cast jest proste i przewidywalne na wszystkich obsługiwanych platformach.
Co będziemy tworzyć?
Po ukończeniu tego ćwiczenia z programowania otrzymasz aplikację wideo na iOS, która będzie mogła przesyłać filmy na urządzenie Google Cast.
Czego się nauczysz
- Jak dodać pakiet SDK Google Cast do przykładowej aplikacji wideo
- Jak dodać przycisk Cast pozwalający wybrać urządzenie Google Cast.
- Jak połączyć się z urządzeniem przesyłającym i uruchomić odbiornik multimediów.
- Jak przesłać film.
- Jak dodać minikontroler Cast do aplikacji
- Jak dodać rozszerzony kontroler.
- Jak dodać nakładkę początkową
- Jak dostosować widżety Cast
- Jak zintegrować Cast Connect
Czego potrzebujesz
- Najnowszy Xcode.
- Jedno urządzenie mobilne z systemem iOS 9 lub nowszym (albo symulator Xcode).
- Kabel USB do transmisji danych, aby podłączyć urządzenie mobilne do komputera programisty (jeśli używasz urządzenia).
- Urządzenie przesyłające Google Cast, takie jak Chromecast czy Android TV, z dostępem do internetu.
- Telewizor lub monitor z wejściem HDMI.
- Do testowania integracji Cast Connect wymagany jest Chromecast z Google TV, ale w pozostałej części Codelabs jest on opcjonalny. Jeśli go nie masz, możesz pominąć krok Dodawanie obsługi Cast Connect pod koniec tego samouczka.
Funkcja
- Aby z nich korzystać, musisz mieć wiedzę z zakresu programowania na iOS.
- Potrzebna będzie też wcześniejsza wiedza o oglądaniu telewizji :)
Jak będziesz korzystać z tego samouczka?
Jak oceniasz swoje doświadczenia z tworzeniem aplikacji na iOS?
Jak oceniasz swoje wrażenia z oglądania telewizji?
2. Pobieranie przykładowego kodu
Możesz pobrać cały przykładowy kod na komputer...
i rozpakuj pobrany plik ZIP.
3. Uruchamianie przykładowej aplikacji
Zobaczmy najpierw, jak wygląda kompletna przykładowa aplikacja. Ta aplikacja to podstawowy odtwarzacz wideo. Użytkownik może wybrać film z listy, a potem odtworzyć go lokalnie na urządzeniu lub przesłać na urządzenie przesyłające Google Cast.
Po pobraniu kodu możesz wykonać poniższe instrukcje, aby dowiedzieć się, jak otworzyć i uruchomić gotową aplikację próbną w Xcode:
Najczęstsze pytania
Konfiguracja CocoaPods
Aby skonfigurować CocoaPods, otwórz konsolę i zainstaluj aplikację za pomocą domyślnej wersji Ruby dostępnej w systemie macOS:
sudo gem install cocoapods
Jeśli masz problemy, zapoznaj się z oficjalną dokumentacją, aby pobrać i zainstalować menedżera zależności.
Konfigurowanie projektu
- Otwórz terminal i przejdź do katalogu ćwiczeń z programowania.
- Zainstaluj zależności z pliku Podfile.
cd app-done pod update pod install
- Otwórz Xcode i wybierz Open another project... (Otwórz inny projekt).
- Wybierz plik
CastVideos-ios.xcworkspace
z kataloguapp-done
w folderze z przykładowym kodem.
Uruchom aplikację
Wybierz wartość docelową i symulator, a potem uruchom aplikację:
Aplikacja wideo powinna pojawić się po kilku sekundach.
Gdy pojawi się powiadomienie o akceptowaniu przychodzących połączeń sieciowych, nie zapomnij kliknąć „Zezwól”. Jeśli ta opcja nie zostanie zaakceptowana, ikona przesyłania się nie pojawi.
Kliknij przycisk Cast i wybierz urządzenie Google Cast.
Wybierz film i kliknij przycisk odtwarzania.
Rozpocznie się odtwarzanie filmu na urządzeniu Google Cast.
Wyświetli się rozwinięty kontroler. Do sterowania odtwarzaniem możesz używać przycisku odtwarzania/wstrzymywania.
Wróć do listy filmów.
Minikontroler jest teraz widoczny u dołu ekranu.
Kliknij przycisk wstrzymania na minikontrolerze, aby wstrzymać odtwarzanie filmu na odbiorniku. Kliknij przycisk odtwarzania na minikontrolerze, aby wznowić odtwarzanie filmu.
Kliknij przycisk Cast, by zatrzymać przesyłanie na urządzenie Google Cast.
4. Przygotuj projekt początkowy
Musimy dodać obsługę Google Cast do pobranej aplikacji startowej. Oto niektóre z terminologii związanej z Google Cast, których będziemy używać w tym ćwiczeniu z programowania:
- aplikacja nadawca działa na urządzeniu mobilnym lub laptopie,
- aplikacja odbiornika działa na urządzeniu Google Cast;
Konfigurowanie projektu
Teraz możesz zacząć tworzyć na podstawie projektu startowego za pomocą Xcode:
- Otwórz terminal i przejdź do katalogu ćwiczeń z programowania.
- Zainstaluj zależności z pliku Podfile.
cd app-start pod update pod install
- Otwórz Xcode i wybierz Open another project... (Otwórz inny projekt).
- Wybierz plik
CastVideos-ios.xcworkspace
z kataloguapp-start
w folderze z przykładowym kodem.
Projektowanie aplikacji
Aplikacja pobiera listę filmów ze zdalnego serwera WWW i udostępnia listę, którą użytkownik może przeglądać. Użytkownicy mogą wybrać film, aby zobaczyć szczegóły, lub odtworzyć go lokalnie na urządzeniu mobilnym.
Aplikacja składa się z 2 kontrolerów widoku głównego: MediaTableViewController
i MediaViewController.
MediaTableViewController
Ten UITableViewController wyświetla listę filmów z instancji MediaListModel
. Lista filmów i powiązane z nimi metadane są przechowywane na serwerze zdalnym jako plik JSON. MediaListModel
pobiera ten plik JSON i przetwarza go, aby utworzyć listę obiektów MediaItem
.
Obiekt MediaItem
modeluje film i powiązane z nim metadane, takie jak tytuł, opis, adres URL obrazu i adres URL strumienia.
MediaTableViewController
tworzy wystąpienie MediaListModel
, a następnie rejestruje się jako MediaListModelDelegate
, aby otrzymywać informacje o pobraniu metadanych multimediów, aby wczytać widok tabeli.
Użytkownikowi wyświetla się lista miniatur filmów wraz z krótkim opisem każdego z nich. Po wybraniu elementu odpowiednie MediaItem
jest przekazywane do funkcji MediaViewController
.
MediaViewController
Kontroler widoku wyświetla metadane konkretnego filmu i umożliwia użytkownikowi odtwarzanie filmu lokalnie na urządzeniu mobilnym.
Kontroler widoku zawiera obiekt LocalPlayerView
, niektóre elementy sterujące multimediami oraz obszar tekstowy do wyświetlania opisu wybranego filmu. Odtwarzacz zakrywa górną część ekranu, pozostawiając miejsce na szczegółowy opis filmu. Użytkownik może włączyć lub wstrzymać odtwarzanie albo przewinąć film do momentu lokalnego odtwarzania.
Najczęstsze pytania
5. Dodawanie przycisku Cast
Aplikacja obsługująca Cast wyświetla przycisk Cast na każdym kontrolerze widoku. Gdy klikniesz przycisk Cast, pojawi się lista urządzeń przesyłających, które użytkownik może wybrać. Jeśli użytkownik odtwarzał treści lokalnie na urządzeniu nadawcy, wybranie urządzenia przesyłającego rozpocznie lub wznowi odtwarzanie na tym urządzeniu. W każdej chwili podczas sesji przesyłania użytkownik może kliknąć przycisk Cast i zatrzymać przesyłanie aplikacji na urządzenie przesyłające. Użytkownik musi mieć możliwość połączenia się z urządzeniem przesyłającym lub ich odłączenia na dowolnym ekranie aplikacji zgodnie z opisem na liście kontrolnej projektowania Google Cast.
Konfiguracja
Projekt początkowy wymaga tych samych zależności i konfiguracji Xcode co w przypadku ukończonej przykładowej aplikacji. Wróć do tej sekcji i wykonaj te same czynności, aby dodać GoogleCast.framework
do początkowego projektu aplikacji.
Zdarzenie inicjujące
Platforma przesyłania zawiera globalny obiekt typu singleton GCKCastContext
, który koordynuje wszystkie działania platformy. Ten obiekt musi zostać zainicjowany na wczesnym etapie cyklu życia aplikacji, zwykle za pomocą metody application(_:didFinishLaunchingWithOptions:)
delegata aplikacji, tak aby automatyczne wznowienie sesji po ponownym uruchomieniu aplikacji nadawcy mogło zostać prawidłowo aktywowane i uruchomione skanowanie w poszukiwaniu urządzeń.
Podczas inicjowania GCKCastContext
należy podać obiekt GCKCastOptions
. Ta klasa zawiera opcje, które wpływają na działanie platformy. Najważniejszy z nich to identyfikator aplikacji odbiorcy, który służy do filtrowania wyników wykrywania urządzeń przesyłających i uruchamiania aplikacji odbierającej po rozpoczęciu sesji przesyłania.
Metoda application(_:didFinishLaunchingWithOptions:)
pozwala też skonfigurować przedstawiciela logowania, który będzie odbierać komunikaty logowania z platformy przesyłania. Przydają się one podczas debugowania i rozwiązywania problemów.
Gdy tworzysz własną aplikację obsługującą Cast, musisz się zarejestrować jako deweloper Cast, a potem uzyskać identyfikator aplikacji. W tym ćwiczeniu z programowania użyjemy przykładowego identyfikatora aplikacji.
Dodaj do pliku AppDelegate.swift
ten kod, aby zainicjować GCKCastContext
przy użyciu identyfikatora aplikacji podanego przez użytkownika jako domyślnego, i dodać rejestrator na potrzeby platformy 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)")
}
}
}
Przycisk Cast
Po zainicjowaniu GCKCastContext
musimy dodać przycisk Cast, by umożliwić użytkownikowi wybranie urządzenia przesyłającego. Pakiet SDK Cast udostępnia komponent przycisku przesyłania o nazwie GCKUICastButton
jako podklasę UIButton
. Można go dodać do paska tytułu aplikacji za pomocą kodu UIBarButtonItem
. Musimy dodać przycisk Cast zarówno do aplikacji MediaTableViewController
, jak i do aplikacji MediaViewController
.
Dodaj ten kod do MediaTableViewController.swift
i 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)
...
}
...
}
Następnie dodaj ten kod do urządzenia MediaViewController.swift
:
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)
...
}
...
}
Teraz uruchom aplikację. Na pasku nawigacyjnym aplikacji powinien być widoczny przycisk Cast, a po jego kliknięciu pojawi się lista urządzeń przesyłających w Twojej sieci lokalnej. Wykrywaniem urządzeń zarządza automatycznie GCKCastContext
. Wybierz urządzenie przesyłające, a przykładowa aplikacja odbiornika zostanie na nim załadowana. Możesz przechodzić między aktywnością przeglądania a aktywnością lokalnego odtwarzacza, a stan przycisku przesyłania jest zsynchronizowany.
Nie obsługujemy odtwarzania multimediów, więc nie możesz jeszcze odtwarzać filmów na urządzeniu przesyłającym. Kliknij przycisk Cast, by zatrzymać przesyłanie.
6. Przesyłanie treści wideo
Udostępnimy przykładową aplikację, aby umożliwić zdalne odtwarzanie filmów na urządzeniach przesyłających. Aby to zrobić, musimy wsłuchiwać się w różne zdarzenia generowane przez platformę przesyłania.
Przesyłanie multimediów
Ogólnie, jeśli chcesz odtworzyć multimedia na urządzeniu przesyłającym, musisz wykonać te czynności:
- Z pakietu SDK Cast utwórz obiekt
GCKMediaInformation
, który będzie modelował element multimedialny. - Użytkownik łączy się z urządzeniem przesyłającym, by uruchomić aplikację odbiornika.
- Wczytaj obiekt
GCKMediaInformation
do odbiornika i odtwórz treści. - Śledzenie stanu multimediów.
- Wysyłaj polecenia odtwarzania do odbiornika na podstawie interakcji użytkownika.
Krok 1 polega na zmapowaniu jednego obiektu na drugi. Pakiet GCKMediaInformation
jest zrozumiały dla pakietu SDK Cast, a MediaItem
to kodowanie elementu multimedialnego w naszej aplikacji. Możemy z łatwością zmapować obiekt MediaItem
na obiekt GCKMediaInformation
. Krok 2 z poprzedniej sekcji został już wykonany. Krok 3 można łatwo zrobić za pomocą pakietu SDK Cast.
W przykładowej aplikacji MediaViewController
można już odróżnić odtwarzanie lokalne i zdalne przy użyciu tej wyliczenia:
enum PlaybackMode: Int {
case none = 0
case local
case remote
}
private var playbackMode = PlaybackMode.none
W tym ćwiczeniu z programowania nie ma potrzeby dokładnego zrozumienia, jak działa cała przykładowa logika odtwarzacza. Pamiętaj, że konieczne będzie zmodyfikowanie odtwarzacza w aplikacji, aby rozpoznawał 2 miejsca odtwarzania w podobny sposób.
W tej chwili lokalny odtwarzacz jest zawsze w lokalnym stanie odtwarzania, ponieważ nie ma jeszcze żadnych informacji o stanach przesyłania. Musimy aktualizować interfejs odpowiednio do zmian stanu w platformie przesyłania. Jeśli na przykład rozpocznie się przesyłanie, musimy zatrzymać odtwarzanie lokalne i wyłączyć niektóre elementy sterujące. Podobnie jeśli zatrzymujemy przesyłanie, gdy korzystamy z kontrolera widoku, musimy przejść na odtwarzanie lokalne. Aby to zrobić, musimy nasłuchiwać różnych zdarzeń generowanych przez platformę przesyłania.
Zarządzanie sesjami przesyłania
W przypadku platformy przesyłania sesja obejmuje etapy połączenia z urządzeniem, uruchamiania (lub dołączania), łączenia się z aplikacją odbierającą i inicjowania kanału sterowania multimediami (jeśli ma to zastosowanie). Kanał sterowania multimediami służy do wysyłania i odbierania wiadomości przez platformę przesyłania z odtwarzacza.
Sesja przesyłania rozpocznie się automatycznie, gdy użytkownik wybierze urządzenie, klikając przycisk Cast, i zakończy się automatycznie, gdy użytkownik rozłączy się. Ponowne łączenie z sesją odbiorcy z powodu problemów z siecią jest również automatycznie obsługiwane przez platformę przesyłania.
Sesje przesyłania są zarządzane przez narzędzie GCKSessionManager
, do którego można uzyskać dostęp przez aplikację GCKCastContext.sharedInstance().sessionManager
. Wywołania zwrotne GCKSessionManagerListener
mogą służyć do monitorowania zdarzeń sesji, takich jak utworzenie, zawieszenie, wznowienie i zakończenie sesji.
Najpierw musimy zarejestrować nasz detektor sesji i zainicjować kilka zmiennych:
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()
}
...
}
W MediaViewController
kraju będziemy Cię informować o połączeniu lub przerwaniu połączenia z urządzeniem przesyłającym, abyśmy mogli przełączyć się na lokalny odtwarzacz lub z niego zrezygnować. Pamiętaj, że łączność może zostać zakłócona nie tylko przez wystąpienie aplikacji uruchomionej na urządzeniu mobilnym, ale także przez inne wystąpienie tej aplikacji uruchomionej na innym urządzeniu mobilnym.
Aktualnie aktywna sesja jest dostępna jako GCKCastContext.sharedInstance().sessionManager.currentCastSession
. Sesje są tworzone i usuwane automatycznie w odpowiedzi na gesty użytkownika wyświetlane w oknach przesyłania.
Wczytuję multimedia
GCKRemoteMediaClient
w pakiecie SDK Cast udostępnia zestaw wygodnych interfejsów API do zarządzania zdalnym odtwarzaniem multimediów na odbiorniku. W przypadku GCKCastSession
, który obsługuje odtwarzanie multimediów, pakiet SDK automatycznie utworzy wystąpienie elementu GCKRemoteMediaClient
. Jest dostępna jako właściwość remoteMediaClient
instancji GCKCastSession
.
Dodaj do aplikacji MediaViewController.swift
ten kod, aby wczytać aktualnie wybrany film na odbiorniku:
@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
}
}
}
...
}
Teraz zaktualizuj różne istniejące metody, aby obsługiwały zdalne odtwarzanie, korzystając z logiki sesji przesyłania:
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
}
Teraz uruchom aplikację na urządzeniu mobilnym. Połącz się z urządzeniem przesyłającym i zacznij odtwarzać film. Film powinien być odtwarzany na odbiorniku.
7. Minikontroler
Lista kontrolna projektu przesyłania wymaga, aby we wszystkich aplikacjach Cast był dostępny minikontroler, gdy użytkownik opuści stronę, na której obecnie znajdują się treści. Minikontroler zapewnia natychmiastowy dostęp i wyświetla przypomnienie o bieżącej sesji przesyłania.
Pakiet SDK Cast zawiera pasek sterowania GCKUIMiniMediaControlsViewController
, który można dodać do scen, w których mają być widoczne stałe elementy sterujące.
W przykładowej aplikacji użyjemy elementu GCKUICastContainerViewController
, który pakuje inny kontroler widoku i dodaje GCKUIMiniMediaControlsViewController
na dole.
Zmodyfikuj plik AppDelegate.swift
i dodaj ten kod warunku if useCastContainerViewController
, korzystając z tej metody:
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()
...
}
Dodaj tę właściwość i metodę szeregowania, aby kontrolować widoczność minikontrolera (użyjemy ich w późniejszej sekcji):
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
}
}
}
Uruchom aplikację i prześlij film. Gdy rozpocznie się odtwarzanie na odbiorniku, u dołu każdej sceny powinien pojawić się minikontroler. Odtwarzaniem zdalnym możesz sterować za pomocą minikontrolera. Jeśli przełączasz się między aktywnością przeglądania a aktywnością w odtwarzaczu lokalnym, stan minikontrolera powinien być zsynchronizowany ze stanem odtwarzania multimediów przez odbiornik.
8. Nakładka wprowadzająca
Lista kontrolna projektu Google Cast wymaga, aby aplikacja nadawcy poinformowała użytkowników o tym, że przycisk Cast może przesyłać treści, i poinformuje ich, że aplikacja nadawcy obsługuje przesyłanie, a także pomaga użytkownikom, którzy dopiero zaczynają korzystać z Google Cast.
Klasa GCKCastContext
ma metodę presentCastInstructionsViewControllerOnce
, której można użyć do podświetlenia przycisku przesyłania, gdy zostanie on wyświetlony użytkownikom. Dodaj ten kod do MediaViewController.swift
i 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)
}
}
Po uruchomieniu aplikacji na urządzeniu mobilnym powinna wyświetlić się nakładka z wprowadzeniem.
9. Rozwinięty kontroler
Lista kontrolna projektu Google Cast wymaga, aby aplikacja nadawcy zapewniła rozszerzony kontroler do przesyłanych multimediów. Rozwinięty kontroler to pełnoekranowa wersja minikontrolera.
Rozwinięty kontroler to widok pełnoekranowy, który zapewnia pełną kontrolę nad zdalnym odtwarzaniem multimediów. Ten widok powinien umożliwiać aplikacji przesyłającej zarządzanie wszystkimi aspektami sesji przesyłania, którymi można zarządzać. Wyjątkiem jest regulacja głośności odbiornika i cykl życia sesji (łączenie/zatrzymywanie przesyłania). Znajdziesz w nim też wszystkie informacje o stanie sesji multimediów (grafiki, tytuł, podtytuł itd.).
Funkcje tego widoku są implementowane przez klasę GCKUIExpandedMediaControlsViewController
.
Najpierw musisz włączyć domyślny rozwinięty kontroler w kontekście przesyłania. Zmodyfikuj AppDelegate.swift
, aby włączyć domyślny rozwinięty kontroler:
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
...
}
...
}
Dodaj do pliku MediaViewController.swift
ten kod, aby wczytywać rozwinięty kontroler, gdy użytkownik rozpocznie przesyłanie filmu:
@objc func playSelectedItemRemotely() {
...
appDelegate?.isCastControlBarsEnabled = false
GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()
}
Rozwinięty kontroler zostanie uruchomiony automatycznie, gdy użytkownik naciśnie minikontroler.
Uruchom aplikację i prześlij film. Powinien być widoczny rozwinięty kontroler. Wróć do listy filmów. Gdy klikniesz minikontroler, rozwinięty kontroler zostanie ponownie wczytany.
10. Dodawanie obsługi Cast Connect
Biblioteka Cast Connect umożliwia dotychczasowym aplikacjom nadawcy komunikację z aplikacjami na Androida TV przez protokół Cast. Cast Connect działa na bazie infrastruktury przesyłania, a aplikacja na Androida TV działa jako odbiornik.
Zależności
Upewnij się, że w usłudze Podfile
pole google-cast-sdk
wskazuje wartość 4.4.8
lub wyższą, jak podano poniżej. Jeśli plik został zmodyfikowany, uruchom polecenie pod update
w konsoli, aby zsynchronizować zmiany ze swoim projektem.
pod 'google-cast-sdk', '>=4.4.8'
GCKLaunchOptions
Aby uruchomić aplikację Android TV (określaną też jako odbiornik Androida), musimy ustawić w obiekcie GCKLaunchOptions
flagę androidReceiverCompatible
na true. Ten obiekt GCKLaunchOptions
określa sposób uruchamiania odbiornika i jest przekazywany do obiektów GCKCastOptions
, które są ustawiane w udostępnionej instancji za pomocą GCKCastContext.setSharedInstanceWith
.
Dodaj do dokumentu AppDelegate.swift
te wiersze:
let options = GCKCastOptions(discoveryCriteria:
GCKDiscoveryCriteria(applicationID: kReceiverAppID))
...
/** Following code enables CastConnect */
let launchOptions = GCKLaunchOptions()
launchOptions.androidReceiverCompatible = true
options.launchOptions = launchOptions
GCKCastContext.setSharedInstanceWith(options)
Ustaw dane logowania do uruchamiania
Po stronie nadawcy możesz określić GCKCredentialsData
, aby reprezentował, kto dołącza do sesji. Ciąg credentials
jest ciągiem zdefiniowanym przez użytkownika, pod warunkiem że jest on zrozumiały dla aplikacji ATV. Element GCKCredentialsData
jest przekazywany do aplikacji na Androida TV tylko w czasie jej uruchomienia lub dołączenia. Jeśli skonfigurujesz je ponownie po połączeniu z siecią, nie zostaną one przekazane do aplikacji na Androida TV.
Aby ustawić dane logowania do uruchamiania, w dowolnym momencie po skonfigurowaniu danych logowania GCKLaunchOptions
musi być zdefiniowane GCKCredentialsData
. Aby to zademonstrować, dodajmy funkcje logiczne przycisku Creds pozwalające na ustawienie danych logowania, które będą przekazywane po rozpoczęciu sesji. Dodaj do karty MediaTableViewController.swift
ten kod:
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))
}
}
Ustaw dane logowania w żądaniu obciążenia
Aby obsługiwać credentials
w aplikacjach internetowych i na odbiornikach Android TV, dodaj w klasie MediaTableViewController.swift
ten kod w sekcji loadSelectedItem
:
let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
...
mediaLoadRequestDataBuilder.credentials = credentials
...
W zależności od aplikacji odbiorcy, do której nadawca przesyła treści, pakiet SDK automatycznie zastosuje podane wyżej dane logowania do trwającej sesji.
Testowanie Cast Connect
Instalowanie pakietu APK na Androida TV na urządzeniu Chromecast z Google TV
- Znajdź adres IP swojego urządzenia z Androidem TV. Zwykle znajdziesz je w sekcji Ustawienia > Sieć i internet > (nazwa sieci, z którą połączone jest urządzenie). Po prawej stronie wyświetlą się szczegóły oraz adres IP urządzenia połączony z siecią.
- Użyj adresu IP urządzenia, aby połączyć się z nim przez ADB za pomocą terminala:
$ adb connect <device_ip_address>:5555
- W oknie terminala przejdź do folderu najwyższego poziomu na przykłady ćwiczeń z programowania pobrane na początku tego ćwiczenia z programowania. Na przykład:
$ cd Desktop/ios_codelab_src
- Zainstaluj plik .apk w tym folderze na Androidzie TV, uruchamiając polecenie:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- Aplikacja powinna być teraz widoczna pod nazwą Przesyłaj filmy w menu Twoje aplikacje na urządzeniu z Androidem TV.
- Po zakończeniu skompiluj i uruchom aplikację w emulatorze lub na urządzeniu mobilnym. Po rozpoczęciu sesji przesyłania na urządzeniu z Androidem TV aplikacja powinna uruchomić się na tym urządzeniu. Odtworzenie filmu od nadawcy z urządzenia z iOS powinno go uruchomić w odbiorniku Androida i umożliwić sterowanie odtwarzaniem za pomocą pilota do urządzenia z Androidem TV.
11. Dostosuj widżety Cast
Zdarzenie inicjujące
Zacznij od folderu Gotowe aplikacji. Dodaj poniższy kod do metody applicationDidFinishLaunchingWithOptions
w pliku AppDelegate.swift
.
func application(_: UIApplication,
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
let styler = GCKUIStyle.sharedInstance()
...
}
Po wprowadzeniu co najmniej jednego dostosowania zgodnie z opisem w pozostałej części tego ćwiczenia z programowania zatwierdź style, wywołując poniższy kod.
styler.apply()
Dostosowywanie widoków Cast
Możesz dostosować wszystkie widoki, którymi zarządza platforma aplikacji Cast, korzystając z domyślnych wskazówek dotyczących stylu w różnych widokach. Zmieńmy na przykład kolor odcienia ikony.
styler.castViews.iconTintColor = .lightGray
W razie potrzeby możesz zastąpić ustawienia domyślne dla poszczególnych ekranów. Możesz na przykład zastąpić kolor jasnoszarego koloru ikony w przypadku rozwiniętego kontrolera multimediów.
styler.castViews.mediaControl.expandedController.iconTintColor = .green
Zmiana kolorów
Możesz dostosować kolor tła dla wszystkich widoków (lub osobno dla każdego widoku). Poniższy kod ustawia kolor tła na niebieski dla wszystkich widoków dostępnych w platformie Cast Application Framework.
styler.castViews.backgroundColor = .blue
styler.castViews.mediaControl.miniController.backgroundColor = .yellow
Zmiana czcionek
Możesz dostosować czcionki dla różnych etykiet widocznych w widokach przesyłania. W celach ilustracyjnych użyjemy wszystkich czcionek jako „Courier-Oblique”.
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)
Zmiana domyślnych obrazów przycisków
Dodaj do projektu własne obrazy niestandardowe i przypisz je do przycisków, aby ustalić ich styl.
let muteOnImage = UIImage.init(named: "yourImage.png")
if let muteOnImage = muteOnImage {
styler.castViews.muteOnImage = muteOnImage
}
Zmienianie motywu przycisku Cast
Widżety Cast możesz też umieszczać w motywach tematycznych, korzystając z protokołu UIPreview Protocol. Ten kod tworzy motywy GCKUICastButton we wszystkich wyświetleniach:
GCKUICastButton.appearance().tintColor = UIColor.gray
12. Gratulacje
Wiesz już, jak włączyć przesyłanie w aplikacji wideo za pomocą widżetów Cast SDK na iOS.
Więcej informacji znajdziesz w przewodniku dla programistów aplikacji na iOS.