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

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

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

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

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

Actions Console の [Overview] タブ

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

Actions Console の [Invocation](呼び出し)パネル

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

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. [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. Exchange Session Description Protocol(SDP)メッセージ

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

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

  1. Firebase コンソールで [ビルド >Realtime Database >データベースを作成する

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

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

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

  1. [テストモードで開始] を選択し、[有効にする] をクリックします。Realtime Database が有効になっている場合は、CameraStream クライアント アプリから参照できる必要があります。
  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. Actions Console で、[Develop] > [Develop] を選択します。アカウントのリンク
  2. [OAuth クライアント情報] セクションで、対応するテキスト ボックスに次の情報を入力します。

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. [Local Video Resolution] パネルで、目的の動画を選択します。
  2. CameraStream クライアント アプリに、ウェブカメラとマイクへのアクセスを許可します。クライアントには、ウェブカメラからの動画フィードが表示されます。
  1. Google Home アプリで [追加 >Works with Google

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

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

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

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

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

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

WebRTC ストリームを開始する

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

[Account Linking トークンの値] テキスト ボックス

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

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

6. 完了

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

その他の情報