そのビデオ通話，そろそろ終わりません？

# はじめに
この記事は[「Twilioを使うためのコツ、TIPSなど、Twilioのことなら何でも共有しよう！」](https://qiita.com/advent-calendar/2021/twilio)の7日目の記事です．

このご時世，業務・プライベート関わらずオンラインでビデオ通話をする機会が多いと思います．
でも，「このビデオ通話そろそろ抜けたいなぁ」と思うことがあるんじゃないでしょうか．
例えば
- 眠たくなってきたオンライン飲み会
- 次の仕事に行きたいけど，延長している会議
- そろそろ休憩したい作業通話 etc...

そんなとき「そろそろ僕はこのへんで...」と伝えるの，すごくハードルが高いですよね．
それを解決するためにボタンを押すとそろそろ抜けたい..と匿名で伝えてくれるビデオ通話アプリを作成しました．


# 本題
こんな感じで終わりたい人がいることを全員に伝えたいです．
![スクリーンショット 2021-11-21 22.00.13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379424/4985b72d-2837-a4e1-c119-027d195945dd.jpeg)


今回の実装は [Github](https://github.com/masibw/twilio-video/pull/4)で確認できます．

## 概要
ビデオ通話機能には，[Twilio](https://www.twilio.com/ja/) を使います．
## 準備
### 1. Twilioアカウントを作成します．

https://www.twilio.com/blog/how-to-create-twilio-account-jp

### 2.  ベースとなるコードをFork & Cloneする．
基本的なビデオ機能は既に実装したものを使いましょう．

https://github.com/masibw/twilio-video



### 3. 必要となる認証情報を準備する
#### ACCOUNT SIDを取得する
ACCOUNT_SIDは [Console](https://twilio.com/console)から取得できます．

#### API KEYを作成する
API_KEY_SIDとAPI_KEY_SECRETは [API Keys](https://www.twilio.com/console/project/api-keys)から「Create API Key」を選択します．

![スクリーンショット 2021-11-12 19.40.19.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379424/7c84e472-df4b-352b-15a9-70cb90c6cdac.jpeg)

API KEYに分かりやすい名前をつけます．今回は「Finish Button」にします．
![スクリーンショット 2021-11-12 19.42.04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379424/77a11651-4419-4dbf-daf2-2bb9b24eceac.jpeg)

するとSIDとSecretが表示されるので，次項の`.env`へコピペします．

#### .envに書き込む
`.env.template`があるので，**これを`.env`という名前に変更**し，中身を以下のように書き換えます．

``` text:.env
TWILIO_ACCOUNT_SID=ACCOUNT_SIDをコピペする
TWILIO_API_KEY_SID=API_KEY_SIDをコピペする
TWILIO_API_KEY_SECRET=API_KEY_SECRETをコピペする
```

### 4. 依存関係をインストールする
今回はPythonを使うので`venv`を使って仮想環境を準備し，`pip install`します．

```
$ python -m venv venv
$ source /venv/bin/activate
(windowsの人は $ source venv\Scripts\activate)
(venv) $ pip install -r requirements.txt
```

### 5. 起動してみる
```
(venv) $ FLASK_ENV=development flask run
```
ブラウザで`localhost:5000`にアクセスし，別タブからも同様に接続すると以下のような画面が表示されると思います．ビデオと音声の使用は許可してください．

![スクリーンショット 2021-11-21 18.34.05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379424/d479a03b-d2d7-d562-9511-926d10733c8a.jpeg)
## 実装していく
ここからがこの記事のメインです．「そろそろ終わりたいなぁと○人が思っています」を伝える機能を作っていきます．
### 1. UI（ボタン・表示領域）を用意する
```diff:templates/index.html
 ... 省略 ...

 <form>
  <label for="username">Name: </label>
  <input type="text" name=”username” id="username">
  <button id="join_leave">Join call</button>
+ <button id="wanna_finish_button" disabled>Wanna finish this call</button>
 </form>
 <p id="count">Disconnected.</p>
+<h2 id="wanna_finish_presenter" class="wanna_finish"></h2>
 <div id="container" class="container">
 ... 省略 ...
```

ボタンを通話がつながっている時に１回だけ押せるようにします．

```diff:static/app.js
+ const wannaFinishButton = document.getElementById("wanna_finish_button");

const disconnect = () => {
 ... 省略 ...
  toogleChat.disabled = true;
+ wannaFinishButton.disabled = true;
  connected = false;
  updateParticipantCount();
};

const connectButtonHandler = (event) => {
    event.preventDefault();
    ... 省略 ...
    connect(username).then(()=>{
      joinLeaveButton.innerText = "Leave call";
      joinLeaveButton.disabled = false;
+     wannaFinishButton.disabled = false;
})
      ... 省略 ...
 }
};

```


### 2. 自分だけに表示する
まずは自分の画面だけに「そろそろ終わりたいなぁと○人が思っています」を表示しましょう

```diff:static/app.js
+ let wannaFinishCount = 1;
+
+ const wannaFinishHandler = (event) => {
+	event.preventDefault();
+	addToWannaFinishPresenter(`そろそろ終わりたいなぁと${wannaFinishCount}人が思っています`)
+	wannaFinishButton.disabled = true;
+ }
+ 
+ 
+ const addToWannaFinishPresenter = (newText) => {
+	let presenter = document.getElementById("wanna_finish_presenter");
+	presenter.innerHTML = newText;
+ }
+ 
+ wannaFinishButton.addEventListener('click', wannaFinishHandler);
```

この時点で以下のように「Wanna finish this call」ボタンが表示されます．
![スクリーンショット 2021-11-21 22.13.32.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379424/c5e9707b-896e-c816-4f43-f318b94a09ba.jpeg)



ボタンを押すと「そろそろ終わりたいなぁと１人が思っています」と表示されます．

![スクリーンショット 2021-11-21 22.12.49.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379424/c2461e70-ecfa-005b-ea6c-ce7e10bbcc1a.jpeg)




### 3. 通話に参加している他の人にも「そろそろ終わりたいなぁと○人が思っています」を送る

```diff:static/app.js
+ let dataTrack;

const connect = (username) =>
  new Promise((resolve, reject) => {
  ... 省略 ...
  fetch("/login", {
   ... 省略 ...
  }).then(_room => {
  room = _room;
+  room.localParticipant.publishTrack(dataTrack);
   room.participants.forEach(participantConnected);
   ... 省略 ...
});

const trackSubscribed = (div, track) => {
+	if (track.kind === 'data') {
+		track.on("message", (data) => {
+       addToWannaFinishPresenter(JSON.parse(data).message);
+			wannaFinishCount++;
+    	});
+	 } else {
    div.appendChild(track.attach());
+  }
};

const trackUnsubscribed = (track) => {
+	if (track.kind === 'data') {
+ document.getElementById("wanna_finish_presenter").innerHTML = "";
+	} else {
  track.detach().forEach((element) => element.remove());
+	}
};

const wannaFinishHandler = (event) => {
	event.preventDefault();
	addToWannaFinishPresenter(`そろそろ終わりたいなぁと${wannaFinishCount}人が思っています`)
+	sendDataToRoom(`そろそろ終わりたいなぁと${wannaFinishCount}人が思っています`);
	wannaFinishButton.disabled = true;
}
+ 
+const addLocalData = () => {
+	dataTrack = new Twilio.Video.LocalDataTrack();
+}
+
+  const sendDataToRoom = (data) => {
+	dataTrack.send(JSON.stringify({
+		message: data
+	}))
+ };
+
+ addLocalData();
```
#### Tracks とは
Twilio の Programmable Video APIのTracksとは 個別の音・ビデオやデータなど，(通話)ルームで共有されるものを表します．今回は，このTracksにメッセージを載せて共有します．


ここまで実装すれば完成です．
複数のタブで`localhost:5000`へアクセスし，`join call` -> `wanna finish this call`を押すことで「そろそろ終わりたいなぁと○人が思っています」と表示されるはずです．


![スクリーンショット 2021-11-21 22.00.13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379424/91a369a1-655a-f947-b56d-bf21a184448a.jpeg)

尚，排他制御は行っていないので，複数人が同時にボタンを押すと数がバグる可能性はあります．

# 最後に
[Twilio](https://www.twilio.com/ja/)を用いることで非常に簡単にビデオ通話機能とデータ共有が実装できました．参考リンクにあるように，emoji機能をつけることもできますし，色々応用が効きそうですね．

# 参考
https://www.twilio.com/blog/emoji-reactions-video-calls-twilio-data-track-api

https://www.twilio.com/blog/build-video-chat-application-python-javascript-twilio-programmable-video