1. 總覽
本程式碼研究室將說明如何修改現有的網頁應用程式應用程式,以在支援 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 瀏覽器。
- HTTPS 代管服務,例如 Firebase 代管或 ngrok。
- Google Cast 裝置,例如已設定連上網際網路的 Chromecast 或 Android TV。
- 具備 HDMI 輸入端的電視或螢幕。
- 你必須使用 Chromecast (支援 Google TV),才能測試 Cast Connect 整合作業,但對於程式碼研究室的其他部分,你可以選擇是否使用。如果您沒有裝置,可在本教學課程結尾略過「新增 Cast Connect 支援」步驟。
體驗
- 您必須具有網站開發背景知識。
- 你也需要事先瞭解看電視的知識 :)
您會如何使用這個教學課程?
針對建立網頁應用程式的經驗,您會給予什麼評價?
你對觀看電視的體驗有什麼評價?
2. 取得程式碼範例
您可以將所有程式碼範例下載至電腦...
然後將下載的 ZIP 檔案解壓縮。
3. 執行範例應用程式
首先,我們來看看完成的範例應用程式是什麼樣子。這款應用程式是基本的影片播放器。使用者可以從清單中選取影片,然後直接在裝置本機播放,或是將影片投放到 Google Cast 裝置。
如要使用完成的成果,必須先代管。
如果你沒有可用伺服器,可改用 Firebase 代管或 ngrok。
執行伺服器
設定好您選擇的服務後,請前往「app-done
」並啟動伺服器。
在瀏覽器中,造訪您代管樣本的 https 網址。
- 系統應會顯示影片應用程式。
- 按一下「投放」按鈕,然後選取你的 Google Cast 裝置。
- 選取影片,按一下播放按鈕。
- 影片隨即會在 Google Cast 裝置上開始播放。
按一下影片元素中的暫停按鈕,即可在接收器上暫停影片。按一下影片元素中的「播放」按鈕,即可繼續播放影片。
按一下「投放」按鈕,即可停止將內容投放到 Google Cast 裝置。
繼續下個步驟前,請先停止伺服器。
4. 準備 start 專案
系統會在您下載的啟動應用程式中新增 Google Cast 支援功能。以下是本程式碼研究室將使用的 Google Cast 術語:
- 寄件者應用程式是在行動裝置或筆記型電腦上執行
- 接收器應用程式是在 Google Cast 裝置上執行。
現在,您可以使用喜愛的文字編輯器,在範例專案上進行建構:
- 從下載的程式碼範例中選取
app-start
目錄。 - 使用伺服器執行應用程式,並探索 UI。
請注意,在執行本程式碼研究室的過程中,您將需要根據服務,在伺服器上重新託管範例。
應用程式設計
應用程式會從遠端網路伺服器擷取影片清單,並提供使用者瀏覽清單。使用者可以選取影片來查看詳細資料,也可以在行動裝置上播放影片。
應用程式包含一個在 index.html
中定義的主要檢視畫面,以及主要控制器 CastVideos.js.
index.html
這個 HTML 檔案宣告幾乎所有網路應用程式的使用者介面。
幾個檢視畫面區段都有 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
是設定所有 Cast 功能的方法。CastPlayer.prototype.switchPlayer
會切換本機玩家和遠端玩家的狀態。CastPlayer.prototype.setupLocalPlayer
和 CastPlayer.prototype.setupRemotePlayer
會初始化本機玩家和遠端玩家。
PlayerHandler
是負責管理媒體播放的類別。還有許多其他方法負責管理媒體和播放作業。
常見問題
5. 新增「投放」按鈕
支援 Cast 的應用程式會在影片元素中顯示「投放」按鈕。按一下「投放」按鈕,系統會顯示投放裝置清單,供使用者選取。如果使用者在傳送端裝置上播放內容,選取投放裝置就會在該投放裝置上開始或繼續播放內容。在投放工作階段期間,使用者隨時可以按一下「投放」按鈕,停止將應用程式投放到投放裝置。如同 Google Cast 設計檢查清單中所述,使用者必須能在應用程式的任何畫面中連線或中斷與投放裝置的連線。
設定
啟動專案需要的依附元件和設定與完整範例應用程式相同,但這次代管 app-start
的內容。
在瀏覽器中,前往您代管的樣本的 https
網址。
請注意,進行變更後,您將需要根據服務,重新託管伺服器中的範例。
初始化
Cast 架構具有全域單例模式物件 CastContext
,用來協調所有架構的活動。您必須在應用程式的生命週期的早期階段初始化此物件,通常是從指派給 window['__onGCastApiAvailable']
的回呼呼叫。系統會在 Cast SDK 載入後呼叫此物件,且可供使用。在這種情況下,CastContext
會在 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 上的選項,並初始化新的 RemotePlayer
和 RemotePlayerControllers
:
/**
* 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)
);
};
最後,我們需要建立 RemotePlayer
和 RemotePlayerController
的變數:
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 SDK 建立
MediaInfo
JSON
物件,以便建立媒體項目模型。 - 使用者連線至投放裝置,以啟動您的接收器應用程式。
- 將
MediaInfo
物件載入接收器並播放內容。 - 追蹤媒體狀態。
- 根據使用者互動,將播放指令傳送至接收器。
步驟 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 架構也會自動處理。
投放工作階段是由 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 中,RemotePlayer
和 RemotePlayerController
提供一組便利的 API,可用來管理接收器上的遠端媒體播放作業。針對支援媒體播放的 CastSession
,SDK 會自動建立 RemotePlayer
和 RemotePlayerController
的執行個體。如先前程式碼研究室所示,您可以分別建立 cast.framework.RemotePlayer
和 cast.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 應用程式當做接收器。
依附元件
- 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 應用程式。如果在連線後再次設定 PIN 碼,就不會傳送到 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.js
的 playerTarget.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:
- 找出 Android TV 裝置的 IP 位址。通常位於 [設定] > []網路和網際網路 >(裝置連線的網路名稱)。畫面右側會顯示詳細資料和裝置的 IP。
- 使用裝置的 IP 位址,以便透過終端機透過 ADB 連線:
$ adb connect <device_ip_address>:5555
- 在終端機視窗中,前往您在本程式碼研究室一開始下載的程式碼研究室範例的頂層資料夾。例如:
$ cd Desktop/chrome_codelab_src
- 請執行下列指令,將此資料夾中的 .apk 檔案安裝至 Android TV:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- 現在,您應該會在 Android TV 裝置上,透過「您的應用程式」選單的「投放影片」名稱看到應用程式。
- 請執行更新的網頁傳送端程式碼,然後透過 Android TV 裝置使用投放圖示,或在 Chrome 瀏覽器的下拉式選單中選取
Cast..
。這樣系統就會在 Android 接收器上啟動 Android TV 應用程式,讓您使用 Android TV 遙控器控製播放內容。
8. 恭喜
您已瞭解如何在 Chrome 網頁應用程式中使用 Cast SDK 小工具啟用投放影片應用程式。
詳情請參閱「網路寄件者」開發人員指南。