2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
はじめに
みなさん、こんにちは。
KDDIウェブコミュニケーションズの Twilio 事業部エバンジェリストの高橋です。
本記事は、以下の記事の内容の続編となります。ただし、以下の記事を実施していなくても大丈夫です。
Twilio WebRTCハンズオン(CSSで見栄えをよくする編)
上記記事では、CSS を使って映像のサイズを動的に変更したり、ボタンの見栄えを良くする方法について解説しました。今回は、より実践的な機能として、会議中にマイクとカメラのON/OFFを行う方法について說明します。
完成予想図は以下の通りです。
カメラとマイクのON/OFFをするためのアイコンと、それぞれが押された場合の処理を実装します。
すでに上記記事を実施している方は、前半部分については割愛することができますますので、ソースコードの準備から実施してください。
準備
環境
本記事を実施するにあたって、以下の環境が必要です。
- 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 はこの画面でしか確認できません)。
- 完了しましたにチェックを入れて、終了ボタンを押します。
ソースコードの準備
この記事のソースコードは以下に準備してあります( device-on-off ブランチ)。
https://github.com/mobilebiz/twilio-video-handson-webrtc-go/tree/device-on-off.git
https://github.com/mobilebiz/twilio-video-handson-webrtc-go/tree/data-track
$ git clone -b device-on-off 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を起動しましょう。
※初回は、カメラとマイクのアクセス許可が出るかもしれませんが、もし出た場合は許可してください。
入室ボタンを押してルームに入ります。
ブラウザのタブを複製して、2枚めのタブでも入室してみましょう。
今回は、この状態にカメラとマイクのON/OFF機能を実装していきます。
STEP 1. 切り替え用のボタンを表示させる
まずはカメラとマイクのON/OFFをするボタンを作ります。ボタンは CSS を使って自分のカメラ映像の上に表示させるようにしましょう。
CSS ファイルにボタンのスタイルを追加する
- assets フォルダ内にある、
video.css
をエディタで開きます。 -
:root
クラスを以下のように書き換えます。
/* rootクラス */
:root {
--video-width: 640px; /* デフォルトの横幅 */
--camera-icon: url('./images/camera_on.png'); /* カメラアイコン */
--mic-icon: url('./images/mic_on.png'); /* マイクアイコン */
}
4行目と5行目を追加して、アイコンファイルの場所を指定しています。
今回はアイコンファイルとして以下の4つを予め準備しておき、これらを動的に切り替える予定です。
-
images/camera_on.png
-
images/camera_off.png
-
images/mic_on.png
-
images/mic_off.png
-
さらに以下の行を CSS ファイルの最後に追加します。
/* カメラボタン */
.toggle-camera {
position: absolute;
bottom: 10px;
left: 8px;
border: 0;
padding: 0;
width: 30px;
height: 30px;
background: rgba(0, 0, 0, 0);
}
.camera-image {
background-image: var(--camera-icon);
background-size: cover;
display: inline-block;
margin: 0;
width: 30px;
height: 30px;
z-index: 10;
}
/* マイクボタン */
.toggle-mic {
position: absolute;
bottom: 10px;
left: 45px;
border: 0;
padding: 0;
width: 30px;
height: 30px;
background: rgba(0, 0, 0, 0);
}
.mic-image {
background-image: var(--mic-icon);
background-size: cover;
display: inline-block;
margin: 0;
width: 30px;
height: 30px;
z-index: 10;
}
これらは、それぞれのアイコンを表示するスタイルになります。
カメラアイコンが自分自身のカメラ映像の左下にくるように、bottom
を10px、left
を8pxに設定します。
マイクアイコンは、カメラアイコンの右側にくるように、left
を45pxにしています。
それぞれのアイコンに、position: absolute;
を指定することで、カメラ映像の上にアイコンが表示されますが、そのためにはカメラ映像側に position: relative;
の指定と、重ね順の指定である、z-index
が必要になる点に注意してください。
-
video.js
をエディタで開きます。 - // STEP 1. から // STEP 1. End までの間を以下のコードで置き換えます。
// STEP 1. カメラ・マイクのON/OFFボタンを表示
const root = document.documentElement;
// カメラボタン
const toggle_camera = document.createElement("button");
toggle_camera.id = "btn-toggle-camera";
toggle_camera.classList.add("toggle-camera");
const toggle_camera_image = document.createElement("span");
toggle_camera_image.classList.add("camera-image");
toggle_camera.append(toggle_camera_image);
document.getElementById('my-video').append(toggle_camera);
// カメラボタンが押されたときの処理
toggle_camera.addEventListener('click', () => {
if (videoRoom) { // ルームに接続済みの場合のみ
videoRoom.localParticipant.videoTracks.forEach(videoTrack =>
videoOn ? videoTrack.track.disable() : videoTrack.track.enable());
}
// フラグを反転
videoOn = !videoOn;
// ボタンのアイコンを切り替え
root.style.setProperty('--camera-icon', `url('./images/camera_${videoOn ? 'on' : 'off'}.png')`);
});
// マイクボタン
const toggle_mic = document.createElement("button");
toggle_mic.id = "btn-toggle-mic";
toggle_mic.classList.add("toggle-mic");
const toggle_mic_image = document.createElement("span");
toggle_mic_image.classList.add("mic-image");
toggle_mic.append(toggle_mic_image);
document.getElementById('my-video').append(toggle_mic);
// マイクボタンが押されたときの処理
toggle_mic.addEventListener('click', () => {
if (videoRoom) { // ルームに接続済みの場合のみ
videoRoom.localParticipant.audioTracks.forEach(audioTrack =>
audioOn ? audioTrack.track.disable() : audioTrack.track.enable());
}
// フラグを反転
audioOn = !audioOn;
// ボタンのアイコンを切り替え
root.style.setProperty('--mic-icon', `url('./images/mic_${audioOn ? 'on' : 'off'}.png')`);
});
// STEP 1. End
これらのコードでは、カメラとマイクのアイコンを動的に生成し、それらが押下されたときの処理を記述しています。
とくに重要なのが、ローカルストリームの映像と音声のトラックの取得方法です。
videoRoom
には、入室済みのルーム情報が入っていますので、そこから localParticipant
を使って自分自身の映像トラックと音声トラックを取得しています。そして、それらのトラックに対して有効(.enable()
)、無効(.disable()
)を指定することで自信からの配信トラックを制御します。
アイコンの切り替えについては、CSS の :room
クラスで指定したアイコンのデータの参照場所を動的に変えることで対応しています。
- 設定を変更します。
- ローカル環境でテストします。
自信のカメラ映像の左下にカメラとマイクのアイコンが表示されます。
それぞれをクリックすると、アイコンが切り替わることも確認しておきます。
STEP 2. 入室時の処理を記述する
STEP 1. では、ルームに入室後のストリームの切り替えを記述しましたが、ルームに入室する前にカメラとマイクを切り替えていたときの処理がないため、ここではその処理を追加します。具体的には、ルームに接続した直後に先程と同じ切り替えの処理を行えばよさそうです。
-
video.js
をエディタで開きます。 - // STEP 2. から // STEP 2. End までの間を以下のコードで置き換えます。
// STEP 2. 入室時のローカルデバイスON/OFF制御
room.localParticipant.videoTracks.forEach(videoTrack =>
videoOn ? videoTrack.track.enable() : videoTrack.track.disable());
room.localParticipant.audioTracks.forEach(audioTrack =>
audioOn ? audioTrack.track.enable() : audioTrack.track.disable());
// STEP 2. End
コード自体は STEP 1. で出てきたものと同じです。ちなみに、videoOn
と audioOn
という状態を保持するフラグについては、video.js
の先頭部分(12〜14行目)に定義されています。
- 変更を保存します。
- カメラをOFFにした状態で、ルームに入室します。
- この状態で別のタブで同じルームに入室します。
- 先に入っていたカメラの映像が送られてこないことを確認します。
STEP 3. リモート側のカメラ・マイクが切り替わったときの処理
では次に、他の参加者がカメラやマイクのON/OFFをしたときの処理を追加していきましょう。
とはいえ、そもそも他の参加者のトラック制御はその参加者側で行われるため、特別になにかする必要はなさそうです。ただ、カメラのON/OFFについては画面を見ればわかるのですが、マイクのON/OFFは視覚的に表示したほうがわかりやすいです。
そこで、リモート側のマイクがOFFになったら、相手のカメラ映像の上にマイクOFFのアイコンを表示させたいと思います。
-
video.js
をエディタで開きます。 - // STEP 3. から // STEP 3. End の間を以下のコードで書き換えます。
// STEP 3. リモート側のカメラ・マイクが切り替わったときの処理
const handleTrackChanged = ((track, participant) => {
const dom = document.getElementById(participant.sid);
// ミュートアイコンを表示
const muteIcon = (dom => {
const remote_mic = document.createElement('div');
remote_mic.id = 'remote-mic';
remote_mic.classList.add('remote-mic');
const mic = document.createElement('sp');
mic.classList.add('mic-image');
mic.style.backgroundImage = "url('./images/mic_off.png')";
remote_mic.append(mic);
dom.append(remote_mic);
});
if (track.kind === 'audio' && !track.isEnabled) {
// 参加中のメンバーがすでにマイクをOFFにしているのでミュートアイコンを表示
muteIcon(dom);
}
// 参加中のメンバーがマイクをOFFにしたときの処理
track.on('disabled', () => {
if (track.kind === 'audio') {
// ミュートアイコンを表示
muteIcon(dom);
}
});
// 参加中のメンバーがマイクをONにしたときの処理
track.on('enabled', () => {
if (track.kind === 'audio') {
// ミュートアイコンを削除
dom.childNodes.forEach(node => {
if (node.id === 'remote-mic') node.remove();
});
}
});
});
// STEP 3. End
ここでは、handleTrackChanged
という関数を定義し、引数として参加者情報(participant
)と参加者のトラック情報(track
)を受け取ります。
まず、すでに参加者がマイクをOFFにしているときはミュートアイコンを表示します。
さらに、参加者のトラックに対して、disabled
と enabled
の2つのイベントが発火したときの処理を記述しています。
ミュートアイコンを表示するときは、やはり CSS を使います。
-
video.css
エディタで開きます。 - 以下のコードを追加します。
/* リモートマイク*/
.remote-mic {
position: absolute;
bottom: 10px;
left: 8px;
border: 0;
padding: 0;
width: 30px;
height: 30px;
background: rgba(0, 0, 0, 0);
}
- 変更を保存します。
この時点では関数を作成しただけですので、まだテストは行いません。
STEP 4. トラックの状態を監視する
では最後に、他の参加者がカメラやマイクをON/OFFしたときに STEP 3. の関数をコールするようにコードを修正します。
-
video.js
をエディタで開きます。 - // STEP 4. から // STEP 4 End. までを以下のコードで置き換えます。
// STEP 4. トラックの状態を監視する
if (publication.isSubscribed) {
trackSubscribed(div, publication.track);
handleTrackChanged(publication.track, participant);
}
publication.on('subscribed', track => handleTrackChanged(track, participant));
// STEP 4. End
STEP 4. は、他の参加者が参加してきたときに、publication
の subscribed
イベントで取得できます。participant
のイベントではなく、publication
のイベントとして定義する必要がある点に注意してください。
自分がルームに入室したときにすでに参加者が入室していたときの処理も併せて記述します。
- 変更を保存します。
- ローカル環境でテストしてみます。
複数タブでテストして、カメラとマイクのON/OFFをテストしてみます。
以上でカメラ・マイクのON/OFFに関する作業は終了です。
サーバーにデプロイ
ローカル環境でのテストが終わりましたので、いよいよサーバーにデプロイします。
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はご自分の環境に合わせてください)にアクセスして、正常にビデオ会議ができることを確認します。
まとめ
今回は、トラックの制御について解説をしました。カメラやマイクだけでなく、画面共有などでもトラックを使いますので、トラックの扱い方については理解をしておくとよいでしょう。
Twilio(トゥイリオ)とは
https://cloudapi.kddi-web.com
Twilioは音声通話、メッセージング(SMS/チャット)、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウドAPIサービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。