WebRTC로 CameraStream 구현

1. 시작하기 전에

CameraStream 트레잇은 스마트 디스플레이, Chromecast 기기, 스마트폰에 동영상 피드를 스트리밍할 수 있는 기기에 속합니다. 이제 WebRTC 프로토콜이 CameraStream 트레잇 내에서 지원되므로 카메라 기기에서 Google Nest 디스플레이 기기로의 시작 및 스트리밍 지연 시간을 크게 줄일 수 있습니다.

Google Nest 디스플레이 기기로 스트리밍하는 카메라 기기

기본 요건

학습할 내용

  • 스마트 홈 클라우드 서비스를 배포하는 방법
  • Google 어시스턴트에 서비스를 연결하는 방법
  • WebRTC 프로토콜을 사용하여 Google Nest 디스플레이 기기로 스트리밍하는 방법

필요한 항목

  • 웹브라우저(예: Chrome)
  • Google Home 앱이 설치된 iOS 또는 Android 기기
  • Node.js 버전 10.16 이상
  • Firebase용 Blaze (사용한 만큼만 지불) 요금제
  • 풀 HD 해상도를 지원할 수 있는 내장 또는 외장 웹캠 기기
  • Google Nest 디스플레이 기기

2. 시작하기

Firebase CLI 설치

Firebase CLI를 사용하면 로컬에서 웹 앱을 제공하고 Firebase 호스팅에 배포할 수 있습니다.

Firebase CLI를 설치하려면 다음 단계를 따르세요.

  1. 터미널에서 Firebase CLI를 다운로드하여 설치합니다.
$ npm install -g firebase-tools
  1. CLI가 올바르게 설치되었는지 확인합니다.
$ firebase --version
  1. Google 계정으로 Firebase CLI를 승인합니다.
$ firebase login

프로젝트 만들기

  1. Google Home Developer Console로 이동합니다.
  2. 프로젝트 만들기를 클릭하고 프로젝트 이름을 입력한 후 프로젝트 만들기를 클릭합니다.

프로젝트 이름

CameraStream 클라이언트 앱 실행

이 Codelab의 소스 코드에는 웹캠과 Google 스마트 홈 디스플레이 기기 간에 WebRTC 세션을 설정, 협상, 관리하는 WebRTC 클라이언트가 포함되어 있습니다.

CameraStream WebRTC 클라이언트 앱을 실행하려면 다음 중 하나를 실행합니다.

  • 다음 버튼을 클릭하여 소스 코드를 개발 머신에 다운로드합니다.

  • 이 GitHub 저장소를 클론합니다.
    $ git clone https://github.com/google-home/smarthome-camerastream-webrtc.git
    

코드에는 다음 디렉터리가 포함됩니다.

  • 빌드의 기반이 되는 시작 코드가 포함된 camerastream-start 디렉터리
  • 완료된 Codelab의 솔루션 코드가 포함된 camerastream-done 디렉터리

camerastream-start 디렉터리에는 다음 하위 디렉터리가 포함됩니다.

  • public 하위 디렉터리: 카메라 기기의 상태를 쉽게 제어하고 모니터링할 수 있는 프런트엔드 UI가 포함되어 있습니다.
  • functions 하위 디렉터리: Firebase용 Cloud Functions 및 실시간 데이터베이스로 카메라를 관리하는 완전히 구현된 클라우드 서비스가 포함되어 있습니다.

시작 코드에는 다음 예와 같이 코드를 추가하거나 변경해야 하는 위치를 나타내는 TODO 주석이 포함되어 있습니다.

// TODO: Implement full SYNC response.

Firebase 프로젝트 만들기

  1. Firebase로 이동합니다.
  2. 프로젝트 만들기를 클릭하고 프로젝트 이름을 입력합니다.
  3. 동의 체크박스를 선택하고 계속을 클릭합니다. 동의 체크박스가 없는 경우 이 단계를 건너뛰어도 됩니다.
    Firebase 프로젝트 생성
  4. Firebase 프로젝트가 생성되면 프로젝트 ID를 찾습니다. 프로젝트 개요로 이동하여 설정 아이콘 > 프로젝트 설정을 클릭합니다.
    프로젝트 설정 열기
  5. 프로젝트가 일반 탭에 표시됩니다.
    일반 프로젝트 설정

Firebase에 연결

  1. camerastream-start 디렉터리로 이동한 후 작업 프로젝트를 사용해 Firebase CLI를 설정합니다.
$ cd camerastream-start
$ firebase use <firebase-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. FunctionsHosting을 선택합니다. 이렇게 하면 프로젝트에 필요한 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 디렉터리로 호스팅을 구성하고 기존 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 주소, 미디어 전송에 사용되는 포트와 같은 P2P 연결의 매개변수를 협상하는 데 사용됩니다.

실시간 데이터베이스를 호스트로 사용하여 웹캠과 스마트 홈 CameraStream 클라이언트 앱 간에 SDP 메시지를 교환하려면 다음 단계를 따르세요.

  1. Firebase Console에서 빌드 > 실시간 데이터베이스 > 데이터베이스 만들기를 클릭합니다.

Firebase Console의 실시간 데이터베이스 페이지

  1. 실시간 데이터베이스 위치 드롭다운 메뉴에서 데이터베이스를 호스팅할 적절한 위치를 선택합니다.

데이터베이스 설정 대화상자의 실시간 데이터베이스 위치 드롭다운 메뉴

  1. 테스트 모드로 시작을 선택한 다음 사용 설정을 클릭합니다. 실시간 데이터베이스를 사용 설정하면 CameraStream 클라이언트 앱에서 실시간 데이터베이스를 참조할 수 있어야 합니다.
  1. Firebase Console에서 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. Console로 이동을 클릭하여 절차를 완료합니다. 프로젝트 설정 페이지에 새로 만든 웹 앱이 표시됩니다.

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
      },
    }],
  },
};
});
  1. USER_ID가 코드에 정의되지 않습니다. const _ = require('underscore'); 아래에 다음을 추가합니다.
// Hardcoded user ID
const USER_ID = '123';

EXECUTE 인텐트 처리

EXECUTE 인텐트는 기기 상태를 업데이트하는 명령어를 처리합니다. 응답은 각 명령어의 상태(예: SUCCESS, ERROR 또는 PENDING)와 새 기기 상태를 반환합니다.

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를 처리하려면 functions/index.js 파일의 allowlist 배열에 Firebase 호스팅 URL을 추가합니다.

index.js

'use strict';
.....

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

CORS에 대한 자세한 내용은 교차 출처 리소스 공유 (CORS)를 참고하세요.

스트림 종료 처리

WebRTC 스트림 종료를 처리하려면 public/webrtc_generator.js 파일에 Firebase '신호' 함수 URL을 추가합니다.

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

이 명령어는 웹 앱과 여러 Firebase용 Cloud Functions를 배포합니다.

...

✔ Deploy complete!

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

개발자 콘솔 프로젝트 구성

  1. Developer Console로 이동합니다.
  2. 프로젝트 만들기를 클릭하고 프로젝트 이름을 입력한 후 프로젝트 만들기를 클릭합니다.

프로젝트 이름

클라우드 간 통합 선택

Developer Console의 프로젝트 홈에서 클라우드 간클라우드 간 통합 추가를 선택합니다.

클라우드 간 통합 추가

  1. 통합 이름을 입력하고 기기 유형에서 카메라를 선택합니다. 이 이름은 나중에 설정할 기기가 있을 때 Google Home 앱에 표시됩니다. 이 Codelab에서는 WebRTC Codelab을 표시 이름으로 입력했지만 다른 이름을 사용할 수도 있습니다.

표시 이름 추가

  1. 앱 브랜딩에서 앱 아이콘의 png 파일을 업로드합니다. 크기는 144x144px이고 이름은 .png입니다.

앱 아이콘 추가

계정 연결 사용 설정

프로젝트가 배포된 후 계정 연결을 사용 설정하려면 다음 단계를 따르세요.

  1. Developer Console로 이동하여 프로젝트를 엽니다.
  2. 클라우드 간 섹션에서 통합 옆에 있는 개발 > 수정을 클릭합니다.
  3. 설정 및 구성 페이지에서 계정 연결 섹션을 찾아 해당 텍스트 상자에 다음 정보를 입력합니다.

클라이언트 ID

ABC123

클라이언트 비밀번호

DEF456

인증 URL

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

토큰 URL

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

계정 연결 URL 업데이트

  1. 저장 > 테스트를 클릭합니다.

5. 가상 WebRTC 카메라 테스트

  1. Firebase 프로젝트를 배포할 때 표시된 호스팅 URL로 이동합니다. CameraStream 클라이언트 앱인 다음 인터페이스가 표시됩니다.

CameraStream 클라이언트 앱 인터페이스

  1. 로컬 동영상 해상도 패널에서 원하는 동영상을 선택합니다.
  2. CameraStream 클라이언트 앱에 웹캠 및 마이크에 액세스할 수 있는 권한을 부여합니다. 웹캠의 동영상 피드가 클라이언트에 표시됩니다.
  1. Google Home 앱에서 추가 > Works with Google을 탭합니다.

Google Home 앱의 기기 설정 페이지

  1. 만든 작업을 검색한 다음 선택합니다.

Google Home 앱의 스마트 홈 작업

  1. 나중에 필요하므로 고유한 영숫자 코드(5자리)를 기록해 둡니다.

고유한 영숫자 5자리 코드

  1. 뒤로 돌아가기를 탭합니다. WebRTC 카메라가 Google Home 앱의 구조에 추가됩니다.

WebRTC 스트림 시작

  1. CameraStream 클라이언트 앱의 웹페이지에서 마지막 섹션의 영숫자 코드를 계정 연결 토큰 값 텍스트 상자에 입력한 다음 제출을 클릭합니다.

계정 연결 토큰 값 텍스트 상자

  1. Google 스마트 디스플레이 기기에서 WebRTC 세션을 시작하려면 다음 중 하나를 실행합니다.
  • "Hey Google, WebRTC 카메라 스트리밍해 줘"라고 말합니다.
  • Google 스마트 디스플레이 기기에서 홈 컨트롤 > 카메라 > WebRTC 카메라를 탭합니다.

Google 스마트 홈 CameraStream 클라이언트 앱에서 Offer SPD 및 Answer SDP가 생성되고 교환된 것을 확인할 수 있습니다. 웹캠의 이미지가 WebRTC를 통해 Google 스마트 디스플레이 기기로 스트리밍됩니다.

6. 축하합니다

축하합니다. WebRTC 프로토콜을 사용하여 웹캠에서 Google Nest 디스플레이 기기로 스트리밍하는 방법을 알아봤습니다.

자세히 알아보기