Amazon Chime SDKアプリで映像データ転送量を削減するアイデア -Among Us Auto Mute-
こんにちは。
前回の記事では、Amazon Chime SDK for javascript(Amazon Chime SDK JS)を用いて、"Among usをAuto Muteする機能"と"ゲーム画面を配信する機能"を持つゲーム実況システムを構築する方法についてご紹介しました。
前回のシステムでは、最大15名のユーザのゲーム画面をリアルタイムに配信します。このため、映像を受信する側にはそれなりのネットワーク帯域が前提とされます。また、受信するデータの総量も増えるため今どきの言い方(?)でいうと、長時間使用すると「ギガ」が枯渇しやすくなるとも言えます。さらに、受信した分だけ映像をデコードする必要があるためブラウザのCPU負荷が大きくなります。
今回は、このように多くの映像データを配信する際に、ネットワークの帯域やCPUが制限されるユーザでも映像を受信して表示できるようにするためのアイデアをご紹介したいと思います。
下図はアイデアを動かした結果です。赤矢印の部分が受信データ量です。4.4Mbpsから1.6Mbpsに削減できていますね。
Amazon Chime SDK JSの映像データの送受信について
Amazon Chime SDK JSは、他の多くのオンライン会議システムと同じようにWebRTCを用いて映像の送受信を行っています。また、複数ユーザ間で映像データをやり取りするためにSFU(Selective Forwarding Unit)を使用しています[1]。WebRTCやSFUについては、各所で解説がされているためここではポイントのみ紹介します。なお、日本語のページでは株式会社時雨堂様の情報が詳しいです(WebRTC コトハジメ, WebRTC SFU コトハジメ)。
元来WebRTCはP2Pでクライアント(ブラウザ)同士が通信して映像や音声を送受信する技術でした。二人のユーザが1対1で使用する場合にはこれでも構わないのですが、3人以上のユーザ間で使用すると少し問題が発生します。
下図は、3人のユーザがオンライン会議を行う場合の図です。(a)がP2Pを用いた場合の図となります。(a)で示すように、P2Pを用いると各クライアントが他の2つのクライアントにメディアデータを送信しなければなりません。また各クライアントが他の2つのクライアントからメディアデータを受信する必要があります。このように各クライアント間でフルメッシュ上にデータをやり取りすることになり、クライアントの数に応じて大量のデータを送受信しなければならなくなります。
この問題を緩和するために使用される技術がSFUやMCU(Multi-point Control Unit)と呼ばれる技術です。これらの技術では、各クライアントが送受信するデータをサーバを経由させることで一本化し転送されるデータ量を削減します。上図(b)はSFUを用いた場合の図です。SFUは各クライアントからサーバへ送信されるデータを一本化します。サーバから各クライアントへ送信されるデータは特に加工されることなくそのまま送信されます。このため各クライアントが受信するデータ量はP2Pと同じです。一方で(c)で示しているMCUは、サーバで各クライアントから受信したデータを一本のデータに合成し、各クライアントに送信します。
P2P, SFU, MCUを比較すると次にようになります。SFUはサーバの負荷は低いが、データ転送量とクライアントでの映像デーコードの負荷がユーザ数に応じて増大します。一方で、MCUはサーバで高度な処理を行うため、サーバに負荷が集中しますが、合成の仕方によってデータ転送量や各クライアントの負荷を一定[2]にすることができます。
SFUとMCUは一長一短あるのですが、現在はほぼSFU一択になっているようです。Amazon ChimeもSFUを採用しています。
Amazon Chime SDK JSにおける映像送受信のデータ転送量コントロール
SFUを採用しているAmazon Chime SDK JSでは、上記の通り、クライアントは他のクライアントの数に比例してデータを受信する必要があります。Amazon Chime SDK JSは、これを緩和するために、送信元のクライアントを選択して受信を停止する機能を持っています(下図(a))。しかし、受信を停止すると、再開するまで、そのクライアントの映像は全く見えなくなります。このため、Among usのゲーム配信のように全てのユーザのゲーム画面をリアルタイムで見たいという場合には適さないです。
また、Amazon Chime SDK JSは、送信する映像データのクオリティ(解像度やビットレート)を変更することができます。受信側の状況に応じて送信側のクオリティを変更する機能を作り込むことで、各クライアントが受信するデータ量をコントロールすることができます(下図(b))。しかし、この方法では、全てのユーザがクオリティ変更の影響を受けてしまいます。十分なネットワーク帯域とCPUを持つクライアントにとっては迷惑かもしれません。
ということで別の方法を考えて見たいと思います。
アイデア:専用クライアントで映像コンポーズして再送
今回は、ユーザが使用するクライアントとは別にクライアント(Dummy Client)を立ち上げて、そのクライアントで複数の映像データを一本の映像データに合成することにします。そして、合成した映像データをAmazon Chimeのサーバ(この記事の文脈でいうとSFU)に送り返し、各ユーザのクライアントに送信してもらいます。つまり、擬似的にMCU的な挙動をすることになります。
各ユーザは、データ受信方法として次の2つから選択することができるようになります。
- 通常通り各ユーザのクライアントから送信されるデータをそのまま受信する
- 上記の合成された映像データを受信する
これにより、各ユーザがネットワークやCPUの状態に応じて受信するデータ量を変更できるようになります。
ところで、このアイデアで懸念となるのが映像のタイムラグです。今回のアイデアの構成上、映像データを合成するクライアントとサーバ間で1往復分の通信と映像合成処理のためオーバヘッドの発生は避けられません。実際どの程度ネットワークの負荷が減るのか、また映像のタイムラグがどの程度になるのかはやってみないとわからないところがあります。ということで、今回もデモを作成して実験してみたいと思います。
実験の構成
映像のタイムラグについては、各ユーザのクライアントとAmazon Chimeのサーバ、映像を合成するクライアントの間のネットワークに依存します。映像を合成するクライアントは、同じAmazonのネットワーク内で動かしたほうが良いだろうという推測のもと、今回はFargate上で動かします。また、各ユーザのクライアントは、実際のユースケースを意識してAmazonのネットワーク外で動かします。具体的にはGCP上で動かします。また、データ転送量などをモニタするために、ローカルPCで、映像受信専用のクライアントを立ち上げます。
使用する映像は、せっかくなのでAmong Usの画面でやりたいのですが、15ユーザ分のアカウントを用意することができなかったため、4アカウント(4ユーザ)で実験しています。おそらくこれでも効果は見えると思います。また、より効果が見えるように、カメラ映像[3]を16ユーザ分使っての挙動も確認したいと思います。
結果
Among Usの場合(4user)
Among Usの4画面分を使ったときのネットワーク使用量を計ってみました。下記のYoutube動画をご覧ください。
0:00-0:20は各クライアントのデータをそのまま受信しています。0:20-50は合成された映像を受信しています。
下図はデータ受信量の部分をキャプチャしたものです。左が各クライアントのデータをそのまま受信しているときの状況です。右側は合成されたデータを受信しているときの状況です。約4.4Mbpsから約1.5Mbpsに削減できていることがわかります。
カメラ映像(16user)
通常のビデオ会議を想定して、16のカメラ映像を受信させてネットワーク使用量を計ってみました。また、データの合成によるタイムラグも見てみました。今回は、カメラ映像はダミーの映像を流しています[4]。また、タイムラグの計測のため1ユーザはストップウォッチの画面を擬似的にカメラ画像として流しています。下記のYoutube動画をご覧ください。
0:00-0:07は各クライアントのデータをそのまま受信しています。0:07-0:30で各クライアントのデータ受信を停止しています。徐々にデータ受信料が減っていくのが見えると思います。0:30-0:55は合成された映像を受信しています。1:15-1:50はタイムラグを見ています。
下図はデータ受信量の部分をキャプチャしたものです。左が各クライアントのデータをそのまま受信しているときの状況です。右側は合成されたデータを受信しているときの状況です。約5.0Mbpsから約1.0Mbpsに削減できていることがわかります。
下図はタイムラグを見ています。左はSFUから直接受信した映像です。右は合成した映像です。1〜2秒程度のタイムラグが発生しています。また、Youtubeの動画を見ていただくとわかるとおり、合成した映像はFPSが3程度しか出ていないです。
考察
上記の実験より、今回のアイデアによりデータ受信量を大幅に減らすことができることがわかりました。また、タイムラグも1〜2秒程度に収まることがわかりました。このタイムラグだと、相手の表情と音声のタイミングが大きくずれたり、微妙な間があいてしまうためビデオ会議では少し使いにくいかもしれません。一方で、Among usの画面配信のようなゲーム配信などでは音声と映像が完全に一致してなくても違和感を感じにくいため、十分使えそうです。実際にこのシステムを何度か使ってAmong usをプレイしていますが、この点で違和感があるとのフィードバックは出ていないです。
FPSが3程度しか出ていない点についてbad pointとしてフィードバックをもらっています。これは映像を合成する処理負荷が高くFargateでさばききれていないというのが大きな原因の一つです。実際ローカルのcore i9マシンを使うともう少しマシ[5]になります。また、参加ユーザが減ればFPSは改善します。例えば4ユーザであれば、上記Among Usのyoutubeで見られるように、それなりのFPSが出ます。今回のアイデアの目的は、ユーザの環境に合わせて受信するビデオ品質を変えて受信するデータ量を減らすことですので、今回はFPSの改善についてはあまり追求しません。しかし、今後の課題として処理の軽量化をもう少し検討していきたいと思います。
また、タイムラグのせいでビデオ会議での使用が少し難しいかも、と上で述べました。しかし、実際のユースケースを考えると、基本的に発言をしているユーザ(アクティブユーザ)は1名になることが多いと思います。アクティブユーザのみ合成していない映像を受信し、残りのユーザは合成したデータで受信するというシステムにすればこの問題はかなり改善されると思われます。上記のYouttube動画でタイムラグを計っていた映像で、左側にアクティブユーザを映すことをイメージしていただければ、おわかりになるかと思います。
リポジトリ
本実験で用いたデモは下記のリポジトリに格納されています。
blog002-reduce-trafficというブランチ名でcloneしてください。デプロイ方法等はリポジトリのReadmeをご覧ください。
$ git clone https://github.com/w-okada/flect-chime-sdk-demo.git -b blog002-reduce-traffic
$ cd flect-chime-sdk-demo/
まとめ
今回は、Amazon Chime SDK JSを用いたアプリケーションで多くの映像データやりとりするユースケースにおいて、ネットワークの帯域やCPUが制限されるユーザでも映像を受信して表示できるようにするためのアイデアをご紹介しました。アイデアの構成上、タイムラグが発生しますが、ユースケースによっては大きな問題にならない可能性が大きいです。また、上記考察に記載したとおり問題がある場合であっても回避策はありそうです。
一方で、FPSの劣化については、実験の環境に少し限界があったんも確かですが、映像の合成処理の負荷が高すぎるというのは改善すべき点だと思われます。今後の改善点として、よりよいシステムにしていきたいと思います。
Disclaimer
本ブログのデモの使用または使用不能により生じたいかなる直接損害・間接損害・波及的損害・結果的損害 または特別損害についても、一切責任を負いません。
Discussion