Twilio Videoのパフォーマンスチューニング

:::note alert
2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
:::

# はじめに

みなさん、こんにちは。
KDDIウェブコミュニケーションズの Twilio事業部エバンジェリストの高橋です。

今回は、最近ご質問が増えてきたTwilio Videoを使ったアプリケーションのパフォーマンスチューニングについて解説します。

# ビデオの品質を左右する要素

Twilio Videoのパフォーマンスは以下の３要素に依存します。

- アクセス回線
- デバイスの処理性能
- 回線を使うトラフィックの総量

## アクセス回線

昨今のコロナ禍によって、在宅勤務などが増えてきたために自宅のインターネット回線を増強した方も多いのではないかと思います。一般的なISPの契約でも、最近は100Mbps以上が多くなってきており、場合によっては1Gbps以上の回線を引いているところもあるでしょう。
ビデオ通信は通常のWebブラウジングなどと異なり、非常に多くの帯域を消費するため、回線速度は非常に重要です。また、WiFi環境と有線LAN環境でも違いが見られますので、できる限り有線LANを利用することをおすすめします。
一方で、モバイル環境であったり、出先のWiFi環境を利用しなくてはいけないケースなども少なくなく、そのようなケースでも一定上の品質を求められることがあります。
回線速度が限定された状況でも一定以上の品質が必要となると、あとはそこを通るトラフィックを制御するしかありません。そのため、本記事では、トラフィックを制御する方法にも触れていきます。

## デバイスの処理性能

ビデオ会議では、カメラからの画像データを実際の通信データとして変換する作業（エンコードと呼びます）と、それを元の形に戻す作業（デコード）が必要です。エンコードとデコードを総称してコーデックと呼びます。
通常、このコーデックは複雑な計算によって実現しているため、デバイスの性能に依存します。そして、このコーデックはビデオ会議の参加者数に比例するので、より多くの参加者がいる会議などでは品質が低下してしまいます。
そこで、たとえばApple社ではiPhone 3Gの頃からコーデック処理をハードウェアチップで実現する方法を採用し、ビデオアプリケーションの品質を上げるとともに、バッテリー消費を抑えています。
それでもやはり参加者が増えると処理が逼迫してくるため、あとはいかにトラフィック量を抑えるかというアプローチが必要になります。

## 回線を使うトラフィックの総量

Twilio Video で扱う情報は、動画、音声、画面共有の３つがほとんどです。これ以外にもデータチャネルがありますが、こちらは今回は除外します。
これらのトラフィックは、それぞれ上りと下りが必要になります。
![Videoの接続形態.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/61d5bf5e-a91e-ddc9-8fd5-d4a307ffd551.png)

上の図のようにAというクライアントからみると、Aが利用できるアクセス回線の帯域を、上りと下りのそれぞれのトラフィックでシェアしながら通信をすることになります。

となると、まずはじめに知っておきたいのは、それぞれのトラフィックがどのくらいの帯域を利用するのかということになります。
実はこのトラフィック量は、前述のコーデックの種類や、表示するビデオ映像の解像度（縦横のサイズ）、さらには1秒間にどのくらいのフレームを流すか（フレームレート）に依存します。

### フレームレート

動画というのは、静止画の連続です。1秒間に複数の静止画を切り替えることで、画像が動いているようにみえます。そのため、切り替えの時間の時間をなるべく短くすることで、よりスムーズな動きを表現することができます。この切替のタイミングを「フレームレート」と呼び、1秒間に何フレームを表示するかで表します。単位はfps（frame per seconds）です。
一般的には、30fpsであれば自然な動画として表現できます（Twilio Videoでは、24fpsを推奨）が、場合によって15fpsまで落として通信させることもできます。30fpsに比べると、ややカタカタした感じにも見えますが、それほど動きの激しい動画でなければ概ね問題はないでしょう。ただし、一般的な使い方では15fpsよりも小さくすることはおすすめできません。
一方、画面共有では表示するコンテンツが静止しているケースが多く（スライドを表示するなど）、15fpsよりも下げても問題ないケースがあります。

### Twilio Video が利用する最低通信帯域

一つの指標としてTwilioが公開している最低通信帯域を以下に紹介します。

**ビデオ（フレームレートは30fps）**

コーデック|解像度|必要帯域（kbps）
:--|:--|:--
VP8|176 x 144|150
VP8|640 x 480|400
VP8|1280 x 720|650
VP8|1920 x 1080|1,200
VP8 Simulcast|176 x 144|150
VP8 Simulcast|640 x 480|550
VP8 Simulcast|1280 x 720|1,400
VP8 Simulcast|1920 x 1080|3,000
H.264|176 x 144|125
H.264|640 x 480|400
H.264|1280 x 720|600

**画面共有（フレームレート5fps）**

コーデック|解像度|必要帯域（kbps）
:--|:--|:--
VP8|1280 x 720|85
VP8|1920 x 1080|175
VP8 Simulcast|1280 x 720|700
VP8 Simulcast|1920 x 1080|1,800
H.264|1280 x 720|90

**オーディオ**

コーデック|必要帯域（kbps）
:--|:--
Opus|32kbps

たとえば2台のPC間でコーデックにVP8を使い、それぞれ640 x 480のサイズでテレビ会議をした場合、最低限必要な帯域は、以下の通りです（コーデックの説明は後ほど行います）。

400kbps（VP8で640x480）+32kbps（音声）=432kbps ・・・ 上り
400kbps（VP8で640x480）+32kbps（音声）=432kbps ・・・ 下り

上りと下りで計算を分けたのは、回線によっては、上りと下りで速度が違うためです。たとえば、下りが1.2Mbpsでも上りが300kbpsしか出ない回線であれば、上りのトラフィックがさばけません。このようなケースでは、上りのトラフィック量を減らす必要があるので、たとえば送信する解像度を176x144に下げるなどの工夫が必要です。

### 解像度を指定してルームに接続する

上記の例をコーディングすると以下のとおりとなります。

```javascript:例（JavaScript）
const { connect } = require('twilio-video');

const room = await connect(token, {
  name: 'my-room-name',
  audio: true,
  video: { height: 640, frameRate: 30, width: 480 },
});
```

この例では、２台での通信で行いましたが、実際には３名以上で利用することもあるので、計算方法はさらに複雑になります。とくにTwilio Videoのルームタイプにより計算方法も変わってくるため、まずはルームタイプと接続形態について説明しましょう。

## 接続形態

接続形態とは、各クライアントがどのような形で通信をしているかという話です。
Twilio Video では大きく、**P2P（ピアツーピア）形式**と**GroupRoom形式**の2種類の接続形態を提供しており、それをルームタイプという名前で分類しています。

### P2P形式

Twilio Video で、ルームタイプに**Peer-to-Peer**か、**Go**を選択した場合がP2P形式になります。P2P形式で接続すると、それぞれの参加者ごとに上りと下りのトラフィックが発生しますので、実際の通信は通常は以下のようになります<font color='red'>（Goでは、最大２名までしか接続できません）</font>。
![Videoの接続形態（P2P）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/38c31e76-3c42-e9ae-7ebe-8a49389061bd.png)

### GroupRoom形式

一方、ルームタイプとして、**GroupRoom**もしくは、**SmallGroupRoom**を利用した場合は、中間にSFUと呼ばれるメディアサーバーが介在する形となり、接続形態としては以下のようになります。
![Videoの接続形態（GroupRoom）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/3819b2e9-0284-4bc2-2c3c-e3b605013130.png)

SFUを介することにより、それぞれの参加者からみると、下りのトラフィックは他の参加者数分必要ですが、上りに関しては１つで済むことがわかります。先程の帯域計算で考えると、同じ４名でのビデオ会議でも、P2P形式ではアクセス回線で６つのトラフィックを賄わなければなりませんが、GroupRoom形式では、4つのトラフィックを賄えばよいことになります。さらに、上りは１トラフィックで済むことから、上り回線が細いサービスでの帯域利用が効率的になります。

以上のことから、**参加者が2名もしくは3名（Goでは2名までの制限があります）で収まるような使い方であればP2P形式のほうがパフォーマンスがよい**ですが、**それ以上の参加者が想定される場合はGroupRoom形式を選択したほうがよい**です。
また、Twilio上での録画が必要な場合も、GroupRoom形式が必須となります。

## サポートされるコーデックについて

前述のように、コーデックというのは画像や音声データを通信に最適な形式に変換するエンコードと、変換されたデータを元に戻すデコードの組み合わせで実現されます。
代表的なビデオコーデックには以下のようなものがあります。

- H.264 従来のコーデック
- VP8 現在主流のコーデック
- VP9 比較的新しいコーデック

オーディオコーデックについては、以下のようなものがあります。ただしオーディオに関しては、現在はほぼOpus一択となります。

- iSAC
- Opus
- PCMU
- PCMA
- G.722

P2P形式では、各参加者がお互いにサポートしているコーデックであれば通信ができます。たとえば、最新のChromeブラウザやFirefoxブラウザ、iOS用のSDKのバージョン2.x、Android用SDKのバージョン2.xではVP8、VP9、H.264のすべてをサポートしますが、それ以外ではサポートしているコーデックが異なります。
ただ、いずれにしろ、P2P形式では参加者同士がそれぞれ個別にネゴシエーションをするので、ほとんどのケースで問題はありません。

一方、GroupRoom形式では中間にSFUが存在するため少し事情が異なります。なぜなら、各参加者は参加者の数に関係なく、上りのトラフィックが１つしか流さないからです。
このケースで、SFUは以下のように動作して、他の参加者とのコーデックのネゴシエーションを行います。

- インバウンドトラフィック（SFUに入ってくるトラフィック）は、**SFUがサポートしているコーデック**である必要があります。
- アウトバウンドトラフィック（SFUから各参加者へのトラフィック）は、**インバウンドで採用したコーデックのみ**となります。

現在、TwilioのSFUがサポートしているコーデックは以下の通りです。

ビデオコーデック

- VP8
- H.264

オーディオコーデック

- Opus
- PCMU

すなわち、参加者の誰かがH.264しかサポートしていない場合（Safariの12.1未満を利用している）、もしくは積極的にH.264を使いたい場合（多くのモバイルデバイスには、H.264に特化したハードウェアチップが搭載されています）は、すべての参加者はH.264を利用しないといけません。
GroupRoomで強制的にH.264を使うには、RestAPIを使ってルームの作成時にコーデックを強制するか、管理コンソールのビデオ設定画面上でコーデックをH.264に指定しておく方法があります。
H.264しか対応しないデバイスが混在するかが不明な場合には、両方のコーデックを許容するという設定もあります。また、最初からH.264を拒絶するために、VP8のみと指定することもできます。デフォルトは**どちらも許容する**（VP8 & H.264）設定になっています。

![スクリーンショット 2021-08-12 11.30.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/f79f1ba4-d986-6b84-17f5-d6497bea7714.png)

また、Video SDK側でも利用したいコーデックの優先順位を指定することができます。
たとえば、ビデオコーデックにH.264を優先したい場合のコードは以下の通りです。

```javascript:例（JavaScript）
const { connect } = require('twilio-video');

const room = await connect(token, {
  name: 'my-room-name',
  audio: true,
  video: { height: 640, frameRate: 30, width: 480 },
  preferredVideoCodecs: ['H264'],
});
```

パフォーマンスチューニングの視点でみると、利用するデバイスの多くがモバイル端末であり、H.264による恩恵が受けられそうな場合や、H.264端末しか利用できないデバイスが必須の場合ではH.264を積極的に採用し、それ以外のケースではVP8で通信できるようにしておくとよいでしょう。特にVP8には、オプションとしてVP8 Simulcastが利用できるため、複数の参加者が利用するビデオ会議でパフォーマンスを出しやすくなります。

### VP8 Simulcast（サイマルキャスト）

GroupRoom形式で利用されるSFUには、受信者側に合わせて解像度を変更する機能がありません。これはすなわち、参加者の中に解像度の低いデータしか通信できないデバイスがあると、送信側がそれに合わせて送信する必要がでてきます。
この場合、高解像度が受信可能な参加者にも低解像度の画像しか配信されず、品質に対してクレームを受ける可能性があります。
そこで、SFUに送信する画像に複数の解像度とフレームレートを同時に送信することで、受信側の状況に応じて最適な解像度でデータを配信することができる技術が、VP8 Simulcastです。Simulcastを利用するには、送信側がSimulcastに対応する必要があります。
Twilio VideoでSimulcastを利用する送信側のサンプルコードは以下の通りです。

```javascript:例（JavaScript）
const { connect } = require('twilio-video');

const room = await connect(token, {
  name: 'my-room-name',
  audio: true,
  video: { height: 1280, frameRate: 24, width: 768 },
  preferredVideoCodecs: [
      { codec: 'VP8', simulcast: true }
  ],
});
```

Simulcastを有効にした場合、Twilioはクライアントが指定した解像度に合わせて、以下の表に示すようなレイヤーを準備します。

解像度|レイヤー1|レイヤー２|レイヤー３
:--|:--|:--|:--
352x288|352x288|disabled|disabled
480x360|240x180|480x360|disabled
640x480|320x240|640x480|disabled
640x480 (with crop)|240x240|480x480|disabled
960x540|240x135|480x270|960x540
1024x768|256x192|512x384|1024x768
1024x768 (with crop)|240x192|480x384|960x768
1280x720|320x180|640x360|1280x720
1280x720 (with crop)|225x180|450x360|900x720
1920x1080|480x270|960x540|1920x1080

この表から分かる通り、960x540以上の解像度を利用した場合は、３種類のレイヤーが用意されます。そして、SFUがクライアントの状態に応じて、各レイヤーから最適な解像度の画像を送信します。よって、上りの帯域に余裕がある場合は、Simulcastの利用を検討してみるのもよいでしょう。
なお、1:1で通信するようなアプリケーションでは、最初のネゴシエーション時に解像度の最適化が行われるため、Simulcastを利用する必要はありません。
**デフォルトではSimulcastはオフになっています。**

## リージョン指定

WebRTCでは、通信に先立ちシグナリングと呼ばれる動作が必要です。シグナリングでは、お互いのIPアドレスの確認や、利用するコーデック情報の交換などが行われますが、その際に利用されるのがシグナリングサーバーです。

シグナリングサーバーは、Twilio上では以下のリージョンに配置されています。
au1（オーストラリア）
br1（ブラジル）
de1（ドイツ）
ie1（アイルランド）
in1（インド）
jp1（日本）
sg1（シンガポール）
us1（米国・バージニア）
us2（米国・オレゴン）

ネゴシエーションの遅延を少なくするために、サーバーはクライアントに一番近いリージョンのものを利用することが望ましく、**日本国内での利用が多い場合はリージョンに日本（`jp1`）を指定しましょう。**
ただし、グローバルで利用するサービスの場合は、特定のリージョンを指定せず、`gll`を指定することもできます。
gllは、Global Low Latencyの略で、Twilioがクライアントの位置を推測して、最適なリージョンを選択する方法です。
シグナリングサーバーのリージョン指定方法は以下のようになります。

```javascript:例（JavaScript）
const { connect } = require('twilio-video');

const room = await connect(token, {
  name: 'my-room',
  region: 'jp1'
});
```

上記のように、シグナリングサーバーの指定はクライアントごとに行われるため、場合によってはクライアントごとに異なる指定がされるケースも想定されますが、一連の動作には問題はありません。

### メディアサーバーのリージョン指定

GroupRoom形式を利用する場合には、SFUのリージョンも指定できます。こちらは、ルームの作成時に指定が必要で、管理コンソール上で初期値を指定しておくこともできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/dd4bf972-4f8e-b50c-09f5-aed60dcea078.png)

RestAPIを使ってルームを作成する際にリージョンを指定する方法は以下のとおりです。

```javascript:例（Node.js）
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = require('twilio')(accountSid, authToken);

client.video.rooms
            .create({
               type: 'group',
               uniqueName: 'DailyStandup',
               mediaRegion: 'jp1'
             })
            .then(room => console.log(room.sid));
```

# GroupRoomにおけるパフォーマンスチューニング

ではここからは、GroupRoomを利用した場合のパフォーマンスチューニングに関連する各種パラメータについて解説します。

## ネットワーク帯域幅プロファイル（bandwidthProfile）

TwilioのSFUを利用した場合に、参加者の下り帯域幅を、**トラックごとにどのように分散するかを決定する**ことができます。これは、下りトラックが複数存在する場合に、トラックごとの優先順位を割り当てることで実現します。

通常、1:1での通信では、下りのビデオトラックは１つ、もしくは２つ（画面共有をする場合）ですが、参加者が増えた場合は、下りのビデオトラックは参加者数に比例して増えるため、トラックごとの帯域制御は有効です。

### 利用できるSDKのバージョン

JavaScript 2.0.0+
Android 5.8.0+
iOS 3.4.0+

bandwidthProfileにはいくつかパラメータが存在します。

### modeパラメータ

トラックごとの帯域幅の量を計算するアルゴリズムを指定します。指定できるのは以下の３つのうちのいずれかです。

#### gridモード（デフォルト）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/a072f200-a04b-dd95-6ef6-182a0993ef49.png)
<font color='gray'>[Twilio Document](https://www.twilio.com/docs/video/tutorials/developing-high-quality-video-applications#network-bandwidth-profile)より画像引用</font>

ダウンリンクのすべてのビデオトラックを均等に分割します。
帯域幅が十分に大きい場合、すべてのビデオトラックに最大帯域幅を割り当てます。最大帯域幅は、おもに解像度（width x height）に依存します。帯域幅が減少すると、すべてのビデオトラックの帯域幅が同じ割合で減っていきます。さらに帯域幅が減少すると、優先度の低いトラックからスイッチオフ（画像の停止）が行われます。
トラックの優先順位（設定方法は後ほど説明します）はすべて`standard`にしておきます。

#### collaborationモード

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/70d6db58-fb12-878a-956f-e3eb8d4ff25f.png)
<font color='gray'>[Twilio Document](https://www.twilio.com/docs/video/tutorials/developing-high-quality-video-applications#network-bandwidth-profile)より画像引用</font>

一部のビデオトラックを他のトラックよりも重要と位置づけます。例えば、Zoomのスピーカービューのように、特定のスピーカーを大きな画角で表示しながら、他の参加者を小さな画角で表示させるような場合です。
どのトラックを優先させるかは、各トラックの解像度をもとにSFU側で自動認識しますが、明示的に特定のトラックの優先度を上げること（下げること）もできます。
なお、このモードでは参加者ごとに異なる帯域幅を利用することになるため、参加者はVP8 Simulcastを使用する必要があります。
また、トラックの優先順位については、参加者のカメラ映像は`low`、画面共有トラックは`high`にしておくことが推奨されています。

#### presentationモード

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/03f4820a-59eb-12fb-202e-28b36a626aed.png)
<font color='gray'>[Twilio Document](https://www.twilio.com/docs/video/tutorials/developing-high-quality-video-applications#network-bandwidth-profile)より画像引用</font>

このモードを指定すると、特定のトラックにすべての帯域幅を割り当てます。特定のトラックの最大幅が割り当てられたあと、それでも余裕がある場合に別の参加者のトラックに帯域幅を割り当てる点が、前述のcollaborationモードとの差分となります。主に、ウェビナーなどの利用で採用するとよいモードになります。
また、トラックの優先順位については、参加者のカメラ映像は`low`、画面共有トラックは`high`にしておきます。

#### モードの指定方法

モードの指定は、ルーム接続時にパラメータで行います。

```javascript:例（JavaScript）
const { connect } = require('twilio-video');

const room = await connect(token, {
  name: 'my-room-name'
  audio: true,
  video: { height: 480, frameRate: 24, width: 640 },
  bandwidthProfile: {
    video: {
      mode: 'collaboration',
    }
  },
  preferredVideoCodecs: [
      { codec: 'VP8', simulcast: true }
  ],
});
```

### トラックの優先順位

前述の通り、Twilio VideoのSFUでは、トラックの優先順位によって帯域の調整が行われます。そのため、上りもしくは下りトラックの優先順位を明示的に行うAPIが用意されています。
設定できる優先順位は、`high`、`standard`、`low`の３種類です。
なお、このAPIは以下のSDKでサポートされます。

- JavaScript SDK：2.0.0+
- Android SDK: 5.8.0+
- iOS SDK: 3.4.0+

#### 上りトラフィックの優先順位を設定する

とくに指定がない場合、上りトラフィックの優先順位は`standard`になります。それ以外の優先順位を指定するには、例えば以下のようなコーディングを行います。
配信を始めるタイミングだけでなく、途中で動的に変更することもできます。

```javascript:例（JavaScript）
// 配信を始めるタイミングでローカルトラックに優先順位を指定する
const localTrackPublication = await room.localParticipant.publishTrack(localTrack, {
  priority: 'high' //choose among 'high', 'standard' or 'low'
});

// 動的にローカルトラックの優先順位を変更する
localTrackPublication.setPriority('low')

// リモートトラックの優先順位が変更されたことをイベントで取得する
remoteTrackPublication.on('publishPriorityChanged', priority => {
  console.log('The publisher has changed the priority this Track to "${priority}"');
});
```

#### 下りトラフィックの優先順位を設定する

前述の通り、上りトラックの優先度は全ての参加者に伝わります。しかし、時にはトラックを受信する側で、独自の優先順位を設定したいケースもあります。
以下のコードは、この仕組みを実装するものです。

```javascript:例（JavaScript）
// リモートトラックの優先順位を調べる
remoteParticipant.on('trackPublished', remoteTrackPublication => {
  console.log(`RemoteTrackPublication with priority ${remoteTrackPublication.publishPriority}`);
});

// リモートトラックの優先順位を動的に変更する
remoteTrackPublication.setPriority('high');

// リモートトラックの優先順位が変更されたことをイベントで取得する
remoteTrackPublication.on('publishPriorityChanged', priority => {
  console.log('The publisher has changed the priority this Track to "${priority}"');
});

// リモートトラックの優先順位を元に戻す
remoteTrackPublication.setPriority(null)
```

### ビデオトラックの自動オフ（trackSwitchOffModeパラメータ）

こちらは、ネットワークが輻輳した場合に、ビデオトラックを自動的にオフにするかどうかのスイッチとなります。
帯域幅が減少した場合でも、極力通信ができるように自動調整が行われますが、ビデオトラックに必要な帯域幅よりも少なくなってしまった場合は、必要に応じてビデオトラックをオフにすることで、他のトラックを保護することができます。
そのための指定が`trackSwitchOffMode`です。
具体的なパラメータとしては、以下の3つを指定することができます。

#### predictedモード（デフォルト）

このモードでは、利用可能な帯域幅を**SFUが推測**します。推測によりビデオトラックをオフにすることで、実際に輻輳が発生したときに、**画面のフリーズなどを事前に防止することが可能**ですが、あくまで推測による動作のため、状況によっては頻繁にスイッチオフが発生する可能性があります。

#### detectedモード

このモードでは、実際に輻輳を検知してからビデオオフが行われます。predictedモードに比べて不必要なビデオオフが起きにくくなる反面、実際に輻輳が発生すると数秒程度のオーディオを欠落、ビデオのフリーズが起きます。

#### disabledモード

このモードに設定すると動的なビデオオフは発生せず、輻輳が発生した場合はビデオがフリーズします。一般的に非常に正当な理由がない限り、このモードを使用することはおすすめしていません。

#### スイッチオフ・オンのイベント

リモートトラックのスイッチオフ・オンを検知するには、たとえば以下のようなコードが必要です。

```javascript:例（JavaScript）
remoteTrackPublication.on('subscribed', remoteTrack => {
  remoteTrack.on('switchedOff', () => {
    //　You may update your UI accordingly
    //　You can also determine whether a particular RemoteTrack is switched off.
    console.log(`The RemoteTrack ${remoteTrack.name} was switched off`);
  });

  remoteTrack.on('switchedOn', () => {
    //　You may update your UI accordingly
    //　You can also determine whether a particular RemoteTrack is switched off.
    console.log(`The RemoteTrack ${remoteTrack.name} was switched on`);
  });
});
```

### 下りの最大帯域幅制限（maxSubscriptionBitrateパラメータ）

Twilio VideoのGroupRoomでは、下りの最大帯域を予め設定しておくことができます。利用できる帯域よりも小さい帯域を利用することで、たとえばモバイルデバイスでのバッテリー消費や通信コストの抑止に役立ちます。ただし、ここで指定ができるのは、各トラックの帯域幅ではなく、下り全体の帯域幅であることに注意してください。
このパラメータのデフォルト値は、デスクトップブラウザでは0（無制限）になっています。ただし、GroupRoomにおけるビデオトラックの最大帯域は4,000,000（4Mbps）なので、これ以上の設定をしても意味はありません。モバイルデバイスでは、2,400,000（2.4Mbps）がデフォルト値です。

#### maxSubscriptionBitrateの指定方法

maxSubscriptionBitrateの指定は、ルーム接続時にパラメータで行います。

```javascript:例（JavaScript）
const { connect } = require('twilio-video');

const room = await connect(token, {
  name: 'my-room-name'
  audio: true,
  video: { height: 480, frameRate: 24, width: 640 },
  bandwidthProfile: {
    video: {
      mode: 'grid',
      maxSubscriptionBitrate: 2500000,
    }
  },
});
```


### リモートトラックの明示的なスイッチ（clientTrackSwitchOffControlパラメータ）

前述の`trackSwitchOffMode`では、回線の輻輳によってビデオオフをコントロールする仕組みについて説明しましたが、たとえば、クライアントのUI上でビデオの表示がされていないのであれば、回線に輻輳が発生していなくともそのトラックはスイッチオフしたほうが効率的です。
`clientTrackSwitchOffControl`は、表示が不要なリモートトラックをSFUが判断し、明示的にスイッチオフすることができるかどうかを指定できます。

#### autoモード（デフォルト）

画面の状態などを判断して自動的にスイッチを切り替えるモードです。

#### manualモード

ユーザがコード内で特定のリモートトラックをビデオオフにすることができます。たとえば、特定のトラックを優先させるために、画面上には存在している他のトラックをオフにするなどが考えられます。
明示的にスイッチをオフにするコードサンプルは以下の通りです。

```javascript:例（JavaScript）
// Explicitly switch off a track
remoteTrack.switchOff();
```

#### clientTrackSwitchOffControlの指定方法

clientTrackSwitchOffControlの指定は、ルーム接続時にパラメータで行います。

```javascript:例（JavaScript）
const { connect } = require('twilio-video');

const room = await connect(token, {
  name: 'my-room-name'
  audio: true,
  video: { height: 480, frameRate: 24, width: 640 },
  bandwidthProfile: {
    video: {
        clientTrackSwitchOffControl: 'manual',
    }
  },
});
```

### レンダリングサイズの指示（contentPreferencesModeパラメータ）

通常、画面上に表示される解像度と、SFUが送信する解像度は一致させることが理想です。そのため、画面上のUIが変更されたことを検知してメディアサーバーに伝えることによって、メディアサーバー側で解像度を動的に変更できるようになっています。この動作を制御するのが、`contentPreferencesMode`です。

#### autoモード（デフォルト）

画面上の解像度によって、自動的にSFUの送信解像度が調整されます。

#### manualモード

クライアント側でリモートトラックの解像度を明示的に伝達することで解像度を調整します。たとえば、画面上は640 x 480で表示しているものを、SFU側には320 x 240の解像度で送信するように指示する場合です。

#### contentPreferencesModeの指定方法

contentPreferencesModeの指定は、ルームの作成時にパラメータで指定します。

```javascript:例（JavaScript）
const { connect } = require('twilio-video');

const room = await connect(token, {
  name: 'my-room-name'
  audio: true,
  video: { height: 480, frameRate: 24, width: 640 },
  bandwidthProfile: {
    video: {
        clientTrackSwitchOffControl: 'manual',
        contentPreferencesMode: 'manual',
    }
  },
});
```

contentPreferencesModeを`manual`にした場合、SFUに指示する解像度の指定`remoteTrack.setContentPreferences`で行います。

```javascript:例（JavaScript）

remoteTrack.setContentPreferences({
    renderDimensions: { width: 320, height: 240 }
});
```

# まとめ

## 参加者が２名で構成されるビデオアプリケーションでのチューニング

- ルームタイプに**Peer-to-Peer**、もしくは**Go**を利用することを検討します。
- 利用するブラウザによっては、コーデックにはより圧縮率が高いVP9が利用できるので、コーデックを変更してみます。
- 片方、もしくは双方がモバイルデバイスの場合には、H.264に対応したチップが利用されている可能性もあるので、特に一昔前のスマートフォンを利用している場合などは、コーデックをH.264にしてテストしてみるのも良いでしょう。
- アクセス回線になるべく高速なもの（特に上りがボトルネックにならないように）を採用します。
- 有線LANを積極的に採用します。

## グループルームビデオアプリケーションでのチューニング

- アプリケーションの用途によって、最適なモードを選択します。
- スピーカービューや、画面共有が多いアプリケーションでは、`collaboration`モード、ウェビナーのような使い方なら`presentation`モードを採用します。
- `grid`モード以外を採用する場合は、配信トラックの優先順位を設定します。ウェブカメラには、`low`、画面共有トラックには`high`を指定しておくことで、帯域が不足したときにSFUがよしなに調整してくれます。優先順位は動的にも変更ができますし、トラック受信側でも設定ができるので、必要に応じて適宜変更することも検討します。
- SFU側の自動設定ではなく、マニュアルでの変更を希望する場合は、`contentPreferenceMode`と`remoteTrack.setContentPreferences`で動的に解像度の指定をすることもできます。
- PCやモバイルなど、デバイスが混在する場合はVP8 Simulcastを採用します。とくに通信速度の遅いモバイルが混在するようなケースで、他のPCの画質が低下しないというメリットがあります。
- アクセス回線や有線LANについては積極的に採用します。
- 解像度やフレームレートも調整できるので、場合によってはUI設計を見直すことも視野に入れて対応しましょう。

## チューニングのテスト

ビデオアプリケーションにおける品質は利用者による主観的なものであることが多く、対応するためには正常な状態（許容できる状態）と、異常な状態（許容できない状態）、やや異常な状態（ギリギリ許容できる状態）の３つくらいを擬似的に発生させられる環境を作ることも大切です。
たとえば、「Network Link Conditioner」などを用いて、PC上でWiFiの通信帯域を擬似的に制限かけながらシュミレーションするなどを行い、最適なパラメータを見つけていくことになります。また、モバイルが絡む場合は実機のパフォーマンスが大きく影響するため、実機を使ったテストも重要になります。
これまで説明したように、Twilio Videoでは、特にSFU周りでの各種パラメータが用意されていますので、ビデオアプリケーションを開発する際には参考にされると良いかと思います。

最後にTwilioが提供しているドキュメントのリンクをご紹介しますので、ぜひこちらも御覧ください。
[Developing High Quality Video Applications](https://www.twilio.com/docs/video/tutorials/developing-high-quality-video-applications)

---
### Twilio（トゥイリオ）とは

https://cloudapi.kddi-web.com
Twilio は音声通話、メッセージング（SMS /チャット）、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウド API サービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。

自己紹介|　
:--|:--
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/b51a8b88-abd5-ce5e-195a-de3564ba2ae6.png" height='200' width='150'/>|高橋克己（Katsumi Takahashi） 自称「<font color='red'>赤い芸人</font>」<br />グローバル・インターネット・ジャパン株式会社　代表取締役<br />株式会社KDDIウェブコミュニケーションズ　Twilio事業部エバンジェリスト<br /><br />2001年より大手通信事業者の法人サービスの教育に携わり、企業における電話のしくみや重要性を研究。2016年よりTwilio事業部にジョインし、Twilioを使ったスマートコミュニケーションの普及活動を精力的に行っている。<br />2015 Hall of Doers<br />2019 Twilio Champions