支援網頁應用程式

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

1. 總覽

Google Cast 標誌

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

什麼是 Google Cast?

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

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

提供 Google Cast 設計檢查清單,以便在所有支援的平台上提供簡單且可預測的 Cast 使用者體驗。

我們要建構的是什麼?

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

課程內容

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

軟硬體需求

  • 最新的 Google Chrome 瀏覽器。
  • npm.
  • Google Cast 裝置,例如已設定網路連線的 ChromecastAndroid TV
  • 具備 HDMI 輸入端的電視或螢幕。
  • 必須使用 Chromecast (支援 Google TV) 才能測試 Cast Connect 整合作業,但在程式碼研究室的其餘部分則為選用功能。如果沒有電視,歡迎在本教學課程結束時略過新增 Cast Connect 支援步驟。

功能

  • 您必須先具備之前的網站開發知識。
  • 您也需要具備先前觀看電視節目的知識 :)

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

唯讀閱讀 閱讀並完成練習

你對建構網頁應用程式體驗的體驗如何?

新手 中級 專業知識

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

新手 中級 專業知識

2. 取得程式碼範例

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

將下載的 ZIP 檔案解壓縮。

3. 執行範例應用程式

Google Chrome 標誌

首先,請查看完成的範例應用程式外觀。這款應用程式是基本的影片播放器。使用者可以選取清單中的影片,然後在裝置上在裝置上播放影片,或將其投放到 Google Cast 裝置。

執行應用程式

如果您沒有可用的伺服器,請勿煩惱。您可以安裝 node.js、http-server 和 ngrok 節點模組。

npm install -g http-server
npm install -g ngrok

如果你使用 http-server,請前往主控台並執行以下操作:

cd app-done
http-server

畫面應如下所示:

Starting up http-server, serving ./
Available on:
  http://127.0.0.1:8080
  http://172.19.17.192:8080
Hit CTRL-C to stop the server

請注意使用的本機通訊埠,然後在新的終端機中執行下列步驟,以便使用 ngrok 透過 HTTPS 公開本機寄件者:(8080 應為 http-server 的通訊埠)

ngrok http 8080

這項操作會將 ngrok 通道設為本機 HTTP 伺服器,以指派給全球的 HTTPS 安全端點,供您在下一個步驟中使用 (https://116ec943.eu.ngrok.io):

ngrok by @inconshreveable                                                                                                                                                                                                                                     (Ctrl+C to quit)

Session Status         online
Version                2.2.4
Web Interface          http://127.0.0.1:8080
Forwarding             http://116ec943.eu.ngrok.io -> localhost:8080
Forwarding             https://116ec943.eu.ngrok.io -> localhost:8080

在程式碼研究室期間,您應讓 ngrokhttp-server 保持執行。您所做的本機變更會立即生效。

透過瀏覽器前往 ngrok 傳回的 https 網址。

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

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

按一下影片元素中的暫停按鈕,即可暫停接收接收器的影片。按一下影片元素中的播放按鈕,即可繼續播放影片。

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

我們需要停止伺服器,才能繼續進行。前往執行 http-server 的終端機,並終止下列程序:

CTRL-C

前往執行 ngrok 的終端機,並終止下列程序:

CTRL-C

4. 準備起始專案

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

我們需要針對你下載的啟動應用程式新增 Google Cast 支援。我們在本程式碼研究室中會使用以下 Google Cast 術語:

  • 寄件者應用程式位於行動裝置或筆記型電腦上執行
  • 接收端應用程式會在 Google Cast 裝置上執行。

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

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

請注意,當您逐步完成本程式碼研究室時,http-server 應挑選您進行的變更。如果發現不符合狀況,請嘗試終止並重新啟動 http-server

應用程式設計

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

這個應用程式包含一個主要檢視畫面,定義於 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 設計檢查清單所述,使用者必須能夠在應用程式的任一畫面中與 Cast 裝置連線或中斷連線,

設定

啟動專案所需的依附元件和設定,與完成範例應用程式的相同。

如果你使用 http-server,請前往主控台並執行以下操作:

cd app-start
http-server

畫面應如下所示:

Starting up http-server, serving ./
Available on:
  http://127.0.0.1:8080
  http://172.19.17.192:8080
Hit CTRL-C to stop the server

請注意使用的本機通訊埠,然後在新的終端機中執行下列步驟,以便使用 ngrok 透過 HTTPS 公開本機寄件者:(8080 應為 http-server 的通訊埠)

ngrok http 8080

這項操作會將 ngrok 通道設為本機 HTTP 伺服器,以指派給全球的 HTTPS 安全端點,供您在下一個步驟中使用 (https://116ec943.eu.ngrok.io):

ngrok by @inconshreveable                                                                                                                                                                                                                                     (Ctrl+C to quit)

Session Status         online
Version                2.2.4
Web Interface          http://127.0.0.1:8080
Forwarding             http://116ec943.eu.ngrok.io -> localhost:8080
Forwarding             https://116ec943.eu.ngrok.io -> localhost:8080

在程式碼研究室期間,您應讓 ngrokhttp-server 保持執行。您所做的本機變更會立即生效。

透過瀏覽器前往 ngrok 傳回的 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 架構自動處理。

投放工作階段是由 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();
};

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

/**
 * 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 應用程式可做為接收器使用。

依附元件

  • 搭載 M87 以上版本的 Chrome 瀏覽器

設定與 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 小工具,以啟用投放應用程式功能。

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