Cast-fähige iOS-App

1. Übersicht

Google Cast-Logo

In diesem Codelab erfahren Sie, wie Sie eine vorhandene iOS-Video-App ändern, um Inhalte auf ein für Google Cast optimiertes Gerät zu streamen.

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 Google Cast-fähige Geräte wie einen Fernseher oder ein Soundsystem steuern. Mit dem Cast SDK können Sie die erforderlichen UI-Komponenten gemäß der Google Cast-Design-Checkliste hinzufügen.

Die Checkliste für das Design von Google Cast soll es Nutzern der Cast-Nutzung auf allen unterstützten Plattformen erleichtern.

Ziele

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

Lerninhalte

  • Das Google Cast SDK einer Beispielvideo-App hinzufügen
  • Hier erfährst du, wie du das Cast-Symbol zur Auswahl eines Google Cast-Geräts hinzufügst.
  • Verbindung zu einem Cast-Gerät herstellen und einen Medienempfänger starten.
  • So streamen Sie ein Video
  • 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
  • Cast Connect einbinden

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 dem Internet verbunden ist.
  • Fernseher oder Monitor mit HDMI-Eingang
  • Zum Testen der Cast Connect-Integration ist Chromecast mit Google TV erforderlich, für den Rest des Codelabs aber optional. Wenn Sie keine haben, überspringen Sie den Schritt Cast Connect-Unterstützung hinzufügen gegen Ende dieser Anleitung.

Plattform

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

Wie werden Sie diese Anleitung verwenden?

Nur Lesen Lesen und lesen

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

Neuling Leicht fortgeschrittener Profi

Wie würdest du deine Erfahrung beim Fernsehen bewerten?

Neuling Fortgeschrittener Anfänger 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 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:

Häufig gestellte Fragen

CocoaPods einrichten

Wenn Sie CocoaPods einrichten möchten, rufen Sie die Konsole auf und installieren Sie das Programm mit dem standardmäßig unter macOS verfügbaren Ruby-Code:

sudo gem install cocoapods

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

Projekt einrichten

  1. Rufen Sie Ihr Terminal auf und gehen Sie zum 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 Open another project... (Weiteres Projekt öffnen...) aus.
  2. Wählen Sie im Beispielcodeordner 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 unbedingt auf „Zulassen“, wenn die Benachrichtigung zum Annehmen eingehender Netzwerkverbindungen angezeigt wird. Das Cast-Symbol wird nicht angezeigt, wenn diese Option nicht akzeptiert wird.

Bestätigungsdialogfeld, in dem Sie aufgefordert werden, eingehende Netzwerkverbindungen zu akzeptieren

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“ kannst du die Wiedergabe steuern.

Zurück zur Videoliste

Unten auf dem Bildschirm ist jetzt ein Mini-Controller zu sehen.

Illustration eines iPhones, auf dem die CastVideos App mit dem Minicontroller unten angezeigt wird

Klicke auf die Pause-Taste im Mini-Controller, um das Video auf dem Receiver zu pausieren. Klicke auf die Wiedergabeschaltfläche im Minicontroller, 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

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

Wir müssen die heruntergeladene Start-App für Google Cast unterstützen. Hier sind einige Google Cast-Begriffe, die wir in diesem Codelab verwenden werden:

  • Eine Absender-App wird auf einem Mobilgerät oder Laptop ausgeführt.
  • 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. Rufen Sie Ihr Terminal auf und gehen Sie zum 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 Open another project... (Weiteres Projekt öffnen...) aus.
  2. Wählen Sie im Beispielcodeordner die Datei CastVideos-ios.xcworkspace aus dem Verzeichnis Ordnersymbolapp-start aus.

App-Design

Die App ruft eine Liste von 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 der Videos einer MediaListModel-Instanz an. Die Liste der Videos und die zugehörigen Metadaten werden als JSON-Datei auf einem Remoteserver gehostet. MediaListModel ruft dieses JSON ab und verarbeitet es, um eine Liste von MediaItem-Objekten zu erstellen.

Ein MediaItem-Objekt modelliert ein Video und die zugehörigen Metadaten, z. B. 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 laden zu können.

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 es dem Nutzer, das Video lokal auf dem Mobilgerät abzuspielen.

Der Ansicht-Controller hostet LocalPlayerView, einige Mediensteuerelemente und einen Textbereich für die Beschreibung des ausgewählten Videos. Der Player deckt den oberen Teil des Bildschirms ab. So bleibt Platz für eine detaillierte Beschreibung des Videos unterhalb des Nutzers, wo die Wiedergabe/Pause oder die lokale Wiedergabe des Videos gestartet werden kann.

Häufig gestellte Fragen

5. Cast-Symbol hinzufügen

Illustration des oberen Drittels eines iPhones, auf dem die CastVideos App läuft, in der die Cast-Schaltfläche oben rechts zu sehen ist

Eine Cast-fähige Anwendung zeigt das Cast-Symbol in jedem Ansichtscontroller an. Durch Klicken auf das Cast-Symbol wird eine Liste der Übertragungsgeräte angezeigt, die der Nutzer auswählen kann. Wenn der Nutzer Inhalte lokal auf dem Absendergerät abspielt, wird die Wiedergabe auf diesem Gerät gestartet oder fortgesetzt. Der Nutzer kann während des Streamens jederzeit auf das Cast-Symbol klicken und die Übertragung der App auf das Übertragungsgerät beenden. Der Nutzer muss in der Lage sein, sich auf einem beliebigen Bildschirm Ihrer App mit dem Übertragungsgerät zu verbinden oder die Verbindung damit zu trennen, wie in der Google Cast-Design-Checkliste beschrieben.

Konfiguration

Für das Startprojekt müssen dieselben Abhängigkeiten und Xcode-Einrichtung wie für die fertige 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 ein globales Singleton-Objekt, das GCKCastContext, das alle Aktivitäten des Frameworks koordiniert. Dieses Objekt muss früh im Lebenszyklus der Anwendung initialisiert werden, in der Regel in der Methode application(_:didFinishLaunchingWithOptions:) des App-Delegaten, damit die automatische Sitzungswiederaufnahme beim Neustart der Absenderanwendung ordnungsgemäß ausgelöst werden kann und das Scannen für Geräte beginnen 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. Die wichtigste davon ist die Empfänger-App-ID, mit der Ergebnisse der Cast-Geräteerkennung gefiltert und die Empfängeranwendung gestartet wird, wenn eine Cast-Sitzung gestartet wird.

Die Methode application(_:didFinishLaunchingWithOptions:) ist auch ein guter Ort, um einen Logging-Bevollmächtigten einzurichten, der die Logging-Nachrichten aus dem Cast-Framework empfängt. Diese können für die Fehlerbehebung und Fehlerbehebung nützlich sein.

Wenn Sie Ihre eigene Cast-fähige App entwickeln, müssen Sie sich als Cast-Entwickler registrieren und dann eine App-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 Nutzer-Standardeinstellungen 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 abgeleitete Klasse von UIButton. Sie kann der Titelleiste der App hinzugefügt werden, indem Sie sie in ein UIBarButtonItem einschließen. Wir müssen das Cast-Symbol sowohl MediaTableViewController als auch 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 sollte ein Cast-Symbol angezeigt werden. 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 Browseraktivität und der lokalen Spieleraktivität wechseln und der Status des Cast-Symbols wird synchronisiert.

Wir haben keine Medienunterstützung aktiviert, sodass du noch keine Videos über das Cast-Gerät abspielen kannst. Klicke auf das Cast-Symbol, um das Streamen zu beenden.

6. Videoinhalte streamen

Abbildung eines iPhones, auf dem die CastVideos App mit Details zu einem bestimmten Video („Tears of Steel“) läuft. Unten befindet sich der Miniplayer.

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

Medien streamen

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

  1. Erstellen Sie ein GCKMediaInformation-Objekt aus dem Cast SDK, mit dem ein Medienelement modelliert wird.
  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. Medienstatus verfolgen
  5. Sende Wiedergabebefehle basierend auf Nutzerinteraktionen 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 die Kapselung unserer App für ein Mediakostenelement. Wir können 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 der lokalen und der 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 Beispielplayerlogik funktioniert. Es ist wichtig zu verstehen, dass der Mediaplayer Ihrer App so angepasst werden muss, dass die beiden Wiedergabeorte auf ähnliche Weise erkannt werden.

Momentan ist der lokale Player immer im lokalen Wiedergabestatus, da er noch nichts über den Streamingstatus weiß. Wir müssen die Benutzeroberfläche basierend auf Statusübergängen aktualisieren, die im Cast-Framework stattfinden. Wenn wir beispielsweise mit der Übertragung 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 überwachen, die vom Cast-Framework generiert werden.

Cast-Sitzungsverwaltung

Beim Cast-Framework umfasst eine Cast-Sitzung die Schritte zum Herstellen einer Verbindung zu einem Gerät, zum Starten bzw. zum Beitritt, zum Herstellen einer Verbindung mit einer Empfängeranwendung und zum Initialisieren eines Mediensteuerkanals. Der Mediensteuerungskanal ist die Methode, mit der 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 das Cast-Symbol auswählt, und wird automatisch beendet, wenn der Nutzer die Verbindung trennt. Die Verbindung zu einer Empfängersitzung aufgrund von Netzwerkproblemen wird auch automatisch vom Cast-Framework übernommen.

Streamingsitzungen werden von der GCKSessionManager verwaltet, auf die über GCKCastContext.sharedInstance().sessionManager zugegriffen werden kann. Mit den GCKSessionManagerListener-Callbacks können Sitzungsereignisse wie Erstellen, Sperren, Wiederaufnahme und Beenden ü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 der MediaViewController geben wir an, dass wir Sie informieren möchten, wenn die Verbindung zum Cast-Gerät getrennt wird, damit wir zum lokalen Player wechseln können. Beachten Sie, dass die Verbindung nicht nur durch die Instanz Ihrer Anwendung unterbrochen werden kann, sondern auch durch eine andere Instanz Ihrer Anwendung (oder eine andere 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 gelöscht, wenn Nutzerbewegungen aus den Übertragungsdialogen ausgeführt werden.

Medien werden geladen

Im Cast SDK bietet der GCKRemoteMediaClient eine Reihe praktischer APIs zur Verwaltung der Remote-Medienwiedergabe auf dem Empfänger. Bei einem GCKCastSession, der die Medienwiedergabe unterstützt, wird vom SDK automatisch eine Instanz von GCKRemoteMediaClient erstellt. Sie kann über die Eigenschaft remoteMediaClient der Instanz GCKCastSession aufgerufen werden.

Fügen Sie 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 nun 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. Das Video sollte auf dem Receiver wiedergegeben werden.

7. Mini-Controller

Gemäß der Checkliste für das Cast-Design müssen alle Cast-Apps einen Mini-Controller bereitstellen, der angezeigt wird, wenn der Nutzer die aktuelle Seite 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. Der Fokus liegt auf dem Minicontroller.

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

In der Beispielanwendung verwenden wir den GCKUICastContainerViewController, der einen weiteren Ansicht-Controller umschließt und unten ein GCKUIMiniMediaControlsViewController hinzufügt.

Ändern Sie die Datei AppDelegate.swift und fügen Sie 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 den Setter/Getter hinzu, um die Sichtbarkeit des Minicontrollers zu steuern. Diese werden wir in einem späteren Abschnitt verwenden.

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 Player-Aktivität wechseln, sollte der Mini-Controller-Status mit dem Medienwiedergabestatus des Empfängers synchron bleiben.

8. Einführungs-Overlay

In der Checkliste für das Design von Google Cast muss eine Absender-App das Cast-Symbol bestehenden Nutzern einführen, um ihnen mitzuteilen, dass die Absender-App jetzt Streaming unterstützt und auch Nutzern hilft, die neu auf Google Cast sind.

Abbildung eines iPhones, auf dem die CastVideos-App mit dem Cast-Symbol-Overlay ausgeführt wird, das Cast-Symbol markiert und die Meldung „Tippen, um Medien auf Ihren Fernseher und Ihre Lautsprecher zu streamen“ angezeigt wird

Die Klasse GCKCastContext hat die Methode presentCastInstructionsViewControllerOnce, mit der das Cast-Symbol hervorgehoben werden kann, wenn es dem Nutzer erstmals 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, sollten Sie das Einführungs-Overlay sehen.

9. Maximierter Controller

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.

Abbildung eines iPhones, auf dem die CastVideos App läuft, während ein Video wiedergegeben wird. Der eingeblendete Controller ist unten zu sehen.

Der maximierte Controller ist eine Vollbildansicht, über die Sie die Remote-Medienwiedergabe vollständig steuern können. In dieser Ansicht sollte eine Streaming-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 hier alle Statusinformationen zur Mediensitzung (z. B. Artwork, Titel und Untertitel).

Die Funktion dieser Ansicht wird von der Klasse GCKUIExpandedMediaControlsViewController implementiert.

Als Erstes müssen Sie den standardmäßigen erweiterten Controller im Cast-Kontext 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, damit der maximierte Controller geladen wird, wenn der Nutzer mit dem Streamen eines Videos beginnt:

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

Der maximierte Controller wird automatisch gestartet, wenn der Nutzer auf den Minicontroller tippt.

App ausführen und Video streamen Der maximierte Controller sollte angezeigt werden. Kehren Sie zur Liste der Videos zurück. Wenn Sie auf den Minicontroller klicken, wird der maximierte Controller wieder geladen.

10. Cast Connect-Unterstützung hinzufügen

Mit der Cast Connect-Bibliothek können bestehende Absender-Apps über Android Cast mit Android TV-Apps kommunizieren. Cast Connect baut auf der Cast-Infrastruktur auf, wobei deine Android TV App als Empfänger fungiert.

Abhängigkeiten

Achten Sie bei Podfile darauf, dass google-cast-sdkauf 4.4.8 oder einen höheren Wert ausgerichtet ist (siehe unten). 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 App für Android TV, auch Android Receiver genannt, müssen Sie das androidReceiverCompatible-Objekt im Objekt GCKLaunchOptions auf „true“ setzen. Dieses GCKLaunchOptions-Objekt gibt vor, wie der Empfänger gestartet wird, und wird an GCKCastOptions übergeben, die in der freigegebenen Instanz mit GCKCastContext.setSharedInstanceWith 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 die Einführung festlegen

Auf der Seite des Absenders können Sie angeben, wer GCKCredentialsData der Sitzung beitreten darf. credentials ist ein String, der vom Nutzer definiert werden kann, solange Ihre ATV-App ihn verstehen kann. Die GCKCredentialsData wird nur während des Starts oder beim Beitritt an deine Android TV App übertragen. Wenn Sie sie noch einmal einrichten, während Sie verbunden sind, wird sie nicht an Ihre Android TV App übertragen.

Zum Festlegen von Anmeldeinformationen muss GCKCredentialsData nach dem Festlegen von GCKLaunchOptions jederzeit definiert werden. Um dies zu veranschaulichen, fügen wir eine Logik für die Schaltfläche Krednen hinzu, um Anmeldedaten festzulegen, die beim Erstellen der Sitzung übergeben werden sollen. Fügen Sie den folgenden Code zu Ihrem MediaTableViewController.swift 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 Anfrage zum Laden festlegen

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

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

Je nach Empfänger-App, an die der Absender streamt, 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. Suchen Sie die IP-Adresse Ihres Android TV-Geräts. Sie finden diese Option in der Regel unter Einstellungen > Netzwerk & Internet > (Netzwerkname, mit dem Ihr Gerät verbunden ist). Rechts 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 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:
$ 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 zu sehen sein.
  2. Anschließend können Sie die App auf einem Emulator oder Mobilgerät erstellen und ausführen. Nach dem Einrichten einer Streamingsitzung mit Ihrem Android TV-Gerät sollte die Android Receiver App auf Ihrem Android TV-Gerät gestartet werden. Wenn Sie ein Video von Ihrem iOS-Absender auf dem Gerät wiedergeben, sollte es im Android Receiver gestartet und die Wiedergabe mit der Fernbedienung Ihres 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()
  ...
}

Wenn Sie eine oder mehrere Anpassungen wie im Rest dieses Codelabs beschrieben abgeschlossen haben, können Sie die Stile mithilfe des folgenden Codes aufrufen.

styler.apply()

Cast-Ansichten anpassen

Sie können alle Ansichten anpassen, die vom Cast Application Framework verwaltet werden, indem Sie für alle Ansichten Standardstilrichtlinien festlegen. Nehmen wir als Beispiel die Farbe der Symbolfärbung.

styler.castViews.iconTintColor = .lightGray

Sie können die Standardwerte bei Bedarf für einzelne Bildschirme überschreiben. So wird beispielsweise „lightGrayColor“ für die Farbe der Symbolfarbe 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 für jede Ansicht individuell 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 zu Illustrationszwecken auf „Courier-Oblique“ festgelegt 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)

Bilder der Standardschaltfläche ändern

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

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

Cast-Symboldesign ändern

Sie können Cast-Widgets auch mithilfe des UIDarstellungsprotokolls gestalten. Der folgende Code spiegelt das GCKUICastButton-Symbol in allen angezeigten Ansichten wider:

GCKUICastButton.appearance().tintColor = UIColor.gray

12. Glückwunsch

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

Weitere Informationen finden Sie im Entwicklerleitfaden iOS Sender.