使用 WebRTC 实现 CameraStream

1. 准备工作

CameraStream 特征属于能够将视频 Feed 流式传输到智能显示屏、Chromecast 设备和智能手机的设备。现在,CameraStream trait 中支持 WebRTC 协议,这意味着,您可以大幅缩短从摄像头设备到 Google Nest 显示设备的启动和流式传输延迟时间。

将摄像头设备流式传输到 Google Nest 显示设备

前提条件

学习内容

  • 如何部署智能家居云服务。
  • 如何将服务关联到 Google 助理。
  • 如何使用 WebRTC 协议流式传输到 Google Nest 显示屏设备。

所需条件

  • 网络浏览器,例如 Google Chrome
  • 安装了 Google Home 应用的 iOS 或 Android 设备。
  • Node.js 10.16 或更高版本。
  • Firebase 的 Blaze(随用随付)方案。
  • 支持全高清分辨率的内置或外接摄像头设备。
  • 一部 Google Nest 显示屏设备。

2. 开始

安装 Firebase CLI

借助 Firebase CLI,您可以在本地提供 Web 应用,并将其部署到 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 控制台,然后点击 New project
  2. 项目名称文本框中,输入项目名称,然后点击创建项目

Actions 控制台中的“New project”对话框

  1. What kind of Action do you want to build? 页面上,依次点击 Smart Home > Start building。该项目会在 Actions 控制台中打开。

Actions 控制台中的“Overview”标签页

  1. 点击开发 > 调用
  2. Display name 文本框中,输入 Action 的名称,然后点击 Save。待有设备可供设置后,Google Home 应用中就会显示此名称。在此 Codelab 中,我们输入了 WebRTC Codelab 作为显示名,但您可以使用其他名称。

Actions 控制台中的“调用”面板

  1. 点击操作
  2. Fulfillment 网址 文本框中,输入占位网址,例如 https://example.com

运行 CameraStream 客户端应用

此 Codelab 的源代码包含一个 WebRTC 客户端,该客户端用于在摄像头与 Google 智能家居显示设备之间建立、协商和管理 WebRTC 会话。

如需运行 CameraStream WebRTC 客户端应用,请执行以下操作之一:

  • 点击以下按钮将源代码下载到开发机器:

  • 克隆以下 GitHub 代码库:
$ git clone https://github.com/google-home/smarthome-camerastream-webrtc.git

该代码包含以下目录:

  • camerastream-start 目录,其中包含您在构建时所依据的起始代码。
  • camerastream-done 目录,其中包含完成后的 Codelab 的解决方案代码。

camerastream-start 目录包含以下子目录:

  • public 子目录,其中包含用于轻松控制和监控摄像头设备状态的前端界面。
  • 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.jspackage.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 控制台中,依次点击构建 > Realtime Database > 创建数据库

Firebase 控制台中的“Realtime Database”页面

  1. 在“Realtime Database 位置”下拉菜单中,选择要托管数据库的适当位置。

“设置数据库”对话框中的“Realtime Database 位置”下拉菜单

  1. 选择以测试模式开始,然后点击启用。启用 Realtime Database 后,您需要能够从 CameraStream 客户端应用引用该数据库。
  1. 在 Firebase 控制台中,依次选择 513f2be95dcd7896.png 项目设置 > 项目设置 > e584a9026e2b407f.png将 Firebase 添加到您的 Web 应用,以启动设置工作流。
  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. 点击前往控制台以完成此流程。您会在项目设置页面上看到新创建的 Web 应用。

4. 创建 WebRTC 摄像头

现在,您已配置 Action,您的云服务需要处理以下 intent:

  • 当 Google 助理想要了解用户已连接哪些设备时发生的 SYNC intent。当用户关联账号时,系统会向您的服务发送此 intent。您应使用用户设备及其功能的 JSON 载荷进行响应。
  • 当 Google 助理想要代表用户控制设备时发生的 EXECUTE/QUERY intent。您应该在响应时提供 JSON 载荷,其中包含所请求的每台设备的执行状态。

在本部分中,您将更新之前部署的用于处理这些 intent 的函数。

更新 SYNC 响应

  1. 找到 functions/index.js 文件。它包含响应来自 Google 助理的请求的代码。
  2. 修改 SYNC intent 以返回设备的元数据和功能:

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 intent

EXECUTE intent 可处理用于更新设备状态的命令。响应会返回每个命令的状态(例如 SUCCESSERRORPENDING)以及新的设备状态。

  • 如需处理 EXECUTE intent,请修改 EXECUTE intent 以返回 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 网址添加到 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“信号”函数网址添加到 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

此命令将部署一个 Web 应用和多个 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 控制台中,依次选择 开发 > 账号关联
  2. OAuth 客户端信息部分的相应文本框中输入以下信息:

Client-ID

ABC123

客户端密钥

DEF456

授权网址

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

令牌网址

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

Actions 控制台中的账号关联页面

  1. 依次点击保存 > 测试

5. 测试虚拟 WebRTC 摄像头

  1. 前往您在部署 Firebase 项目时看到的 Hosting 网址。您会看到以下接口,即 CameraStream 客户端应用:

CameraStream 客户端应用接口

  1. Local Video Resolution 面板中,选择所需的视频。
  2. 授权 CameraStream 客户端应用访问您的摄像头和麦克风。客户端上出现了摄像头拍摄的视频画面。
  1. 在 Google Home 应用中,依次点按添加 > 与 Google 服务兼容

Google Home 应用中的“设置设备”页面

  1. 搜索您创建的 Action,然后选择它。

Google Home 应用中的智能家居 Action

  1. 请记下包含 5 个字符的唯一字母数字代码,因为稍后您会用到它。

具有唯一性的五位数字母数字代码

  1. 点按返回。WebRTC 摄像头已添加到 Google Home 应用中的结构。

启动 WebRTC 流

  1. 在 CameraStream 客户端应用的网页上,在帐号关联令牌值文本框中输入上一部分中的字母数字代码,然后点击提交

“帐号关联令牌值”文本框

  1. 如需在 Google 智能显示屏设备上启动 WebRTC 会话,请执行以下操作之一:
  • 说“Hey Google,流式传输 WebRTC 相机”。
  • 在 Google 智能显示屏设备上,依次点按家居控制 > 摄像头 > WebRTC 摄像头

在 Google 智能家居 CameraStream 客户端应用中,您可以看到“优惠 SPD 和应答 SDP”已成功生成并交换。通过 WebRTC 将摄像头拍摄的图片流式传输到 Google 智能显示屏设备。

6. 恭喜

恭喜!您已了解如何使用 WebRTC 协议从摄像头流式传输内容到 Google Nest 显示屏设备。

了解详情