1. Omówienie
Dzięki tym ćwiczeniom w Codelabs dowiesz się, jak zmodyfikować istniejącą aplikację wideo na iOS, aby móc przesyłać treści na urządzenie obsługujące Google Cast.
Co to jest Google Cast?
Google Cast umożliwia użytkownikom przesyłanie treści z urządzenia mobilnego na telewizor. Użytkownicy mogą wtedy używać urządzenia mobilnego jako pilota do odtwarzania multimediów na telewizorze.
Pakiet Google Cast SDK umożliwia rozszerzenie aplikacji i sterowanie urządzeniami obsługującymi Google Cast (takimi jak telewizor czy system audio). Pakiet Cast SDK umożliwia dodawanie niezbędnych komponentów interfejsu użytkownika na podstawie listy kontrolnej dotyczącej projektowania Google Cast.
Znajdziesz tam listę kontrolną projektowania Google Cast, która ułatwia i przewidywalność korzystania z Google Cast na wszystkich obsługiwanych platformach.
Co utworzymy?
Gdy ukończysz to ćwiczenie w Codelabs, będziesz mieć aplikację wideo na iOS, która pozwoli przesyłać filmy na urządzenie Google Cast.
Czego się nauczysz
- Jak dodać pakiet Google Cast SDK do przykładowej aplikacji wideo.
- Jak dodać przycisk przesyłania umożliwiający wybór urządzenia Google Cast?
- Jak połączyć się z urządzeniem przesyłającym i uruchomić odbiornik multimediów.
- Jak przesyłać filmy.
- Jak dodać minikontroler Cast do aplikacji.
- Jak dodać rozszerzony kontroler.
- Jak utworzyć nakładkę wprowadzającą.
- Jak dostosować widżety Cast.
- Jak zintegrować Cast Connect
Czego potrzebujesz
- najnowsza wersja Xcode,
- 1 urządzenie mobilne z systemem iOS 9 lub nowszym (lub symulatorem Xcode).
- Kabel USB do podłączenia urządzenia mobilnego do komputera programisty (jeśli używasz urządzenia).
- Urządzenie przesyłające Google Cast, takie jak Chromecast lub Android TV, skonfigurowane z dostępem do internetu.
- Telewizor lub monitor z wejściem HDMI.
- Chromecast z Google TV jest wymagany do przetestowania integracji Cast Connect, ale nie jest wymagany w pozostałych częściach tego Codelaba. Jeśli nie masz konta, na końcu tego samouczka możesz pominąć krok Dodawanie obsługi Cast Connect.
Doświadczenie
- Musisz mieć już doświadczenie w programowaniu na iOS.
- Wymagamy też wcześniejszej wiedzy na temat oglądania telewizji. :)
Jak wykorzystasz ten samouczek?
Jak oceniasz swoje doświadczenia z tworzeniem aplikacji na iOS?
Jak oceniasz oglądanie telewizji?
2. Pobieranie przykładowego kodu
Możesz pobrać cały przykładowy kod na swój komputer...
i rozpakuj pobrany plik ZIP.
3. Uruchamianie przykładowej aplikacji
Najpierw zobaczmy, jak wygląda gotowa przykładowa aplikacja. Aplikacja jest podstawowym odtwarzaczem wideo. Użytkownik może wybrać film z listy i odtworzyć go lokalnie na urządzeniu lub przesłać na urządzenie przesyłające Google Cast.
Po pobraniu kodu postępuj zgodnie z poniższymi instrukcjami, aby otworzyć i uruchomić pełną aplikację w Xcode:
Najczęstsze pytania
Konfiguracja CocoaPods
Aby skonfigurować CocoaPods, otwórz konsolę i zainstaluj je za pomocą domyślnej wersji Ruby dostępnej w systemie macOS:
sudo gem install cocoapods
W razie problemów z pobieraniem i instalowaniem menedżera zależności zapoznaj się z oficjalną dokumentacją.
Konfigurowanie projektu
- Otwórz terminal i przejdź do katalogu codelab.
- Zainstaluj zależności z pliku Podfile.
cd app-done pod update pod install
- Otwórz Xcode i kliknij Otwórz inny projekt….
- W katalogu
app-done
w folderze z przykładowym kodem wybierz plikCastVideos-ios.xcworkspace
.
Uruchom aplikację
Wybierz wartość docelową i symulator, a potem uruchom aplikację:
Po kilku sekundach powinna pojawić się aplikacja do odtwarzania filmów.
Pamiętaj, aby kliknąć „Zezwól”. gdy pojawi się powiadomienie o akceptowaniu przychodzących połączeń sieciowych. Jeśli ta opcja nie zostanie zaakceptowana, ikona przesyłania nie będzie widoczna.
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żyć przycisku odtwarzania/wstrzymywania.
Wróć do listy filmów.
U dołu ekranu pojawi się minikontroler.
Kliknij przycisk wstrzymania na minikontrolerze, aby wstrzymać odtwarzanie wideo na odbiorniku. Kliknij przycisk odtwarzania na minikontrolerze, aby wznowić odtwarzanie filmu.
Aby zatrzymać przesyłanie na urządzenie Google Cast, kliknij przycisk Cast.
4. Przygotowanie projektu startowego
Musimy dodać obsługę Google Cast do pobranej przez Ciebie aplikacji startowej. Oto terminologia dotycząca Google Cast, której będziemy używać w tym ćwiczeniu z programowania:
- aplikacja nadawcy działa na urządzeniu mobilnym lub laptopie,
- na urządzeniu Google Cast działa aplikacja odbiornikowa.
Konfigurowanie projektu
Teraz możesz tworzyć na podstawie projektu startowego za pomocą Xcode:
- Otwórz terminal i przejdź do katalogu codelab.
- Zainstaluj zależności z pliku Podfile.
cd app-start pod update pod install
- Otwórz Xcode i kliknij Otwórz inny projekt….
- Wybierz plik
CastVideos-ios.xcworkspace
z kataloguapp-start
w folderze przykładowego kodu.
Projektowanie aplikacji
Aplikacja pobiera listę filmów ze zdalnego serwera WWW i udostępnia użytkownikowi listę do przeglądania. Użytkownicy mogą wybrać film, aby wyświetlić szczegóły lub odtworzyć go lokalnie na urządzeniu mobilnym.
Aplikacja składa się z 2 głównych kontrolerów widoku: MediaTableViewController
i MediaViewController.
.
MediaTableViewController
Ten obiekt UITableViewController wyświetla listę filmów z poziomu instancji MediaListModel
. Lista filmów i powiązanych z nimi metadanych jest hostowana 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, URL obrazu i URL strumienia.
MediaTableViewController
tworzy instancję MediaListModel
, a następnie rejestruje się jako MediaListModelDelegate
, aby otrzymywać informacje o pobieraniu metadanych multimediów, które pozwolą mu załadować widok tabeli.
Użytkownik widzi listę miniatur filmów z krótkim opisem każdego z nich. Po wybraniu elementu do funkcji MediaViewController
przekazywana jest wartość MediaItem
.
MediaViewController
Ten kontroler widoku wyświetla metadane dotyczące konkretnego filmu i pozwala użytkownikowi odtworzyć go lokalnie na urządzeniu mobilnym.
Kontroler widoku zawiera LocalPlayerView
, niektóre elementy sterowania multimediami oraz obszar tekstowy z opisem wybranego filmu. Odtwarzacz zajmuje górną część ekranu, pozostawiając miejsce na szczegółowy opis filmu poniżej. Użytkownik może odtwarzać/wstrzymywać lub przewijać lokalny film.
Najczęstsze pytania
5. Dodawanie przycisku Przesyłanie
Aplikacja obsługująca Cast wyświetla przycisk Cast na każdym ze swoich kontrolerów widoku. Po kliknięciu tego przycisku wyświetla 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 nadawczym, wybór urządzenia przesyłającego spowoduje rozpoczęcie lub wznowienie odtwarzania na tym urządzeniu. W dowolnym momencie 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 jego rozłączenia na dowolnym ekranie aplikacji, co opisano na liście kontrolnej projektowania w Google Cast.
Konfiguracja
Projekt startowy wymaga tych samych zależności i tej samej konfiguracji Xcode co gotowa przykładowa aplikacja. Wróć do tej sekcji i postępuj zgodnie z tymi samymi instrukcjami, aby dodać GoogleCast.framework
do projektu startowego aplikacji.
Zdarzenie inicjujące
Platforma Cast ma globalny obiekt typu singleton – GCKCastContext
, który koordynuje wszystkie działania w ramach platformy. Ten obiekt musi zostać zainicjowany na wczesnym etapie cyklu życia aplikacji, zazwyczaj w metodzie application(_:didFinishLaunchingWithOptions:)
delegowanego obiektu aplikacji, aby można było prawidłowo uruchamiać automatyczne wznawianie sesji po ponownym uruchomieniu aplikacji nadawcy i rozpoczęcie skanowania urządzeń.
Podczas inicjowania GCKCastContext
należy podać obiekt GCKCastOptions
. Ta klasa zawiera opcje, które wpływają na działanie frameworka. Najważniejszy z nich jest identyfikator aplikacji odbiornika, który służy do filtrowania wyników wykrywania urządzeń przesyłających i uruchamiania aplikacji odbiornika po rozpoczęciu sesji przesyłania.
Metoda application(_:didFinishLaunchingWithOptions:)
to także dobre miejsce do skonfigurowania przedstawiciela logowania, który będzie odbierał komunikaty logowania z platformy Cast. Mogą one być przydatne podczas debugowania i rozwiązywania problemów.
Podczas tworzenia własnej aplikacji z obsługą Cast musisz zarejestrować się jako deweloper Cast, a potem uzyskać identyfikator aplikacji. W tym samouczku użyjemy przykładowego identyfikatora aplikacji.
Dodaj ten kod do pliku AppDelegate.swift
, aby zainicjować GCKCastContext
za pomocą identyfikatora aplikacji z ustawień domyślnych użytkownika, i dodaj rejestrator dla 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
Urządzenie GCKCastContext
jest już zainicjowane. Teraz trzeba dodać przycisk Cast, aby użytkownik mógł wybrać urządzenie przesyłające. Pakiet Cast SDK udostępnia komponent przycisku przesyłania o nazwie GCKUICastButton
jako podklasę UIButton
. Możesz go dodać do paska tytułu aplikacji, umieszczając go w elementach UIBarButtonItem
. Musimy dodać przycisk przesyłania do obu aplikacji: MediaTableViewController
i MediaViewController
.
Dodaj do plików MediaTableViewController.swift
i MediaViewController.swift
ten kod:
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 do pliku MediaViewController.swift
ten kod:
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)
...
}
...
}
Uruchom aplikację. Na pasku nawigacyjnym aplikacji powinien pojawić się przycisk przesyłania. Po kliknięciu tego przycisku zobaczysz listę urządzeń przesyłających w swojej sieci lokalnej. Wykrywaniem urządzeń zarządza automatycznie GCKCastContext
. Wybierz urządzenie przesyłające. Aplikacja odbiornika załaduje się na to urządzenie. Możesz przechodzić między aktywnością związaną z przeglądaniem a aktywnością lokalnego odtwarzacza, a stan przycisku przesyłania jest zsynchronizowany.
Nie wprowadziliśmy jeszcze obsługi odtwarzania multimediów, więc nie możesz jeszcze odtwarzać filmów na urządzeniu Cast. Aby zatrzymać przesyłanie, kliknij przycisk Cast.
6. Przesyłanie treści wideo
Rozszerzymy przykładową aplikację o możliwość zdalnego odtwarzania filmów na urządzeniu przesyłającym. Aby to zrobić, musimy nasłuchiwać różnych zdarzeń generowanych przez platformę Cast.
Przesyłanie multimediów
Ogólnie rzecz biorąc, jeśli chcesz odtworzyć multimedia na urządzeniu z Cast, musi się wydarzyć to:
- Utwórz w pakiecie SDK Cast obiekt
GCKMediaInformation
, który modeluje element multimedialny. - Użytkownik łączy się z urządzeniem Cast, aby uruchomić aplikację odbiorczą.
- Wczytaj obiekt
GCKMediaInformation
do odbiornika i odtwórz treści. - Śledź stan multimediów.
- Wysyłaj polecenia odtwarzania do odbiornika na podstawie interakcji użytkownika.
Krok 1 polega na mapowaniu jednego obiektu na inny. GCKMediaInformation
jest obiektem zrozumiałym dla pakietu Cast SDK, a MediaItem
jest elementem multimedialnym zakapsułowanym w naszej aplikacji. Możemy łatwo mapować MediaItem
na GCKMediaInformation
. Krok 2 został już wykonany w poprzedniej sekcji. Krok 3 jest łatwy do wykonania dzięki pakietowi SDK przesyłania.
Przykładowa aplikacja MediaViewController
odróżnia odtwarzanie lokalne od odtwarzania zdalnego za pomocą tego wyliczenia:
enum PlaybackMode: Int {
case none = 0
case local
case remote
}
private var playbackMode = PlaybackMode.none
W tym ćwiczeniu nie musisz dokładnie rozumieć, jak działa cała przykładowa logika odtwarzacza. Pamiętaj, że trzeba zmodyfikować odtwarzacz multimediów w aplikacji, aby w podobny sposób rozpoznawał 2 miejsca odtwarzania.
Obecnie odtwarzacz lokalny jest zawsze w stanie odtwarzania lokalnego, ponieważ nie wie jeszcze nic o stanach przesyłania. Musimy zaktualizować interfejs na podstawie przejść stanów, które mają miejsce w ramach platformy Cast. Jeśli na przykład rozpoczniemy przesyłanie, musimy zatrzymać odtwarzanie lokalne i wyłączyć niektóre elementy sterujące. Podobnie, jeśli zatrzymamy przesyłanie w tym kontrolerze widoku, musimy przełączyć się na odtwarzanie lokalne. Aby to zrobić, musimy nasłuchiwać różnych zdarzeń generowanych przez platformę Cast.
Zarządzanie sesją przesyłania
W ramach platformy Cast sesja Cast obejmuje połączenie z urządzeniem, uruchomienie (lub dołączenie), połączenie z aplikacją odbiorczą i w odpowiednich przypadkach zainicjowanie kanału sterowania multimediami. Kanał sterowania multimediami to sposób, w jaki platforma Cast wysyła i odbiera wiadomości z odbiornika.
Sesja przesyłania rozpocznie się automatycznie, gdy użytkownik wybierze urządzenie za pomocą przycisku przesyłania, i zatrzyma się automatycznie, gdy użytkownik się rozłączy. Ponowne nawiązanie połączenia z sesją odbiorczą z powodu problemów z siecią jest również obsługiwane automatycznie przez platformę Cast.
Sesje przesyłania zarządza aplikacja GCKSessionManager
, do której można uzyskać dostęp w aplikacji GCKCastContext.sharedInstance().sessionManager
. Wywołania zwrotne GCKSessionManagerListener
mogą służyć do monitorowania zdarzeń sesji, takich jak utworzenie, zawieszenie, wznowienie i zakończenie.
Najpierw musimy zarejestrować 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 przypadku MediaViewController
będziemy Cię informować o połączeniu lub rozłączeniu z urządzeniem przesyłającym, aby umożliwić przełączenie się na lokalny odtwarzacz lub z niego. Pamiętaj, że połączenie może zostać zakłócone nie tylko przez wystąpienie aplikacji uruchomionej na urządzeniu mobilnym, ale również przez inne wystąpienie Twojej (lub innej) aplikacji uruchomionej na innym urządzeniu mobilnym.
Obecnie aktywna sesja jest dostępna jako GCKCastContext.sharedInstance().sessionManager.currentCastSession
. Sesje są tworzone i wyłączane automatycznie w odpowiedzi na gesty użytkownika w oknach przesyłania.
Wczytuję multimedia
W pakiecie Cast SDK interfejs GCKRemoteMediaClient
udostępnia zestaw wygodnych interfejsów API do zdalnego zarządzania odtwarzaniem multimediów na odbiorniku. W przypadku GCKCastSession
, który obsługuje odtwarzanie multimediów, pakiet SDK automatycznie utworzy instancję GCKRemoteMediaClient
. Dostęp do niego można uzyskać jako właściwości remoteMediaClient
instancji GCKCastSession
.
Dodaj do MediaViewController.swift
ten kod, aby wczytać wybrany film do odbiornika:
@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
}
}
}
...
}
Zaktualizuj teraz różne dotychczasowe metody, aby używać logiki sesji Cast do obsługi odtwarzania zdalnego:
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
}
Uruchom aplikację na urządzeniu mobilnym. Połącz się z urządzeniem przesyłającym i zacznij odtwarzać film. Film powinien się odtwarzać na odbiorniku.
7. Mini kontroler
Lista kontrolna projektowania przesyłania wymaga, by wszystkie aplikacje przesyłające miały minikontroler, który był wyświetlany, gdy użytkownik opuści bieżącą stronę z treściami. Minikontroler zapewnia natychmiastowy dostęp i widoczne przypomnienie o bieżącej sesji przesyłania.
Pakiet Cast SDK udostępnia pasek sterowania GCKUIMiniMediaControlsViewController
, który można dodać do scen, w których chcesz wyświetlać trwałe elementy sterujące.
W przypadku przykładowej aplikacji użyjemy funkcji GCKUICastContainerViewController
, która opakowuje inny kontroler widoku danych i dodaje GCKUIMiniMediaControlsViewController
na dole.
Zmodyfikuj plik AppDelegate.swift
i w metodzie if useCastContainerViewController
dodaj ten kod dla warunku if useCastContainerViewController
:
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 ustawię/pobieraj, by sterować widocznością minikontrolera (użyjemy ich w dalszej 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. Po rozpoczęciu odtwarzania na odbiorniku u dołu każdej sceny powinien pojawić się minikontroler. Odtwarzaniem możesz sterować za pomocą minikontrolera. Jeśli przechodzisz między aktywnością związaną z przeglądaniem a aktywnością lokalnego odtwarzacza, stan minikontrolera powinien być zsynchronizowany ze stanem odtwarzania multimediów na odbiorniku.
8. Wstępna nakładka
Lista kontrolna dotycząca projektu Google Cast wymaga, aby aplikacja wysyłająca wprowadziła użytkowników w przycisk Cast, aby poinformować ich, że aplikacja wysyłająca obsługuje teraz przesyłanie treści za pomocą Google Cast. Lista pomaga też nowym użytkownikom Google Cast.
Klasa GCKCastContext
zawiera metodę presentCastInstructionsViewControllerOnce
, której można użyć do wyróżnienia przycisku Cast, gdy zostanie on wyświetlony użytkownikom po raz pierwszy. Dodaj do MediaViewController.swift
i MediaTableViewController.swift
ten kod:
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)
}
}
Uruchom aplikację na urządzeniu mobilnym, a powinien pojawić się wstępny ekran.
9. Rozwinięty kontroler
Lista kontrolna Google Cast wymaga, aby aplikacja nadawcza udostępniała rozszerzony kontroler dla przesyłanych multimediów. Rozwinięty kontroler to pełnoekranowa wersja minikontrolera.
Rozwinięty kontroler umożliwia oglądanie na pełnym ekranie, co zapewnia pełną kontrolę nad zdalnym odtwarzaniem multimediów. Widok ten powinien umożliwiać aplikacji do przesyłania treści zarządzanie wszystkimi aspektami sesji przesyłania, z wyjątkiem kontroli głośności odbiornika i cyklu życia sesji (łączenia i zatrzymywania przesyłania). Zawiera ona też wszystkie informacje o stanie sesji multimediów (grafika, tytuł, napisy itp.).
Funkcje tego widoku są implementowane przez klasę GCKUIExpandedMediaControlsViewController
.
Najpierw musisz włączyć domyślny rozszerzony kontroler w kontekście przesyłania. Zmień wartość AppDelegate.swift
, aby włączyć domyślny rozszerzony 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 ten kod do pliku MediaViewController.swift
, aby wczytać rozszerzony kontroler, gdy użytkownik zacznie przesyłać film:
@objc func playSelectedItemRemotely() {
...
appDelegate?.isCastControlBarsEnabled = false
GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()
}
Rozwinięty kontroler uruchomi się też automatycznie, gdy użytkownik naciśnie minikontroler.
Uruchom aplikację i prześlij film. Powinien się wyświetlić rozwinięty kontroler. Wróć do listy filmów, a gdy klikniesz minikontroler, rozwinięty kontroler zostanie ponownie załadowany.
10. Dodawanie obsługi Cast Connect
Biblioteka Cast Connect umożliwia istniejącym aplikacjom nadawczym komunikowanie się z aplikacją Androida TV za pomocą protokołu Cast. Cast Connect opiera się na infrastrukturze Cast, a aplikacja na Androida TV pełni rolę odbiornika.
Zależności
Sprawdź, czy w elemencie Podfile
google-cast-sdk
wskazuje wartość 4.4.8
lub wyższą, jak pokazano poniżej. Jeśli wprowadzisz w pliku zmiany, uruchom pod update
w konsoli, aby zsynchronizować je z projektem.
pod 'google-cast-sdk', '>=4.4.8'
GCKLaunchOptions
Aby uruchomić aplikację na Androida TV (znaną też jako odbiornik Androida), w obiekcie GCKLaunchOptions
trzeba ustawić flagę androidReceiverCompatible
na „true”. Ten obiekt GCKLaunchOptions
określa sposób uruchamiania odbiornika i jest przekazywany do obiektu GCKCastOptions
, którego ustawienia są określane w przypadku współdzielonym za pomocą obiektu GCKCastContext.setSharedInstanceWith
.
Dodaj do 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)
Ustawianie danych logowania do uruchamiania
Po stronie nadawcy możesz określić wartość GCKCredentialsData
, aby wskazać, kto dołącza do sesji. Wartość credentials
to ciąg znaków, który może być zdefiniowany przez użytkownika, o ile tylko aplikacja ATV może go zinterpretować. Identyfikator GCKCredentialsData
jest przekazywany do aplikacji na Androida TV tylko podczas uruchamiania lub dołączania do niej. Jeśli ustawisz go ponownie, gdy jesteś połączony, nie zostanie ono przekazane do aplikacji Android TV.
Aby można było skonfigurować dane uwierzytelniające uruchamiania, pole GCKCredentialsData
musi być zdefiniowane w dowolnym momencie po ustawieniu GCKLaunchOptions
. Aby to zademonstrować, dodajmy funkcje logiczne przycisku Dane uwierzytelniające, aby ustawić dane logowania, które będą przekazywane po utworzeniu sesji. Dodaj do pliku 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))
}
}
Ustawianie danych logowania w żądaniu wczytania
Aby obsłużyć credentials
w aplikacji internetowej i w odbiorniku Androida TV, dodaj ten kod w klasie MediaTableViewController.swift
w ramach funkcji loadSelectedItem
:
let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
...
mediaLoadRequestDataBuilder.credentials = credentials
...
W zależności od aplikacji odbiornika, do której nadawca przesyła treści, pakiet SDK automatycznie zastosuje powyższe dane uwierzytelniające do trwającej sesji.
Testuję Cast Connect
Instalowanie pliku APK Android TV na Chromecastzie z Google TV
- Znajdź adres IP urządzenia z Androidem TV. Zazwyczaj ta opcja jest dostępna w sekcji Ustawienia > Sieć i Internet > Nazwa sieci, z którą połączone jest urządzenie. Po prawej stronie pojawią się szczegóły oraz adres IP urządzenia w 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 najwyższego poziomu folderu przykładów kodu pobranego na początku tego ćwiczenia. Na przykład:
$ cd Desktop/ios_codelab_src
- Zainstaluj plik .apk z tego folderu w Androidzie TV, uruchamiając polecenie:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- Na urządzeniu z Androidem TV w menu Twoje aplikacje powinieneś teraz zobaczyć aplikację o nazwie Przesyłaj filmy.
- Gdy aplikacja będzie gotowa, skompiluj ją i uruchom w emulatorze lub na urządzeniu mobilnym. Po nawiązaniu sesji przesyłania na urządzeniu z Androidem TV powinna uruchomić się aplikacja Android Receiver na Androidzie TV. Odtwarzanie filmu od nadawcy na urządzeniu mobilnym z systemem iOS powinno uruchomić je 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 App-Done. Dodaj do metody applicationDidFinishLaunchingWithOptions
w pliku AppDelegate.swift
ten kod:
func application(_: UIApplication,
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
let styler = GCKUIStyle.sharedInstance()
...
}
Po zastosowaniu co najmniej jednej opcji personalizacji opisanej w pozostałych częściach tego CodeLab zapisz style, wywołując kod poniżej.
styler.apply()
Dostosowywanie widoków przesyłania
Możesz dostosowywać wszystkie widoki zarządzane przez platformę Cast Application Framework, korzystając z domyślnych wytycznych dotyczących stylów. Zmieńmy na przykład kolor zabarwienia ikony.
styler.castViews.iconTintColor = .lightGray
W razie potrzeby możesz zastąpić wartości domyślne na poszczególnych ekranach. Na przykład, aby zastąpić kolor lightGrayColor kolorem zabarwienia ikony tylko 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 z nich). Ten kod ustawia kolor tła na niebieskim we wszystkich widokach środowiska 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. Dla celów poglądowych ustawmy wszystkie czcionki na „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)
Zmienianie domyślnych obrazów przycisków
Dodaj własne obrazy niestandardowe do projektu i przypisz je do przycisków, aby określić 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ż motywować, korzystając z protokołu UIAppearance Protocol. Następujący motyw kodu zawiera element GCKUICastButton we wszystkich wyświetlanych widokach:
GCKUICastButton.appearance().tintColor = UIColor.gray
12. Gratulacje
Wiesz już, jak włączyć przesyłanie aplikacji wideo, korzystając z widżetów Cast SDK na iOS.
Więcej informacji znajdziesz w przewodniku dla deweloperów iOS Sender.