ウェブアプリをキャスト対応にする

1. 概要

Google Cast ロゴ

この Codelab では、Google Cast 対応デバイスでコンテンツをキャストするように既存のウェブ動画アプリを変更する方法について説明します。

Google Cast とは

Google Cast では、ユーザーはモバイル デバイスからテレビにコンテンツをキャストできます。ユーザーは自分のモバイル デバイスをリモコンとして使い、テレビでのメディア再生を行うことが可能です。

Google Cast SDK を使うと、アプリを拡張してテレビやサウンド システムを制御できます。Cast SDK では、Google Cast デザイン チェックリストに基づいて必要な UI コンポーネントを追加できます。

Google Cast デザイン チェックリストは、サポートされているすべてのプラットフォームにわたって、Cast ユーザー エクスペリエンスをシンプルで予測可能なものにするために使用します。

達成目標

この Codelab を完了すると、動画を Google Cast デバイスにキャストできる Chrome ウェブ動画アプリを作成できるようになります。

学習内容

  • サンプル動画アプリに Google Cast SDK を追加する方法
  • Google Cast デバイスを選択するキャスト アイコンを追加する方法
  • キャスト デバイスに接続してメディア レシーバーを起動する方法
  • 動画をキャストする方法
  • Cast Connect の統合方法

必要なもの

  • 最新の Google Chrome ブラウザ
  • HTTPS ホスティング サービス(Firebase Hostingngrok など)。
  • ChromecastAndroid TV など、インターネットにアクセスできる Google Cast デバイス。
  • HDMI 入力対応のテレビまたはモニター
  • Chromecast with Google TV は、Cast Connect の統合のテストに必要ですが、残りの Codelab ではオプションです。お持ちでない場合は、このチュートリアルの最後にある Cast Connect サポートの追加の手順をスキップしてください。

テスト

  • ウェブ開発に関する経験が必要です。
  • 一般的なテレビの視聴経験も必要です。

このチュートリアルの利用方法をお選びください。

通読するのみ 通読し、演習を行う

ウェブアプリの構築について評価してください。

初心者 中級者 上級者

テレビ視聴のご経験についてお答えください。

初心者 中級 上級

2. サンプルコードを取得する

サンプルコードはすべてパソコンにダウンロードできます。

ダウンロードした ZIP ファイルを解凍します。

3. サンプルアプリを実行する

Google Chrome のロゴ

まず、完成したサンプルアプリがどのようなものか見てみましょう。このアプリは基本的な動画プレーヤーです。ユーザーはリストから動画を選択し、デバイス上でローカルに再生するか、Google Cast デバイスにキャストできます。

完了したタスクを使用できるようにするには、ホスティングを行う必要があります。

使用できるサーバーがない場合は、Firebase Hosting または ngrok を使用できます。

サーバーを実行する

必要なサービスを設定したら、app-done に移動して、サーバーを起動します。

ブラウザで、ホストしたサンプルの https URL にアクセスします。

  1. 動画アプリが表示されます。
  2. キャスト アイコンをクリックし、使用する Google Cast デバイスを選択します。
  3. 動画を選択し、再生ボタンをクリックします。
  4. Google Cast デバイスで動画の再生が開始されます。

キャスト デバイスで再生中の動画の画像

動画要素の一時停止ボタンをクリックして、レシーバーで動画を一時停止します。動画要素の再生ボタンをクリックすると、動画が引き続き再生されます。

キャスト ボタンをクリックすると、Google Cast デバイスへのキャストが停止します。

次に進む前に、サーバーを停止します。

4. 開始プロジェクトを準備する

キャスト デバイスで再生中の動画の画像

ダウンロードした開始用アプリに Google Cast のサポートを追加する必要があります。この Codelab では、以下の Google Cast の用語を使用します。

  • 送信側アプリはモバイル デバイスやノートパソコンで動作します。
  • レシーバー アプリは Google Cast デバイスで動作します。

これで、お気に入りのテキスト エディタを使用してスターター プロジェクトを基盤にする準備が整いました。

  1. ダウンロードしたサンプルコードから フォルダ アイコンapp-start ディレクトリを選択します。
  2. サーバーを使用してアプリを実行し、UI を確認します。

この Codelab では、サービスによってはサーバーでサンプルを再ホストする必要があります。

アプリの設計

アプリはリモートのウェブサーバーから動画のリストを取得し、ユーザーがブラウジングできるようにリストを提供します。動画を選択すると、その詳細情報を表示したり、モバイル デバイスで動画をローカルに再生したりできます。

このアプリは、index.html とメイン コントローラである CastVideos.js. で定義された 1 つのメインビューで構成されます。

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 URL にアクセスします。

変更を加えた場合は、サービスによってはサーバーでサンプルを再ホストする必要があります。

初期化

キャスト フレームワークには、シングル サインオン オブジェクト CastContext があります。このオブジェクトは、フレームワークのすべてのアクティビティを調整します。このオブジェクトは、アプリのライフサイクルの早い段階で初期化する必要があります。通常は、window['__onGCastApiAvailable'] に割り当てられたコールバックから呼び出します。このコールバックは Cast SDK の読み込み後に呼び出され、使用できます。この場合、CastContextCastPlayer.prototype.initializeCastPlayer で呼び出されます。これは、前述のコールバックから呼び出されます。

CastContext を初期化するときに、options JSON オブジェクトを指定する必要があります。このクラスには、フレームワークの動作に影響するオプションが含まれています。最も重要なのはレシーバー アプリケーション ID です。この ID を使用すると、利用可能なキャスト デバイスのリストをフィルタリングして、指定したアプリを実行できるデバイスのみを表示し、キャスト セッションの開始時にレシーバー アプリを起動できます。

独自の Cast 対応アプリを開発する場合は、Cast デベロッパーとして登録してからアプリのアプリ ID を取得する必要があります。この Codelab では、サンプルのアプリ 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>

次に、index.html で呼び出したメソッドに対応する新しいメソッドを CastVideos.js に追加する必要があります。CastContext のオプションを設定し、新しい RemotePlayerRemotePlayerControllers を初期化する initializeCastPlayer という新しいメソッドを追加しましょう。

/**
 * 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 には、ID が castbutton"google-cast-launcher というキャスト ボタン コンポーネントが用意されています。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 はすでに完了しています。ステップ 3 は Cast SDK を使用して簡単に行うことができます。

サンプルアプリ CastPlayer は、switchPlayer メソッドですでにローカル再生とリモート再生を区別しています。

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

この Codelab では、すべてのサンプル プレーヤーにおけるロジックの仕組みを正確に理解する必要はありません。ただし、アプリのメディア プレーヤーを修正してローカル再生とリモート再生の両方を認識する必要があることに留意してください。

現時点では、ローカル プレーヤーはキャスト状態について何も認識していないため、常にローカルの再生状態になっています。キャスト フレームワークで発生する状態遷移に基づいて UI を更新する必要があります。たとえば、キャストを開始するときは、ローカルの再生を停止して、いくつかのコントロールを無効にする必要があります。同様に、このビュー コントローラを使用しているときにキャストを停止した場合は、ローカル再生に移行する必要があります。これに対応するには、キャスト フレームワークによって生成された各種イベントをリッスンする必要があります。

キャスト セッションの管理

キャスト フレームワークの場合、キャスト セッションは、デバイスへの接続、起動(または既存のセッションへの参加)、レシーバー アプリへの接続、メディア コントロール チャネルの初期化(該当する場合)のステップを組み合わせたものです。メディア コントロール チャンネルは、キャスト フレームワークがレシーバーからメディア再生関連のメッセージを送受信するための方法です。

ユーザーがキャスト アイコンからデバイスを選択するとキャスト セッションが自動的に開始され、接続を解除すると自動的に停止されます。ネットワークの問題によるレシーバー セッションへの再接続も、キャスト フレームワークによって自動的に処理されます。

キャスト セッションは 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 の場合、RemotePlayerRemotePlayerController のインスタンスが SDK によって自動的に作成されます。Codelab ですでに示したように、それぞれ cast.framework.RemotePlayercast.framework.RemotePlayerController のインスタンスを作成することでアクセスできます。

次に、SDK がリクエストを処理して渡すための MediaInfo オブジェクトを作成し、現在選択されている動画をレシーバーに読み込みます。そのために、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 ライブラリを使用すると、既存の送信側アプリがキャスト プロトコル経由で 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.loadCastVideos.js に次のコードを追加します。

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

送信者のキャスト先のレシーバ アプリによっては、現在のセッションで使用する認証情報が SDK で自動的に処理されるようになりました。

Cast Connect のテスト

Chromecast with Google TV に Android TV APK をインストールする手順:

  1. Android TV デバイスの IP アドレスを確認します。通常は、[設定] > [ネットワークとインターネット] >(デバイスが接続されているネットワーク名)で確認できます。右側に、詳細情報とネットワーク上のデバイスの IP が表示されます。
  2. お使いのデバイスの IP アドレスを使用して、ターミナルから ADB 経由でデバイスに接続します。
$ adb connect <device_ip_address>:5555
  1. ターミナル ウィンドウで、この Codelab の開始時にダウンロードした Codelab サンプルの最上位フォルダに移動します。次に例を示します。
$ 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 Receiver で Android TV アプリが起動され、Android TV リモコンを使用して再生を操作できるようになります。

8. 完了

Chrome ウェブアプリの Cast SDK ウィジェットを使用して動画アプリをキャスト対応にする方法を学びました。

詳しくは、ウェブ送信者デベロッパー ガイドをご覧ください。