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 の [新しいプロジェクト] ダイアログ

  1. [What kind of Action do you want to build?](アクションの種類を選んでください)で、[Smart home](スマートホーム)> [Start building](作成を開始)をクリックします。Actions Console でプロジェクトが開きます。

Actions Console の [概要] タブ

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

Actions Console の [Invocation] パネル

  1. [操作] をクリックします。
  2. [Fulfillment 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 ディレクトリに移動し、Firebase CLI に Actions プロジェクトを設定します。
$ 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.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
        },
      }],
    },
  };
});

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 の「signaling」関数の 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 で、[開発] > [アカウントのリンク] を選択します。
  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. [Save] > [Test] をクリックします。

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] をクリックします。

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

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

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

6. 完了

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

詳細