Für Google Cast optimierte iOS-App

1. Übersicht

Google Cast-Logo

In diesem Codelab erfahren Sie, wie Sie eine bestehende iOS-Video-App so anpassen, dass sie Inhalte auf ein für Google Cast optimiertes Gerät streamen kann.

Was ist Google Cast?

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

Mit dem Google Cast SDK können Sie Ihre App erweitern, um Google Cast-kompatible Geräte wie Fernseher oder Soundsysteme zu steuern. Mit dem Cast SDK können Sie die erforderlichen UI-Komponenten anhand der Checkliste für das Google Cast-Design hinzufügen.

Die Checkliste für das Google Cast-Design wurde bereitgestellt, um die Nutzung von Cast auf allen unterstützten Plattformen einfach und vorhersehbar zu machen.

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

  • Hinzufügen des Google Cast SDK zu einer Beispiel-Video-App
  • So fügen Sie die Schaltfläche „Streamen“ zum Auswählen eines Google Cast-Geräts hinzu.
  • So verbindest du dich mit einem Übertragungsgerät und startest einen Media-Receiver.
  • So streamen Sie ein Video.
  • So fügen Sie Ihrer App einen Cast-Mini-Controller hinzu.
  • So fügen Sie einen erweiterten Controller hinzu.
  • So fügst du ein Einführungs-Overlay hinzu.
  • So passen Sie Cast-Widgets an.
  • Cast Connect integrieren

Voraussetzungen

  • Den aktuellen Xcode.
  • Ein Mobilgerät mit iOS 9 oder höher oder der Xcode Simulator
  • Ein USB-Datenkabel, um Ihr Mobilgerät mit dem Entwicklungscomputer zu verbinden (falls Sie ein Gerät verwenden).
  • Ein Google Cast-Gerät wie Chromecast oder Android TV mit Internetverbindung
  • Einen Fernseher oder Monitor mit HDMI-Eingang
  • Zum Testen der Cast Connect-Integration ist Chromecast mit Google TV erforderlich, für den Rest des Codelabs ist sie jedoch optional. Wenn Sie keinen haben, können Sie den Schritt Unterstützung für Cast Connect hinzufügen am Ende dieser Anleitung überspringen.

Erfahrung

  • Sie benötigen Vorkenntnisse in der iOS-Entwicklung.
  • Außerdem benötigen Sie Vorkenntnisse in Bezug auf Fernsehen. :)

Wie möchten Sie diese Anleitung verwenden?

Nur durchlesen Durchlesen und die Übungen absolvieren

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

Anfänger Fortgeschrittene Erfahren

Wie würden Sie Ihre Erfahrungen mit Fernsehen bewerten?

Neuling Mittel Kompetent

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 zunächst an, wie die fertige Beispiel-App aussieht. Die App ist ein einfacher Videoplayer. Der Nutzer kann ein Video aus einer Liste auswählen und es dann lokal auf dem Gerät wiedergeben oder auf ein Google Cast-Gerät streamen.

Nachdem Sie den Code heruntergeladen haben, können Sie die fertige Beispielanwendung in Xcode öffnen und ausführen. Gehen Sie dazu so vor:

Häufig gestellte Fragen

CocoaPods-Einrichtung

Rufe die Konsole auf und installiere CocoaPods mit der standardmäßigen Ruby-Version, die unter macOS verfügbar ist:

sudo gem install cocoapods

Wenn Probleme auftreten, finden Sie in der offiziellen Dokumentation Informationen zum Herunterladen und Installieren des Abhängigkeitsmanagers.

Projekt einrichten

  1. Öffnen Sie das Terminal und wechseln Sie zum Codelab-Verzeichnis.
  2. Installieren Sie die Abhängigkeiten aus dem Podfile.
cd app-done
pod update
pod install
  1. Öffnen Sie Xcode und wählen Sie Open another project... (Weiteres Projekt öffnen...) aus.
  2. Wählen Sie im Ordner mit dem Beispielcode 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

Nach einigen Sekunden sollte die Video-App angezeigt werden.

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

Bestätigungsdialogfeld mit Berechtigung zum Akzeptieren eingehender Netzwerkverbindungen

Klicken Sie auf die Schaltfläche „Streamen“ 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 wiedergegeben.

Der erweiterte Controller wird angezeigt. Mit der Wiedergabe-/Pausetaste kannst du die Wiedergabe steuern.

Gehe zurück zur Liste der Videos.

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

Abbildung eines iPhones, auf der die CastVideos App ausgeführt wird, mit dem Mini-Controller unten

Klicke auf die Pause-Schaltfläche auf der Minifernbedienung, um das Video auf dem Receiver anzuhalten. Klicke im Mini-Controller auf die Wiedergabeschaltfläche, um die Wiedergabe des Videos fortzusetzen.

Klicken Sie auf die Schaltfläche „Streamen“, um das Streaming auf dem Google Cast-Gerät zu beenden.

4. Startprojekt vorbereiten

Abbildung eines iPhones mit der CastVideos App

Wir müssen der heruntergeladenen Start-App Unterstützung für Google Cast hinzufügen. Hier sind einige Google Cast-Begriffe, die wir in diesem Codelab verwenden werden:

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

Projekt einrichten

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

  1. Rufen Sie auf Ihrem Terminal das Codelab-Verzeichnis auf.
  2. Installieren Sie die Abhängigkeiten aus dem Podfile.
cd app-start
pod update
pod install
  1. Öffnen Sie Xcode und wählen Sie Open another project... (Weiteres Projekt öffnen...) aus.
  2. Wählen Sie im Ordner mit dem Beispielcode im Verzeichnis Ordnersymbolapp-start die Datei CastVideos-ios.xcworkspace aus.

App-Design

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

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

MediaTableViewController

Dieser UITableViewController zeigt eine Liste von Videos aus einer MediaListModel-Instanz an. Die Liste der Videos und die zugehörigen Metadaten werden auf einem Remoteserver als JSON-Datei gehostet. MediaListModel ruft dieses JSON ab und verarbeitet es, um eine Liste von MediaItem-Objekten zu erstellen.

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

MediaTableViewController erstellt eine MediaListModel-Instanz und registriert sich dann als MediaListModelDelegate, um benachrichtigt zu werden, wenn die Medienmetadaten heruntergeladen wurden, damit die Tabellenansicht geladen werden kann.

Der Nutzer sieht eine Liste mit Video-Thumbnails und einer kurzen Beschreibung für jedes Video. Wenn ein Element ausgewählt wird, wird die 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 View Controller enthält einen LocalPlayerView, einige Mediensteuerelemente und ein Textfeld, in dem die Beschreibung des ausgewählten Videos angezeigt wird. Der Player deckt den oberen Teil des Bildschirms ab und lässt darunter Platz für die detaillierte Beschreibung des Videos. Der Nutzer kann die lokale Videowiedergabe abspielen/pausieren oder vor-/rückspringen.

Häufig gestellte Fragen

5. Schaltfläche „Streamen“ hinzufügen

Abbildung des oberen Drittels eines iPhones, auf dem die CastVideos App ausgeführt wird. Oben rechts ist die Schaltfläche „Streamen“ zu sehen.

In einer für Google Cast optimierten Anwendung wird die Schaltfläche „Streamen“ in jedem Viewcontroller angezeigt. Wenn du auf die Schaltfläche „Streamen“ klickst, wird eine Liste der Übertragungsgeräte angezeigt, die du auswählen kannst. Wenn der Nutzer Inhalte lokal auf dem Sendergerät abspielte, wird durch die Auswahl eines Übertragungsgeräts die Wiedergabe auf diesem Gerät gestartet oder fortgesetzt. Der Nutzer kann während der Übertragung jederzeit auf das Cast-Symbol klicken und die Übertragung Ihrer App auf das Übertragungsgerät beenden. Der Nutzer muss auf jedem Bildschirm Ihrer App eine Verbindung zum Übertragungsgerät herstellen oder trennen können. Weitere Informationen hierzu finden Sie in der Checkliste für das Google Cast-Design.

Konfiguration

Für das Startprojekt sind dieselben Abhängigkeiten und die gleiche Xcode-Einrichtung erforderlich wie für die fertige Beispiel-App. Kehren Sie zu diesem Abschnitt zurück und führen Sie dieselben Schritte aus, um GoogleCast.framework dem Start-App-Projekt hinzuzufügen.

Initialisierung

Das Cast-Framework hat ein globales Singleton-Objekt, das GCKCastContext, das alle Aktivitäten des Frameworks koordiniert. Dieses Objekt muss früh im Lebenszyklus der Anwendung initialisiert werden, normalerweise in der application(_:didFinishLaunchingWithOptions:)-Methode des App-Delegates, damit die automatische Sitzungswiederaufnahme beim Neustart der Senderanwendung ordnungsgemäß ausgelöst und das Scannen nach Geräten 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. Das Wichtigste ist die ID der Empfänger-App, die verwendet wird, um die Ergebnisse der Übertragungsgeräteerkennung zu filtern und die Empfänger-App zu starten, wenn eine Übertragung gestartet wird.

Über die Methode application(_:didFinishLaunchingWithOptions:) können Sie auch einen Logging-Delegaten einrichten, der die Logging-Nachrichten vom Cast-Framework empfängt. Diese können für die Fehlerbehebung und Fehlersuche nützlich sein.

Wenn Sie Ihre eigene für Google Cast optimierte App entwickeln, müssen Sie sich als Cast-Entwickler registrieren und dann eine Anwendungs-ID für Ihre App abrufen. In diesem Codelab verwenden wir eine Beispiel-App-ID.

Füge in AppDelegate.swift den folgenden Code ein, um GCKCastContext mit der App-ID aus den Standardeinstellungen des Nutzers zu initialisieren, und füge einen Listener 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 die Schaltfläche „Streamen“ hinzufügen, damit der Nutzer ein Streaminggerät auswählen kann. Das Cast SDK bietet eine Schaltfläche für die Übertragung namens GCKUICastButton als UIButton-Unterklasse. Er kann der Titelleiste der App hinzugefügt werden, indem er in ein UIBarButtonItem-Element eingebunden wird. Wir müssen das Cast-Symbol sowohl zu MediaTableViewController als auch zu MediaViewController hinzufügen.

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

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 Ihr 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 jetzt die App aus. In der Navigationsleiste der App sollte das Cast-Symbol angezeigt werden. Wenn Sie darauf klicken, werden die Cast-Geräte in Ihrem lokalen Netzwerk aufgelistet. Die Geräteerkennung wird automatisch vom GCKCastContext verwaltet. Wählen Sie Ihr Übertragungsgerät aus. Die Beispiel-Empfänger-App wird auf dem Übertragungsgerät geladen. Sie können zwischen der Suchaktivität und der Aktivität im lokalen Player wechseln. Der Status des Cast-Symbols wird synchronisiert.

Wir haben keine Unterstützung für die Medienwiedergabe, daher kannst du noch keine Videos auf dem Übertragungsgerät wiedergeben. Klicken Sie auf das Cast-Symbol, um das Streamen zu beenden.

6. Videoinhalte streamen

Abbildung eines iPhones, auf der die CastVideos-App läuft, die Details zu einem bestimmten Video zeigt („Tears of Steel“). Unten ist der Miniplayer zu sehen.

Wir erweitern die Beispiel-App so, dass Videos auch per Fernzugriff auf Übertragungsgeräten abgespielt werden können. Dazu müssen wir die verschiedenen vom Cast-Framework generierten Ereignisse überwachen.

Medien werden gestreamt

Wenn Sie Medien auf einem Übertragungsgerät wiedergeben möchten, müssen folgende Voraussetzungen erfüllt sein:

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

In Schritt 1 wird ein Objekt einem anderen zugeordnet. GCKMediaInformation ist ein Objekt, das vom Cast SDK verstanden wird, und MediaItem ist die Kapselung eines Medienelements in unserer App. Wir können MediaItem ganz einfach einem GCKMediaInformation zuordnen. Schritt 2 haben wir bereits im vorherigen Abschnitt ausgeführt. Schritt 3 lässt sich mit dem Cast SDK ganz einfach durchführen.

Die Beispiel-App MediaViewController unterscheidet bereits zwischen lokaler und Remote-Wiedergabe, indem sie diese Aufzählung verwendet:

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

private var playbackMode = PlaybackMode.none

In diesem Codelab ist es nicht wichtig, dass Sie genau verstehen, wie die gesamte Logik des Beispiel-Players funktioniert. Der Mediaplayer Ihrer App muss so angepasst werden, dass er die beiden Wiedergabeorte auf ähnliche Weise erkennt.

Derzeit befindet sich der lokale Player immer im Status der lokalen Wiedergabe, da er noch nichts über die Streamingstatus weiß. Wir müssen die Benutzeroberfläche basierend auf Statusübergängen im Cast-Framework aktualisieren. Wenn wir beispielsweise mit dem Streamen beginnen, müssen wir die lokale Wiedergabe beenden und einige Steuerelemente deaktivieren. Wenn wir das Streaming beenden, während wir uns in diesem Ansicht-Controller befinden, müssen wir ebenfalls zur lokalen Wiedergabe wechseln. Dazu müssen wir auf die verschiedenen Ereignisse warten, die vom Cast-Framework generiert werden.

Streamingsitzung verwalten

Beim Cast-Framework umfasst eine Cast-Sitzung die Schritte: Verbinden mit einem Gerät, Starten oder Teilnehmen, Herstellen einer Verbindung zu einer Empfänger-App und Initialisieren eines Mediensteuerungskanals, falls erforderlich. Über den Mediensteuerungskanal sendet und empfängt das Cast-Framework Nachrichten vom Empfänger-Mediaplayer.

Das Streamen wird automatisch gestartet, wenn der Nutzer ein Gerät über das Cast-Symbol auswählt, und automatisch beendet, wenn der Nutzer die Verbindung trennt. Die automatische Wiederverbindung mit einer Empfängersitzung aufgrund von Netzwerkproblemen wird ebenfalls vom Cast-Framework verwaltet.

Übertragungssitzungen werden über die GCKSessionManager verwaltet, auf die über GCKCastContext.sharedInstance().sessionManager zugegriffen werden kann. Mit den GCKSessionManagerListener-Callbacks können Sitzungsereignisse wie Erstellung, Sperrung, Wiederaufnahme und Beendigung überwacht werden.

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

  ...
}

In MediaViewController möchten wir wissen, wenn eine Verbindung zum Cast-Gerät hergestellt oder getrennt wird, damit wir zum lokalen Player wechseln können. Die Verbindung kann nicht nur durch die Instanz Ihrer Anwendung unterbrochen werden, die auf Ihrem Mobilgerät ausgeführt wird, sondern auch durch eine andere Instanz Ihrer (oder einer anderen) Anwendung, die auf einem anderen Mobilgerät ausgeführt wird.

Auf die derzeit aktive Sitzung kann als GCKCastContext.sharedInstance().sessionManager.currentCastSession zugegriffen werden. Sitzungen werden als Reaktion auf Nutzergesten im Cast-Dialogfeld automatisch erstellt und gelöscht.

Medien werden geladen

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

Füge MediaViewController.swift den folgenden Code hinzu, um das aktuell ausgewählte Video auf dem Receiver 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
      }
    }
  }
  ...
}

Aktualisiere jetzt verschiedene vorhandene Methoden, um die Logik der Übertragungssitzung zur Unterstützung der 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 nun die App auf Ihrem Mobilgerät aus. Stellen Sie eine Verbindung zu Ihrem Übertragungsgerät her und starten Sie die Wiedergabe eines Videos. Das Video sollte jetzt auf dem Receiver wiedergegeben werden.

7. Mini-Controller

Gemäß der Checkliste für das Google Cast-Design müssen alle Cast-Apps einen Mini-Controller haben, der angezeigt wird, wenn der Nutzer die aktuelle Inhaltsseite verlässt. Der Mini-Controller bietet sofortigen Zugriff und eine sichtbare Erinnerung an die aktuelle Übertragungssitzung.

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

Das Cast SDK bietet eine Steuerleiste (GCKUIMiniMediaControlsViewController), die den Szenen hinzugefügt werden kann, in denen die permanenten Steuerelemente eingeblendet werden sollen.

Für die Beispiel-App verwenden wir GCKUICastContainerViewController, das einen anderen View Controller umschließt und unten ein GCKUIMiniMediaControlsViewController hinzufügt.

Ändern Sie die Datei AppDelegate.swift und fügen Sie in 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 Eigenschaft und einen Setter/Getter hinzu, um die Sichtbarkeit des Mini-Controllers zu steuern (diese werden in einem späteren Abschnitt verwendet):

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

Starten Sie die App und streamen Sie ein Video. Wenn die Wiedergabe auf dem Receiver beginnt, sollte der Mini-Controller unten in jeder Szene angezeigt werden. Sie können die Remote-Wiedergabe mit dem Mini-Controller steuern. Wenn du zwischen der Suchaktivität und der Aktivität des lokalen Players wechselst, sollte der Status des Mini-Controllers mit dem Status der Medienwiedergabe des Receivers synchronisiert bleiben.

8. Einleitendes Overlay

Gemäß der Google Cast-Design-Checkliste muss eine Sender-App bestehenden Nutzern die Cast-Schaltfläche präsentieren, um sie darüber zu informieren, dass die Sender-App jetzt das Streaming unterstützt. Außerdem sollte die Schaltfläche für Nutzer, die Google Cast noch nicht kennen, gut sichtbar sein.

Abbildung eines iPhones, auf dem die CastVideos App mit der Überlagerung der Übertragungsschaltfläche ausgeführt wird. Die Übertragungsschaltfläche ist hervorgehoben und die Meldung „Zum Streamen von Medien auf deinen Fernseher und deine Lautsprecher tippen“ wird angezeigt.

Die Klasse GCKCastContext hat eine Methode namens presentCastInstructionsViewControllerOnce, mit der die Schaltfläche „Streamen“ hervorgehoben werden kann, wenn sie Nutzern zum ersten Mal angezeigt wird. Fügen Sie MediaViewController.swift und MediaTableViewController.swift den folgenden Code hinzu:

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 Einleitungs-Overlay zu sehen sein.

9. Maximierter Controller

Gemäß der Checkliste für das Design von Google Cast muss eine Sender-App einen erweiterten Controller für die gestreamten Medien bereitstellen. Der erweiterte Controller ist eine Vollbildversion des Mini-Controllers.

Abbildung eines iPhones, auf denen die CastVideos App ausgeführt wird und ein Video abgespielt wird, wobei der maximierte Controller unten angezeigt wird

Die erweiterte Steueroberfläche ist eine Vollbildansicht, die die vollständige Kontrolle über die Remote-Medienwiedergabe bietet. Über diese Ansicht sollte eine Streaming-App alle verwaltebaren Aspekte einer Streamingsitzung verwalten können, mit Ausnahme der Lautstärkeregelung des Empfängers und des Sitzungslebenszyklus (Verbinden/Streaming beenden). Sie liefert außerdem alle Statusinformationen zur Mediensitzung (Artwork, Titel, Untertitel usw.).

Die Funktionen dieser Ansicht werden von der Klasse GCKUIExpandedMediaControlsViewController implementiert.

Zuerst musst du den standardmäßigen erweiterten Controller im Streaming-Kontext aktivieren. Ändern Sie AppDelegate.swift, um den standardmäßigen maximierten 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 erweiterten 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.

Starte die App und streame ein Video. Der maximierte Controller sollte angezeigt werden. Gehe zurück zur Liste der Videos. Wenn du auf den Mini-Controller klickst, wird der maximierte Controller wieder geladen.

10. Unterstützung für Cast Connect hinzufügen

Mit der Cast Connect-Mediathek können vorhandene Sender-Apps über das Cast-Protokoll mit Android TV-Apps kommunizieren. Cast Connect baut auf der Cast-Infrastruktur auf, wobei deine Android TV-App als Receiver fungiert.

Abhängigkeiten

Achten Sie darauf, dass in Ihrer Podfile die google-cast-sdk auf 4.4.8 oder höher verweist, wie unten aufgeführt. Wenn Sie die Datei geändert haben, führen Sie pod update in der Console aus, um die Änderung mit Ihrem Projekt zu synchronisieren.

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

GCKLaunchOptions

Zum Starten der Android TV App, die auch als Android-Receiver bezeichnet wird, müssen wir das androidReceiverCompatible-Flag im GCKLaunchOptions-Objekt auf „true“ setzen. Dieses GCKLaunchOptions-Objekt gibt vor, wie der Empfänger gestartet wird, und wird an den GCKCastOptions übergeben, 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 den Start festlegen

Auf der Absenderseite können Sie GCKCredentialsData angeben, um anzugeben, wer an der Sitzung teilnimmt. credentials ist ein String, der vom Nutzer definiert werden kann, sofern die ATV-App ihn lesen kann. Die GCKCredentialsData wird nur beim Starten oder Beitreten an deine Android TV-App übergeben. Wenn du sie während einer Verbindung noch einmal festlegst, wird sie nicht an deine Android TV App übergeben.

Zum Festlegen von Startanmeldedaten muss GCKCredentialsData nach dem Festlegen von GCKLaunchOptions jederzeit definiert werden. Zur Veranschaulichung fügen wir der Schaltfläche Creds eine Logik hinzu, mit der die Anmeldedaten festgelegt werden, die beim Aufbau der Sitzung übergeben werden. Fügen Sie Ihrem MediaTableViewController.swift den folgenden Code hinzu:

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

Um credentials sowohl in deiner Web-App als auch in deiner Android TV-Receiver-App zu verarbeiten, füge in der Klasse MediaTableViewController.swift unter der Funktion loadSelectedItem den folgenden Code hinzu:

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

Abhängig von der Empfänger-App, an die Ihr Absender etwas überträgt, wendet das SDK die oben genannten Anmeldedaten automatisch auf die laufende Sitzung an.

Cast Connect testen

Android TV-APK auf Chromecast mit Google TV installieren

  1. Ermitteln Sie die IP-Adresse Ihres Android TV-Geräts. Normalerweise finden Sie sie unter Einstellungen > Netzwerk und Internet > (Name des Netzwerks, mit dem Ihr Gerät verbunden ist). Auf der rechten Seite werden die Details und die IP-Adresse Ihres Geräts im Netzwerk angezeigt.
  2. Verwenden Sie die IP-Adresse des Geräts, um über ADB mit dem Terminal eine Verbindung zu ihm 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. Installieren Sie die APK-Datei in diesem Ordner auf Ihrem Android TV-Gerät:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. Auf Ihrem Android TV-Gerät sollte jetzt im Menü Meine Apps eine App mit dem Namen Videos streamen angezeigt werden.
  2. Anschließend können Sie die App in einem Emulator oder auf einem Mobilgerät erstellen und ausführen. Sobald Sie mit Ihrem Android TV-Gerät streamen, sollte die Android Receiver App auf Ihrem Android TV-Gerät gestartet werden. Wenn du ein Video von deinem mobilen iOS-Sender abspielst, sollte das Video im Android Receiver gestartet werden. Außerdem kannst du die Wiedergabe über die Fernbedienung deines Android TV-Geräts steuern.

11. Cast-Widgets anpassen

Initialisierung

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

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

Wenn Sie eine oder mehrere Anpassungen wie im Rest dieses Codelabs beschrieben vorgenommen haben, können Sie die Stile committen, indem Sie den folgenden Code aufrufen:

styler.apply()

Wiedergabeansichten anpassen

Sie können alle Ansichten anpassen, die vom Cast Application Framework verwaltet werden, indem Sie für alle Ansichten Standardstilrichtlinien verwenden. Ändern wir als Beispiel die Farbe der Symbolfarbe.

styler.castViews.iconTintColor = .lightGray

Bei Bedarf können Sie die Standardeinstellungen für jeden Bildschirm überschreiben. So können Sie beispielsweise die Farbe „lightGrayColor“ für die Symbolfarbe nur für den erweiterten Mediacontroller überschreiben.

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

Farben ändern

Sie können die Hintergrundfarbe für alle Ansichten oder für jede Ansicht einzeln anpassen. Im folgenden Code wird die Hintergrundfarbe für alle vom 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 Wiedergabeansichten angezeigt werden. Wir wählen für alle Schriftarten „Courier-Oblique“ aus. zu Illustrationszwecken.

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)

Standardschaltflächenbilder ändern

Füge dem Projekt deine eigenen Bilder hinzu und weise sie deinen Schaltflächen zu, um sie zu gestalten.

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

Design des Cast-Symbols ändern

Mit dem UIAppearance-Protokoll kannst du Cast Widgets auch entwerfen. Mit dem folgenden Code wird das GCKUICastButton in allen angezeigten Ansichten gestaltet:

GCKUICastButton.appearance().tintColor = UIColor.gray

12. Glückwunsch

Jetzt wissen Sie, wie Sie unter iOS mithilfe der Cast SDK-Widgets eine Video-App für Google Cast aktivieren.

Weitere Informationen findest du im Entwicklerhandbuch für iOS-Sender.