Für Google Cast optimierte iOS-App

1. Übersicht

Google Cast-Logo

In diesem Codelab erfahren Sie, wie Sie eine vorhandene iOS-Video-App so ändern, dass Inhalte auf einem für Google Cast optimierten Gerät wiedergegeben 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, um für Google Cast optimierte Geräte wie einen Fernseher oder ein Soundsystem zu steuern. Mit dem Cast SDK können Sie die erforderlichen UI-Komponenten basierend auf der Google Cast-Design-Checkliste hinzufügen.

Die Google Cast-Design-Checkliste soll dafür sorgen, dass die Cast-User Experience auf allen unterstützten Plattformen einfach und vorhersehbar ist.

Ziele

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

Lerninhalte

  • So fügen Sie das Google Cast SDK einer Beispiel-Video-App hinzu.
  • So fügen Sie die Schaltfläche „Streamen“ zum Auswählen eines Google Cast-Geräts hinzu.
  • Verbindung zu einem Übertragungsgerät herstellen und einen Media Receiver starten
  • So streamst du ein Video.
  • So fügen Sie Ihrer App einen Cast-Mini-Controller hinzu.
  • So fügen Sie einen erweiterten Controller hinzu:
  • So stellen Sie ein Intro-Overlay bereit.
  • Cast-Widgets anpassen
  • Cast Connect einbinden

Voraussetzungen

  • Die aktuelle Version von Xcode.
  • Ein Mobilgerät mit iOS 9 oder höher (oder der Xcode-Simulator).
  • Ein USB-Datenkabel zum Verbinden Ihres Mobilgeräts mit Ihrem Entwicklercomputer (falls Sie ein Gerät verwenden).
  • Ein Google Cast-Gerät wie ein Chromecast oder Android TV, das für den Internetzugriff konfiguriert ist.
  • Einen Fernseher oder Monitor mit HDMI-Eingang
  • Für das Testen der Cast Connect-Integration ist ein Chromecast mit Google TV erforderlich. Für den Rest des Codelabs ist er optional. Wenn Sie keine haben, können Sie den Schritt Cast Connect-Unterstützung hinzufügen am Ende dieser Anleitung überspringen.

Erfahrung

  • Sie benötigen Vorkenntnisse in der iOS-Entwicklung.
  • Außerdem sollten Sie sich mit dem Fernsehen auskennen :)

Wie werden Sie diese Anleitung verwenden?

Nur lesen Lesen und Übungen durchführen

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

Anfänger Mittelstufe Fortgeschrittene

Wie würdest du deine Erfahrung beim Fernsehen bewerten?

Anfänger Mittelstufe Fortgeschritten

2. Beispielcode abrufen

Sie können 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 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 abspielen oder auf ein Google Cast-Gerät streamen.

Nachdem Sie den Code heruntergeladen haben, folgen Sie der Anleitung unten, um die fertige Beispielanwendung in Xcode zu öffnen und auszuführen:

Häufig gestellte Fragen

CocoaPods einrichten

So richten Sie CocoaPods ein: Öffnen Sie die Konsole und installieren Sie CocoaPods mit der standardmäßigen Ruby-Version, die unter macOS verfügbar ist:

sudo gem install cocoapods

Wenn Probleme auftreten, lesen Sie die offizielle Dokumentation zum Herunterladen und Installieren des Abhängigkeitsmanagers.

Projekt einrichten

  1. Wechseln Sie im Terminal 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 Weiteres Projekt öffnen… aus.
  2. Wählen Sie die Datei CastVideos-ios.xcworkspace aus dem Verzeichnis Ordnersymbolapp-done im Beispielcodeordner aus.

Anwendung ausführen

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

Symbolleiste des Xcode-App-Simulators

Nach einigen Sekunden sollte die Video-App angezeigt werden.

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

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

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

Wählen Sie ein Video aus und klicken Sie auf die Wiedergabeschaltfläche.

Die Wiedergabe des Videos beginnt auf deinem Google Cast-Gerät.

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

Kehren Sie zur Liste der Videos zurück.

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

Abbildung eines iPhones mit der CastVideos-App und der Mini-Steuerung unten auf dem Display

Klicke im Mini-Controller auf die Pausenschaltfläche, um das Video auf dem Empfängergerät zu pausieren. Klicke auf die Wiedergabeschaltfläche in der Mini-Steuerung, 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 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 in diesem Codelab verwendet werden:

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

Projekt einrichten

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

  1. Wechseln Sie im Terminal 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 Weiteres Projekt öffnen… aus.
  2. Wählen Sie die Datei CastVideos-ios.xcworkspace aus dem Verzeichnis Ordnersymbolapp-start im Beispielcodeordner aus.

App-Design

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

Die App besteht aus zwei Haupt-View-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 Remote-Server 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 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 benachrichtigt zu werden, wenn die Media-Metadaten heruntergeladen wurden, damit die Tabellenansicht geladen werden kann.

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

MediaViewController

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

Der View-Controller enthält eine LocalPlayerView, einige Media-Steuerelemente und einen Textbereich, in dem die Beschreibung des ausgewählten Videos angezeigt wird. Der Player deckt den oberen Teil des Bildschirms ab und lässt Platz für die detaillierte Beschreibung des Videos darunter. Der Nutzer kann die lokale Videowiedergabe starten/pausieren oder vor- und zurückspulen.

Häufig gestellte Fragen

5. Cast-Symbol hinzufügen

Abbildung des oberen Drittels eines iPhones, auf dem die CastVideos App ausgeführt wird. Das Cast-Symbol befindet sich oben rechts.

In einer für Google Cast optimierten App wird das Cast-Symbol in jedem ihrer Ansichtscontroller angezeigt. Wenn ein Nutzer auf das Cast-Symbol klickt, wird eine Liste mit Cast-Geräten angezeigt, die er auswählen kann. Wenn der Nutzer Inhalte lokal auf dem Absendergerät wiedergegeben hat, wird die Wiedergabe auf dem ausgewählten Übertragungsgerät gestartet oder fortgesetzt. Der Nutzer kann während einer Cast-Sitzung jederzeit auf das Cast-Symbol klicken und die Übertragung Ihrer Anwendung auf das Cast-Gerät beenden. Der Nutzer muss sich auf jedem Bildschirm Ihrer Anwendung mit dem Cast-Gerät verbinden oder die Verbindung trennen können, wie in der Google Cast Design Checklist beschrieben.

Konfiguration

Für das Startprojekt sind dieselben Abhängigkeiten und dieselbe Xcode-Einrichtung erforderlich wie für die fertige Beispiel-App. Kehren Sie zu diesem Abschnitt zurück und folgen Sie denselben Schritten, 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, in der Regel in der Methode application(_:didFinishLaunchingWithOptions:) des App-Delegaten, damit die automatische Wiederaufnahme der Sitzung beim Neustart der Senderanwendung richtig ausgelöst werden kann und die Suche nach Geräten gestartet werden kann.

Beim Initialisieren 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 Receiver-Anwendungs-ID, die zum Filtern von Ergebnissen der Cast-Geräteerkennung und zum Starten der Receiver-Anwendung beim Starten einer Cast-Sitzung verwendet wird.

Die Methode application(_:didFinishLaunchingWithOptions:) ist auch ein guter Ort, um einen Logging-Delegate einzurichten, der die Logging-Nachrichten vom Cast-Framework empfängt. Sie können bei der Fehlerbehebung und beim Debugging 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 erhalten. In diesem Codelab verwenden wir eine Beispiel-App-ID.

Fügen Sie den folgenden Code zu AppDelegate.swift hinzu, um GCKCastContext mit der Anwendungs-ID aus den Standardeinstellungen des Nutzers 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 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 Cast-Schaltflächenkomponente namens GCKUICastButton als UIButton-Unterklasse. Sie kann der Titelleiste der Anwendung hinzugefügt werden, indem sie in ein UIBarButtonItem-Tag eingeschlossen wird. Wir müssen die Schaltfläche „Streamen“ sowohl der MediaTableViewController als auch der MediaViewController hinzufügen.

Fügen Sie den folgenden Code zu MediaTableViewController.swift und MediaViewController.swift 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 Ihre MediaViewController.swift ein:

import GoogleCast

@objc(MediaViewController)
class MediaViewController: UIViewController, GCKSessionManagerListener, GCKRemoteMediaClientListener,
  LocalPlayerViewDelegate, GCKRequestDelegate {
  private var castButton: GCKUICastButton!
  ...
  override func viewDidLoad() {
    super.viewDidLoad()
    print("in MediaViewController viewDidLoad")
    ...
    castButton = GCKUICastButton(frame: CGRect(x: CGFloat(0), y: CGFloat(0),
                                               width: CGFloat(24), height: CGFloat(24)))
    // Overwrite the UIAppearance theme in the AppDelegate.
    castButton.tintColor = UIColor.white
    navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)

    ...
  }
  ...
}

Führen Sie die App jetzt aus. In der Navigationsleiste der App sollte ein Cast-Symbol angezeigt werden. Wenn Sie darauf klicken, werden die Cast-Geräte in Ihrem lokalen Netzwerk aufgeführt. Die Geräteerkennung wird automatisch von GCKCastContext verwaltet. Wählen Sie Ihr Übertragungsgerät aus. Die Beispiel-Receiver-App wird auf dem Übertragungsgerät geladen. Sie können zwischen der Browseraktivität und der lokalen Playeraktivität wechseln. Der Status der Schaltfläche „Streamen“ wird synchronisiert.

Wir haben noch keine Unterstützung für die Medienwiedergabe eingerichtet, daher können Sie noch keine Videos auf dem Cast-Gerät abspielen. Klicke auf das Cast-Symbol, um das Streamen zu beenden.

6. Videoinhalte streamen

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

Wir werden die Beispiel-App so erweitern, dass Videos auch auf einem Cast-Gerät wiedergegeben werden können. Dazu müssen wir auf die verschiedenen Ereignisse achten, die vom Cast-Framework generiert werden.

Medien streamen

Wenn Sie Medien auf einem Cast-Gerät abspielen möchten, muss Folgendes passieren:

  1. Erstelle ein GCKMediaInformation-Objekt aus dem Cast SDK, das ein Media-Element modelliert.
  2. Der Nutzer stellt eine Verbindung zum Cast-Gerät her, um deine Empfängeranwendung zu starten.
  3. Lade das GCKMediaInformation-Objekt auf deinen Empfänger und spiele die Inhalte ab.
  4. Medienstatus verfolgen
  5. Sende Wiedergabebefehle basierend auf Nutzerinteraktionen an den Empfänger.

In Schritt 1 wird ein Objekt einem anderen zugeordnet. GCKMediaInformation ist etwas, das das Cast SDK versteht, und MediaItem ist die Kapselung unserer App für ein Media-Element. Wir können MediaItem ganz einfach GCKMediaInformation zuordnen. Schritt 2 haben wir bereits im vorherigen Abschnitt ausgeführt. Schritt 3 ist mit dem Cast SDK ganz einfach.

In der Beispiel-App MediaViewController wird bereits zwischen lokaler und Remote-Wiedergabe unterschieden. Dazu wird dieses Enum 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 Beispiel-Player-Logik funktioniert. Es ist wichtig zu wissen, dass der Media Player Ihrer App so angepasst werden muss, dass er die beiden Wiedergabeorte auf ähnliche Weise erkennt.

Der lokale Player befindet sich derzeit immer im lokalen Wiedergabestatus, da er noch nichts über die Streaming-Status 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 Casting beenden, während wir uns in diesem Ansichtscontroller befinden, müssen wir zur lokalen Wiedergabe wechseln. Dazu müssen wir auf die verschiedenen Ereignisse achten, die vom Cast-Framework generiert werden.

Verwaltung von Cast-Sitzungen

Im Cast-Framework werden die Schritte zum Herstellen einer Verbindung zu einem Gerät, zum Starten (oder Beitreten) einer Empfängeranwendung, zum Herstellen einer Verbindung zu dieser Anwendung und zum Initialisieren eines Mediensteuerung-Channels (falls erforderlich) in einer Cast-Sitzung zusammengefasst. Über den Media-Steuerkanal werden Nachrichten zwischen dem Cast-Framework und dem Media-Player des Receivers gesendet und empfangen.

Die Cast-Sitzung wird automatisch gestartet, wenn der Nutzer über die Cast-Schaltfläche ein Gerät auswählt, und automatisch beendet, wenn der Nutzer die Verbindung trennt. Auch das erneute Herstellen einer Verbindung zu einer Empfängersitzung aufgrund von Netzwerkproblemen wird automatisch vom Cast-Framework übernommen.

Cast-Sitzungen werden von GCKSessionManager verwaltet, auf das über GCKCastContext.sharedInstance().sessionManager zugegriffen werden kann. Mit den GCKSessionManagerListener-Callbacks können Sie Sitzungsereignisse wie Erstellung, Unterbrechung, Fortsetzung und Beendigung überwachen.

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 informiert werden, wenn wir eine Verbindung zum Cast-Gerät herstellen oder die Verbindung trennen, damit wir zum lokalen Player wechseln können. Die Verbindung kann nicht nur durch die Instanz Ihrer Anwendung, 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, unterbrochen werden.

Die derzeit aktive Sitzung ist als GCKCastContext.sharedInstance().sessionManager.currentCastSession verfügbar. Sitzungen werden automatisch als Reaktion auf Nutzeraktionen in den Cast-Dialogfeldern erstellt und beendet.

Medien werden geladen

Im Cast SDK bietet GCKRemoteMediaClient eine Reihe praktischer APIs zum Verwalten der Remote-Medienwiedergabe auf dem Empfängergerät. Für ein GCKCastSession, das die Medienwiedergabe unterstützt, wird vom SDK automatisch eine Instanz von GCKRemoteMediaClient erstellt. Sie kann als remoteMediaClient-Attribut der GCKCastSession-Instanz aufgerufen werden.

Fügen Sie den folgenden Code in MediaViewController.swift ein, um das aktuell ausgewählte Video auf dem Empfängergerät 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 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 die App nun auf Ihrem Mobilgerät aus. Verbinde dich mit deinem Cast-Gerät und starte die Wiedergabe eines Videos. Das Video sollte jetzt auf dem Empfängergerät wiedergegeben werden.

7. Mini-Controller

Gemäß der Checkliste für das Cast-Design muss in allen Cast-Apps eine Mini-Steuerung angezeigt werden, wenn der Nutzer die aktuelle Inhaltsseite verlässt. Der Mini-Controller bietet sofortigen Zugriff und eine sichtbare Erinnerung an die aktuelle Cast-Sitzung.

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

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

In der Beispiel-App verwenden wir GCKUICastContainerViewController, das einen anderen Ansichtscontroller umschließt und unten eine 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üge diese Property und Setter/Getter hinzu, um die Sichtbarkeit des Mini-Controllers zu steuern. Wir werden sie 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
      }
    }
  }

Starte die App und übertrage ein Video. Wenn die Wiedergabe auf dem Empfängergerät beginnt, sollte unten in jeder Szene die Mini-Steuerung angezeigt werden. Mit dem Mini-Controller kannst du die Wiedergabe auf dem Remote-Gerät steuern. Wenn du zwischen der Browseraktivität und der Aktivität des lokalen Players wechselst, sollte der Status der Mini-Steuerung mit dem Status der Medienwiedergabe des Empfängers synchronisiert bleiben.

8. Einführungs-Overlay

Die Google Cast-Design-Checkliste erfordert, dass in einer Sender-App das Cast-Symbol eingeführt wird, um bestehende Nutzer darüber zu informieren, dass die Sender-App jetzt Casting unterstützt. Außerdem wird Nutzern, die Google Cast noch nicht kennen, geholfen.

Abbildung eines iPhones, auf dem die CastVideos-App ausgeführt wird, mit dem Cast-Symbol als Overlay. Das Cast-Symbol ist hervorgehoben und es wird die Meldung „Tippen, um Medien auf deinen Fernseher und deine Lautsprecher zu streamen“ angezeigt.

Die Klasse GCKCastContext hat eine Methode, presentCastInstructionsViewControllerOnce, mit der die Cast-Schaltfläche hervorgehoben werden kann, wenn sie Nutzern zum ersten Mal angezeigt wird. Fügen Sie den folgenden Code zu MediaViewController.swift und MediaTableViewController.swift 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)
  }
}

Führen Sie die App auf Ihrem Mobilgerät aus. Das Einführungs-Overlay sollte angezeigt werden.

9. Erweiterter Controller

Die Google Cast-Design-Checkliste erfordert, dass eine Sender-App einen erweiterten Controller für die übertragenen Medien bereitstellt. Der maximierte Controller ist eine Vollbildversion des Mini-Controllers.

Abbildung eines iPhones, auf dem die CastVideos-App ausgeführt wird und ein Video abgespielt wird. Unten wird die maximierte Steuerung angezeigt.

Der erweiterte Controller ist eine Vollbildansicht, die die vollständige Steuerung der Medienwiedergabe ermöglicht. In dieser Ansicht sollte eine Casting-App alle verwaltbaren Aspekte einer Cast-Sitzung verwalten können, mit Ausnahme der Lautstärkeregelung des Empfängers und des Sitzungslebenszyklus (Verbinden/Beenden des Castings). Außerdem werden alle Statusinformationen zur Mediensitzung bereitgestellt (Artwork, Titel, Untertitel usw.).

Die Funktionalität 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 maximierten Standardcontroller 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 den folgenden Code zu MediaViewController.swift 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 maximierte Controller wird auch automatisch gestartet, wenn der Nutzer auf den Mini-Controller tippt.

Starte die App und übertrage ein Video. Der erweiterte Controller sollte angezeigt werden. Kehre zur Liste der Videos zurück. Wenn du auf die Mini-Steuerung klickst, wird die erweiterte Steuerung wieder geladen.

10. Cast Connect-Unterstützung hinzufügen

Mit der Cast Connect-Mediathek können vorhandene Senderanwendungen über das Cast-Protokoll mit Android TV-Anwendungen kommunizieren. Cast Connect basiert auf der Cast-Infrastruktur. Ihre Android TV-App fungiert dabei als Empfänger.

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 Konsole aus, um die Änderung mit Ihrem Projekt zu synchronisieren.

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

GCKLaunchOptions

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

Fügen Sie 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)

Startanmeldedaten festlegen

Auf der Senderseite können Sie GCKCredentialsData angeben, um darzustellen, wer der Sitzung beitritt. credentials ist ein String, der vom Nutzer definiert werden kann, sofern Ihre ATV-App ihn interpretieren kann. Der GCKCredentialsData wird nur beim Start oder beim Beitreten an Ihre Android TV-App übergeben. Wenn Sie die Einstellung noch einmal festlegen, während Sie verbunden sind, wird sie nicht an Ihre Android TV-App übergeben.

Damit Sie Anmeldedaten für den Start festlegen können, muss GCKCredentialsData jederzeit nach dem Festlegen von GCKLaunchOptions definiert werden. Um dies zu veranschaulichen, fügen wir der Schaltfläche Creds (Anmeldedaten) Logik hinzu, um Anmeldedaten festzulegen, die beim Einrichten der Sitzung übergeben werden sollen. Fügen Sie Ihrer 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 die Ladeanfrage festlegen

Damit credentials sowohl in Ihrer Web- als auch in Ihrer Android TV Receiver-App verarbeitet werden kann, fügen Sie den folgenden Code in Ihrer MediaTableViewController.swift-Klasse unter der loadSelectedItem-Funktion hinzu:

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

Je nachdem, auf welche Empfänger-App der Absender streamt, wendet das SDK die oben genannten Anmeldedaten automatisch auf die laufende Sitzung an.

Cast Connect testen

So installierst du die Android TV-APK auf dem Chromecast mit Google TV

  1. Finde die IP-Adresse deines Android TV-Geräts. Normalerweise finden Sie sie unter Einstellungen > Netzwerk & Internet > (Name des Netzwerks, 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 Ihres Geräts, um über das Terminal eine Verbindung über ADB herzustellen:
$ adb connect <device_ip_address>:5555
  1. Wechseln Sie im Terminalfenster in den 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, indem Sie Folgendes ausführen:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. In der Meine Apps-Ansicht auf deinem Android TV-Gerät sollte jetzt die App Videos streamen angezeigt werden.
  2. Erstellen Sie die App und führen Sie sie auf einem Emulator oder Mobilgerät aus. Wenn du eine Cast-Sitzung mit deinem Android TV-Gerät startest, sollte jetzt die Android-Empfängeranwendung auf deinem Android TV gestartet werden. Wenn du ein Video von deinem iOS-Mobilgerät als Sender abspielst, sollte das Video auf dem Android-Empfänger gestartet werden. Du kannst die Wiedergabe dann mit der 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 Ihrer AppDelegate.swift-Datei 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, übernehmen Sie die Formatierungen mit dem folgenden Code.

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. Ändern wir beispielsweise die Farbe des Symbols.

styler.castViews.iconTintColor = .lightGray

Bei Bedarf können Sie die Standardeinstellungen für jeden Bildschirm einzeln überschreiben. Wenn Sie beispielsweise die lightGrayColor für die Symbol-Tönungsfarbe nur für die erweiterte Media-Steuerung überschreiben möchten,

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 Ansichten, die vom Cast Application Framework bereitgestellt werden, auf Blau festgelegt.

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

Schriftarten ändern

Sie können Schriftarten für verschiedene Labels in Ansichten für die Übertragung anpassen. Wir legen zu Illustrationszwecken alle Schriftarten auf „Courier-Oblique“ fest.

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 die Bilder Ihren 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-Buttons ändern

Sie können Cast-Widgets auch mit dem UIAppearance-Protokoll gestalten. Im folgenden Code wird die GCKUICastButton in allen Ansichten, in denen sie angezeigt wird, mit einem Design versehen:

GCKUICastButton.appearance().tintColor = UIColor.gray

12. Glückwunsch

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

Weitere Informationen finden Sie im Entwicklerleitfaden für iOS-Sender.