Cast-fähige iOS-App

Mit Sammlungen den Überblick behalten Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.

1. Übersicht

Logo: Google Cast

In diesem Codelab erfahren Sie, wie Sie eine vorhandene iOS-Video-App so ändern, dass Inhalte auf ein Google Cast-fähiges 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 Geräte steuern, die für Google Cast optimiert sind, zum Beispiel einen Fernseher oder ein Soundsystem. Mit dem Cast SDK können Sie die erforderlichen UI-Komponenten gemäß der Checkliste für das Google Cast-Design hinzufügen.

Die Checkliste für das Google Cast-Design soll dafür sorgen, dass Cast auf allen unterstützten Plattformen einfach und vorhersehbar funktioniert.

Ziele

Nach Abschluss dieses Codelabs hast du eine iOS-Video-App, mit der du Videos auf ein Google Cast-Gerät streamen kannst.

Lerninhalte

  • So fügen Sie das Google Cast SDK einer Beispielvideo-App hinzu
  • So fügen Sie das Cast-Symbol zum Auswählen eines Google Cast-Geräts hinzu
  • Eine Verbindung zu einem Übertragungsgerät herstellen und einen Medienempfänger starten
  • Ein Video streamen
  • So fügen Sie Ihrer App einen Cast Mini-Controller hinzu
  • So fügen Sie einen erweiterten Controller hinzu.
  • Einführungs-Overlay bereitstellen
  • Cast-Widgets anpassen
  • So integrieren Sie Cast Connect

Voraussetzungen

  • Der aktuelle Xcode.
  • Ein Mobilgerät mit iOS 9 oder höher (oder der 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 Chromecast oder Android TV, das mit Internetzugang konfiguriert ist.
  • Ein 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 dies optional. Wenn Sie keine haben, können Sie den Schritt Support für Cast Connect hinzufügen am Ende dieser Anleitung überspringen.

Plattform

  • Dafür benötigen Sie Vorkenntnisse in der iOS-Entwicklung.
  • Außerdem benötigst du Vorkenntnisse zum Fernsehen.

Wie verwenden Sie diese Anleitung?

Nur durchlesen Lies dir die Übungen durch

Wie würden Sie Ihre Erfahrungen im Erstellen von iOS-Apps bewerten?

Anfänger Fortgeschritten Profi

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

Neuling Fortgeschritten 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

Logo: Apple iOS

Sehen wir uns zuerst an, wie die ausgefüllte 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 der Code heruntergeladen wurde, wird anhand der folgenden Anleitung beschrieben, wie Sie die fertige Beispiel-App in Xcode öffnen und ausführen:

Häufig gestellte Fragen

CocoaPods einrichten

Wenn Sie CocoaPods einrichten möchten, rufen Sie die Konsole auf und installieren Sie den Dienst mit dem auf macOS verfügbaren Standardstandard:

sudo gem install cocoapods

Sollten Probleme auftreten, lesen Sie die offizielle Dokumentation, um den Abhängigkeitsmanager herunterzuladen und zu installieren.

Projekt einrichten

  1. Rufen Sie Ihr Terminal auf und gehen Sie zum Codelab-Verzeichnis.
  2. Installieren Sie die Abhängigkeiten aus der Podfile-Datei.
cd app-done
pod update
pod install
  1. Öffnen Sie Xcode und wählen Sie Open another project... aus.
  2. Wählen Sie im Verzeichnis 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 das Annehmen eingehender Netzwerkverbindungen erscheint. Das Cast-Symbol wird nicht angezeigt, wenn diese Option nicht akzeptiert wird.

Bestätigungsdialogfeld, in dem um die Berechtigung zum Annehmen eingehender Netzwerkverbindungen gebeten wird

Klicke auf das Cast-Symbol und wähle dein 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 maximierte Controller wird angezeigt. Mit der Schaltfläche „Wiedergabe/Pause“ können Sie die Wiedergabe steuern.

Zurück zur Videoliste.

Unten auf dem Bildschirm ist jetzt ein Minicontroller zu sehen.

Illustration eines iPhones, auf dem die CastVideos-App läuft. Der Minicontroller wird unten angezeigt.

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 das Streamen auf das Google Cast-Gerät zu beenden.

4. Startprojekt vorbereiten

Abbildung eines iPhones, auf dem die CastVideos App läuft

Wir müssen die heruntergeladene Start-App für Google Cast unterstützen. Im Folgenden finden Sie einige Begriffe von Google Cast, die wir in diesem Codelab verwenden werden:

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

Projekt einrichten

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

  1. Rufen Sie Ihr Terminal auf und gehen Sie zum Codelab-Verzeichnis.
  2. Installieren Sie die Abhängigkeiten aus der Podfile-Datei.
cd app-start
pod update
pod install
  1. Öffnen Sie Xcode und wählen Sie Open another project... aus.
  2. Wählen Sie im Verzeichnis mit dem Beispielcode die Datei CastVideos-ios.xcworkspace aus dem Verzeichnis Ordnersymbolapp-start aus.

App-Design

Die App ruft eine Liste mit Videos von einem Remote-Webserver ab und stellt dem Nutzer eine Liste 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 Hauptansicht-Controllern: 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 als JSON-Datei auf einem Remoteserver gehostet. MediaListModel ruft diesen JSON-Code ab und verarbeitet ihn, um eine Liste von MediaItem-Objekten zu erstellen.

Ein MediaItem-Objekt modelliert ein Video und die zugehörigen Metadaten wie 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, damit die Tabellenansicht geladen werden kann.

Dem Nutzer wird eine Liste mit Video-Thumbnails mit einer kurzen Beschreibung für jedes Video angezeigt. Wenn ein Element ausgewählt wird, wird die entsprechende MediaItem an die 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 ein LocalPlayerView, einige Mediensteuerelemente und einen Textbereich, um die Beschreibung des ausgewählten Videos anzuzeigen. Der Player verdeckt den oberen Teil des Bildschirms. So bleibt unter der Wiedergabe eine detaillierte Beschreibung des Videos.

Häufig gestellte Fragen

5. Cast-Symbol hinzufügen

Illustration eines oberen Drittels eines iPhones, auf dem die CastVideos App läuft. In der Abbildung ist das Cast-Symbol rechts oben zu sehen.

Für Google Cast optimierte Apps zeigen das Cast-Symbol in jedem Ansichts-Controller an. Wenn Nutzer auf das Cast-Symbol klicken, wird eine Liste der Übertragungsgeräte angezeigt, die der Nutzer auswählen kann. Wenn der Nutzer Inhalte lokal auf dem Gerät des Absenders abgespielt hat, wird durch Auswahl eines Übertragungsgeräts die Wiedergabe auf diesem Übertragungsgerät gestartet oder fortgesetzt. Der Nutzer kann jederzeit während einer Streamingsitzung auf das Cast-Symbol klicken und die Übertragung Ihrer App an das Cast-Gerät beenden. Der Nutzer muss in der Lage sein, auf jedem Bildschirm Ihrer App eine Verbindung zum Cast-Gerät herzustellen bzw. zu trennen, wie in der Google Cast-Design-Checkliste beschrieben.

Konfiguration

Das Startprojekt erfordert dieselben Abhängigkeiten und die Xcode-Einrichtung wie für die fertige Beispiel-App. Kehren Sie zu diesem Abschnitt zurück und führen Sie dieselben Schritte aus, um GoogleCast.framework zum App-Startprojekt 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, normalerweise in der Methode application(_:didFinishLaunchingWithOptions:) des Anwendungsdelegats. Dadurch kann die automatische Sitzungswiederaufnahme beim Neustart der Absenderanwendung ordnungsgemäß ausgelöst werden und das Scannen nach Geräten kann gestartet werden.

Bei der Initialisierung von GCKCastContext muss ein GCKCastOptions-Objekt angegeben werden. Diese Klasse enthält Optionen, die sich auf das Verhalten des Frameworks auswirken. Die wichtigste ist die ID der Empfängeranwendung, mit der die Ergebnisse der Cast-Geräteerkennung gefiltert und die Empfängeranwendung gestartet werden, wenn eine Cast-Sitzung gestartet wird.

Die Methode application(_:didFinishLaunchingWithOptions:) eignet sich auch gut zum Einrichten eines Logging-Bevollmächtigten, um die Logging-Nachrichten aus dem Cast-Framework zu empfangen. Diese können für die Fehlerbehebung und Fehlerbehebung hilfreich 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. Für dieses Codelab verwenden wir eine Beispiel-App-ID.

Fügen Sie AppDelegate.swift den folgenden Code hinzu, um GCKCastContext mit der Anwendungs-ID aus den Nutzereinstellungen zu initialisieren, und 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 bietet eine Cast-Schaltflächenkomponente namens GCKUICastButton als Unterklasse UIButton. Sie kann der Titelleiste der App hinzugefügt werden, indem sie in UIBarButtonItem eingeschlossen werden. Wir müssen das Cast-Symbol sowohl dem MediaTableViewController als auch dem 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 zu MediaViewController.swift hinzu:

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 sollten Sie ein Cast-Symbol sehen. Wenn Sie darauf klicken, werden die Cast-Geräte in Ihrem lokalen Netzwerk aufgelistet. Die Geräteerkennung wird automatisch von GCKCastContext verwaltet. Wählen Sie Ihr Übertragungsgerät aus. Die Beispiel-Empfänger-App wird dann auf dem Übertragungsgerät geladen. Sie können zwischen der Browser- und der lokalen Playeraktivität wechseln. Der Cast-Status wird dabei synchronisiert.

Die Medienwiedergabe ist noch nicht verfügbar, sodass Sie noch keine Videos auf dem Übertragungsgerät abspielen können. Klicke auf das Cast-Symbol, um das Streaming zu beenden.

6. Streamen von Videoinhalten

Illustration eines iPhones, auf dem die CastVideos-App mit Details zu einem bestimmten Video zu sehen ist ('Tears of Steel'). Unten ist der Miniplayer

Wir erweitern die Beispiel-App, damit du auch Videos per Remotezugriff auf einem Übertragungsgerät abspielen kannst. Dazu müssen wir die verschiedenen Ereignisse im Cast-Framework beobachten.

Medien streamen

Wenn Sie Medien auf einem Übertragungsgerät abspielen möchten, gilt Folgendes:

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

Schritt 1 ist die Zuordnung eines Objekts zu einem anderen. GCKMediaInformation versteht das Cast SDK und MediaItem steht für die Kapselung eines Media-Elements in der App. Wir können ein MediaItem einfach einem GCKMediaInformation zuordnen. Wir haben Schritt 2 bereits im vorherigen Abschnitt 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

In diesem Codelab ist es nicht wichtig, dass du genau verstehst, wie die gesamte Beispiellogik funktioniert. Es ist wichtig zu verstehen, dass der Mediaplayer deiner App so angepasst werden muss, dass die beiden Wiedergabeorte ähnlich erkannt werden.

Im Moment ist der lokale Player immer im lokalen Wiedergabestatus, da er noch nichts über den Streaming-Status weiß. Wir müssen die Benutzeroberfläche basierend auf Statusübergängen aktualisieren, die im Cast-Framework erfolgen. Wenn wir beispielsweise mit dem Streamen beginnen, müssen wir die lokale Wiedergabe beenden und einige Steuerelemente deaktivieren. Wenn wir in diesem Ansicht-Controller das Streamen beenden, müssen wir zur lokalen Wiedergabe übergehen. Dazu müssen wir die verschiedenen Ereignisse im Cast-Framework beobachten.

Cast-Sitzungsverwaltung

Beim Cast-Framework werden bei einer Cast-Sitzung die folgenden Schritte durchgeführt: Verbinden mit einem Gerät, Starten (oder Teilnehmen) des Geräts, Verbinden mit einer Empfängeranwendung und Initialisieren eines Mediensteuerungskanals (falls zutreffend). Über den Mediensteuerungskanal wird festgelegt, wie das Cast-Framework Nachrichten vom Mediaplayer des Empfängers sendet und empfängt.

Die Streamingsitzung wird automatisch gestartet, wenn der Nutzer ein Gerät über die Cast-Schaltfläche auswählt, und automatisch beendet, wenn der Nutzer die Verbindung trennt. Die Verbindung mit einer Empfängersitzung aufgrund von Netzwerkproblemen wird ebenfalls automatisch über das Cast-Framework hergestellt.

Streamingsitzungen werden von der GCKSessionManager verwaltet, auf die über GCKCastContext.sharedInstance().sessionManager zugegriffen werden kann. Mit den GCKSessionManagerListener-Callbacks können Sie Sitzungsereignisse wie das Erstellen, Sperren, Wiederaufnahme und Beenden von Sitzungen beobachten.

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

  ...
}

MediaViewController interessiert uns, wenn wir eine Verbindung zum Cast-Gerät herstellen oder trennen, damit wir zum lokalen Player wechseln können. Beachten Sie, dass die Verbindung nicht nur durch die Instanz Ihrer Anwendung unterbrochen werden kann, die auf Ihrem Mobilgerät ausgeführt wird, sondern auch durch eine andere Instanz Ihrer (oder eine andere) Anwendung, die auf einem anderen Mobilgerät ausgeführt wird.

Die aktuell aktive Sitzung kann als GCKCastContext.sharedInstance().sessionManager.currentCastSession aufgerufen werden. Sitzungen werden automatisch erstellt und gelöscht, wenn Nutzer in den Cast-Dialogen auf Touch-Gesten reagieren.

Medien werden geladen

Im Cast SDK bietet die GCKRemoteMediaClient eine Reihe von praktischen APIs für die 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. Es kann als Attribut remoteMediaClient der Instanz GCKCastSession aufgerufen werden.

Fügen Sie den folgenden Code zu MediaViewController.swift 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 Remote-Wiedergabe mithilfe der Logik der Cast-Sitzung zu unterstützen:

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 auf dem Empfänger sehen.

7. Mini-Controller

Für die Checkliste für das Cast-Design muss in allen Cast-Apps ein Mini-Controller verfügbar sein, 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 läuft, mit Fokus auf dem Minicontroller

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

Für die Beispiel-App verwenden wir die GCKUICastContainerViewController, die einen weiteren Ansichts-Controller umschließt und unten GCKUIMiniMediaControlsViewController hinzufügt.

Ändern Sie die Datei AppDelegate.swift und fügen Sie den folgenden Code für die Bedingung if useCastContainerViewController in der folgenden Methode 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 den Setter/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
      }
    }
  }

App ausführen und Video streamen Wenn die Wiedergabe auf dem Receiver beginnt, sollte unten in jeder Szene der Minicontroller angezeigt werden. Sie können die Wiedergabe über den Mini-Controller steuern. Wenn Sie zwischen der Browseraktivität und der lokalen Playeraktivität wechseln, sollte der Status des Minicontrollers mit dem Medienwiedergabestatus des Empfängers übereinstimmen.

8. Einführungs-Overlay

In der Checkliste für das Google Cast-Design muss eine Absender-App das Cast-Symbol bei bestehenden Nutzern einführen, um sie darüber zu informieren, dass die Absender-App jetzt das Streamen unterstützt. Außerdem hilft sie Nutzern, die neu bei Google Cast sind.

Illustration eines iPhones, auf dem die CastVideos-App mit dem Cast-Symbol als Overlay läuft, das Cast-Symbol markiert und die Nachricht angezeigt wird, um Medien auf Ihren Fernseher und Ihre Lautsprecher zu streamen'

Die Klasse GCKCastContext hat eine Methode, presentCastInstructionsViewControllerOnce, mit der das Cast-Symbol hervorgehoben werden kann, wenn es 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 Einführungs-Overlay angezeigt werden.

9. Maximierter Controller

In der Checkliste für das Google Cast-Design muss eine Absender-App einen maximierten 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, während ein Video wiedergegeben wird. Der maximierte Controller wird unten angezeigt.

Der erweiterte Controller ist ein Vollbild, über den Sie die vollständige Wiedergabe der Remote-Medien steuern können. In dieser Ansicht sollte eine Cast-App alle verwaltbaren Aspekte einer Streamingsitzung verwalten können – mit Ausnahme der Lautstärkeregelung für Empfänger und des Sitzungslebenszyklus (Verbindung herstellen/stoppen). Außerdem finden Sie dort alle Statusinformationen zur Mediensitzung (z. B. Artwork, Titel oder Untertitel).

Die Funktionalität dieser Ansicht wird von der Klasse GCKUIExpandedMediaControlsViewController implementiert.

Als Erstes 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 beginnt, ein Video zu streamen:

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

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

App ausführen und Video streamen Sie sollten den erweiterten Controller sehen. Wenn Sie zur Liste der Videos zurückkehren, wird der maximierte Controller wieder geladen.

10. Cast Connect-Unterstützung hinzufügen

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

Abhängigkeiten

In der Podfile muss der google-cast-sdk auf 4.4.8 oder höher verweisen (siehe unten). Wenn Sie eine Änderung an der Datei vorgenommen haben, führen Sie pod update über die Konsole 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 wird an GCKCastOptions übergeben, die mithilfe von GCKCastContext.setSharedInstanceWith in der freigegebenen Instanz festgelegt werden.

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 Absenderseite können Sie GCKCredentialsData angeben, um anzugeben, wer an der Sitzung teilnimmt. credentials ist ein String, der vom Nutzer definiert werden kann, solange Ihre ATV-App sie verstehen kann. GCKCredentialsData wird nur während der Einführung oder beim Beitritt an deine Android TV App übergeben. Wenn du sie später noch einmal verbindest, wird sie nicht an deine Android TV App weitergegeben.

Zum Festlegen von Anmeldedaten für die Einführung muss GCKCredentialsData jederzeit definiert werden, nachdem GCKLaunchOptions festgelegt wurde. Um das zu zeigen, fügen wir Logik für die Schaltfläche Creds hinzu, um Anmeldedaten festzulegen, die beim Erstellen der Sitzung übergeben werden. Fügen Sie den folgenden Code in Ihre 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
...

Abhängig von der Empfänger-App, an die Ihr Absender streamt, wendet das SDK die obigen Anmeldedaten automatisch auf die laufende Sitzung an.

Cast Connect wird getestet

Android TV-APK auf Chromecast mit Google TV installieren

  1. Suchen Sie die IP-Adresse Ihres Android TV-Geräts. Sie finden sie in der Regel unter Einstellungen > Netzwerk > Internetname des Geräts. Auf der rechten Seite werden die Details und die IP-Adresse Ihres Geräts im Netzwerk angezeigt.
  2. Verwenden Sie die IP-Adresse für Ihr Gerät, um über ADB eine Verbindung über das Terminal herzustellen:
$ adb connect <device_ip_address>:5555
  1. Gehen Sie im Terminalfenster zum Ordner auf oberster 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. Sie sollten jetzt auf Ihrem Android TV-Gerät im Menü Meine Apps den Namen Videos streamen sehen.
  2. Anschließend können Sie die App auf einem Emulator oder Mobilgerät erstellen. Wenn Sie eine Übertragungssitzung mit Ihrem Android TV-Gerät einrichten, sollte jetzt die App "Android Receiver" auf Ihrem Android TV-Gerät gestartet werden. Wenn Sie ein Video von Ihrem iOS-Gerät abspielen, sollte es im Android-Receiver gestartet und die Wiedergabe über die Fernbedienung des Android TV-Geräts gesteuert werden.

11. Cast-Widgets anpassen

Initialisierung

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

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

Sobald Sie eine oder mehrere Anpassungen wie im Rest dieses Codelabs erwähnt haben, können Sie die Stile mit dem folgenden Code aufrufen

styler.apply()

Cast-Ansichten anpassen

Sie können alle Ansichten, die vom Cast Application Framework verwaltet werden, anpassen, indem Sie für alle Ansichten Standardrichtlinien für Stile festlegen. Beispielsweise ändern wir die Färbungsfarbe des Symbols.

styler.castViews.iconTintColor = .lightGray

Sie können Standardwerte bei Bedarf für einzelne Bildschirme überschreiben. So kann beispielsweise „lightGrayColor“ für die Farbe des Symbols nur für den maximierten Mediacontroller überschrieben werden.

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

Farben ändern

Sie können die Hintergrundfarbe für alle Datenansichten individuell anpassen. Mit dem folgenden Code wird die Hintergrundfarbe für alle von Cast Application Framework bereitgestellten Ansichten auf Blau gesetzt.

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

Schriftarten ändern

Sie können Schriftarten für verschiedene Labels anpassen, die in den Cast-Ansichten zu sehen sind. Zur Verdeutlichung werden alle Schriftarten auf Courier Oblique eingestellt.

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 für Schaltflächen ändern

Fügen Sie dem Projekt eigene benutzerdefinierte Bilder hinzu und weisen Sie Ihren Bildern die Bilder 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

Sie können Cast-Widgets auch mithilfe des UIDarstellungsprotokolls entwerfen. Mit dem folgenden Code wird das GCKUICastButton-Symbol in allen angezeigten Ansichten thematisch gestaltet:

GCKUICastButton.appearance().tintColor = UIColor.gray

12. Glückwunsch

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

Weitere Informationen finden Sie im Entwicklerleitfaden iOS Sender.