Für Google Cast optimierte iOS-App

1. Überblick

Google Cast-Logo

In diesem Codelab erfahren Sie, wie Sie eine bestehende iOS-Video-App ändern können, 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 dann ihr Mobilgerät als Fernbedienung für die Medienwiedergabe auf dem Fernseher verwenden.

Mit dem Google Cast SDK können Sie Ihre App auf die Steuerung von Google Cast-fähigen Geräten wie Fernsehern oder Soundsystemen erweitern. 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 enthält eine einfache und vorhersehbare Checkliste für alle unterstützten Plattformen.

Ziele

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

Lerninhalte

  • Google Cast SDK einer Beispielvideo-App hinzufügen
  • Hinzufügen des Cast-Symbols zur Auswahl eines Google Cast-Geräts
  • Hier erfahren Sie, wie Sie eine Verbindung zu einem Übertragungsgerät herstellen und einen Medienempfänger starten.
  • So streamen Sie ein Video.
  • Cast Mini-Controller zur App hinzufügen
  • So fügst du einen erweiterten Controller hinzu.
  • So stellst du ein einführendes Overlay bereit.
  • Streaming-Widgets anpassen
  • Cast Connect-Integration

Voraussetzungen

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

Plattform

  • Sie müssen über vorherige Kenntnisse in der iOS-Entwicklung verfügen.
  • Außerdem benötigst du Vorkenntnisse im Fernsehen.

Wie werden Sie dieses Tutorial verwenden?

Nur lesen Lesen und die Übungen abschließen

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

Neuling Fortgeschritten Profi

Wie würdest du deine Erfahrung mit 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

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, wird in der folgenden Anleitung beschrieben, wie Sie die fertige Beispiel-App in Xcode öffnen und ausführen:

Häufig gestellte Fragen

CocoaPods einrichten

Um CocoaPods einzurichten, rufen Sie die Konsole auf und installieren Sie die App mit der unter macOS standardmäßig verfügbaren Ruby-Version:

sudo gem install cocoapods

Sollten Probleme auftreten, lesen Sie in der offiziellen Dokumentation nach, wie Sie den Abhängigkeitsmanager herunterladen und installieren.

Projekt einrichten

  1. Gehen Sie zu Ihrem Terminal und rufen Sie das Codelab-Verzeichnis auf.
  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... (Weiteres Projekt öffnen...) aus.
  2. Wählen Sie im Ordner mit dem Beispielcode aus dem Verzeichnis Ordnersymbolapp-done die Datei CastVideos-ios.xcworkspace 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 zu sehen sein.

Achten Sie darauf, auf "Zulassen" zu klicken, wenn die Benachrichtigung über die Annahme eingehender Netzwerkverbindungen angezeigt wird. Das Cast-Symbol wird nicht angezeigt, wenn diese Option nicht akzeptiert wird.

Bestätigungsdialogfeld für Berechtigung zum Akzeptieren eingehender Netzwerkverbindungen

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.

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

Der maximierte Controller wird angezeigt. Mit der Schaltfläche für Wiedergabe/Pause können Sie die Wiedergabe steuern.

Zurück zur Videoliste.

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

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

Klicke auf die Pause-Taste auf dem Mini-Controller, um das Video auf dem Empfänger anzuhalten. Klicke auf die Wiedergabetaste auf dem 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 ausgeführt wird

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

  • eine Absender-App auf einem Mobilgerät oder Laptop
  • 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. Gehen Sie zu Ihrem Terminal und rufen Sie das Codelab-Verzeichnis auf.
  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... (Weiteres Projekt öffnen...) aus.
  2. Wählen Sie im Ordner mit dem Beispielcode aus dem Verzeichnis Ordnersymbolapp-start die Datei CastVideos-ios.xcworkspace aus.

App-Design

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

Die App besteht aus zwei Haupt-Controllern für die Ansicht: MediaTableViewController und MediaViewController..

MediaTableViewController

Dieser UITableViewController zeigt eine Liste von Videos aus einer MediaListModel-Instanz an. Die Liste der Videos und die zugehörigen Metadaten werden auf einem Remoteserver als JSON-Datei gehostet. MediaListModel ruft diese JSON-Datei ab und verarbeitet sie, um eine Liste mit 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, damit die Tabellenansicht geladen werden kann.

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

MediaViewController

Mit diesem Ansichts-Controller werden die Metadaten zu einem bestimmten Video angezeigt und der Nutzer kann das Video lokal auf dem Mobilgerät wiedergeben.

Der Ansichts-Controller hostet ein LocalPlayerView, einige Mediensteuerelemente und einen Textbereich für die Beschreibung des ausgewählten Videos. Der Player deckt den oberen Teil des Bildschirms ab und lässt Platz für die detaillierte Beschreibung des Videos. Der Nutzer kann das Video abspielen/pausieren oder zur lokalen Videowiedergabe wechseln.

Häufig gestellte Fragen

5. Cast-Symbol hinzufügen

Abbildung des oberen Drittels eines iPhones mit CastVideos App mit dem Cast-Symbol in der oberen rechten Ecke

In einer für Google Cast optimierten App wird das Cast-Symbol auf allen Wiedergabe-Controllern angezeigt. Wenn der Nutzer auf das Cast-Symbol klickt, wird eine Liste mit Übertragungsgeräten angezeigt. Wenn der Nutzer Inhalte lokal auf dem Sendergerät wiedergegeben hat, wird durch die Auswahl eines Übertragungsgeräts die Wiedergabe auf diesem Übertragungsgerät gestartet oder fortgesetzt. Der Nutzer kann jederzeit während einer Sitzung auf das Cast-Symbol klicken und das Streaming Ihrer App auf das Übertragungsgerät beenden. Der Nutzer muss sich in der Lage sein, sich mit dem Übertragungsgerät zu verbinden bzw. die Verbindung zu trennen, während er einen beliebigen Bildschirm Ihrer App geöffnet hat, wie in der Checkliste für das Design für Google Cast beschrieben.

Konfiguration

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

Beim Initialisieren von GCKCastContext muss ein GCKCastOptions-Objekt angegeben werden. Diese Klasse enthält Optionen, die das Verhalten des Frameworks beeinflussen. Die wichtigste davon ist die App-ID des Empfängers. Sie wird zum Filtern der Erkennungsergebnisse des Übertragungsgeräts und zum Starten der Empfänger-App verwendet, wenn eine Streamingsitzung gestartet wird.

Mit der Methode application(_:didFinishLaunchingWithOptions:) lässt sich auch ein Logging-Delegate einrichten, der die Logging-Nachrichten vom Cast-Framework empfängt. Diese können bei der 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 anfordern. Für dieses Codelab verwenden wir eine Beispiel-App-ID.

Füge den folgenden Code in AppDelegate.swift ein, um GCKCastContext mit der App-ID aus den Standardeinstellungen des Nutzers zu initialisieren und einen Logger für das Google Cast-Framework hinzuzufügen:

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 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 UIButton-Klasse. Es kann der Titelleiste der Anwendung hinzugefügt werden, indem es in ein UIBarButtonItem eingeschlossen wird. Wir müssen das Cast-Symbol sowohl zu MediaTableViewController als auch zu MediaViewController hinzufügen.

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

import GoogleCast

@objc(MediaTableViewController)
class MediaTableViewController: UITableViewController, GCKSessionManagerListener,
  MediaListModelDelegate, GCKRequestDelegate {
  private var castButton: GCKUICastButton!
  ...
  override func viewDidLoad() {
    print("MediaTableViewController - viewDidLoad")
    super.viewDidLoad()

    ...
    castButton = GCKUICastButton(frame: CGRect(x: CGFloat(0), y: CGFloat(0),
                                               width: CGFloat(24), height: CGFloat(24)))
    // Overwrite the UIAppearance theme in the AppDelegate.
    castButton.tintColor = UIColor.white
    navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)

    ...
  }
  ...
}

Fügen Sie als Nächstes den folgenden Code 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 nun die App aus. In der Navigationsleiste der App sollte ein Cast-Symbol zu sehen sein. Wenn Sie darauf klicken, werden die Übertragungsgeräte in Ihrem lokalen Netzwerk aufgelistet. Die Geräteerkennung wird automatisch vom GCKCastContext verwaltet. Wähle dein Übertragungsgerät aus. Die Beispiel-Empfänger-App wird dann auf das Übertragungsgerät geladen. Du kannst zwischen den Suchaktivitäten und der Aktivität des lokalen Players wechseln und der Status des Cast-Symbols wird synchronisiert.

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

6. Videoinhalte streamen

Abbildung eines iPhones, auf dem die CastVideos App ausgeführt wird. Sie zeigt Details zu einem bestimmten Video („Tears of Steel“). Unten befindet sich der Miniplayer

Wir werden die Beispiel-App erweitern, damit Videos auch aus der Ferne auf einem Übertragungsgerät abgespielt werden können. Dazu müssen wir auf die verschiedenen Ereignisse warten, die vom Cast-Framework generiert werden.

Medien werden gestreamt

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

  1. Erstellen Sie aus dem Cast SDK ein GCKMediaInformation-Objekt, das ein Medienelement modelliert.
  2. Der Nutzer stellt eine Verbindung zum Übertragungsgerät her, um die Empfangs-App zu starten.
  3. Laden Sie das GCKMediaInformation-Objekt in den Empfänger und geben Sie den Inhalt wieder.
  4. Verfolgen Sie den Medienstatus.
  5. Sendet Wiedergabebefehle auf der Grundlage von 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 eines Medienelements in unserer App. Ein MediaItem kann ganz einfach einem GCKMediaInformation zugeordnet werden. Wir haben Schritt 2 bereits im vorherigen Abschnitt ausgeführt. Schritt 3 lässt sich ganz einfach mit dem Cast SDK durchführen.

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

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

private var playbackMode = PlaybackMode.none

Es ist nicht wichtig, dass du in diesem Codelab genau verstehst, wie die gesamte Beispiel-Playerlogik funktioniert. Es ist wichtig zu verstehen, dass der Mediaplayer Ihrer App modifiziert werden muss, um die beiden Wiedergabeorte auf ähnliche Weise erkennen zu können.

Momentan befindet sich der lokale Player immer im lokalen Wiedergabestatus, da er noch nichts über die Übertragungsstatus hat. Wir müssen die Benutzeroberfläche basierend auf Statusübergängen aktualisieren, die im Cast-Framework stattfinden. Wenn Sie beispielsweise mit dem Streamen beginnen, müssen wir die lokale Wiedergabe beenden und einige Steuerelemente deaktivieren. Das Gleiche gilt, wenn wir das Streaming beenden, während wir uns im Ansichts-Controller befinden, müssen wir zur lokalen Wiedergabe wechseln. Dazu müssen wir auf die verschiedenen Ereignisse warten, die vom Cast-Framework generiert werden.

Streamingsitzung verwalten

Für das Cast-Framework umfasst eine Streamingsitzung die Schritte: Verbinden mit einem Gerät, Starten (oder Betreten), Verbinden mit einer Empfänger-App und Initialisieren eines Mediensteuerungskanals (falls erforderlich). Über den Mediensteuerungskanal sendet und empfängt das Cast-Framework Nachrichten vom Mediaplayer des Empfängers.

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

Streamingsitzungen werden von der GCKSessionManager verwaltet, auf die über GCKCastContext.sharedInstance().sessionManager zugegriffen werden kann. Die GCKSessionManagerListener-Callbacks können zum Überwachen von Sitzungsereignissen wie der Erstellung, Sperrung, Wiederaufnahme und Beendigung verwendet 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()
  }

  ...
}

Wir möchten in MediaViewController darüber informiert werden, wenn eine Verbindung zum Übertragungsgerät hergestellt oder getrennt wird, damit wir zum lokalen Player wechseln können. Beachten Sie, dass die Verbindung nicht nur durch die Instanz Ihrer Anwendung, die auf Ihrem Mobilgerät ausgeführt wird, unterbrochen werden kann, sondern auch durch eine andere Instanz Ihrer (oder eine andere) Anwendung, die auf einem anderen Mobilgerät ausgeführt wird.

Auf die derzeit aktive Sitzung kann als GCKCastContext.sharedInstance().sessionManager.currentCastSession zugegriffen werden. Sitzungen werden automatisch als Reaktion auf Nutzergesten in den Cast-Dialogfeldern erstellt und entfernt.

Medien werden geladen

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

Füge MediaViewController.swift den folgenden Code hinzu, um das aktuell ausgewählte Video auf den 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 zur Unterstützung der Remote-Wiedergabe zu verwenden:

required init?(coder: NSCoder) {
  super.init(coder: coder)
  ...
  castMediaController = GCKUIMediaController()
  ...
}

func switchToLocalPlayback() {
  print("switchToLocalPlayback")
  if playbackMode == .local {
    return
  }
  setQueueButtonVisible(false)
  var playPosition: TimeInterval = 0
  var paused: Bool = false
  var ended: Bool = false
  if playbackMode == .remote {
    playPosition = castMediaController.lastKnownStreamPosition
    paused = (castMediaController.lastKnownPlayerState == .paused)
    ended = (castMediaController.lastKnownPlayerState == .idle)
    print("last player state: \(castMediaController.lastKnownPlayerState), ended: \(ended)")
  }
  populateMediaInfo((!paused && !ended), playPosition: playPosition)
  sessionManager.currentCastSession?.remoteMediaClient?.remove(self)
  playbackMode = .local
}

func switchToRemotePlayback() {
  print("switchToRemotePlayback; mediaInfo is \(String(describing: mediaInfo))")
  if playbackMode == .remote {
    return
  }
  // If we were playing locally, load the local media on the remote player
  if playbackMode == .local, (_localPlayerView.playerState != .stopped), (mediaInfo != nil) {
    print("loading media: \(String(describing: mediaInfo))")
    let paused: Bool = (_localPlayerView.playerState == .paused)
    let mediaQueueItemBuilder = GCKMediaQueueItemBuilder()
    mediaQueueItemBuilder.mediaInformation = mediaInfo
    mediaQueueItemBuilder.autoplay = !paused
    mediaQueueItemBuilder.preloadTime = TimeInterval(UserDefaults.standard.integer(forKey: kPrefPreloadTime))
    mediaQueueItemBuilder.startTime = _localPlayerView.streamPosition ?? 0
    let mediaQueueItem = mediaQueueItemBuilder.build()

    let queueDataBuilder = GCKMediaQueueDataBuilder(queueType: .generic)
    queueDataBuilder.items = [mediaQueueItem]
    queueDataBuilder.repeatMode = .off

    let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
    mediaLoadRequestDataBuilder.queueData = queueDataBuilder.build()

    let request = sessionManager.currentCastSession?.remoteMediaClient?.loadMedia(with: mediaLoadRequestDataBuilder.build())
    request?.delegate = self
  }
  _localPlayerView.stop()
  _localPlayerView.showSplashScreen()
  setQueueButtonVisible(true)
  sessionManager.currentCastSession?.remoteMediaClient?.add(self)
  playbackMode = .remote
}

/* Play has been pressed in the LocalPlayerView. */
func continueAfterPlayButtonClicked() -> Bool {
  let hasConnectedCastSession = sessionManager.hasConnectedCastSession
  if mediaInfo != nil, hasConnectedCastSession() {
    // Display an alert box to allow the user to add to queue or play
    // immediately.
    if actionSheet == nil {
      actionSheet = ActionSheet(title: "Play Item", message: "Select an action", cancelButtonText: "Cancel")
      actionSheet?.addAction(withTitle: "Play Now", target: self,
                             selector: #selector(playSelectedItemRemotely))
    }
    actionSheet?.present(in: self, sourceView: _localPlayerView)
    return false
  }
  return true
}

Führen Sie nun die App auf Ihrem Mobilgerät aus. Stellen Sie eine Verbindung zu Ihrem Übertragungsgerät her und starten Sie die Wiedergabe eines Videos. Das Video sollte auf dem Receiver wiedergegeben werden.

7. Mini-Controller

Gemäß der Checkliste für das Streaming müssen alle Cast-Apps über einen Mini-Controller verfügen, der angezeigt wird, wenn der Nutzer die aktuelle Inhaltsseite verlässt. Der Mini-Controller bietet sofortigen Zugriff und eine sichtbare Erinnerung für das aktuelle Streaming.

Darstellung des unteren Teils eines iPhones, auf dem die CastVideos App ausgeführt wird und der Fokus auf dem Mini-Controller liegt

Das Cast SDK bietet eine Steuerleiste (GCKUIMiniMediaControlsViewController), die Sie den Szenen hinzufügen können, in denen die dauerhaften Steuerelemente eingeblendet werden sollen.

Für die Beispiel-App verwenden wir GCKUICastContainerViewController, das einen weiteren Ansichts-Controller umschließt und am unteren Rand ein 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 diesen Setter/Getter hinzu, um die Sichtbarkeit des Mini-Controllers zu steuern. Diese Funktionen werden in einem späteren Abschnitt verwendet:

var isCastControlBarsEnabled: Bool {
    get {
      if useCastContainerViewController {
        let castContainerVC = (window?.rootViewController as? GCKUICastContainerViewController)
        return castContainerVC!.miniMediaControlsItemEnabled
      } else {
        let rootContainerVC = (window?.rootViewController as? RootContainerViewController)
        return rootContainerVC!.miniMediaControlsViewEnabled
      }
    }
    set(notificationsEnabled) {
      if useCastContainerViewController {
        var castContainerVC: GCKUICastContainerViewController?
        castContainerVC = (window?.rootViewController as? GCKUICastContainerViewController)
        castContainerVC?.miniMediaControlsItemEnabled = notificationsEnabled
      } else {
        var rootContainerVC: RootContainerViewController?
        rootContainerVC = (window?.rootViewController as? RootContainerViewController)
        rootContainerVC?.miniMediaControlsViewEnabled = notificationsEnabled
      }
    }
  }

Führe die App aus und streame ein Video. Wenn die Wiedergabe auf dem Receiver beginnt, sollte der Mini-Controller unten in jeder Szene erscheinen. Die Remote-Wiedergabe kannst du mit dem Mini-Controller steuern. Wenn Sie zwischen den Suchaktivitäten und dem lokalen Player wechseln, sollte der Status des Mini-Controllers mit dem Status der Medienwiedergabe auf dem Empfänger synchron bleiben.

8. Einleitendes Overlay

Gemäß der Design-Checkliste für Google Cast muss eine Sender-App für bestehende Nutzer das Cast-Symbol einführen, um sie darauf hinzuweisen, dass die Sender-App jetzt Streaming unterstützt und neue Google Cast-Nutzer ist.

Abbildung eines iPhones, auf dem die CastVideos App ausgeführt wird, mit dem Cast-Symbol-Overlay, auf dem das Cast-Symbol hervorgehoben ist und auf dem die Meldung „Durch Tippen auf den Fernseher und die Lautsprecher gestreamt werden“ zu sehen ist.

Die Klasse GCKCastContext hat die 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, sollten Sie das Overlay mit Einführung sehen.

9. Maximierter Controller

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

Abbildung eines iPhones, auf dem die CastVideos App ausgeführt wird, wobei der erweiterte Controller unten angezeigt wird

Der maximierte Controller ist eine Vollbildansicht, die volle Kontrolle über die Remote-Medienwiedergabe bietet. Diese Ansicht sollte es einer Streaming-App ermöglichen, alle verwaltbaren Aspekte einer Streamingsitzung zu verwalten, mit Ausnahme der Lautstärkeregelung des Empfängers und des Sitzungslebenszyklus (Streaming verbinden/beenden). Außerdem enthält es alle Statusinformationen zur Mediensitzung (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 Streaming-Kontext aktivieren. Ändern Sie AppDelegate.swift, um den erweiterten Standard-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 in MediaViewController.swift den folgenden Code ein, 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 auch automatisch gestartet, wenn der Nutzer auf den Mini-Controller tippt.

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

10. Cast Connect-Unterstützung hinzufügen

Mit der Cast Connect-Bibliothek können vorhandene Sender-Apps über das Cast-Protokoll mit Android TV-Apps kommunizieren. Cast Connect basiert auf der Cast-Infrastruktur, wobei Ihre Android TV App als Empfänger fungiert.

Abhängigkeiten

Achten Sie darauf, dass das google-cast-sdk in Podfile auf 4.4.8 oder höher verweist, wie unten aufgeführt. 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'

GCKLaunchOptions

Um die Android TV App zu starten, die auch als Android-Empfänger bezeichnet wird, muss das androidReceiverCompatible-Flag im GCKLaunchOptions-Objekt auf „true“ gesetzt werden. Dieses GCKLaunchOptions-Objekt gibt vor, wie der Empfänger gestartet wird, und wird an die GCKCastOptions übergeben, die in der gemeinsam genutzten 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)

Start-Anmeldedaten festlegen

Auf der Absenderseite können Sie GCKCredentialsData angeben, um anzugeben, wer der Sitzung beitritt. „credentials“ ist ein String, der vom Nutzer definiert werden kann, solange die ATV-App ihn verstehen kann. Die GCKCredentialsData wird nur beim Start oder der Zeit des Beitritts an deine Android TV-App weitergegeben. Wenn du sie wieder festlegst, während eine Verbindung besteht, wird sie nicht an deine Android TV App übergeben.

Zum Festlegen von Start-Anmeldedaten muss GCKCredentialsData jederzeit definiert werden, nachdem GCKLaunchOptions festgelegt wurden. Zur Veranschaulichung fügen wir der Schaltfläche Creds eine Logik hinzu, um Anmeldedaten festzulegen, die beim Aufbau der Sitzung weitergegeben werden. 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 Ladeanfrage festlegen

Damit credentials sowohl in der Web-App als auch in der Receiver-App von Android TV verarbeitet werden kann, füge den folgenden Code in deine MediaTableViewController.swift-Klasse unter der Funktion loadSelectedItem ein:

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

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

Cast Connect wird getestet

Schritte zur Installation des Android TV-APKs auf Chromecast mit Google TV

  1. Ermitteln Sie die IP-Adresse Ihres Android TV-Geräts. Sie ist normalerweise unter Einstellungen > Netzwerk und Internet > (Netzwerkname, mit dem Ihr Gerät verbunden ist) zu finden. Auf der rechten Seite werden die Details und die IP-Adresse Ihres Geräts im Netzwerk angezeigt.
  2. Verwende die IP-Adresse deines Geräts, um über das Terminal über ADB eine Verbindung zu ihm herzustellen:
$ adb connect <device_ip_address>:5555
  1. Gehen Sie im Terminalfenster zum Ordner der obersten Ebene mit den Codelab-Beispielen, die Sie zu Beginn dieses Codelabs heruntergeladen haben. Beispiel:
$ cd Desktop/ios_codelab_src
  1. Installieren Sie die in diesem Ordner enthaltene APK-Datei auf Ihrem Android TV-Gerät .Führen Sie dazu den folgenden Befehl aus:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. Sie sollten nun eine App mit dem Namen Videos streamen im Menü Meine Apps auf Ihrem Android TV-Gerät sehen.
  2. Anschließend kannst du die App in einem Emulator oder auf einem Mobilgerät erstellen und ausführen. Nach dem Einrichten einer Streamingsitzung mit Ihrem Android TV-Gerät sollte die App „Android Receiver“ auf Ihrem Android TV-Gerät gestartet werden. Wenn du ein Video von deinem mobilen iOS-Sender abspielst, sollte es im Android-Receiver gestartet werden und du kannst die Wiedergabe über die Fernbedienung deines Android TV-Geräts steuern.

11. Streaming-Widgets anpassen

Initialisierung

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

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

Nachdem Sie eine oder mehrere Anpassungen angewendet haben, wie im Rest dieses Codelabs beschrieben, können Sie die Stile festlegen, indem Sie den Code unten aufrufen.

styler.apply()

Streaming-Ansichten anpassen

Sie können alle von Cast App Framework verwalteten Ansichten anpassen, indem Sie für alle Ansichten Standardstilrichtlinien festlegen. Als Beispiel ändern wir die Färbung des Symbols.

styler.castViews.iconTintColor = .lightGray

Sie können die Standardeinstellungen bei Bedarf für einzelne Bildschirme überschreiben. So können Sie beispielsweise „lightGrayColor“ für die Symbolfärbung nur für die maximierte Mediensteuerung überschreiben.

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

Farben ändern

Sie können die Hintergrundfarbe für alle Ansichten oder für jede Ansicht einzeln anpassen. Mit dem folgenden Code wird die Hintergrundfarbe für alle von Cast App 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 in Cast-Ansichten anpassen. Lassen Sie uns zur Veranschaulichung alle Schriftarten auf "Courier-Schräg" setzen.

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 Ihre eigenen benutzerdefinierten Images 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-Symbols ändern

Sie können Cast Widgets auch mithilfe des UIAppearance-Protokolls gestalten. Der folgende Code gestaltet das GCKUICastButton für alle angezeigten Ansichten:

GCKUICastButton.appearance().tintColor = UIColor.gray

12. Glückwunsch

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

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