WebRTC を使用して CameraStream を実装する

1. 始める前に

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

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

Prerequisites

学習内容

  • スマートホーム クラウド サービスをデプロイする方法。
  • サービスを 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

Actions プロジェクトを作成して構成する

  1. Actions Console に移動して、[New project](新しいプロジェクト)をクリックします。
  2. [プロジェクト名] テキスト ボックスにプロジェクトの名前を入力し、[プロジェクトを作成] をクリックします。

Actions Console の [New project] ダイアログ

  1. [What do you want to build?] ページで、[Smart home] > [Start Building] をクリックします。Actions Console でプロジェクトが開きます。

Actions Console の [概要] タブ

  1. [Develop](開発)> [Invocation](呼び出し)をクリックします。
  2. [表示名] テキスト ボックスにアクションの名前を入力し、[Save] をクリックします。この名前は、後でセットアップするデバイスがある場合に Google Home アプリに表示されます。この Codelab では表示名として「WebRTC Codelab」を使用しましたが、別の名前を使用することもできます。

Actions Console の呼び出しパネル

  1. [操作] をクリックします。
  2. [Fulfillment URL] テキスト ボックスに、https://example.com などのプレースホルダ URL を入力します。

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. camerastream-start ディレクトリに移動し、Actions プロジェクトで Firebase CLI を設定します。
$ cd camerastream-start
$ firebase use 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. [関数] と [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. Exchange Session Description Protocol(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 クライアント アプリから Realtime Database を参照できる必要があります。
  1. Firebase コンソールで、513f2be95dcd7896.png [プロジェクトの設定] > [プロジェクトの設定] > [e584a9026e2b407f.pngFirebase をウェブアプリに追加する] を選択し、設定ワークフローを起動します。
  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
        },
      }],
    },
  };
});

EXECUTE インテントを処理する

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

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

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';

const functions = require('firebase-functions');
const {smarthome} = require('actions-on-google');
const {google} = require('googleapis');
const util = require('util');
const admin = require('firebase-admin');

var allowList = ['https:www.gstatic.com','https://<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. アクション コンソールで、[開発] > [アカウントのリンク] を選択します。
  2. [OAuth client information] セクションで、対応するテキスト ボックスに次の情報を入力します。

Client-ID

ABC123

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

DEF456

認可 URL

https://us-central1-{project-id}.cloudfunctions.net/fakeauth

トークン URL

https://us-central1-{project-id}.cloudfunctions.net/faketoken

Actions Console のアカウントのリンクページ

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

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

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

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

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

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

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

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

  1. 後で必要になるため、一意の 5 文字の英数字のコードに注意してください。

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

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

WebRTC ストリームを開始する

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

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

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

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

6. 完了

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

その他の情報