2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
はじめに
みなさん、こんにちは。
KDDIウェブコミュニケーションズの Twilio 事業部エバンジェリストの高橋です。
本記事は、以下の記事の内容をリメイクして、バーチャル背景を追加したものです。
上記記事は、Twilio を初めて触る方などを対象としており、管理コンソール上のエディタを使って WebRTC Go を理解する目的で書かれています。
一方、Twilio をある程度理解されているエンジニアの方にとっては、ローカル環境で開発をしたいというニーズも多いかと思われます。そこで本記事では、WebRTC Go のアプリケーションをローカル環境で構築する方法について説明したいと思います。
準備
環境
本記事を実施するにあたって、以下の環境が必要です。
- Twilio アカウント : こちらから作成しましょう(トライアルアカウントでも OK です)
- Twilio CLI のセットアップ : こちらの記事を参考にしてください
- Twilio CLI Serverless plugins : こちらの記事を参考にしてください
- VSCodeなどのエディタ
Twilio 側の準備
サブアカウントの作成
すでに作業用のサブディレクトリがある場合は、そちらで作業をしていただいても問題ありません。
その場合は、作業ディレクトリのAccountSidを控えておいてください。
- Twilio 管理コンソールにログインします。
- Dashboard > 設定 > サブアカウント を選択します。
- 赤い+アイコンを押します。
- わかりやすい名前欄に、「WebRTC Go CLI」と入力して、Createボタンを押します。
- アカウント名の一覧に今作成した WebRTC Go CLI が表示されるので、そちらをクリックしてサブアカウントに移動します。
- 表示されたサブアカウントのアカウントSIDと、AUTHTOKEN(View を押すと表示されます)を控えておきます。
API キーの作成
- サブアカウントの Dashboard > 設定 > API キー を選択します。
- 赤い新しい API キーを作成するボタンを押します。
- わかりやすい名前欄に、「Video」と入力して、キータイプは「Standard」を選択し、API キーを作成するボタンを押します。
新コンソールでは、API Keyの作成は画面上部のAccountメニューから行うようになります。
また、リージョンの指定ができるようになりますが、USを選択してください。
- 生成された SID(これが API Key になります)と、SECRET を控えておきます。
SECRET はこの画面でしか確認できませんのでご注意ください。
- 完了しましたにチェックを入れて、終了ボタンを押します。
ソースコードの準備
この記事のソースコードは以下に準備してあります( virtual-background ブランチ)。
https://github.com/mobilebiz/twilio-video-handson-webrtc-go/tree/virtual-background
$ git clone -b virtual-background https://github.com/mobilebiz/twilio-video-handson-webrtc-go.git
$ cd twilio-video-handson-webrtc-go
$ npm install
環境変数の設定
ダウンロードしたフォルダの、.env.sample
を .env
にコピーしてご自分の環境に併せて編集します。
$ cp .env.sample .env
ACCOUNT_SID=AC から始まる文字列(先程控えた作業アカウントのものと一致していることを確認)
AUTH_TOKEN=(設定しなくてOKです)
# Variables for function ".env"
# ---
TWILIO_VIDEO_KEY=上記、ACCOUNT_SIDと同じ SK から始まる文字列
TWILIO_VIDEO_SECRET=上記、AUTH_TOKENと同じ、API SECRET
ローカル環境での実行
ここまでの状態で、ローカル環境での実行環境が整いましたので、npm start
コマンドを使って早速起動してみましょう。
$ npm start
> twilio-video-handson-webrtc-go@1.0.0 start
> twilio-run --env
│ WARNING Different Node.js Version Found
│
│ You are currently running Node.js 12.8.0 on this local machine. The production environment for Twilio Serverless currently supports versions 10.x.
│
│ When you deploy to Twilio Serverless, you may encounter differences between local development and production.
│
│ For a more accurate local development experience, please switch your Node.js version.
│ A tool like nvm (https://github.com/creationix/nvm) can help.
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ │
│ Twilio functions available: │
│ └── /video-token | http://localhost:3000/video-token │
│ │
│ Twilio assets available: │
│ ├── /video.html | http://localhost:3000/video.html │
│ ├── /video.js | http://localhost:3000/video.js │
│ ├── /images/background.jpeg | http://localhost:3000/images/background.jpeg │
│ ├── /twilio-video-processors-assets/selfie_segmentation_landscape.tflite | http://localhost:3000/twilio-video-processors-assets/selfie_segmentation_landscape.tflite │
│ ├── /twilio-video-processors-assets/tflite-1-0-0.wasm | http://localhost:3000/twilio-video-processors-assets/tflite-1-0-0.wasm │
│ ├── /twilio-video-processors-assets/tflite-simd-1-0-0.js | http://localhost:3000/twilio-video-processors-assets/tflite-simd-1-0-0.js │
│ ├── /twilio-video-processors-assets/tflite-1-0-0.js | http://localhost:3000/twilio-video-processors-assets/tflite-1-0-0.js │
│ ├── /twilio-video-processors-assets/tflite-simd-1-0-0.wasm | http://localhost:3000/twilio-video-processors-assets/tflite-simd-1-0-0.wasm │
│ ├── /twilio-video-processors-assets/twilio-video-processors.js | http://localhost:3000/twilio-video-processors-assets/twilio-video-processors.js │
│ └── /twilio-video-processors-assets/twilio-video-processors.min.js | http://localhost:3000/twilio-video-processors-assets/twilio-video-processors.min.js │
│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
無事に起動が確認できたら、早速ブラウザ上で以下のURLを起動しましょう。
※初回は、カメラとマイクのアクセス許可が出るかもしれませんが、もし出た場合は許可してください。
入室ボタンを押してルームに入ります。
ターミナル画面には、以下のような HTML ファイル、 JS ファイルへのアクセスと、アクセストークン取得用 Function である video-token?roomName=VideoRoom
が表示されます。
200 GET /video.html │ Response Type text/html; charset=UTF-8
200 GET /video.js?version=1.01 │ Response Type application/javascript; charset=UTF-8
200 GET /twilio-video-processors-assets/twilio-video-processors.js │ Response Type application/javascript; charset=UTF-8
200 GET /images/background.jpeg │ Response Type image/jpeg
200 GET /twilio-video-processors-assets/tflite-simd-1-0-0.js │ Response Type application/javascript; charset=UTF-8
200 GET /twilio-video-processors-assets/selfie_segmentation_landscape.tflite │ Response Type application/octet-stream
200 GET /twilio-video-processors-assets/tflite-simd-1-0-0.wasm │ Response Type application/wasm
ローカルサーバーを停止するには、Ctrl+C
を押下します。
コードの解説
バーチャル背景は少し難解なので、ここでコード解説をします。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Twilio Video</title>
</head>
<body>
<div id="room-controls">
<video id="myStream" autoplay muted="true"></video>
<button id="button-join">入室</button>
<button id="button-leave" disabled>退室</button>
</div>
</body>
<script src="./twilio-video-processors-assets/twilio-video-processors.js"></script>
<script src="//media.twiliocdn.com/sdk/js/video/releases/2.21.1/twilio-video.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="./video.js?version=1.01"></script>
</html>
下から5行目、twilio-video-processors.js
は、CDNで一般公開されていないので、今回はAssestsから読み込むようにしています。
次の行のtwilio-video
のSDKは、バージョンが古いと動作しないので、最新版(この記事執筆時は、2.21.1)を読み込むようにしてください。
(async () => {
'use strict';
const TWILIO_DOMAIN = location.host; // 現在のURL
const ROOM_NAME = 'VideoRoom'; // 部屋の名前
const Video = Twilio.Video; // Twilio Video JS SDK
const VideoProcessors = Twilio.VideoProcessors; // Twilio Video Processors
const BACKGROUND_IMAGE = './images/background.jpeg'; // 背景画像
let videoRoom;
let localTracks, virtualBackground;
const img = new Image();
// プレビュー画面の表示
localTracks = await Video.createLocalTracks();
const localVideo = document.getElementById('myStream');
localTracks.forEach((track) => {
if (track.kind === 'video' || track.kind === 'audio') {
track.attach(localVideo);
}
});
// バーチャル背景を設定
img.onload = () => {
virtualBackground = new VideoProcessors.VirtualBackgroundProcessor({
assetsPath: `${document.location.protocol}//${TWILIO_DOMAIN}/twilio-video-processors-assets/`,
backgroundImage: img,
});
virtualBackground.loadModel().then(async () => {
if (localTracks) {
const videoTrack = localTracks.filter(
(track) => track.kind === 'video',
);
if (!videoTrack[0].processor) {
videoTrack[0].addProcessor(virtualBackground);
}
}
});
};
img.src = BACKGROUND_IMAGE;
// ボタンの準備
const btnJoinRoom = document.getElementById('button-join');
const btnLeaveRoom = document.getElementById('button-leave');
// 入室ボタンが押されたときの処理
btnJoinRoom.onclick = () => {
// アクセストークンを取得
axios
.get(
`${document.location.protocol}//${TWILIO_DOMAIN}/video-token?roomName=${ROOM_NAME}`,
)
.then(async (body) => {
const token = body.data.token;
console.log(`Token got. ${token}`); // 本番環境ではコメントアウトしましょう
connectRoom(token); // ルームに接続
});
};
// 退出ボタンが押されたときの処理
btnLeaveRoom.onclick = () => {
// 部屋から退室
videoRoom.disconnect();
console.log(`Disconnected to Room ${videoRoom.name}`);
btnJoinRoom.disabled = false;
btnLeaveRoom.disabled = true;
};
// ルームに接続
const connectRoom = (token) => {
// 部屋に入室
Video.connect(token, { name: ROOM_NAME, tracks: localTracks })
.then((room) => {
console.log(`Connected to Room ${room.name}`);
videoRoom = room;
// すでに入室している参加者を表示
room.participants.forEach(participantConnected);
// 誰かが入室してきたときの処理
room.on('participantConnected', participantConnected);
// 誰かが退室したときの処理
room.on('participantDisconnected', participantDisconnected);
// 自分が退室したときの処理
room.once('disconnected', (error) =>
room.participants.forEach(participantDisconnected),
);
btnJoinRoom.disabled = true;
btnLeaveRoom.disabled = false;
})
.catch((err) => console.error(err));
};
// 他の参加者が入室したとき
const participantConnected = (participant) => {
console.log(`Participant ${participant.identity} connected'`);
// 参加者を表示する
const div = document.createElement('div');
div.id = participant.sid;
// 参加者のトラック(映像、音声など)を処理
participant.tracks.forEach((publication) => {
if (publication.isSubscribed) {
trackSubscribed(div, publication.track);
}
});
// 参加者の映像が届いたとき
participant.on('trackSubscribed', (track) => trackSubscribed(div, track));
// 参加者の映像が切れたとき
participant.on('trackUnsubscribed', trackUnsubscribed);
document.body.appendChild(div);
};
// 他の参加者が退室したとき
const participantDisconnected = (participant) => {
console.log(`Participant ${participant.identity} disconnected.`);
// 他の参加者の画面を削除する
document.getElementById(participant.sid).remove();
};
// トラックの購読
const trackSubscribed = (div, track) => {
// トラックをアタッチする
div.appendChild(track.attach());
};
// トラックの非購読
const trackUnsubscribed = (track) => {
// トラックのデタッチ
track.detach().forEach((element) => element.remove());
};
})();
表示したい画像ファイルは、コードの上の方で指定します。
const BACKGROUND_IMAGE = './images/background.jpeg'; // 背景画像
画像のソースを外部サーバーに指定した場合、外部サーバーがクロスサイトスクリプティングチェックをしていると、画像が取得できません。その場合は、
img.src = '外部サイトの画像'
img.crossorigin = 'anonymous';
のように指定ができます。ただし、不正なコードを実行してしまう可能性もあるので、利用するときは気をつけてください。
バーチャル背景を表示させるためには、Twilio のlocalVideoTrack.addProcessor()
が使えなくてはいけないので、今回はプレビュー画面の表示にnavigator.mediaDevices.getUserMedia()
を利用せず、以下のようにVideo.createLocalTracks()
を使ってローカルトラックを取得します。
Video.createLocalTracks()
では、ビデオやオーディオ以外にも、データトラックなども一括で取得できるので、念の為にビデオとオーディオトラックだけをDOMにアタッチしています。
localTracks = await Video.createLocalTracks();
const localVideo = document.getElementById('myStream');
localTracks.forEach((track) => {
if (track.kind === 'video' || track.kind === 'audio') {
track.attach(localVideo);
}
});
バーチャル背景の設定するには、VideoProcessors.VirtualBackgroundProcessor()
を利用してプロセッサを作る必要があります。
プロセッサというのは、画像などを加工するための仕組みで、適用したいトラックに追加して利用します。
ちなみに、バーチャル背景に画像ではなく背景ぼかしを使いたい場合は、VideoProcessors.GaussianBlurBackgroundProcessor()
を利用します。
assetsPath
には、バーチャル背景を表示させるための機械学習モデルを指定します。今回、このモデルはAssetsフォルダのtwilio-video-processors-assets
に格納されていて、これが外部から参照できる必要があります。
学習モデルが適用できたら、次にモデルを利用できるようにします(loadModel()
)。ここは場合によっては少し時間がかかることもあります。
ロードが完了したら、最後にローカルビデオトラックにaddProcessor()
することによって、以後このローカルビデオトラックにプロセッサによる加工がされるようになります。
なお、今回は実装には入れていませんが、バーチャル背景を解除したい場合は、removeProcessor()
を利用します。
img.onload = () => {
virtualBackground = new VideoProcessors.VirtualBackgroundProcessor({
assetsPath: `${document.location.protocol}//${TWILIO_DOMAIN}/twilio-video-processors-assets/`,
backgroundImage: img,
});
virtualBackground.loadModel().then(async () => {
if (localTracks) {
const videoTrack = localTracks.filter(
(track) => track.kind === 'video',
);
if (!videoTrack[0].processor) {
videoTrack[0].addProcessor(virtualBackground);
}
}
});
};
img.src = BACKGROUND_IMAGE;
以上がバーチャル背景のコード詳細となります。
サーバーにデプロイ
ローカル環境でのテストが終わりましたので、いよいよサーバーにデプロイします。
Twilio CLI の設定
このステップは、すでにプロファイルが作成されている場合は不要です。
まずは、Twilio CLI の Profile を設定します。
twilio profiles:create
コマンドを使って新しく Profile を作成してください。
$ twilio profiles:create
You can find your Account SID and Auth Token at https://www.twilio.com/console
» Your Auth Token will be used once to create an API Key for future CLI access to your Twilio Account or Subaccount, and then forgotten.
? The Account SID for your Twilio Account or Subaccount: <-- 控えておいたACから始まるアカウントSID を入力
? Your Twilio Auth Token for your Twilio Account or Subaccount: [hidden] <-- 控えておいたAuthTokenを入力
? Shorthand identifier for your profile: WebRTC Go CLI <-- プロファイル名として「WebRTC Go CLI」を入力
Created API Key SKxxxxxxxxxxxxxxxxxxxxxxxx and stored the secret in your keychain. See: https://www.twilio.com/console/runtime/api-keys/SKxxxxxxxxxxxxxxxxxxxxxxxx
twilio-cli configuration saved to "/Users/katsumi/.twilio-cli/config.json"
Saved WebRTC Go CLI.
このような感じで Profile が作成されますので、早速切り替えてみましょう。
$ twilio profiles:use 'WebRTC Go CLI'
set "WebRTC Go CLI" as active profile
twilio-cli configuration saved to "/Users/katsumi/.twilio-cli/config.json"
※プロファイル名にスペースを入れた場合、シングルコーテーションで囲う必要があります。
デプロイ
ではいよいよサーバーへのデプロイです。
twilio serverless:deploy
コマンドを使ってデプロイをしてみましょう。
$ twilio serverless:deploy
Deploying functions & assets to the Twilio Runtime
Account SK****************************
Token pQRS****************************
Service Name twilio-video-handson-webrtc-go
Environment dev
Root Directory /Users/katsumi/Documents/workspace/webRTC/twilio-video-handson-webrtc-go
Dependencies
Env Variables MAIN_ACCOUNT_SID, TWILIO_VIDEO_KEY, TWILIO_VIDEO_SECRET
✔ Serverless project successfully deployed
Deployment Details
Domain: twilio-video-handson-webrtc-go-XXXX-dev.twil.io
Service:
twilio-video-handson-webrtc-go (ZS****************************)
Environment:
dev (ZE****************************)
Build SID:
ZB****************************
View Live Logs:
Open the Twilio Console
Functions:
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/video-token
Assets:
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/images/background.jpeg
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/twilio-video-processors-assets/selfie_segmentation_landscape.tflite
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/twilio-video-processors-assets/tflite-1-0-0.js
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/twilio-video-processors-assets/tflite-1-0-0.wasm
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/twilio-video-processors-assets/tflite-simd-1-0-0.js
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/twilio-video-processors-assets/tflite-simd-1-0-0.wasm
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/twilio-video-processors-assets/twilio-video-processors.js
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/twilio-video-processors-assets/twilio-video-processors.min.js
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/video.html
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/video.js
上記のように、正常にデプロイが完了すると URL が払い出されます。
では、払い出された https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/video.html (XXXXはご自分の環境に合わせてください)にアクセスして、正常にビデオ会議ができることを確認します。
Assets の キャッシュについて
このように Twilio CLI を使うと、ローカルでテストしてからサーバーにデプロイができるので、開発がよりやりやすくなります。
ただ、今回のように、HTML ファイル内から同じ Assets 内にある JavaScript ファイルを参照しているようなケースですと、Assets にデプロイしたはずの JavaScript ファイルがうまく読み取られないことがあります。
これは、Assets のキャッシュが効いてしまうことにより発生します。
対応方法としては、video.html
側の JavaScript の指定部分に以下のようなversion
パラメータなどを付加し、それをデプロイの度に更新することで常に最新の JavaScript
が読まれるようになります。
<script src="./video.js?version=1.00"></script>
まとめ
今回の記事では Twilio Video のルームタイプについては触れませんでしたが、デフォルトのルームタイプを Go にしておくことで、無駄なコストをかけずにテストができます。
Twilio(トゥイリオ)とは
https://cloudapi.kddi-web.com
Twilioは音声通話、メッセージング(SMS/チャット)、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウドAPIサービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。