WebRTC で CameraStream を実装する

1. 始める前に

CameraStream トレイトは、スマートディスプレイ、Chromecast デバイス、スマートフォンに動画フィードをストリーミングできるデバイスに使用します。WebRTC プロトコルが CameraStream トレイト内でサポートされるようになりました。これにより、カメラ デバイスから Google Nest ディスプレイ デバイスへの起動時間とストリーミング レイテンシを大幅に短縮できます。

Google Nest ディスプレイ デバイスにストリーミングしているカメラ デバイス

前提条件

学習内容

  • スマートホーム クラウド サービスをデプロイする方法。
  • サービスを Google アシスタントに接続する方法。
  • WebRTC プロトコルを使用して Google Nest ディスプレイ デバイスにストリーミングする方法。

必要なもの

  • ウェブブラウザ(Google Chrome など)
  • Google Home アプリがインストールされている iOS または Android デバイス。
  • Node.js バージョン 10.16 以降。
  • Firebase の Blaze(従量課金制)プラン。
  • フル HD 解像度に対応できる内蔵または外付けのウェブカメラ デバイス。
  • Google Nest ディスプレイ デバイス

2. 始める

Firebase CLI をインストールする

Firebase CLI を使用すると、ウェブアプリをローカルで提供し、Firebase Hosting にデプロイできます。

Firebase CLI をインストールする手順は次のとおりです。

  1. ターミナルで Firebase CLI をダウンロードしてインストールします。
$ npm install -g firebase-tools
  1. CLI が正しくインストールされていることを確認します。
$ firebase --version
  1. Google アカウントで Firebase CLI を承認します。
$ firebase login

プロジェクトを作成する

  1. Google Home Developer Console に移動します。
  2. [プロジェクトを作成] をクリックし、プロジェクトの名前を入力して [プロジェクトを作成] をクリックします。

プロジェクトに名前を付ける

CameraStream クライアント アプリを実行する

この Codelab のソースコードには、ウェブカメラと Google スマートホーム ディスプレイ デバイス間の WebRTC セッションを確立、ネゴシエート、管理する WebRTC クライアントが含まれています。

CameraStream WebRTC クライアント アプリを実行するには、次のいずれかを行います。

  • 次のボタンをクリックして、ソースコードを開発マシンにダウンロードします。

  • この GitHub リポジトリのクローンを作成します。
    $ git clone https://github.com/google-home/smarthome-camerastream-webrtc.git
    

コードには次のディレクトリが含まれています。

  • camerastream-start ディレクトリ。ビルドのベースとなるスターター コードが含まれています。
  • camerastream-done ディレクトリ。完成した Codelab の解答コードが含まれています。

camerastream-start ディレクトリには、次のサブディレクトリが含まれています。

  • public サブディレクトリ。カメラデバイスの状態を簡単に制御、モニタリングするためのフロントエンド UI が含まれています。
  • functions サブディレクトリ。Cloud Functions for Firebase と Realtime Database を使用してカメラを管理する、実装が完了したクラウド サービスが含まれています。

スターター コードには、コードを追加または変更する必要がある場所を示す TODO コメントが含まれています。次の例をご覧ください。

// TODO: Implement full SYNC response.

Firebase プロジェクトを作成する

  1. Firebase に移動します。
  2. [プロジェクトを作成] をクリックし、プロジェクト名を入力します。
  3. 同意チェックボックスをオンにして、[続行] をクリックします。同意チェックボックスがない場合は、この手順をスキップできます。
    Firebase プロジェクトを作成する
  4. Firebase プロジェクトが作成されたら、プロジェクト ID を確認します。[プロジェクトの概要] に移動し、設定アイコン > [プロジェクトの設定] をクリックします。
    プロジェクト設定を開く
  5. プロジェクトが [全般] タブに表示されます。
    プロジェクトの一般設定

Firebase への接続

  1. camerastream-start ディレクトリに移動し、Actions プロジェクトに Firebase CLI を設定します。
$ cd camerastream-start
$ firebase use <firebase-project-id>
  1. camerastream-start ディレクトリで functions フォルダに移動し、必要な依存関係をすべてインストールします。
$ cd functions
$ npm install
  1. 次のメッセージが表示された場合は、無視してください。この警告は、古い依存関係が原因で発生します。詳しくは、GitHub でこの問題をご覧ください。
found 5 high severity vulnerabilities
  run `npm audit fix` to fix them, or `npm audit` for details
  1. Firebase プロジェクトを初期化します。
$ firebase init
  1. [Functions] と [Hosting] を選択します。これにより、プロジェクトに必要な API と機能が初期化されます。
? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. 
❯◯ Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance
 ◯ Firestore: Deploy rules and create indexes for Firestore
 ◉ Functions: Configure a Cloud Functions directory and its files
 ◉ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
 ◯ Hosting: Set up GitHub Action deploys
 ◯ Storage: Configure a security rules file for Cloud Storage
 ◯ Extensions: Set up an empty Extensions manifest
  1. デフォルト ファイルを使用して Cloud Functions を構成し、プロジェクト サンプル内の既存の index.js ファイルと package.json ファイルを上書きしないようにします。
? Would you like to initialize a new codebase, or overwrite an existing one?
Overwrite

? What language would you like to use to write Cloud Functions? 
JavaScript

? File functions/package.json already exists. Overwrite? 
No

? File functions/index.js already exists. Overwrite? 
No

? Do you want to install dependencies with npm now? 
Yes
  1. プロジェクト コードの public ディレクトリで Hosting を構成し、既存の index.html ファイルを使用します。
? What do you want to use as your public directory? 
public

? Configure as a single-page app (rewrite all urls to /index.html)? 
Yes

? Set up automatic builds and deploys with GitHub?
No

? File public/index.html already exists. Overwrite?
 No

3. セッション記述プロトコル(SDP)メッセージを交換します。

SDP メッセージの交換は、WebRTC ストリームを確立するうえで重要なステップです。SDP は、マルチメディア セッションの特性を表すテキストベースのプロトコルです。WebRTC では、使用されるコーデック、参加者の IP アドレス、メディア転送に使用されるポートなど、ピアツーピア接続のパラメータをネゴシエートするために使用されます。

Realtime Database をホストとして使用して、ウェブカメラとスマートホームの CameraStream クライアント アプリ間で SDP メッセージを交換する手順は次のとおりです。

  1. Firebase コンソールで、[Build] > [Realtime Database] > [Create database] をクリックします。

Firebase コンソールの [Realtime Database] ページ

  1. [Realtime Database のロケーション] プルダウン メニューで、データベースをホストする適切なロケーションを選択します。

[データベースをセットアップ] ダイアログの [Realtime Database のロケーション] プルダウン メニュー

  1. [テストモードで開始] を選択し、[有効にする] をクリックします。Realtime Database を有効にすると、CameraStream クライアント アプリから参照できる必要があります。
  1. Firebase コンソールで、513f2be95dcd7896.png [プロジェクトの設定] > [プロジェクトの設定] > e584a9026e2b407f.png [ウェブアプリに Firebase を追加] を選択し、設定ワークフローを開始します。
  2. すでに Firebase プロジェクトにアプリを追加している場合は、[アプリを追加] をクリックするとプラットフォームのオプションが表示されます。
  3. アプリのニックネーム(My web app など)を入力し、[アプリを登録] をクリックします。
  4. [Firebase SDK の追加] セクションで、[ <script> タグを使用する] を選択します。
  5. firebasebaseConfig オブジェクトの値をコピーして、camaerastream-start/public/webrtc_generator.js ファイルに貼り付けます。
const firebaseConfig = {
  apiKey: "XXXXX",
  authDomain: "XXXXX",
  projectId: "XXXXX",
  storageBucket: "XXXXX",
  messagingSenderId: "XXXXX",
  appId: "XXXXX",
  measurementId: "XXXXX"
};
  1. [コンソールに進む] をクリックして手順を完了します。新しく作成されたウェブアプリが [プロジェクト設定] ページに表示されます。

4. WebRTC カメラを作成する

アクションを構成したので、クラウド サービスでは、以下のインテントを処理する必要があります。

  • SYNC インテント。ユーザーがリンク済みのデバイスについてアシスタントが問い合わせたときに発生します。これは、ユーザーがアカウントをリンクしたときにサービスに送信されます。ユーザーのデバイスとその機能を格納した JSON ペイロードで応答する必要があります。
  • EXECUTE/QUERY インテント。アシスタントがユーザーに代わってデバイスを制御するときに発生します。リクエストされた各デバイスの実行状態を格納した JSON ペイロードで応答する必要があります。

このセクションでは、すでにデプロイした関数を、これらのインテントを処理できるように更新します。

SYNC レスポンスを更新する

  1. functions/index.js ファイルに移動します。アシスタントからのリクエストに応答するコードが含まれています。
  2. SYNC インテントを編集して、デバイスのメタデータと機能を返します。

index.js

app.onSync((body) => {
return {
  requestId: body.requestId,
  payload: {
    agentUserId: USER_ID,
    devices: [{
      id: 'camera',
      type: 'action.devices.types.CAMERA',
      traits: [
        'action.devices.traits.OnOff',
        'action.devices.traits.CameraStream',
      ],
      name: {
        defaultNames: ['My WebRTC Camera'],
        name: 'Camera',
        nicknames: ['Camera'],
      },
      deviceInfo: {
        manufacturer: 'Acme Co',
        model: 'acme-camera',
        hwVersion: '1.0',
        swVersion: '1.0.1',
      },
      willReportState: false,
      attributes: {
        cameraStreamSupportedProtocols:['webrtc'],
        cameraStreamNeedAuthToken: true, 
        cameraStreamSupportsPreview: true
      },
    }],
  },
};
});
  1. USER_ID はコードで定義されていません。const _ = require('underscore'); の下に以下を追加します。
// Hardcoded user ID
const USER_ID = '123';

EXECUTE インテントを処理する

EXECUTE インテントは、デバイスの状態を更新するコマンドを処理します。レスポンスが返すのは、各コマンドのステータス(SUCCESSERRORPENDING など)と更新後のデバイスの状態です。

EXECUTE インテントを処理するには、EXECUTE インテントを編集して、functions/index.js ファイル内の Firebase プロジェクトの signaling エンドポイントを返します。

index.js

app.onExecute(async (body,headers) => {
  var array = headers.authorization.split(' ');
  var snapshot = await firebaseRef.ref('/userId/'+array[1]).once('value');
  var offerGenLocation = snapshot.val().type;
  const {requestId} = body;

  var result = {
    status: 'SUCCESS',
    states: {
      cameraStreamProtocol: 'webrtc',
      cameraStreamSignalingUrl:'https://us-central1-<project-id>.cloudfunctions.net/signaling?token='+array[1], // TODO: Add Firebase hosting URL
      cameraStreamIceServers: '',
      cameraStreamOffer:'',
      cameraStreamAuthToken:'',
    },
    ids: [ 
      'camera'
    ],
  };
  
  return {
    requestId: requestId,
    payload: {
      commands: [result],
    },
  };
});

クロスオリジン リソース シェアリング(CORS)を処理する

POST メソッドを使用して SDP を送信するため CORS を処理するには、Firebase Hosting URL を functions/index.js ファイルの allowlist 配列に追加します。

index.js

'use strict';
.....

var allowList = ['https://www.gstatic.com','https://<firebase-project-id>.web.app']; //TODO Add Firebase hosting URL.

CORS の詳細については、クロスオリジン リソース シェアリング(CORS)をご覧ください。

ストリームの終了を処理する

WebRTC ストリームの終了を処理するには、Firebase の「シグナリング」関数の URL を public/webrtc_generator.js ファイルに追加します。

webrtc_generator.js

terminateButton.onclick = function(){
  console.log('Terminating Stream!!')
  var signalingURL = 'https://us-central1-<project-id>.cloudfunctions.net/signaling'; //TODO Add Firebase hosting URL
   var http = new XMLHttpRequest();

Firebase にデプロイする

Firebase にデプロイするには、Firebase CLI を使用して更新したクラウド フルフィルメントをデプロイします。

$ firebase deploy

このコマンドによって、ウェブアプリといくつかの Cloud Functions for Firebase がデプロイされます。

...

✔ Deploy complete!

Project Console: https://console.firebase.google.com/project/<project-id>/overview
Hosting URL: https://<project-id>.web.app

デベロッパー コンソール プロジェクトを設定する

  1. Developer Console に移動します。
  2. [プロジェクトを作成] をクリックし、プロジェクトの名前を入力して [プロジェクトを作成] をクリックします。

プロジェクトに名前を付ける

クラウド間インテグレーションを選択する

デベロッパー コンソールの [プロジェクトのホーム] で、[クラウド間] の [クラウド間統合を追加] を選択します。

クラウド間インテグレーションを追加する

  1. 統合名を入力し、[デバイスタイプ] で [カメラ] を選択します。この名前は、後でデバイスをセットアップするときに Google Home アプリに表示されます。この Codelab では、表示名として「WebRTC Codelab」と入力しましたが、別の名前を使用することもできます。

表示名を追加する

  1. [アプリのブランディング] で、アプリアイコンの png ファイルをアップロードします。サイズは 144 x 144 ピクセルで、名前は .png にします。

アプリアイコンを追加する

アカウントのリンクを有効にする

プロジェクトのデプロイ後にアカウント リンクを有効にする手順は次のとおりです。

  1. デベロッパー コンソールに移動して、プロジェクトを開きます。
  2. [Cloud-to-Cloud] セクションで、統合の横にある [開発] > [編集] をクリックします。
  3. [設定と構成] ページで [アカウントのリンク] セクションを見つけ、対応するテキスト ボックスに次の情報を入力します。

クライアント ID

ABC123

クライアント シークレット

DEF456

認証 URL

https://us-central1-
.cloudfunctions.net/fakeauth

トークンの URL

https://us-central1-
.cloudfunctions.net/faketoken

アカウントのリンク URL を更新する

  1. [保存] > [テスト] をクリックします。

5. 仮想 WebRTC カメラをテストする

  1. Firebase プロジェクトのデプロイ時に表示された Hosting の URL に移動します。次のインターフェース(CameraStream クライアント アプリ)が表示されます。

CameraStream クライアント アプリのインターフェース

  1. [ローカル動画の解像度] パネルで、目的の動画を選択します。
  2. ウェブカメラとマイクにアクセスする権限を CameraStream クライアント アプリに付与します。ウェブカメラからの動画フィードがクライアントに表示されます。
  1. Google Home アプリで、[追加] > [Works with Google] をタップします。

Google Home アプリの [デバイスをセットアップ] ページ

  1. 作成したアクションを検索して選択します。

Google Home アプリのスマートホーム アクション

  1. 後で必要になるため、一意の 5 文字の英数字コードをメモしておきます。

一意の 5 桁の英数字コード

  1. [戻る] をタップします。Google Home アプリで、WebRTC カメラがストラクチャに追加されます。

WebRTC ストリームを開始する

  1. CameraStream クライアント アプリのウェブページで、最後のセクションの英数字コードを [アカウント リンク トークンの値] テキスト ボックスに入力し、[送信] をクリックします。

[アカウントのリンク トークン値] テキスト ボックス

  1. Google スマートディスプレイ デバイスから WebRTC セッションを開始するには、次のいずれかを行います。
  • OK Google, WebRTC カメラをストリーミングして」と話しかけます。
  • Google スマートディスプレイ デバイスで、[ホーム コントロール] > [カメラ] > [WebRTC カメラ] をタップします。

Google スマートホームの CameraStream クライアント アプリで、Offer SPD と Answer SDP が正常に生成され、交換されたことを確認します。ウェブカメラからの画像は、WebRTC を使用して Google スマートディスプレイ デバイスにストリーミングされます。

6. 完了

これで、WebRTC プロトコルを使用してウェブカメラから Google Nest ディスプレイ デバイスにストリーミングする方法について学習しました。

その他の情報