Cómo implementar CameraStream con WebRTC

1. Antes de comenzar

El atributo CameraStream pertenece a los dispositivos con la capacidad de transmitir feeds de video a pantallas inteligentes, dispositivos Chromecast y smartphones. El protocolo WebRTC ahora es compatible con el atributo CameraStream, lo que significa que puedes reducir en gran medida la latencia de inicio y transmisión de un dispositivo de cámara a un dispositivo de pantalla Google Nest.

Dispositivos de cámara que transmiten contenido a una pantalla Google Nest

Requisitos previos

Qué aprenderás

  • Cómo implementar un servicio en la nube para casa inteligente
  • Cómo conectar tu servicio a Asistente de Google
  • Cómo transmitir contenido a un dispositivo de pantalla Google Nest con el protocolo WebRTC

Requisitos

  • Un navegador web, como Google Chrome
  • Un dispositivo iOS o Android con la app de Google Home
  • Node.js versión 10.16 o posterior
  • Plan Blaze (pago por uso) para Firebase
  • Un dispositivo de cámara web integrado o externo que admita resolución Full HD
  • Un dispositivo de pantalla Google Nest

2. Comenzar

Instala Firebase CLI

Firebase CLI te permite publicar tus apps web a nivel local y, luego, implementarlas en Firebase Hosting.

Para instalar Firebase CLI, sigue estos pasos:

  1. En la terminal, descarga e instala Firebase CLI:
$ npm install -g firebase-tools
  1. Verifica que la CLI se haya instalado correctamente:
$ firebase --version
  1. Autoriza Firebase CLI con tu Cuenta de Google:
$ firebase login

Crea un proyecto

  1. Ve a la Developer Console de Google Home.
  2. Haz clic en Create Project, ingresa un nombre para el proyecto y haz clic en Create Project.

Asigna un nombre al proyecto

Ejecuta la app cliente de CameraStream

El código fuente de este codelab incluye un cliente WebRTC que establece, negocia y administra la sesión de WebRTC entre la cámara web y el dispositivo de pantalla de la casa inteligente de Google.

Para ejecutar la app cliente de WebRTC de CameraStream, haz una de las siguientes acciones:

  • Haz clic en el siguiente botón para descargar el código fuente en tu máquina de desarrollo:

Descargar código fuente

  • Clona este repositorio de GitHub:
    $ git clone https://github.com/google-home/smarthome-camerastream-webrtc.git
    

El código contiene los siguientes directorios:

  • El directorio camerastream-start, que contiene el código de partida en el que compilas
  • El directorio camerastream-done, que contiene el código de la solución del codelab finalizado

El directorio camerastream-start contiene los siguientes subdirectorios:

  • El subdirectorio public, que contiene una IU de frontend para controlar y supervisar fácilmente el estado del dispositivo de la cámara
  • El subdirectorio functions, que contiene un servicio de nube completamente implementado que administra la cámara con Cloud Functions para Firebase y Realtime Database

El código de partida contiene comentarios TODO que indican dónde debes agregar o cambiar código, como en el siguiente ejemplo:

// TODO: Implement full SYNC response.

Crea un proyecto de Firebase

  1. Ve a Firebase.
  2. Haz clic en Crear un proyecto y, luego, ingresa el nombre del proyecto.
  3. Marca la casilla de verificación del acuerdo y haz clic en Continuar. Si no hay una casilla de verificación de acuerdo, puedes omitir este paso.
    Crea un proyecto de Firebase
  4. Una vez que crees tu proyecto de Firebase, busca el ID del proyecto. Ve a Descripción general del proyecto y haz clic en el ícono de configuración > Configuración del proyecto.
    Cómo abrir la configuración del proyecto
  5. Tu proyecto aparecerá en la pestaña General.
    Configuración general del proyecto

Cómo conectarse a Firebase

  1. Navega al directorio camerastream-start y configura Firebase CLI con tu proyecto de Acciones:
$ cd camerastream-start
$ firebase use <firebase-project-id>
  1. En el directorio camerastream-start, navega a la carpeta functions y, luego, instala todas las dependencias necesarias:
$ cd functions
$ npm install
  1. Si ves el siguiente mensaje, ignóralo. Esta advertencia se debe a dependencias más antiguas. Para obtener más información, consulta este problema de GitHub.
found 5 high severity vulnerabilities
  run `npm audit fix` to fix them, or `npm audit` for details
  1. Inicializa un proyecto de Firebase:
$ firebase init
  1. Selecciona Functions y Hosting. De este modo, se inicializarán las APIs y las funciones necesarias para tu proyecto.
? 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. Configura Cloud Functions con los archivos predeterminados y asegúrate de no reemplazar los archivos index.js y package.json existentes en el ejemplo del proyecto:
? 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. Configura Hosting con el directorio public en el código del proyecto y usa el archivo index.html existente:
? 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. Intercambia mensajes del protocolo de descripción de sesión (SDP)

El intercambio de mensajes SDP es un paso importante en el establecimiento de una transmisión de WebRTC. El SDP es un protocolo basado en texto que describe las características de una sesión multimedia. Se usa en WebRTC para negociar los parámetros de una conexión punto a punto, como los códecs utilizados, las direcciones IP de los participantes y los puertos utilizados para el transporte de contenido multimedia.

Para usar Realtime Database como host para intercambiar mensajes SDP entre tu cámara web y la app cliente CameraStream de la casa inteligente, sigue estos pasos:

  1. En Firebase console, haz clic en Compilación > Base de datos en tiempo real > Crear base de datos.

La página de Realtime Database en Firebase console

  1. En el menú desplegable Ubicación de Realtime Database, selecciona una ubicación adecuada desde la que alojar tu base de datos.

Menú desplegable de ubicación de Realtime Database en el diálogo Configurar base de datos

  1. Selecciona Comenzar en modo de prueba y, luego, haz clic en Habilitar. Con Realtime Database habilitada, debes poder hacer referencia a ella desde la app cliente de CameraStream.
  1. En Firebase console, selecciona 513f2be95dcd7896.png Configuración del proyecto > Configuración del proyecto > e584a9026e2b407f.pngAgrega Firebase a tu app web para iniciar el flujo de trabajo de configuración.
  2. Si ya agregaste una app a tu proyecto de Firebase, haz clic en Agregar app para que se muestren las opciones de plataforma.
  3. Ingresa un sobrenombre para la app, como My web app, y haz clic en Registrar app.
  4. En la sección Agregar el SDK de Firebase, selecciona Usar la etiqueta <script>.
  5. Copia los valores del objeto firebasebaseConfig y, luego, pégalos en el archivo camaerastream-start/public/webrtc_generator.js.
const firebaseConfig = {
  apiKey: "XXXXX",
  authDomain: "XXXXX",
  projectId: "XXXXX",
  storageBucket: "XXXXX",
  messagingSenderId: "XXXXX",
  appId: "XXXXX",
  measurementId: "XXXXX"
};
  1. Haz clic en Ir a la consola para completar el proceso. Verás la app web recién creada en la página Configuración del proyecto.

4. Cómo crear una cámara WebRTC

Ahora que configuraste tu Acción, tu servicio en la nube debe controlar los siguientes intents:

  • Un intent SYNC que se produce cuando Asistente desea saber qué dispositivos conectó el usuario. Este se envía a tu servicio cuando el usuario vincula una cuenta. Deberías responder con una carga útil JSON de los dispositivos del usuario y sus capacidades.
  • Un intent EXECUTE/QUERY que se produce cuando el Asistente desea controlar un dispositivo en nombre de un usuario. Deberías responder con una carga útil JSON con el estado de ejecución de cada dispositivo solicitado.

En esta sección, actualizarás las funciones que implementaste antes para controlar estos intents.

Actualiza la respuesta de SYNC

  1. Navega al archivo functions/index.js. Contiene el código para responder las solicitudes del Asistente.
  2. Edita el intent SYNC para mostrar los metadatos y las capacidades del dispositivo:

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 no se define en el código. Agrega lo siguiente a const _ = require('underscore');:
// Hardcoded user ID
const USER_ID = '123';

Cómo controlar el intent EXECUTE

El intent EXECUTE controla los comandos para actualizar el estado del dispositivo. La respuesta muestra el estado de cada comando (por ejemplo, SUCCESS, ERROR o PENDING) y el estado del dispositivo nuevo.

Para controlar un intent EXECUTE, edita el intent EXECUTE para que devuelva el extremo signaling del proyecto de Firebase en el archivo functions/index.js:

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],
    },
  };
});

Controla el uso compartido de recursos entre dominios (CORS)

Para controlar el CORS debido al uso del método POST para enviar el SDP, agrega la URL de Firebase Hosting al array allowlist en el archivo functions/index.js:

index.js

'use strict';
.....

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

Para obtener más información sobre CORS, consulta Uso compartido de recursos entre dominios (CORS).

Controla la finalización de la transmisión

Para controlar la finalización de la transmisión de WebRTC, agrega la URL de la función de "indicadores" de Firebase al archivo 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();

Cómo implementar en Firebase

Para implementar en Firebase, implementa la entrega en la nube actualizada con Firebase CLI:

$ firebase deploy

Este comando implementa una app web y varias Cloud Functions para Firebase:

...

✔ Deploy complete!

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

Configura tu proyecto de Play Console

  1. Ve a Developer Console.
  2. Haz clic en Create Project, ingresa un nombre para el proyecto y haz clic en Create Project.

Asigna un nombre al proyecto

Selecciona la integración de nube a nube.

En Página principal del proyecto en la Consola de Play, selecciona Agregar integración de nube a nube en Nube a nube.

Agrega integración de nube a nube

  1. Ingresa un nombre de integración y selecciona Cámara en Tipo de dispositivo. Este nombre aparecerá en la app de Google Home más adelante, cuando haya un dispositivo para configurar. Para este codelab, ingresamos Codelab de WebRTC como nombre visible, pero puedes usar otro nombre.

Agrega un nombre visible

  1. En Desarrollo de la marca de la app, sube un archivo png para el ícono de la app, con un tamaño de 144 × 144 px y el nombre .png.

Agrega un ícono de app

Habilita la vinculación de cuentas

Para habilitar la vinculación de cuentas después de que se implemente tu proyecto, sigue estos pasos:

  1. Ve a Developers Console y abre el proyecto.
  2. En la sección De nube a nube, haz clic en Desarrollo > Editar junto a la integración.
  3. En la página Configuración, busca la sección Vinculación de cuentas y, luego, ingresa la siguiente información en los cuadros de texto correspondientes:

ID de cliente

ABC123

Secreto del cliente

DEF456

URL de autorización

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

URL del token

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

Actualiza las URLs de vinculación de cuentas

  1. Haz clic en Guardar > Probar.

5. Prueba la cámara virtual de WebRTC

  1. Navega a la URL de Hosting que viste cuando implementaste tu proyecto de Firebase. Verás la siguiente interfaz, que es la app cliente de CameraStream:

Interfaz de la app cliente de CameraStream

  1. En el panel Local Video Resolution, selecciona el video que quieras.
  2. Otorga permiso a la app cliente de CameraStream para que acceda a tu cámara web y micrófono. Aparecerá un feed de video de tu cámara web en el cliente.
  1. En la app de Google Home, presiona Agregar > Funciona con Google.

La página Configurar un dispositivo en la app de Google Home

  1. Busca la acción que creaste y selecciónala.

La Acción de casa inteligente en la app de Google Home

  1. Anota el código alfanumérico único de cinco caracteres, ya que lo necesitarás más adelante.

El código alfanumérico único de cinco dígitos

  1. Presiona Volver atrás. La cámara WebRTC se agregará a tu estructura en la app de Google Home.

Cómo iniciar una transmisión de WebRTC

  1. En la página web de la app cliente de CameraStream, ingresa el código alfanumérico de la última sección en el cuadro de texto Account linking token value y, luego, haz clic en Submit.

El cuadro de texto del valor del token de vinculación de cuentas

  1. Para iniciar una sesión de WebRTC desde tu dispositivo de pantalla inteligente de Google, haz lo siguiente:
  • Di "Hey Google, transmite la cámara WebRTC".
  • En tu dispositivo de pantalla inteligente de Google, presiona Control de la casa > Cámara > Cámara WebRTC.

En la app cliente de CameraStream de la casa inteligente de Google, verás que se generaron y se intercambiaron correctamente el SPD de oferta y el SDP de respuesta. La imagen de la cámara web se transmite a tu dispositivo de pantalla inteligente de Google con WebRTC.

6. Felicitaciones

¡Felicitaciones! Aprendiste a transmitir desde tu cámara web a un dispositivo de pantalla Google Nest con el protocolo WebRTC.

Más información