支援網頁應用程式

1. 總覽

Google Cast 標誌

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

什麼是 Google Cast?

Google Cast 可讓使用者將內容從行動裝置投放到電視上。之後,使用者就能利用行動裝置做為電視媒體播放的遙控器。

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

我們提供了 Google Cast 設計檢查清單,讓您在所有支援平台上都能享有簡單且可預測的投放體驗。

我們要建構的是什麼?

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

課程內容

  • 如何將 Google Cast SDK 新增至影片樣本應用程式。
  • 如何新增 Google Cast 裝置的投放按鈕。
  • 如何連線至 Cast 裝置及啟動媒體接收器。
  • 如何投放影片。
  • 如何整合 Cast Connect

軟硬體需求

  • 最新的 Google Chrome 瀏覽器。
  • HTTPS 代管服務,例如 Firebase 託管ngrok
  • Google Cast 裝置,例如已設定網際網路連線的 ChromecastAndroid TV
  • 具備 HDMI 輸入端的電視或螢幕。
  • 你必須使用 Chromecast (支援 Google TV) 才能測試 Cast Connect 整合功能,但在程式碼研究室的其他部分則為選用功能。如果您還沒有 Chromecast 支援,請略過本教學課程結尾的「新增 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. 準備起始專案

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

我們需要針對支援下載的應用程式提供 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

這個指令碼可管理 Cast Video 網頁應用程式的所有邏輯。CastVideos.js 中定義的影片清單及其相關中繼資料已包含在名為 mediaJSON 的物件中。

以下幾個主要章節共同負責在本地及遠端管理和播放影片。整體而言,這是相當直觀的網路應用程式。

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

PlayerHandler 是負責管理媒體播放的類別。其他管理媒體和播放細節的方法也有很多種。

常見問題

5. 新增投放按鈕

支援 Cast 的應用程式

支援 Cast 的應用程式會在影片元素中顯示「投放」按鈕。按一下「投放」按鈕即可顯示可供使用者選取的 Cast 裝置清單。如果使用者在本機裝置上播放內容,選取投放裝置,或是在該投放裝置上繼續播放內容。在投放工作階段期間,使用者隨時可以按一下 [投放] 按鈕,停止將應用程式投放到 Cast 裝置。如 Google Cast 設計檢查清單所述,使用者必須能在應用程式的任何畫面中連線到或中斷與 Cast 裝置的連線。

設定

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

透過瀏覽器前往您所代管的範例的 https 網址。

請記住,在您變更設定時,您需要根據服務重新託管伺服器中的範例。

初始化

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

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

自行開發支援 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 裝置選項選單的 Cast 應用程式圖片

我們也會將範例應用程式延伸到遠端投放的影片。因此,我們必須監聽 Cast 架構產生的各種事件。

正在投放媒體

整體而言,如要在投放裝置上播放媒體,必須發生以下情況:

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

步驟 1 要對應至另一個物件;MediaInfo 是 Cast SDK 能夠理解的內容,mediaJSON 則是應用程式封裝的媒體項目封裝工具;我們很容易就能將 mediaJSON 對應至 MediaInfo。我們已經按照上一節的說明步驟 2 完成。步驟 3 可讓您輕鬆使用 Cast SDK。

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

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

在本程式碼研究室中,您不需要瞭解所有範例播放器邏輯的運作方式。但請務必瞭解,您的應用程式媒體播放器必須修改設定,以利偵測本機和遠端播放。

由於本機播放器目前還不知道投放狀態,因此該玩家永遠都處於本機播放狀態。我們必須根據 Cast 架構中發生的狀態轉換,更新使用者介面。舉例來說,如果我們開始投放內容,就必須停止本機播放並停用部分控制項。同理,如果我們在這個檢視控制器中停止投放,就必須轉換至本機播放。為解決這個問題,我們必須監聽 Cast 架構產生的各種事件。

投放工作階段管理

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 : '');
  }
};

接著,執行應用程式。連線到你的 Cast 裝置,然後開始播放影片。您應該會在接收器中看見播放影片。

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 應用程式。如果您在連線時再次設定裝置,系統就不會將卡片傳送到您的 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 位址。通常可在 [設定] > [網路和網際網路] > (您的裝置已連線的網路名稱) 下存取。畫面右側會顯示網路的詳細資訊和裝置的 IP。
  2. 使用裝置的 IP 位址,透過終端機透過 ADB 連線至裝置:
$ 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. 執行更新的網路寄件者程式碼,並使用投放圖示在 Android TV 裝置上建立投放工作階段,或從 Chrome 瀏覽器的下拉式選單中選取 Cast..。接著,系統會啟動 Android 接收器的 Android TV 應用程式,讓你使用 Android TV 遙控器控製播放。

8. 恭喜

您現已瞭解如何在 Chrome 網路應用程式中使用 Cast SDK 小工具,以啟用 Cast 影片應用程式。

詳情請參閱網路寄件者開發人員指南。