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

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 裝置並啟動媒體接收器。
  • 如何投放影片。
  • 如何整合 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

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

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

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

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

常見問題

5. 新增「投放」按鈕

支援 Cast 的應用程式圖片

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

設定

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

在瀏覽器中,前往您代管的樣本的 https 網址。

請注意,根據服務的不同,您需要在伺服器上重新代管範例。

初始化

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

初始化 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 的 Cast 按鈕元件,其 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 裝置上遠端播放影片。為此,我們需要監聽 Cast 架構產生的各種事件。

投放媒體

整體來說,如要在投放裝置上播放媒體,必須完成以下步驟:

  1. 使用 Cast SDK 建立 MediaInfo JSON 物件,模擬媒體項目。
  2. 使用者連線至 Cast 裝置,啟動接收器應用程式。
  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 架構中發生的狀態轉換,更新 UI。舉例來說,如果我們開始投放,就必須停止本機播放並停用部分控制項。同樣地,如果在進入這個檢視控制器時停止投放,我們也將需要轉換至本機播放。如要處理這項問題,我們需要監聽 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 : '');
  }
};

接著執行應用程式,連線至 Chromecast 裝置並開始播放影片。你應該會在接收端看到播放的影片。

7. 新增 Cast Connect 支援功能

Cast Connect 程式庫可讓現有的發送端應用程式透過 Cast 通訊協定與 Android TV 應用程式通訊。Cast Connect 建構於 Cast 基礎架構上,並將 Android TV 應用程式當做接收器。

依附元件

  • Chrome 瀏覽器 M87 以上版本

設定 Android 接收器相容性

如要啟動 Android TV 應用程式 (也稱為 Android Receiver),我們需要在 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 的方式不同,您可能需要為每個應用程式分別定義憑證。如要解決這個問題,請在 CastVideos.jsplayerTarget.load 函式中,將下列程式碼新增至 setupRemotePlayer 函式:

...
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 裝置的「Your Apps」選單中,看到名為「Cast Videos」的應用程式。
  2. 執行更新後的網頁傳送程式碼,然後使用投放圖示或在 Chrome 瀏覽器的下拉式選單中選取 Cast..,與 Android TV 裝置建立投放工作階段。這樣系統就會在 Android 接收器上啟動 Android TV 應用程式,讓您使用 Android TV 遙控器控製播放內容。

8. 恭喜

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

詳情請參閱 Web Sender 開發人員指南。