2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
はじめに
みなさん、こんにちは。
KDDIウェブコミュニケーションズの Twilio 事業部エバンジェリストの高橋です。
本記事は、以下の記事の内容の続編となります。ただし、以下の記事を実施していなくても大丈夫です。
Twilio WebRTCハンズオン(マイク・カメラ ON/OFF編)
上記記事では、会議中にマイクとカメラのON/OFFを行う方法について說明しました。本記事ではさらに、複数人で会議を行っているときに、話している人を検知してカメラ画像を強調する機能を実装していきたいと思います。
なお、本機能は Group ルームにのみ対応していますので、WebRTC GO や Peer-to-peer ルームでは利用できない点に注意してください。
完成予想図は以下の通りです。
上の画像にもあるように、現在話している人のカメラ映像に赤い枠が表示されるようになります。
すでに上記記事を実施している方は、前半部分については割愛することができますますので、ソースコードの準備から実施してください。
準備
環境
本記事を実施するにあたって、以下の環境が必要です。
- Twilio アカウント : こちらから作成しましょう(トライアルアカウントでも OK です)
- Twilio CLI のセットアップ : こちらの記事を参考にしてください
- Twilio CLI Serverless plugins : こちらの記事を参考にしてください
- VSCodeなどのエディタ
Twilio 側の準備
サブアカウントの作成
- Twilio 管理コンソールにログインします。
- Dashboard > 設定 > サブアカウント を選択します。
- 赤い+アイコンを押します。
- わかりやすい名前欄に、「WebRTC Go CLI」と入力して、Createボタンを押します。
- アカウント名の一覧に今作成した WebRTC Go CLI が表示されるので、そちらをクリックしてサブアカウントに移動します。
- 表示されたサブアカウントのアカウントSIDと、AUTHTOKEN(View を押すと表示されます)を控えておきます。
API キーの作成
- サブアカウントの Dashboard > 設定 > API キー を選択します。
- 赤い新しい API キーを作成するボタンを押します。
- わかりやすい名前欄に、「Video」と入力して、キータイプは「Standard」を選択し、API キーを作成するボタンを押します。
- 生成された SID(これが API Key になります)と、SECRET を控えておきます(SECRET はこの画面でしか確認できません)。
- 完了しましたにチェックを入れて、終了ボタンを押します。
ソースコードの準備
この記事のソースコードは以下に準備してあります( dominant ブランチ)。
https://github.com/mobilebiz/twilio-video-handson-webrtc-go/tree/dominant
https://github.com/mobilebiz/twilio-video-handson-webrtc-go/tree/data-track
$ git clone -b dominant 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=先程控えておいた API Key(SK から始まる文字列)
AUTH_TOKEN=先程控えておいた API Secret(本当の AuthToken ではないので注意)
# Variables for function ".env"
# ---
MAIN_ACCOUNT_SID=先程控えておいたアカウント SID (AC から始まる文字列)
TWILIO_VIDEO_KEY=上記、ACCOUNT_SIDと同じ SK から始まる文字列
TWILIO_VIDEO_SECRET=上記、AUTH_TOKENと同じ、API SECRET
本来は、最初の ACCOUNT_SID
とAUTH_TOKEN
にそれぞれ正規のアカウント SID と AUTH TOKEN を入れたいのですけど、Twilio CLI の関係で API Key と SECRET を入れています。
ローカル環境での実行
ここまでの状態で、ローカル環境での実行環境が整いましたので、npm start
コマンドを使って早速起動してみましょう。
✋🏼 ヒント
contents
フォルダに完成版の video.html
、video.js
、video.css
が用意されていますので、ハンズオンをやらずに結果を確かめたい場合は、これらのファイルをassets
フォルダにコピーしてから実行してください。
$ 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.js | http://localhost:3000/video.js │
│ ├── /video.html | http://localhost:3000/video.html │
│ ├── /video.css | http://localhost:3000/video.css │
│ ├── /images/camera_off.png | http://localhost:3000/images/camera_off.png │
│ ├── /images/camera_on.png | http://localhost:3000/images/camera_on.png │
│ ├── /images/mic_on.png | http://localhost:3000/images/mic_on.png │
│ └── /images/mic_off.png | http://localhost:3000/images/mic_off.png │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
無事に起動が確認できたら、早速ブラウザ上で以下のURLを起動しましょう。
※初回は、カメラとマイクのアクセス許可が出るかもしれませんが、もし出た場合は許可してください。
入室ボタンを押してルームに入ります。
今回は、この状態をベースに話者を強調させる実装を行っていきます。
STEP 1. ドミナントスピーカーを有効にする
今回は、Twilio が提供するドミナントスピーカー( Dominant Speaker )の API を使って話者が切り替わったことをイベントとして取得していきます。ドミナントスピーカーを有効にするためには、ルームに接続するときにオプションを指定する必要があります。
オプションを設定する
- assets フォルダ内にある、
video.js
をエディタで開きます。 - // STEP 1. から // STEP 1. End の間を以下のコードに書き換えます。
// STEP 1. ドミナントスピーカーを有効にする
dominantSpeaker: true,
// STEP 1. End
コードを見ていただくとわかるように、dominantSpeaker
オプションを true
にしてルームに接続することで、話者が切り替わったことがイベントとして取得できるようになります。
STEP 2. 話者の画面を強調させる
では次に、話者のカメラ映像の周りに赤い枠線を表示させる部分をつくります。枠線は、 CSS で実現させます。
CSS にスタイルを追加する
-
video.css
をエディタで開きます。 - 以下のコードを追加します。
/* ドミナントスピーカー */
.dominant-speaker {
outline: solid 5px #f0506e;
outline-offset: -5px;
}
枠線は outline
で表示させるようにし、outline-offset: -5px;
を指定することで、内側に枠を引くことでカメラ映像のレイアウトが崩れないようにしています。
話者の映像にスタイルを適用させる
-
video.js
をエディタで開きます。 - // STEP 2. から // STEP 2 End. までを以下のコードで置き換えます。
// STEP 2. ドミナントスピーカーを目立たせる
const dominantSpeaker = (participant => {
if (!participant) return;
console.log(`Dominant speaker changed. ${participant.sid}`);
// 映像を表示するエリアからDIVタグを検索
const videoZone = document.getElementById('video-zone');
videoZone.childNodes.forEach(divNode => {
if (divNode.nodeName === 'DIV') {
// さらにその下のVIDEOタグを検索
divNode.childNodes.forEach(node => {
if (node.nodeName === 'VIDEO') {
if (node.parentElement.id === participant.sid) {
// ドミナントスピーカーなので、スタイルを適用
node.classList.add('dominant-speaker');
} else {
// ドミナントスピーカーではないので、スタイルを削除
node.classList.remove('dominant-speaker');
}
}
});
}
});
});
// STEP 2. End
CSS で作成したスタイルは、<video> タグに適用させるための Function を定義しています。引数には、話者を特定するための participant
オブジェクトを指定しています。
スタイルを適用するために、現在のドキュメント内を検索し、引数として渡された participant.sid
と一致する id
を持った <video> タグが見つかったらスタイルを適用し、それ以外の <video> タグからはスタイルを削除しています。
これによって、現在の話者に対してスタイルが適用されるようになります。
STEP 3. ドミナントスピーカーを検出したときのイベントを定義する
STEP 1. で指定した dominantSpeaker
オプションを使ってルームに接続すると、ルームの dominantSpeakerChanged
イベントが発火するようになりますので、それを補足して STEP 2. で定義した Function を呼ぶようにします。
-
video.js
をエディタで開きます。 - // STEP 3. から // STEP 3. End の間を以下のコードで置き換えます。
// STEP 3. ドミナントスピーカーを検出したときの処理
room.on('dominantSpeakerChanged', participant => dominantSpeaker(participant));
// STEP 3. End
dominantSpeakerChanged
イベントが発火すると、引数として participant
を受け取れるので、それをそのまま先程作成した Function に渡しています。
ここまでの処理で、話者が切り替わったことをイベントとして、話者をハイライトさせることができます。
しかしこのままではあまりうまくありません。なぜなら、自分自身が話し始めたときには、 dominantSpeakerChanged
イベントが発火しないのです。そのため、最後に話していた人が強調されたままになってしまいます。
これを解決するためには、自分自身でマイクの音量を検出して、自分が話していることを知る必要があります。
STEP 4. 自身のマイクから発話状態を検知する
自分のPCのマイク音量を取得するには、AudioContext
から生成する AnalyserNode
を使って、リアルタイム時間領域/周波数領域分析を一定時間ごとに繰り返す必要があります。
-
video.js
をエディタで開きます。 - // STEP 4. から // STEP 4. End までの間を以下のコードで置き換えます。
// STEP 4. 自身のマイクから発話状態を検知する
const audioContext = new AudioContext();
const analyser = audioContext.createAnalyser();
const timeDomain = new Float32Array(analyser.frequencyBinCount);
const frequency = new Uint8Array(analyser.frequencyBinCount);
audioContext.createMediaStreamSource(stream).connect(analyser);
// 検出ループに入る
detectOwnSpeak(analyser, timeDomain, frequency);
// STEP 4. End
最初の5行で、ローカルメディアストリームに analyser
を接続しています。定期的な検知については、detectOunSpeak
という関数で行うようになっています。
STEP 5. 音声を検知し、カメラ映像を強調させる
では最後に、定期的に音声を検知しながら一定音量を超えた場合(話している)に、自身のカメラ映像を強調させたいと思います。話し終わったときは、普通のスタイルに戻します。
-
video.js
をエディタで開きます。 - // STEP 5. から // STEP 5. End の間を以下のコードで書き換えます。
// STEP 5. 検出ループ
const detectOwnSpeak = ((analyser, timeDomain, frequency) => {
setTimeout(() => {
// 音量の平均スコアを計算する
analyser.getFloatTimeDomainData(timeDomain);
analyser.getByteFrequencyData(frequency);
let score = frequency.reduce((p, x) => p + x, 0) / frequency.length;
// スコアが10を超えたらなにか話していると判断して、スタイルを適用させる
if (score > 10 && videoRoom && audioOn) dominantSpeaker({ sid: 'my-video'});
// スコアが10以下の場合は、自身のスタイルを削除
if (score <= 10) document.getElementById('myStream').classList.remove('dominant-speaker');
// ループ
detectOwnSpeak(analyser, timeDomain, frequency);
}, 100);
});
// STEP 5. End
検出時間内の音量平均を算出して、スコアが10を超えたら話し中と判断しています。マイクの性能などで常に話し中になってしまったり、逆に検知できないことが多い場合はここのしきい値を変更してください。
なお、この検知ロジックは、100msごとに実施しています。
以上で話者特定に関する作業は終了です。
サーバーにデプロイ
ローカル環境でのテストが終わりましたので、いよいよサーバーにデプロイします。
Twilio CLI の設定
※すでに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 --force
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-****-dev.twil.io
Service:
twilio-video-handson-webrtc-go (ZS****************************)
Environment:
dev (ZE****************************)
Build SID:
ZB****************************
View Live Logs:
https://www.twilio.com/console/assets/api/ZS****************************/environment/ZE****************************
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/camera_off.png
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/images/camera_on.png
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/images/mic_off.png
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/images/mic_on.png
https://twilio-video-handson-webrtc-go-XXXX-dev.twil.io/video.css
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はご自分の環境に合わせてください)にアクセスして、正常にビデオ会議ができることを確認します。
今回の機能については、1台のPCではテストができません。どなたかに協力してもらって、リモートから上記URLにアクセスしてもらうことでテストしてみてください。
まとめ
今回は、話者の検知について解説をしました。自身の音声レベルの検知方法などは、Twilio に限らず使える技術だと思うので、ぜひ参考にしていただけると幸いです。
Twilio(トゥイリオ)とは
https://cloudapi.kddi-web.com
Twilioは音声通話、メッセージング(SMS/チャット)、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウドAPIサービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。