WebRTC Chrome で映像ビットレートが音声に食われる件
Tags: webrtc
Published: 2020-03-26 00:31:36

(2019/07/16:追記) 考察と結論をアップデートしました。

モバイル環境でも WebRTC を利用する場合、回線が細かったり、キャリアの通信量上限などで映像・音声の配信ビットレートは結構重要なウェイトを占めると思います。

色々とビットレート制御を調べているうちに、Chrome では指定した映像ビットレート通りに配信されないという事象を見つけました。
どうも、音声ビットレートに食われてしまっているようなので、配信ビットレートの制御について説明しつつ、どういう事象が起きるか説明したいと思います。

# WebRTC の配信ビットレートの制御

WebRTC では配信側のフレームサイズ、フレームレートおよび圧縮コードによって配信ビットレートが決まる。
フレームサイズやフレームレートは getUserMedia の constraints などで指定し、圧縮コードは SDP で指定することになる。

フレームサイズやフレームレートは、カメラデバイスのスペックに依存するため、constraints の exact を使って厳密に指定することは現実的ではないため、max/min などで幅を持たせることになる。

その際、ウェブブラウザの実装に応じてフレームサイズやフレームレートが最適化されて、配信ビットレートが決まる。

# SDP による映像配信ビットレート制御

WebRTC では SDP の m=video 内で TIAS および AS を指定することでビットレートを制御できるで配信ビットレートを指定することができる。
映像の場合、下記のようにすることで 512 kbps に制限することを伝えている。

```:sdp
m=video 9 UDP/TLS/RTP/SAVPF 120 116 117 96 97
b=TIAS:512000
b=AS:512
...
```

SDP で配信ビットレートを指定すると、ブラウザが上記のビットレートに収まるように、フレームサイズやフレームレートを上手く調整してくれる。

例えば、getUserMedia の constraints で下記を指定して、配信ビットレートの変えてみよう。

```js
const constraints = {
  video: {
    enable: true,
    width: { min: 640, ideal: 1280, max: 1920 },
    height: { min: 480, ideal: 720, max: 1080 },
    facingMode: 'user'
  },
  audio: { enabled: false }
};

navigator.mediaDevices.getUserMedia(constraints).then(stream => {
  ...
}.catch(...);
```

## AS:512 を指定した場合

<img width="306" alt="SnapCrab_NoName_2019-7-9_13-28-16_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/72554989-1c6d-1ca2-ed5f-3812f1621b20.png">

解像度 720p のフレームを 20fps でエンコードしており、55513 Bytes/s = 444kbps で配信していることがわかる。
配信ビットレートはフレームの大きさで変わるため、少し上下する。
少し眺めていた感じでは、52000 ~ 60000 Byts/s あたりに収まっていた。

## AS:256 を指定した場合

<img width="304" alt="SnapCrab_NoName_2019-7-9_13-30-39_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/a2c1c2bd-6519-ce00-e97e-00110b65feb3.png">

解像度が 960x540 に変わり、配信ビットレートが 28225 Bytes/s = 225kbps になっている。
フレームレートは変わっていないので、フレームサイズを変更して指定された配信ビットレートに合わせようとしていることがわかる。

# 音声配信ビットレートの指定

音声も同様に SDP で maxaveragebitrate を指定することで音声配信ビットレートを制御できる。

```:audio-sdp
m=audio 9 UDP/TLS/RTP/SAVPF 109
a=fmtp:109 maxplaybackrate=48000;maxaveragebitrate=256000;stereo=1;sprop-stereo=1;minptime=10;useinbandfec=1
...
```

Chrome の場合、6000 ~ 510000 bps の範囲でサポートされているようだ。
https://chromium.googlesource.com/external/webrtc/+/HEAD/api/audio_codecs/opus/audio_encoder_opus_config.h#28

# Chrome の映像配信ビットレートの指定がうまく行かない件

前述のとおり、配信ビットレートを指定することで、フレームサイズやフレームレートはブラウザが調整してくれる。
上記は Chrome の例だったが、Firefox でも同様である (Safari や Edge は知らない)。

さて、本題。
上記の例では audio を disable にしていた。
ここで audio を true にして、映像・音声の配信ビットレートをそれぞれ指定して chrome://webrtc-internals を見てみる。

## 映像：512kbps、音声：32kbps の場合

### 映像まわりの stats

<img width="301" alt="SnapCrab_NoName_2019-7-9_14-47-42_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/437d736b-882c-46fd-40c5-166028df3f54.png">
<img width="170" alt="SnapCrab_NoName_2019-7-9_14-48-29_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/c7d2b5a9-41a9-4615-c22b-332fe474909c.png">
<img width="635" alt="SnapCrab_NoName_2019-7-9_14-48-57_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/7c899e80-6193-048b-3747-3cc5db947e03.png">

### 音声まわりの stats

<img width="311" alt="SnapCrab_NoName_2019-7-9_14-48-1_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/d9a431e1-4777-f320-cf9e-40d209e33814.png">
<img width="167" alt="SnapCrab_NoName_2019-7-9_14-48-42_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/9ab2d0a8-2fdf-372a-08c4-bdf781aa83c5.png">


## 映像：512kbps、音声：256kbps の場合

### 映像まわりの stats

<img width="294" alt="SnapCrab_NoName_2019-7-9_14-51-18_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/79a7cc37-0c4e-4e37-b56f-500906712ac0.png">
<img width="160" alt="SnapCrab_NoName_2019-7-9_14-51-56_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/feb314e0-0763-b0ac-4109-8adc507f01be.png">
<img width="796" alt="SnapCrab_NoName_2019-7-9_14-52-13_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/fe5c3aa3-5cd6-cf1a-e61b-5d0bb9ac49e1.png">

### 音声まわりの stats

<img width="300" alt="SnapCrab_NoName_2019-7-9_14-51-40_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/524ea6b3-b029-bf2e-bfcd-0b55fe5a6311.png">
<img width="163" alt="SnapCrab_NoName_2019-7-9_14-52-46_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/c8d3aacd-64c3-26b5-0a61-3e5af342a143.png">

# 映像ビットレートが音声ビットレートに食われてる？

比較しやすいように映像の bitrate を並べてみる
左が音声 32kbps で右が 256kbps である。

<img width="170" alt="SnapCrab_NoName_2019-7-9_14-48-29_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/c7d2b5a9-41a9-4615-c22b-332fe474909c.png">
<img width="160" alt="SnapCrab_NoName_2019-7-9_14-51-56_No-00.png" src="https://m-yoshimo.github.io/dev-images/webrtc_chrome/feb314e0-0763-b0ac-4109-8adc507f01be.png">

画像が小さくて申し訳ないが、明らかに映像の bitrate が下がっている。
この事象は Firefox では発生しない。
スクショが取れていないが、音声ビットレートの値を 192kbps などの大きい値にしたり、マルチストリーム環境で配信者を複数に増やすと、この差が顕著に見受けられる。

# 考察

<a href="https://tools.ietf.org/html/rfc4566#section-5.8">RFC4566</a> や <a href="https://tools.ietf.org/html/rfc3890#section-6.2">RFC3890</a> を読む限り、RTP session 毎なので m=video の下に書いていれば映像にだけ影響すると思っているのだが、AS の略が **A**pplication **I**ndicated bandwidth なので、映像・音声を含めた全体を示すのかもしれない。

また、Chrome でこのような事象が発生して、Firefox では発生していないが、Firefox ではそもそも音声ビットレート制御 (maxaveragebitrate の指定) が効かないようなので、Chrome の方が WebRTC の挙動として正しい可能性がある。

# 結論

映像のビットレートを指定することは出来ない。
SDP において、AS に指定する値は映像・音声ビットレートを足し合わせた値を指定し、音声ビットレートを maxaveragebitrate で指定することで、映像のビットレートを間接的に指定する方法が良さそう。

注意事項として、maxaveragebitrate は Firefox では効いていないため、Chrome と Firefox で映像ビットレートは異なるので注意 (全体のビットレートは同じはず)。