為網頁應用程式啟用投放功能

1. 總覽

Google Cast 標誌

本程式碼研究室將說明如何修改現有的網頁應用程式應用程式,以便在支援 Google Cast 的裝置上投放內容。

什麼是 Google Cast?

Google Cast 可讓使用者將行動裝置上的內容投放到電視上。讓使用者能將行動裝置當做電視播放媒體的遙控器。

Google Cast SDK 可讓您擴充應用程式,輕鬆控制電視或音響系統。Cast SDK 可讓您根據 Google Cast 設計檢查清單新增必要的 UI 元件。

我們歸納了 Google Cast 設計檢查清單,目的是讓所有支援的平台都能簡單且可預測的 Cast 使用者體驗。

我們要建構什麼?

完成本程式碼研究室後,您將擁有可以將影片投放到 Google Cast 裝置的 Chrome 網頁應用程式。

課程內容

  • 如何將 Google Cast SDK 加入範例應用程式。
  • 如何新增「投放」按鈕供選取 Google Cast 裝置。
  • 如何連線至投放裝置並啟動媒體接收器。
  • 如何投放影片。
  • 如何整合 Cast Connect

軟硬體需求

  • 最新版的 Google Chrome 瀏覽器。
  • HTTPS 代管服務,例如 Firebase 託管ngrok
  • 一部 Google Cast 裝置,例如已設定網際網路連線的 ChromecastAndroid TV
  • 具備 HDMI 輸入端的電視或顯示器。
  • 如要測試 Cast Connect 整合功能,必須使用 Chromecast (支援 Google TV),但程式碼研究室的其餘部分為選用。如果您沒有裝置,可以略過本教學課程結尾的「新增 Cast Connect 支援」步驟。

功能

  • 您必須事先具備網頁開發知識。
  • 並具備觀看電視的知識 :)

您將會如何使用這個教學課程?

僅供閱讀 閱讀並完成練習

針對建構網頁應用程式的體驗,您會給予什麼評價?

初級 中級 達人

針對觀看電視的體驗,你會給予什麼評價?

初級 中級 專業

2. 取得程式碼範例

您可以將所有程式碼範例下載至電腦...

然後將下載的 ZIP 檔案解壓縮

3. 執行範例應用程式

Google Chrome 標誌

首先,來看看完成的範例應用程式看起來會是什麼樣子。這款應用程式是基本的影片播放器。使用者只要從清單中選取影片,即可將影片投放到裝置本機或投放到 Google Cast 裝置。

必須先代管,才能使用這個元件。

如果沒有可以使用的伺服器,可以使用 Firebase 託管ngrok

執行伺服器

設定完畢後,請前往 app-done 並啟動伺服器。

在瀏覽器中前往代管範例的 HTTPS 網址。

  1. 畫面上應該會顯示影片應用程式。
  2. 按一下「投放」按鈕,然後選取你的 Google Cast 裝置。
  3. 選取影片,然後按一下播放按鈕。
  4. 影片就會開始在 Google Cast 裝置上播放。

圖片:在投放裝置上播放影片

按一下影片元素中的暫停按鈕,即可在接收端暫停播放影片。按一下影片元素中的播放按鈕,即可再次播放影片。

按一下「投放」按鈕,即可停止投放到 Google Cast 裝置。

在我們繼續之前,請先停止伺服器。

4. 準備 start 專案

圖片:在投放裝置上播放影片

對於你下載的啟動應用程式,我們需要新增 Google Cast 支援功能。以下是本程式碼研究室會使用的一些 Google Cast 術語:

  • 傳送者應用程式是在行動裝置或筆記型電腦上執行,
  • 接收端應用程式必須在 Google Cast 裝置上執行。

您現在可以使用喜愛的文字編輯器,以範例專案為基礎進行建構了:

  1. 從程式碼範例下載中選取 「資料夾」圖示app-start 目錄。
  2. 使用您的伺服器執行應用程式,並探索 UI。

請注意,您在執行本程式碼研究室時,需要在伺服器上重新託管範例 (視服務而定)。

應用程式設計

應用程式會從遠端網路伺服器擷取影片清單,並提供可供使用者瀏覽的清單。使用者可以選取影片來查看詳細資訊,或在行動裝置上直接播放影片。

應用程式包含一個主要檢視畫面 (定義於 index.html 內),以及主要控制器 CastVideos.js.

index.html

這個 HTML 檔案可宣告網頁應用程式幾乎所有 UI。

其中幾個檢視畫面的部分,我們的 div#main_video 包含影片元素。與影片 div 相關,您可以使用 div#media_control,定義影片元素的所有控制項。下方的 media_info 會顯示影片在檢視畫面中的詳細資料。最後,carousel div 會顯示 div 中的影片清單。

index.html 檔案也會啟動 Cast SDK,並指示 CastVideos 函式載入。

填入這些元素的內容大部分內容都是在 CastVideos.js 中定義、插入和控制。我們來看一下

CastVideos.js

這個指令碼會管理投放影片網頁應用程式的所有邏輯。CastVideos.js 中定義的影片清單及其相關中繼資料位於名為 mediaJSON 的物件內。

而有幾個主要部分則負責在本機和遠端管理及播放影片。整體而言,這是一款相當直觀的網頁應用程式。

CastPlayer 是管理整個應用程式、設定播放器、選取媒體,以及將事件繫結至 PlayerHandler 的主要類別,用於播放媒體。CastPlayer.prototype.initializeCastPlayer 是設定所有投放功能的方法。CastPlayer.prototype.switchPlayer 會在本機和遠端播放器之間切換狀態。CastPlayer.prototype.setupLocalPlayerCastPlayer.prototype.setupRemotePlayer 會初始化本機和遠端玩家。

PlayerHandler 是負責管理媒體播放的類別。還有很多其他方法負責管理媒體和播放作業。

常見問題

5. 新增「投放」按鈕

支援 Cast 的應用程式圖片

支援 Cast 的應用程式會在影片元素中顯示「投放」按鈕。按一下「投放」按鈕,就會顯示使用者可選取的投放裝置清單。如果使用者是透過傳送者的裝置本機播放內容,選取投放裝置時,該投放裝置就會開始或繼續播放內容。在投放過程中,使用者隨時可以按一下「投放」按鈕,停止將應用程式投放到投放裝置。使用者必須能在應用程式的任何畫面中,與投放裝置連線或中斷連線,詳情請參閱 Google Cast 設計檢查清單

設定

啟動專案需要的依附元件和設定與完成的範例應用程式相同,但這次代管 app-start 的內容。

在瀏覽器中前往代管範例的 https 網址。

請記住,當您進行變更時,必須依據服務在伺服器上重新託管範例。

初始化

Cast 架構具有全域單例模式物件 CastContext,可協調架構的所有活動。這個物件必須在應用程式的生命週期中及早初始化,通常是從指派給 window['__onGCastApiAvailable'] 的回呼中呼叫,系統會在 Cast SDK 載入後呼叫該物件可供使用。在此情況下,系統會在 CastPlayer.prototype.initializeCastPlayer 中呼叫 CastContext,並透過上述回呼呼叫。

初始化 CastContext 時必須提供 options JSON 物件。這個類別包含會影響架構行為的選項。其中最重要的就是接收器應用程式 ID,用於篩選可用的投放裝置清單,只顯示能執行指定應用程式的裝置,並在投放工作階段開始時啟動接收器應用程式。

自行開發支援 Cast 的應用程式時,您必須註冊為 Cast 開發人員,然後取得應用程式的應用程式 ID。在本程式碼研究室中,我們將使用範例應用程式 ID。

將下列程式碼新增至 body 區段結尾的 index.html

<script type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>

將下列程式碼新增至 index.html,即可初始化 CastVideos 應用程式以及初始化 CastContext

<script src="CastVideos.js"></script>
<script type="text/javascript">
var castPlayer = new CastPlayer();
window['__onGCastApiAvailable'] = function(isAvailable) {
  if (isAvailable) {
    castPlayer.initializeCastPlayer();
  }
};
</script>

現在,我們需要在 CastVideos.js 中新增方法,對應至剛才在 index.html 中呼叫的方法。讓我們新增名為 initializeCastPlayer 的方法,用於設定 CastContext 的選項,並初始化新的 RemotePlayerRemotePlayerControllers

/**
 * This method sets up the CastContext, and a few other members
 * that are necessary to play and control videos on a Cast
 * device.
 */
CastPlayer.prototype.initializeCastPlayer = function() {

    var options = {};

    // Set the receiver application ID to your own (created in
    // the Google Cast Developer Console), or optionally
    // use the chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID
    options.receiverApplicationId = 'C0868879';

    // Auto join policy can be one of the following three:
    // ORIGIN_SCOPED - Auto connect from same appId and page origin
    // TAB_AND_ORIGIN_SCOPED - Auto connect from same appId, page origin, and tab
    // PAGE_SCOPED - No auto connect
    options.autoJoinPolicy = chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED;

    cast.framework.CastContext.getInstance().setOptions(options);

    this.remotePlayer = new cast.framework.RemotePlayer();
    this.remotePlayerController = new cast.framework.RemotePlayerController(this.remotePlayer);
    this.remotePlayerController.addEventListener(
        cast.framework.RemotePlayerEventType.IS_CONNECTED_CHANGED,
        this.switchPlayer.bind(this)
    );
};

最後,我們需要為 RemotePlayerRemotePlayerController 建立變數:

var CastPlayer = function() {
  //...
  /* Cast player variables */
  /** @type {cast.framework.RemotePlayer} */
  this.remotePlayer = null;
  /** @type {cast.framework.RemotePlayerController} */
  this.remotePlayerController = null;
  //...
};

投放按鈕

現在 CastContext 已初始化,我們需要新增「投放」按鈕,讓使用者能夠選取投放裝置。Cast SDK 會提供名為 google-cast-launcher 的投放按鈕元件,其 ID 為「castbutton"」。只要在 media_control 部分加入 button,即可加到應用程式的影片元素中。

按鈕元素的外觀如下:

<google-cast-launcher id="castbutton"></google-cast-launcher>

將下列程式碼新增至 media_control 區段的 index.html 中:

<div id="media_control">
  <div id="play"></div>
  <div id="pause"></div>
  <div id="progress_bg"></div>
  <div id="progress"></div>
  <div id="progress_indicator"></div>
  <div id="fullscreen_expand"></div>
  <div id="fullscreen_collapse"></div>
  <google-cast-launcher id="castbutton"></google-cast-launcher>
  <div id="audio_bg"></div>
  <div id="audio_bg_track"></div>
  <div id="audio_indicator"></div>
  <div id="audio_bg_level"></div>
  <div id="audio_on"></div>
  <div id="audio_off"></div>
  <div id="duration">00:00:00</div>
</div>

現在在 Chrome 瀏覽器中重新整理頁面。你應該會在影片元素中看到「投放」按鈕。只要按一下該按鈕,畫面就會列出您區域網路中的投放裝置。裝置探索設定是由 Chrome 瀏覽器自動管理。選取你的投放裝置,範例接收器應用程式隨即會在投放裝置上載入。

我們尚未支援任何媒體播放功能,因此目前無法在投放裝置上播放影片。按一下「投放」按鈕即可停止投放。

6. 投放影片內容

圖片:支援投放功能的應用程式 (包含投放裝置選取選單)

我們將擴充範例應用程式,以便同時在投放裝置上遠端播放影片。為此,我們需要監聽 Cast 架構產生的各種事件。

投放媒體

大致上,如果您想在投放裝置上播放媒體,必須滿足以下條件:

  1. 從 Cast SDK 建立 MediaInfo JSON 物件,用來建立媒體項目的模型。
  2. 使用者連線至投放裝置,以啟動接收器應用程式。
  3. MediaInfo 物件載入接收器並播放內容。
  4. 追蹤媒體狀態。
  5. 根據使用者互動情形,將播放指令傳送給接收器。

步驟 1 說明如何將一個物件對應至另一個物件;MediaInfo 是 Cast SDK 可理解的,而 mediaJSON 是應用程式的媒體項目封裝;我們可以輕鬆將 mediaJSON 對應至 MediaInfo。我們已經完成上一節中的步驟 2。輕鬆透過 Cast SDK 完成步驟 3。

範例應用程式 CastPlayer 已區分 switchPlayer 方法中的本機和遠端播放:

if (cast && cast.framework) {
  if (this.remotePlayer.isConnected) {
    //...

想瞭解所有範例播放器邏輯的運作方式,不是本程式碼研究室中的重點。但請注意,應用程式的媒體播放器需要經過調整,才能得知本機和遠端播放功能。

目前,本機播放器一律會在本機播放狀態,因為其尚無「投放狀態」的相關資訊。我們需要根據 Cast 架構中的狀態轉換作業更新 UI。舉例來說,如果我們開始投放,就必須停止本機播放,並停用部分控制項。同樣地,如果我們在使用這個檢視控制器時停止投放,就必須轉換為本機播放。為了處理這種情況,我們需要監聽 Cast 架構產生的各種事件。

投放工作階段管理

針對投放架構,「投放」工作階段結合了連線至裝置、啟動 (或加入現有工作階段)、連線至接收器應用程式,以及 (如適用) 初始化媒體控制管道的步驟。媒體控制管道是 Cast 架構從接收端傳送及接收媒體播放相關訊息的方式。

當使用者透過「投放」按鈕選取裝置時,「投放」工作階段就會自動啟動,並在使用者中斷連線時自動停止投放。因網路問題而重新連線至接收器工作階段時,Cast 架構也會自動處理。

投放工作階段是由 CastSession 管理,您可以透過 cast.framework.CastContext.getInstance().getCurrentSession() 存取。EventListener 回呼可用於監控工作階段事件,例如建立、暫停、恢復和終止等。

在目前的應用程式中,所有工作階段和狀態管理都會透過 setupRemotePlayer 方法處理。將下列程式碼新增至 CastVideos.js,即可開始在應用程式中進行設定:

/**
 * Set the PlayerHandler target to use the remote player
 */
CastPlayer.prototype.setupRemotePlayer = function () {
    var castSession = cast.framework.CastContext.getInstance().getCurrentSession();

    this.playerHandler.setTarget(playerTarget);

    // Setup remote player volume right on setup
    // The remote player may have had a volume set from previous playback
    if (this.remotePlayer.isMuted) {
        this.playerHandler.mute();
    }
    var currentVolume = this.remotePlayer.volumeLevel * FULL_VOLUME_HEIGHT;
    var p = document.getElementById('audio_bg_level');
    p.style.height = currentVolume + 'px';
    p.style.marginTop = -currentVolume + 'px';

    this.hideFullscreenButton();

    this.playerHandler.play();
};

我們仍需繫結所有回呼中的事件,並處理所有傳入的事件。這是相當簡單直接的做法,因此我們現在要處理:

/**
 * Set the PlayerHandler target to use the remote player
 */
CastPlayer.prototype.setupRemotePlayer = function () {
    var castSession = cast.framework.CastContext.getInstance().getCurrentSession();

    // Add event listeners for player changes which may occur outside sender app
    this.remotePlayerController.addEventListener(
        cast.framework.RemotePlayerEventType.IS_PAUSED_CHANGED,
        function() {
            if (this.remotePlayer.isPaused) {
                this.playerHandler.pause();
            } else {
                this.playerHandler.play();
            }
        }.bind(this)
    );

    this.remotePlayerController.addEventListener(
        cast.framework.RemotePlayerEventType.IS_MUTED_CHANGED,
        function() {
            if (this.remotePlayer.isMuted) {
                this.playerHandler.mute();
            } else {
                this.playerHandler.unMute();
            }
        }.bind(this)
    );

    this.remotePlayerController.addEventListener(
        cast.framework.RemotePlayerEventType.VOLUME_LEVEL_CHANGED,
        function() {
            var newVolume = this.remotePlayer.volumeLevel * FULL_VOLUME_HEIGHT;
            var p = document.getElementById('audio_bg_level');
            p.style.height = newVolume + 'px';
            p.style.marginTop = -newVolume + 'px';
        }.bind(this)
    );

    // This object will implement PlayerHandler callbacks with
    // remotePlayerController, and makes necessary UI updates specific
    // to remote playback
    var playerTarget = {};

    playerTarget.play = function () {
        if (this.remotePlayer.isPaused) {
            this.remotePlayerController.playOrPause();
        }

        var vi = document.getElementById('video_image');
        vi.style.display = 'block';
        var localPlayer = document.getElementById('video_element');
        localPlayer.style.display = 'none';
    }.bind(this);

    playerTarget.pause = function () {
        if (!this.remotePlayer.isPaused) {
            this.remotePlayerController.playOrPause();
        }
    }.bind(this);

    playerTarget.stop = function () {
         this.remotePlayerController.stop();
    }.bind(this);

    playerTarget.getCurrentMediaTime = function() {
        return this.remotePlayer.currentTime;
    }.bind(this);

    playerTarget.getMediaDuration = function() {
        return this.remotePlayer.duration;
    }.bind(this);

    playerTarget.updateDisplayMessage = function () {
        document.getElementById('playerstate').style.display = 'block';
        document.getElementById('playerstatebg').style.display = 'block';
        document.getElementById('video_image_overlay').style.display = 'block';
        document.getElementById('playerstate').innerHTML =
            this.mediaContents[ this.currentMediaIndex]['title'] + ' ' +
            this.playerState + ' on ' + castSession.getCastDevice().friendlyName;
    }.bind(this);

    playerTarget.setVolume = function (volumeSliderPosition) {
        // Add resistance to avoid loud volume
        var currentVolume = this.remotePlayer.volumeLevel;
        var p = document.getElementById('audio_bg_level');
        if (volumeSliderPosition < FULL_VOLUME_HEIGHT) {
            var vScale =  this.currentVolume * FULL_VOLUME_HEIGHT;
            if (volumeSliderPosition > vScale) {
                volumeSliderPosition = vScale + (pos - vScale) / 2;
            }
            p.style.height = volumeSliderPosition + 'px';
            p.style.marginTop = -volumeSliderPosition + 'px';
            currentVolume = volumeSliderPosition / FULL_VOLUME_HEIGHT;
        } else {
            currentVolume = 1;
        }
        this.remotePlayer.volumeLevel = currentVolume;
        this.remotePlayerController.setVolumeLevel();
    }.bind(this);

    playerTarget.mute = function () {
        if (!this.remotePlayer.isMuted) {
            this.remotePlayerController.muteOrUnmute();
        }
    }.bind(this);

    playerTarget.unMute = function () {
        if (this.remotePlayer.isMuted) {
            this.remotePlayerController.muteOrUnmute();
        }
    }.bind(this);

    playerTarget.isMuted = function() {
        return this.remotePlayer.isMuted;
    }.bind(this);

    playerTarget.seekTo = function (time) {
        this.remotePlayer.currentTime = time;
        this.remotePlayerController.seek();
    }.bind(this);

    this.playerHandler.setTarget(playerTarget);

    // Setup remote player volume right on setup
    // The remote player may have had a volume set from previous playback
    if (this.remotePlayer.isMuted) {
        this.playerHandler.mute();
    }
    var currentVolume = this.remotePlayer.volumeLevel * FULL_VOLUME_HEIGHT;
    var p = document.getElementById('audio_bg_level');
    p.style.height = currentVolume + 'px';
    p.style.marginTop = -currentVolume + 'px';

    this.hideFullscreenButton();

    this.playerHandler.play();
};

正在載入媒體

在 Cast SDK 中,RemotePlayerRemotePlayerController 提供了一組便利的 API,方便您管理接收端上的遠端媒體播放作業。對於支援媒體播放的 CastSession,SDK 會自動建立 RemotePlayerRemotePlayerController 的執行個體。如前文程式碼研究室所示,您可以分別建立 cast.framework.RemotePlayercast.framework.RemotePlayerController 的執行個體,即可存取這些例項。

接著,我們需要建立 MediaInfo 物件,讓 SDK 處理並傳入要求,在接收器上載入目前選取的影片。在 setupRemotePlayer 中加入下列程式碼,即可執行此操作:

/**
 * Set the PlayerHandler target to use the remote player
 */
CastPlayer.prototype.setupRemotePlayer = function () {
    //...

    playerTarget.load = function (mediaIndex) {
        console.log('Loading...' + this.mediaContents[mediaIndex]['title']);
        var mediaInfo = new chrome.cast.media.MediaInfo(
            this.mediaContents[mediaIndex]['sources'][0], 'video/mp4');

        mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
        mediaInfo.metadata.metadataType = chrome.cast.media.MetadataType.GENERIC;
        mediaInfo.metadata.title = this.mediaContents[mediaIndex]['title'];
        mediaInfo.metadata.images = [
            {'url': MEDIA_SOURCE_ROOT + this.mediaContents[mediaIndex]['thumb']}];

        var request = new chrome.cast.media.LoadRequest(mediaInfo);
        castSession.loadMedia(request).then(
            this.playerHandler.loaded.bind(this.playerHandler),
            function (errorCode) {
                this.playerState = PLAYER_STATE.ERROR;
                console.log('Remote media load error: ' +
                    CastPlayer.getErrorMessage(errorCode));
            }.bind(this));
    }.bind(this);

    //...
};

現在,新增要在本機和遠端播放之間切換的方法:

/**
 * This is a method for switching between the local and remote
 * players. If the local player is selected, setupLocalPlayer()
 * is run. If there is a cast device connected we run
 * setupRemotePlayer().
 */
CastPlayer.prototype.switchPlayer = function() {
    this.stopProgressTimer();
    this.resetVolumeSlider();
    this.playerHandler.stop();
    this.playerState = PLAYER_STATE.IDLE;
    if (cast && cast.framework) {
        if (this.remotePlayer.isConnected) {
            this.setupRemotePlayer();
            return;
        }
    }
    this.setupLocalPlayer();
};

最後,請新增一個方法來處理任何 Cast 錯誤訊息:

/**
 * Makes human-readable message from chrome.cast.Error
 * @param {chrome.cast.Error} error
 * @return {string} error message
 */
CastPlayer.getErrorMessage = function(error) {
  switch (error.code) {
    case chrome.cast.ErrorCode.API_NOT_INITIALIZED:
      return 'The API is not initialized.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.CANCEL:
      return 'The operation was canceled by the user' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.CHANNEL_ERROR:
      return 'A channel to the receiver is not available.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.EXTENSION_MISSING:
      return 'The Cast extension is not available.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.INVALID_PARAMETER:
      return 'The parameters to the operation were not valid.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.RECEIVER_UNAVAILABLE:
      return 'No receiver was compatible with the session request.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.SESSION_ERROR:
      return 'A session could not be created, or a session was invalid.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.TIMEOUT:
      return 'The operation timed out.' +
        (error.description ? ' :' + error.description : '');
  }
};

現在請執行應用程式。連上投放裝置,然後開始播放影片。接收端應會顯示影片正在播放。

7. 新增 Cast Connect 支援

Cast Connect 程式庫可讓現有的發送端應用程式透過 Cast 通訊協定與 Android TV 應用程式進行通訊。Cast Connect 是以 Cast 基礎架構為基礎,並將 Android TV 應用程式做為接收器使用。

依附元件

  • Chrome 瀏覽器 M87 以上版本

設定與 Android 接收器相容的

如要啟動 Android TV 應用程式 (也稱為 Android 接收器),我們需要將 CastOptions 物件中的 androidReceiverCompatible 標記設為 true。

將下列程式碼新增至 initializeCastPlayer 函式中的 CastVideos.js

var options = {};
...
options.androidReceiverCompatible = true;

cast.framework.CastContext.getInstance().setOptions(options);

設定啟動憑證

您可以在傳送者端指定 CredentialsData 來代表會議參與者。credentials 是一個可由使用者定義的字串,只要您的 ATV 應用程式能夠解讀即可。CredentialsData 只會在啟動或加入時傳遞至 Android TV 應用程式。如果你在連線時再次設定 Wi-Fi,系統就不會將這部裝置傳送到 Android TV 應用程式。

設定啟動選項之後,每當您設定推出憑證 CredentialsData 後,就必須隨時定義發布憑證,才能設定。

將下列程式碼新增至 initializeCastPlayer 函式下的 CastVideos.js 類別中:

cast.framework.CastContext.getInstance().setOptions(options);
...
let credentialsData = new chrome.cast.CredentialsData("{\"userId\": \"abc\"}");
cast.framework.CastContext.getInstance().setLaunchCredentialsData(credentialsData);
...

設定載入要求上的憑證

如果 Web Receiver 應用程式和 Android TV 應用程式處理 credentials 的方式不同,您可能需要為每個應用程式定義專屬憑證。為解決此問題,請在 setupRemotePlayer 函式的 playerTarget.load 底下的 CastVideos.js 中新增下列程式碼:

...
var request = new chrome.cast.media.LoadRequest(mediaInfo);
request.credentials = 'user-credentials';
request.atvCredentials = 'atv-user-credentials';
...

視傳送者所投放的接收端應用程式而定,SDK 現在會自動處理目前工作階段要使用的憑證。

正在測試 Cast Connect

在 Chromecast (支援 Google TV) 上安裝 Android TV APK 的步驟:

  1. 找出 Android TV 裝置的 IP 位址。通常可以在「設定」>「網路和網際網路」>「(裝置連上的網路名稱)」底下找到這個 ID。畫面右側會顯示詳細資料和裝置這個網路 IP。
  2. 使用終端機透過 ADB 使用裝置的 IP 位址連線至該裝置:
$ adb connect <device_ip_address>:5555
  1. 在終端機視窗中,前往頂層資料夾,以取得您在本程式碼研究室開始時下載的程式碼研究室範例。例如:
$ cd Desktop/chrome_codelab_src
  1. 請執行下列指令,將這個資料夾中的 .apk 檔案安裝至 Android TV:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. 現在,你應該可以在 Android TV 裝置的「您的應用程式」選單中,透過「投放影片」名稱找到應用程式。
  2. 執行更新後的網路傳送者程式碼,然後使用投放圖示或在 Chrome 瀏覽器的下拉式選單中選取「Cast..」,透過 Android TV 裝置建立投放工作階段。系統現在應該會在 Android 接收器上啟動 Android TV 應用程式,並讓您使用 Android TV 遙控器控製播放。

8. 恭喜

您已瞭解如何在 Chrome 網頁應用程式上使用 Cast SDK 小工具啟用影片應用程式。

詳情請參閱「Web Sender」(網路傳送者) 開發人員指南。