SkyWayでREDを使って音声品質のパケロス耐性を高める
こんにちは、SkyWay 開発チーム所属の@shinyoshiakiです。
WebRTC に限らず、インターネットを経由した音声通信では、パケットロスが原因で音声品質の低下を招く可能性があります。
RED(REdundant Audio Data)は Chrome 96 から利用できるようになった、パケットロスによる音声品質の低下を抑制する技術です。
本記事では skyway-jsでREDを有効化する方法と、skyway-jsでREDのより応用的な活用をする方法について紹介します。
※本記事の内容は Chromium/Chrome のバージョン 96 以降でしか動作しないのでご注意下さい。
この記事を読むとできるようになること
skyway-js で RED を有効化できる
RED の冗長度を上げることができる
通信品質を基に動的に RED の冗長度を調整できる
2 と 3 については、実装例として補助ライブラリを用意したので、skyway-js でこの補助ライブラリを使う方法について解説します。
RED とは
WebRTC のメディア(映像/音声)通信には RTP/RTCP というプロトコルが使用されています。メディアのパケットは RTP パケットのペイロード領域に格納され送受信されます。
RED は RFC2198で定義されている RTP パケットのペイロード領域に対する拡張仕様です。
RTP ペイロードに、最新のメディアパケットだけではなく、その直前に送信した N 個のメディアパケットを詰め込むことでメディアパケットの冗長化を行います。
この変数 N のことを Distance と呼びます。Distance が長いほど冗長度が上がり、音声品質のパケットロスへの耐性が強くなります。パケットロスへの耐性が強くなる一方で、送信する冗長パケットの数が多くなるので、通信量は多くなります。
音声の通信量は映像に比べて遥かに小さいため、冗長な音声メディアパケットを 幾つか付加してもトランスポート全体で見るとそれほど影響はありませんが、通信帯域が非常に狭いネットワークで高い Distance 値を設定すると、通信が輻輳して映像側の画質が落ちるようなケースも考えられるので、ユースケースに応じて Distance 値を決めると良いでしょう。
現状、Chrome における RED の Distance は 1 で固定されているので Distance を変更するためには特殊な方法を用いる必要があります。
RED の実力を聞いてみよう
パケットロスのある通信環境で RED の有無で音声品質の比較をしてみましょう。
音声ソースは以下を使用させていただいています。
RED なし
RED あり(distance=1)
Chrome の標準の RED(Distance=1) だとパケロス率 30~40%あたりまでは効果を発揮できそうです。
1. skyway-js で RED を有効化する
Chrome のRED 機能を skyway-js で有効化する方法について解説します。
現状、SkyWay の SFU サーバが RED 機能に対応していないので、P2P においてのみ利用可能です。
サンプルコードを以下に示します。
const peer = new Peer({
key: window.__SKYWAY_KEY__,
});
....
const connection = peer.call(targetId, stream);
connection.open = true;
const pc = connection.getPeerConnection();
connection.open = false;
if (RTCRtpSender.getCapabilities) {
const { codecs } = RTCRtpSender.getCapabilities("audio");
const transceiver = pc
.getTransceivers()
.find((t) => t.sender.track.kind === "audio")
if (transceiver.setCodecPreferences) {
transceiver.setCodecPreferences([
codecs.find((c) => c.mimeType.includes("red")),
...codecs,
]);
}
}
順を追って解説します。
まず connection が接続を完了する前に生の RTCPeerConnection を取り出します。
const connection = peer.call(targetId, stream);
connection.open = true;
const pc = connection.getPeerConnection();
connection.open = false;
skyway-jsでは生の RTCPeerConnection は本来、接続完了後にしか取得できないのでややハック的なコードになっています。
次に RED を有効化します。
if (RTCRtpSender.getCapabilities) {
const { codecs } = RTCRtpSender.getCapabilities("audio");
const transceiver = pc
.getTransceivers()
.find((t) => t.sender.track.kind === "audio")
if (transceiver.setCodecPreferences) {
transceiver.setCodecPreferences([
codecs.find((c) => c.mimeType.includes("red")),
...codecs,
]);
}
}
Chrome の WebRTC で RED を有効化するには、音声のコーデックの優先順位の最上位に RED のコーデック情報が来るようにする必要があるので、Transceiver.setCodecPreferences を使ってコーデックの優先順位を編集します。
サンプル
skyway-js-sdk のサンプルを編集して RED を有効化する際のコードの差分を以下に示します。
2. RED の冗長度を上げる
Chrome の RED の Distance は 1 で固定されているものの、EncodedTransform(旧 InsertableStreams)を使うことで任意の Distance に変更することが出来ます。
冗長度の比較
冗長度による音声品質の違いについて聞いてみましょう。
パケットロス率は 50%の環境で検証します。
Distance 2 までは、音声品質の低下を認識できますが、Distance 3 あたりからは音声品質の劣化を感じられなくなっています。
このことから冗長度を上げることで音声の品質が、向上することを確認できました。
実装方法
Chrome の RED の Distance 値は 1 で固定されているので、任意の値に変更するためには、特殊な実装をする必要があります。
必要な処理
EncodedTransform で生の RTP Payload(Red パケット) を取り出す
RED パケット中の最新のメディアパケットをキャッシュする
任意の Distance 数のメディアパケットをキャッシュから取り出して RED パケットに入れる
RED パケットを送信する
skyway-js 向けにこれらの処理を行うサンプル実装を補助ライブラリという形にまとめてみました。
このライブラリは現状、「電話モデル(Call モデル)」にのみ対応しています(ルームモデルでは動作しません)。
補助ライブラリの使い方
インストール方法
npm i @shinyoshiaki/skyway-red
利用方法
skyway-js のサンプルコード に補助ライブラリを組み込む方法について解説します。
まず、補助ライブラリを import します。
次に EncodedTransform を有効化するためのオプションを指定して Peer のインスタンスを作ります。その後、補助ライブラリのインスタンスを作ります。
最後に、補助ライブラリのactivateREDメソッドを使って mediaConnection に対して RED を有効化していきます。
answer 側は必ず、.answer()メソッドを実行した後にactivateRED(mediaConnection)を実行する必要があります。
補助ライブラリを組み込んだサンプルアプリの完全なコードはhttps://github.com/shinyoshiaki/skyway-red/blob/master/example/script.ts にあります。サンプルアプリの実行方法はリポジトリのREADMEに記載されています。
仮想マシンやTC(Trafic Controll)コマンドを使ってネットワークの擬似的なパケットロスを発生させた状況で、REDを利用していないデフォルトのサンプルアプリとREDを有効化したサンプルアプリの音声品質を比較すると違いがよく分かると思います。
通信品質を基に動的に RED の冗長度を調整する
RED では、冗長度を上げることでより強いパケットロス耐性を得ることが出来る一方で通信量が倍々に増えるデメリットもあります。
WebRTC では getStats API を使ってネットワークのパケットロス率を取得することが出来ます。
パケットロス率に応じて RED の冗長度を上下させることで、パケットロス率が低いときには通信量を抑えつつ、パケットロス率が高いときには、音声の品質低下をより有効に抑える事を期待できます。
先程の補助ライブラリにこの機能を実装しているので、有効化の方法について説明します。
補助ライブラリでの有効化方法
const skywayRED = new SkyWayRED({ useAdaptiveRedDistance: true });
補助ライブラリのインスタンス作成時に、useAdaptiveRedDistanceオプションを有効にすると、「動的に RED の冗長度を調整する」機能が有効になります。
getStatsのパケットロス率の数値はパケットロスが発生してから反映されるまでに少し時間がかかるので、Distanceの変化するのにラグがあります。
パケットロスによる音声品質を抑えることを最優先としたいようなユースケースの場合は初めからある程度高いDistanceを指定するとよりユーザ体験が良くなると考えられます。
参考文献
https://groups.google.com/g/discuss-webrtc/c/5761etCrSuA/m/VrbQL3_LBwAJ
https://webrtchacks.com/red-improving-audio-quality-with-redundancy/
https://webrtchacks.com/implementing-redundant-audio-on-an-sfu/
▼SkyWayサービスサイトはこちら▼
https://webrtc.ecl.ntt.com/