Cast-fähige iOS-App

1) Übersicht

Google Cast-Logo

In diesem Codelab erfahren Sie, wie Sie eine vorhandene iOS-Video-App so ändern, dass Inhalte auf ein für Google Cast optimiertes Gerät gestreamt werden.

Was ist Google Cast?

Mit Google Cast können Nutzer Inhalte von einem Mobilgerät auf einen Fernseher streamen. Nutzer können ihr Mobilgerät dann als Fernbedienung für die Medienwiedergabe auf dem Fernseher verwenden.

Mit dem Google Cast SDK können Sie Ihre App erweitern und so Google Cast-fähige Geräte wie einen Fernseher oder ein Soundsystem steuern. Mit dem Cast SDK können Sie basierend auf der Checkliste für das Google Cast-Design die erforderlichen UI-Komponenten hinzufügen.

Die Google Cast Design-Checkliste soll Nutzern die Arbeit mit allen unterstützten Plattformen erleichtern.

Ziele

Wenn Sie dieses Codelab abgeschlossen haben, haben Sie eine iOS-Video-App, mit der Sie Videos auf ein Google Cast-Gerät streamen können.

Lerninhalte

  • Das Google Cast SDK einer Beispiel-Video-App hinzufügen
  • Wie Sie das Cast-Symbol hinzufügen, um ein Google Cast-Gerät auszuwählen.
  • Verbindung zu einem Übertragungsgerät herstellen und einen Medienempfänger starten
  • Ein Video streamen
  • So fügen Sie Ihrer App einen Mini-Cast hinzu
  • Erweiterten Controller hinzufügen
  • Einführendes Overlay bereitstellen
  • Cast-Widgets anpassen
  • Cast Connect einbinden

Voraussetzungen

  • Die neueste Version des Xcode.
  • Ein Mobilgerät mit iOS 9 oder höher oder mit dem Xcode-Simulator.
  • Ein USB-Datenkabel zum Verbinden Ihres Mobilgeräts mit Ihrem Entwicklungscomputer (falls ein Gerät verwendet wird).
  • Ein Google Cast-Gerät wie ein Chromecast oder Android TV, das mit dem Internet verbunden ist.
  • Ein Fernseher oder Monitor mit HDMI-Eingang.
  • Für das Testen der Einbindung von Cast Connect ist Chromecast mit Google TV erforderlich. Für den Rest des Codelabs ist das optional. Wenn Sie keine haben, können Sie den Schritt Cast Connect-Support hinzufügen am Ende dieser Anleitung überspringen.

Plattform

  • Sie benötigen Vorkenntnisse zu iOS-Entwicklungen.
  • Außerdem brauchst du Erfahrung mit dem Fernsehen.

Wie werden Sie dieses Tutorial verwenden?

Nur lesen Lies es durch und schließe die Übungen ab

Wie würden Sie Ihre Erfahrungen mit der Entwicklung von iOS-Apps bewerten?

Anfänger Fortgeschrittene Profi

Wie würdest du deine Erfahrung mit dem Fernsehen bewerten?

Neuling Fortgeschrittene Profi

2. Beispielcode abrufen

Sie können entweder den gesamten Beispielcode auf Ihren Computer herunterladen...

und entpacken Sie die heruntergeladene ZIP-Datei.

3. Beispiel-App ausführen

Apple iOS-Logo

Sehen wir uns zuerst die fertige Beispiel-App an. Die App ist ein einfacher Videoplayer. Der Nutzer kann ein Video aus einer Liste auswählen und es dann lokal auf dem Gerät abspielen oder auf ein Google Cast-Gerät streamen.

Nachdem der Code heruntergeladen wurde, wird in der folgenden Anleitung beschrieben, wie Sie die fertige Beispiel-App in Xcode öffnen und ausführen:

Häufig gestellte Fragen

CocoaPods-Einrichtung

Rufen Sie zum Einrichten von CocoaPods die Konsole auf und installieren Sie sie mit dem standardmäßig unter macOS verfügbaren Ruby-Code:

sudo gem install cocoapods

Sollten Probleme auftreten, lesen Sie die offizielle Dokumentation und laden Sie den Abhängigkeits-Manager herunter und installieren Sie ihn.

Projekt einrichten

  1. Öffnen Sie in Ihrem Terminal das Codelab-Verzeichnis.
  2. Installieren Sie die Abhängigkeiten aus der Podfile.
cd app-done
pod update
pod install
  1. Öffnen Sie Xcode und wählen Sie Anderes Projekt öffnen... aus.
  2. Wählen Sie im Verzeichnis des Beispielcodes die Datei CastVideos-ios.xcworkspace aus dem Verzeichnis Ordnersymbolapp-done aus.

Anwendung ausführen

Wählen Sie das Ziel und den Simulator aus und führen Sie die App aus:

Symbolleiste des XCode-App-Simulators

Die Video-App sollte nach einigen Sekunden angezeigt werden.

Klicken Sie auf „Zulassen“, wenn die Benachrichtigung über die Annahme eingehender Netzwerkverbindungen angezeigt wird. Das Cast-Symbol wird nicht angezeigt, wenn diese Option nicht akzeptiert wird.

Bestätigungsdialogfeld für die Berechtigung zum Annehmen eingehender Netzwerkverbindungen

Klicken Sie auf das Cast-Symbol und wählen Sie Ihr Google Cast-Gerät aus.

Wähle ein Video aus und klicke auf die Wiedergabeschaltfläche.

Das Video wird auf Ihrem Google Cast-Gerät abgespielt.

Der erweiterte Controller wird angezeigt. Mit der Schaltfläche für Wiedergabe/Pause kannst du die Wiedergabe steuern.

Zurück zur Videoliste.

Unten auf dem Bildschirm wird jetzt ein Mini-Controller angezeigt.

Illustration eines iPhones, auf dem die CastVideos App ausgeführt wird, wobei der Mini-Controller unten angezeigt wird

Klicke auf die Pause-Taste im Mini-Controller, um das Video auf dem Empfänger zu pausieren. Klicke auf die Wiedergabeschaltfläche im Mini-Controller, um die Wiedergabe des Videos fortzusetzen.

Klicke auf das Cast-Symbol, um die Übertragung auf das Google Cast-Gerät zu beenden.

4. Startprojekt vorbereiten

Illustration eines iPhones, auf dem die CastVideos App ausgeführt wird

Wir benötigen Unterstützung für Google Cast in der heruntergeladenen Start-App. Die folgenden Google Cast-Begriffe werden in diesem Codelab verwendet:

  • eine Absender-App auf einem Mobilgerät oder Laptop ausgeführt wird und
  • Eine Empfänger-App wird auf dem Google Cast-Gerät ausgeführt.

Projekt einrichten

Jetzt können Sie auf dem Startprojekt mit Xcode aufbauen:

  1. Öffnen Sie in Ihrem Terminal das Codelab-Verzeichnis.
  2. Installieren Sie die Abhängigkeiten aus der Podfile.
cd app-start
pod update
pod install
  1. Öffnen Sie Xcode und wählen Sie Anderes Projekt öffnen... aus.
  2. Wählen Sie im Verzeichnis des Beispielcodes die Datei CastVideos-ios.xcworkspace aus dem Verzeichnis Ordnersymbolapp-start aus.

App-Design

Die App ruft eine Liste der Videos von einem Remote-Webserver ab und stellt dem Nutzer eine Liste mit diesen zur Verfügung. Nutzer können ein Video auswählen, um sich die Details anzusehen, oder das Video lokal auf dem Mobilgerät abspielen.

Die App besteht aus zwei Controllern für die Hauptansicht: MediaTableViewController und MediaViewController..

MediaTableViewController

Mit diesem UITableViewController wird eine Liste der Videos aus einer MediaListModel-Instanz angezeigt. Die Liste der Videos und die zugehörigen Metadaten werden als JSON-Datei auf einem Remoteserver gehostet. MediaListModel ruft dieses JSON-Objekt ab und verarbeitet es, um eine Liste mit MediaItem-Objekten zu erstellen.

Ein MediaItem-Objekt modelliert ein Video und die zugehörigen Metadaten, beispielsweise Titel, Beschreibung, URL für ein Bild und URL für den Stream.

MediaTableViewController erstellt eine MediaListModel-Instanz und registriert sich dann als MediaListModelDelegate, um informiert zu werden, wenn die Medienmetadaten heruntergeladen wurden, um die Tabellenansicht zu laden.

Dem Nutzer wird eine Liste mit Video-Thumbnails mit einer kurzen Beschreibung für jedes Video angezeigt. Wenn ein Element ausgewählt wird, wird der entsprechende MediaItem an MediaViewController übergeben.

MediaViewController

Dieser Ansichts-Controller zeigt die Metadaten zu einem bestimmten Video an und ermöglicht dem Nutzer, das Video lokal auf dem Mobilgerät abzuspielen.

Der Ansichts-Controller hostet eine LocalPlayerView, einige Mediensteuerelemente und einen Textbereich, um die Beschreibung des ausgewählten Videos anzuzeigen. Der Player verdeckt den oberen Teil des Bildschirms. So bleibt Platz für eine detaillierte Beschreibung des Videos unter dem Nutzer. Der Nutzer kann die lokale Videowiedergabe starten, pausieren oder fortsetzen.

Häufig gestellte Fragen

5. Cast-Symbol hinzufügen

Illustration des oberen Drittels eines iPhones, auf dem die CastVideos-App ausgeführt wird, und in der rechts oben das Cast-Symbol zu sehen ist

In einer für Google Cast optimierten App wird das Cast-Symbol in den einzelnen Controllern angezeigt. Wenn der Nutzer auf das Cast-Symbol klickt, wird eine Liste mit Übertragungsgeräten angezeigt, die der Nutzer auswählen kann. Wenn der Nutzer Inhalte lokal auf dem Absendergerät wiedergegeben hat, wird durch Auswahl eines Übertragungsgeräts die Wiedergabe auf diesem Übertragungsgerät gestartet oder fortgesetzt. Während des Streamings kann der Nutzer jederzeit auf das Cast-Symbol klicken und die Übertragung Ihrer App an das Übertragungsgerät beenden. Der Nutzer muss eine Verbindung zum Cast-Gerät herstellen bzw. trennen können, während die App auf einem beliebigen Bildschirm geöffnet wird. Dies wird in der Checkliste für das Google Cast-Design beschrieben.

Konfiguration

Für das Startprojekt müssen dieselben Abhängigkeiten und Xcode-Einrichtung wie für die abgeschlossene Beispiel-App verwendet werden. Kehren Sie zu diesem Abschnitt zurück und führen Sie dieselben Schritte aus, um GoogleCast.framework zum Start-App-Projekt hinzuzufügen.

Initialisierung

Das Cast-Framework hat das globale Singleton-Objekt GCKCastContext, das alle Aktivitäten des Frameworks koordiniert. Dieses Objekt muss frühzeitig im Lebenszyklus der Anwendung initialisiert werden. Dies ist normalerweise in der application(_:didFinishLaunchingWithOptions:)-Methode des Anwendungsdelegats der Fall, sodass die automatische Sitzungswiederaufnahme beim Neustart der Absenderanwendung korrekt ausgelöst und das Scannen für Geräte gestartet werden kann.

Bei der Initialisierung von GCKCastContext muss ein GCKCastOptions-Objekt angegeben werden. Diese Klasse enthält Optionen, die sich auf das Verhalten des Frameworks auswirken. Am wichtigsten ist die ID der Receiver-Anwendung. Damit wird die Erkennung der Übertragungsgeräte gefiltert und die Empfängeranwendung gestartet, wenn eine Cast-Sitzung gestartet wird.

Die Methode application(_:didFinishLaunchingWithOptions:) ist auch eine gute Möglichkeit, einen Logging-Bevollmächtigten einzurichten, um die Logging-Nachrichten vom Cast-Framework zu empfangen. Dies kann bei der Fehlerbehebung hilfreich sein.

Wenn Sie Ihre eigene Cast-fähige App entwickeln, müssen Sie sich als Cast-Entwickler registrieren und dann eine Anwendungs-ID für Ihre App abrufen. Für dieses Codelab verwenden wir eine Beispiel-App-ID.

Fügen Sie zu AppDelegate.swift den folgenden Code hinzu, um GCKCastContext mit der Anwendungs-ID aus den Standardeinstellungen des Nutzers zu initialisieren. Fügen Sie einen Logger für das Google Cast-Framework hinzu:

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)")
    }
  }
}

Cast-Symbol

Nachdem die GCKCastContext initialisiert wurde, müssen wir das Cast-Symbol hinzufügen, damit der Nutzer ein Übertragungsgerät auswählen kann. Das Cast SDK umfasst eine Cast-Schaltflächenkomponente namens GCKUICastButton als UIButton-Unterklasse. Sie kann der Titelleiste der App hinzugefügt werden, indem Sie den Parameter in einen UIBarButtonItem umschließen. Wir müssen das Cast-Symbol sowohl zu MediaTableViewController als auch zu MediaViewController hinzufügen.

Fügen Sie den folgenden Code in MediaTableViewController.swift und MediaViewController.swift ein:

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)

    ...
  }
  ...
}

Fügen Sie als Nächstes den folgenden Code in die Datei MediaViewController.swift ein:

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)

    ...
  }
  ...
}

Führen Sie die App jetzt aus. Wenn Sie in der Navigationsleiste der App auf ein Cast-Symbol klicken, wird eine Liste der Übertragungsgeräte in Ihrem lokalen Netzwerk angezeigt. Die Erkennung von Geräten wird automatisch von GCKCastContext verwaltet. Wählen Sie Ihr Übertragungsgerät aus. Die Beispiel-Empfänger-App wird auf dem Übertragungsgerät geladen. Du kannst zwischen den Stöberaktivitäten und der lokalen Spieleraktivität wechseln, während der Cast-Symbolstatus synchronisiert wird.

Wir haben keine Unterstützung für die Medienwiedergabe festgelegt, sodass Sie noch keine Videos auf dem Übertragungsgerät abspielen können. Klicke auf das Cast-Symbol, um die Übertragung zu beenden.

6. Videoinhalte streamen

Illustration eines iPhones mit der CastVideos App, auf der Details zu einem bestimmten Video („Tears of Steel“) angezeigt werden. Unten befindet sich der Miniplayer

Die Beispiel-App wird erweitert, damit du auch Videos per Fernzugriff auf einem Übertragungsgerät abspielen kannst. Dazu müssen wir die verschiedenen Ereignisse beobachten, die vom Cast-Framework generiert werden.

Medien werden gestreamt

Wenn Sie Medien auf einem Übertragungsgerät abspielen möchten, müssen Sie folgende Voraussetzungen erfüllen:

  1. Erstellen Sie über das Cast SDK ein GCKMediaInformation-Objekt, das ein Medienelement modelliert.
  2. Der Nutzer stellt eine Verbindung zum Übertragungsgerät her, um die Empfängeranwendung zu starten.
  3. Laden Sie das GCKMediaInformation-Objekt in den Receiver und spielen Sie den Inhalt ab.
  4. Verfolgen Sie den Medienstatus.
  5. Sende basierend auf Nutzerinteraktionen Wiedergabebefehle an den Empfänger.

Im ersten Schritt wird ein Objekt einem anderen zugeordnet. GCKMediaInformation ist etwas, das vom Cast SDK verstanden wird, und MediaItem ist das Kapselsystem unserer App für ein Medienelement. Wir können ein MediaItem ganz einfach einem GCKMediaInformation zuordnen. Wir haben Schritt 2 bereits ausgeführt. Schritt 3 ist mit dem Cast SDK ganz einfach.

Die Beispiel-App MediaViewController unterscheidet mithilfe der folgenden Aufzählung bereits zwischen lokaler und Remote-Wiedergabe:

enum PlaybackMode: Int {
  case none = 0
  case local
  case remote
}

private var playbackMode = PlaybackMode.none

Es ist nicht wichtig, in diesem Codelab zu verstehen, wie die Beispiellogik funktioniert. Es ist wichtig zu verstehen, dass der Mediaplayer deiner App so verändert werden muss, dass die beiden Wiedergabeorte ähnlich erkannt werden.

Zurzeit befindet sich der lokale Player immer im lokalen Wiedergabestatus, da er noch nichts über den Streamingstatus weiß. Wir müssen die UI auf der Grundlage von Statusübergängen aktualisieren, die im Cast-Framework stattfinden. Wenn wir beispielsweise mit dem Streamen beginnen, müssen wir die lokale Wiedergabe beenden und einige Steuerelemente deaktivieren. Wenn wir das Streaming beenden, wenn wir uns in diesem Ansicht-Controller befinden, müssen wir zur lokalen Wiedergabe übergehen. Dazu müssen wir die verschiedenen Ereignisse überwachen, die vom Cast-Framework generiert werden.

Streamingsitzungen verwalten

Für das Cast-Framework werden bei Cast-Sitzungen die Schritte zum Verbinden mit einem Gerät, Starten (oder Beitreten) zum Verbinden mit einer Empfängeranwendung und Initialisieren eines Mediensteuerungskanals (sofern zutreffend) durchgeführt. Über den Media Control Channel wird festgelegt, wie das Cast-Framework Nachrichten vom Empfänger des Mediaplayers sendet und empfängt.

Die Streamingsitzung wird automatisch gestartet, wenn der Nutzer ein Gerät aus dem Cast-Symbol auswählt, und wird automatisch beendet, wenn der Nutzer die Verbindung trennt. Eine Verbindung zu einer Empfängersitzung aufgrund von Netzwerkproblemen wird auch automatisch vom Cast-Framework hergestellt.

Streamingsitzungen werden über die GCKSessionManager verwaltet, auf die über GCKCastContext.sharedInstance().sessionManager zugegriffen werden kann. Mit den Callbacks „GCKSessionManagerListener“ können Sie Sitzungsereignisse überwachen, z. B. Erstellung, Sperrung, Wiederaufnahme und Beendigung.

Zuerst müssen wir unseren Sitzungs-Listener registrieren und einige Variablen initialisieren:

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()
  }

  ...
}

Wir würden uns sehr freuen, wenn du in MediaViewController informiert wirst, wenn wir das Streaming-Gerät verbinden oder trennen. Dann können wir zum lokalen Player wechseln. Beachten Sie, dass die Konnektivität nicht nur durch die Instanz Ihrer App, die auf Ihrem Mobilgerät ausgeführt wird, unterbrochen werden kann, sondern auch durch eine andere Instanz Ihrer (oder einer anderen) Anwendung, die auf einem anderen Mobilgerät ausgeführt wird.

Auf die aktuell aktive Sitzung kann über GCKCastContext.sharedInstance().sessionManager.currentCastSession zugegriffen werden. Sitzungen werden automatisch erstellt und als Reaktion auf Nutzerbewegungen aus den Streaming-Dialogfeldern gelöscht.

Medien werden geladen

Im Cast SDK umfasst die GCKRemoteMediaClient eine Reihe praktischer APIs für die Verwaltung der Remote-Medienwiedergabe auf dem Empfänger. Für ein GCKCastSession, das die Medienwiedergabe unterstützt, wird automatisch eine Instanz von GCKRemoteMediaClient vom SDK erstellt. Sie können darauf als Eigenschaft remoteMediaClient der Instanz GCKCastSession zugreifen.

Fügen Sie zu MediaViewController.swift den folgenden Code hinzu, um das aktuell ausgewählte Video auf dem Empfänger zu laden:

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

Aktualisieren Sie jetzt verschiedene vorhandene Methoden, um die Cast-Sitzungslogik für die Remote-Wiedergabe zu verwenden:

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
}

Führen Sie die App jetzt auf Ihrem Mobilgerät aus. Stelle eine Verbindung zu deinem Übertragungsgerät her und starte die Wiedergabe eines Videos. Sie sollten das Video sehen, das auf dem Empfänger wiedergegeben wird.

7. Mini-Controller

Gemäß der Checkliste für das Cast-Design müssen alle Cast-Apps einen Mini-Controller bereitstellen, damit sie angezeigt werden, wenn der Nutzer die aktuelle Inhaltsseite verlässt. Der Mini-Controller bietet sofortigen Zugriff und eine sichtbare Erinnerung für die aktuelle Streamingsitzung.

Illustration des unteren Teils eines iPhones, auf dem die CastVideos-App ausgeführt wird, mit Fokus auf dem Mini-Controller

Das Cast SDK umfasst die Steuerleiste GCKUIMiniMediaControlsViewController, die den Szenen hinzugefügt werden kann, in denen die persistenten Steuerelemente angezeigt werden sollen.

Für die Beispiel-App verwenden wir den GCKUICastContainerViewController, der einen weiteren Ansichts-Controller umschließt und unten ein GCKUIMiniMediaControlsViewController-Element einfügt.

Ändern Sie die Datei AppDelegate.swift und fügen Sie mit der folgenden Methode den folgenden Code für die Bedingung if useCastContainerViewController hinzu:

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()
  ...
}

Fügen Sie diese Property und diesen Sender bzw. Getter hinzu, um die Sichtbarkeit des Mini-Controllers zu steuern. Wir verwenden diese in einem späteren Abschnitt:

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

Führen Sie die App aus und streamen Sie ein Video. Wenn die Wiedergabe auf dem Empfänger beginnt, sollte unten in jeder Szene der Mini-Controller angezeigt werden. Mit dem Mini-Controller kannst du die Wiedergabe per Fernzugriff steuern. Wenn Sie zwischen den „Surfen“-Aktivitäten und der lokalen Player-Aktivität wechseln, sollte der Mini-Controller mit dem Medienwiedergabestatus des Empfängers synchron bleiben.

8. Einführendes Overlay

In der Checkliste für das Google Cast-Design muss eine Absender-App Nutzern das Cast-Symbol vorstellen, um sie darüber zu informieren, dass die Absender-App jetzt Cast unterstützt und Nutzern hilft, neu bei Google Cast zu werden.

Illustration eines iPhones, auf dem die CastVideos App mit dem Overlay für das Cast-Symbol läuft. Das Symbol markiert die Schaltfläche „Zum Streamen von Medien auf den Fernseher und die Lautsprecher tippen“.

Die Klasse GCKCastContext hat die Methode presentCastInstructionsViewControllerOnce, mit der Sie das Cast-Symbol hervorheben können, wenn es Nutzern zum ersten Mal angezeigt wird. Fügen Sie den folgenden Code in MediaViewController.swift und MediaTableViewController.swift ein:

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)
  }
}

Wenn Sie die App auf Ihrem Mobilgerät ausführen, sollte das Einführungs-Overlay zu sehen sein.

9. Controller maximiert

In der Checkliste für das Design von Google Cast muss eine Absender-App einen erweiterten Controller für die gestreamten Medien bereitstellen. Der maximierte Controller ist eine Vollbildversion des Mini-Controllers.

Illustration eines iPhones, auf dem die CastVideos App läuft, in der ein Video zu sehen ist. Der erweiterte Controller wird unten angezeigt.

Der erweiterte Controller ist eine Vollbildansicht, die die vollständige Kontrolle über die Remote-Medienwiedergabe bietet. In dieser Ansicht sollte eine Streaming-App alle verwaltbaren Aspekte einer Streamingsitzung verwalten können – mit Ausnahme der Lautstärkeregelung des Empfängers und des Sitzungslebenszyklus (Verbindung herstellen/beenden). Außerdem finden Sie dort alle Statusinformationen zur Mediasitzung, z. B. Artwork, Titel und Untertitel.

Die Funktion dieser Ansicht wird von der GCKUIExpandedMediaControlsViewController-Klasse implementiert.

Zuerst müssen Sie den standardmäßigen erweiterten Controller im Umwandlungskontext aktivieren. Ändern Sie AppDelegate.swift, um den standardmäßigen erweiterten Controller zu aktivieren:

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

Fügen Sie MediaViewController.swift den folgenden Code hinzu, um den maximierten Controller zu laden, wenn der Nutzer mit dem Streamen eines Videos beginnt:

@objc func playSelectedItemRemotely() {
  ...
  appDelegate?.isCastControlBarsEnabled = false
  GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()
}

Der erweiterte Controller wird auch automatisch gestartet, wenn der Nutzer auf den Mini-Controller tippt.

Führen Sie die App aus und streamen Sie ein Video. Sie sollten den maximierten Controller sehen. Kehren Sie zur Liste der Videos zurück. Wenn Sie auf den Mini-Controller klicken, wird der erweiterte Controller wieder geladen.

10. Cast Connect-Support hinzufügen

Über die Cast Connect-Bibliothek können bestehende Absender-Apps über das Cast-Protokoll mit Android TV-Apps kommunizieren. Cast Connect baut auf der Cast-Infrastruktur auf und die Android TV App fungiert als Empfänger.

Abhängigkeiten

In Podfile muss der google-cast-sdk wie unten angegeben auf 4.4.8 oder höher verweisen. Wenn Sie eine Änderung an der Datei vorgenommen haben, führen Sie pod update über die Console aus, um die Änderung mit Ihrem Projekt zu synchronisieren.

pod 'google-cast-sdk', '>=4.4.8'

GCKLaunch-Optionen

Zum Starten der Android TV App, die auch als Android Receiver bezeichnet wird, muss das Flag androidReceiverCompatible im Objekt GCKLaunchOptions auf „true“ gesetzt werden. Dieses GCKLaunchOptions-Objekt bestimmt, wie der Empfänger gestartet wird und an den GCKCastOptions übergeben wird, der in der gemeinsam genutzten Instanz mit GCKCastContext.setSharedInstanceWith festgelegt wird.

Fügen Sie Ihrem AppDelegate.swift die folgenden Zeilen hinzu:

let options = GCKCastOptions(discoveryCriteria:
                          GCKDiscoveryCriteria(applicationID: kReceiverAppID))
...
/** Following code enables CastConnect */
let launchOptions = GCKLaunchOptions()
launchOptions.androidReceiverCompatible = true
options.launchOptions = launchOptions

GCKCastContext.setSharedInstanceWith(options)

Anmeldedaten für die Aktivierung festlegen

Auf Absenderseite können Sie mit GCKCredentialsData angeben, wer an der Sitzung teilnehmen soll. credentials ist ein String, der vom Nutzer definiert werden kann, solange er von deiner ATV-App erkannt wird. Die GCKCredentialsData wird nur während der Einführung oder bei der Registrierung an deine Android TV App übergeben. Wenn Sie die Verbindung noch einmal einrichten, während Sie verbunden sind, wird sie nicht an Ihre Android TV App übergeben.

Zum Festlegen von Anmeldedaten für den Start muss GCKCredentialsData jederzeit nach dem Festlegen von GCKLaunchOptions definiert werden. Um dies zu demonstrieren, fügen wir der Logik Creds eine Logik hinzu, mit der die Anmeldedaten festgelegt werden, die beim Erstellen der Sitzung übergeben werden. Fügen Sie den folgenden Code in MediaTableViewController.swift ein:

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))
  }
}

Anmeldedaten für Ladeanfrage festlegen

Fügen Sie den folgenden Code in die Klasse MediaTableViewController.swift unter der Funktion loadSelectedItem ein, um credentials sowohl in Ihrer Web-App als auch in Ihrer Android TV Receiver App zu verarbeiten:

let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
...
mediaLoadRequestDataBuilder.credentials = credentials
...

Je nachdem, an welche Empfänger-App Ihr Absender sendet, werden die oben genannten Anmeldedaten automatisch auf die laufende Sitzung angewendet.

Cast Connect testen

Anleitung zum Installieren des Android TV-APK auf Chromecast mit Google TV

  1. Suchen Sie die IP-Adresse Ihres Android TV-Geräts. Sie ist normalerweise unter Einstellungen > Netzwerk & Internet > (Netzwerkname, mit dem Ihr Gerät verbunden ist) verfügbar. Auf der rechten Seite werden die Details und die IP-Adresse deines Geräts im Netzwerk angezeigt.
  2. Verwenden Sie die IP-Adresse Ihres Geräts, um eine Verbindung über ADB mit dem Terminal herzustellen:
$ adb connect <device_ip_address>:5555
  1. Gehen Sie im Terminalfenster zum Ordner der obersten Ebene für die Codelab-Beispiele, die Sie zu Beginn dieses Codelabs heruntergeladen haben. Beispiel:
$ cd Desktop/ios_codelab_src
  1. Installiere die APK-Datei in diesem Ordner mit folgendem Befehl auf deinem Android TV:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. Auf dem Android TV-Gerät sollte im Menü Meine Apps jetzt die App YouTube-Videos streamen angezeigt werden.
  2. Erstellen Sie die App und führen Sie sie auf einem Emulator oder Mobilgerät aus. Wenn Sie eine Streamingsitzung mit Ihrem Android TV-Gerät beginnen, sollte jetzt die Android Receiver App auf Ihrem Android TV-Gerät gestartet werden. Wenn Sie ein Video von Ihrem mobilen iOS-Sender abspielen, sollte es auf dem Android Receiver starten und die Wiedergabe über die Fernbedienung Ihres Android TV-Geräts steuern.

11. Cast-Widgets anpassen

Initialisierung

Beginnen Sie mit dem Ordner „App-Fertig“. Fügen Sie der Methode applicationDidFinishLaunchingWithOptions in der Datei AppDelegate.swift Folgendes hinzu:

func application(_: UIApplication,
                 didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  ...
  let styler = GCKUIStyle.sharedInstance()
  ...
}

Wenn Sie wie in diesem Codelab beschrieben eine oder mehrere Anpassungen vorgenommen haben, können Sie die Stile übernehmen, indem Sie den Code unten aufrufen.

styler.apply()

Streaming-Ansichten anpassen

Sie können alle Ansichten anpassen, die vom Cast Application Framework verwaltet werden. Dazu lassen sich Standard-Stilrichtlinien für alle Ansichten festlegen. Ändern wir hier als Beispiel die Farbe der Symbolfarbe.

styler.castViews.iconTintColor = .lightGray

Sie können die Standardeinstellungen für einzelne Bildschirme überschreiben. So wird beispielsweise „lightGrayColor“ für die Symbol-Farbfarbe nur für den maximierten Mediacontroller überschrieben.

styler.castViews.mediaControl.expandedController.iconTintColor = .green

Farben ändern

Sie können die Hintergrundfarbe für alle Ansichten oder einzeln für jede Ansicht anpassen. Mit dem folgenden Code wird die Hintergrundfarbe für alle von Cast Application Framework bereitgestellten Ansichten auf Blau festgelegt.

styler.castViews.backgroundColor = .blue
styler.castViews.mediaControl.miniController.backgroundColor = .yellow

Schriftarten ändern

Sie können Schriftarten für verschiedene Labels anpassen, die in Streamingansichten zu sehen sind. Zur Verdeutlichung sollen alle Schriftarten auf „ Kurier Oblique“ gesetzt werden.

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)

Standardbilder von Schaltflächen ändern

Sie können dem Projekt eigene benutzerdefinierte Images hinzufügen und sie den Schaltflächen zuweisen, um sie zu gestalten.

let muteOnImage = UIImage.init(named: "yourImage.png")
if let muteOnImage = muteOnImage {
  styler.castViews.muteOnImage = muteOnImage
}

Design des Cast-Symbols ändern

Sie können Streaming-Widgets auch mit dem UIDarstellungsprotokoll gestalten. Der folgende Code steht für GCKUICastButton in allen Ansichten:

GCKUICastButton.appearance().tintColor = UIColor.gray

12. Glückwunsch

Jetzt wissen Sie, wie Sie mit den Cast SDK-Widgets unter iOS eine Video-App aktivieren.

Weitere Informationen finden Sie im Entwicklerleitfaden für iOS.