Instantly share code, notes, and snippets.

Embed
What would you like to do?
WebRTC SFU コトハジメ

WebRTC SFU コトハジメ

日時:2018-04-22
作:時雨堂
バージョン:18.4.0
url:https://shiguredo.jp

この記事が良いと思ったらこの記事に Star をお願いします

概要

この資料は WebRTC SFU を 1 から開発した知見を、一般的な WebRTC の知識がある人向けで書いています。

もし一般的な WebRTC の知識を知りたい場合は WebRTC コトハジメ をお勧めします。

自己紹介

株式会社時雨堂 が開発、販売している WebRTC SFU Sora の開発責任者です。主に製品設計や WebRTC スタックの開発を担当しています。

どんな感じで作ったのかは別件で話しているので、興味あればどうぞ。

WebRTC SFU をフルスクラッチで作ってみた

注意

この資料はもともと WebRTC Conference Japan 2016-02-16 ~ 17 の発表者向け資料でしたが、 発表後に全面書き直しを行いました。そのため発表当時の資料とは別物になっています。

データチャネル

今回は WebRTC のデータチャネルの話をしません。 理由としては単に自分が SFU を実装した上でデータチャネルは一切実装していないためです。データチャネルという機能は P2P で生きる機能だと考えているため SFU に搭載するのは向いていないとも考えています。

MCU

今回は MCU の話ほとんどしません。

WebRTC SFU

WebRTC SFU (Selective Forwarding Unit) は全ての通信をサーバ経由で配信する一つの方法です。今までは MCU が主力でしたが MCU は CPU リソースを消費しすぎることから SFU が注目されています。

簡単な SFU の図

https://dl.dropbox.com/s/5rewuc203gz48ou/webrtc_sfu.png

マルチストリームの図

https://dl.dropbox.com/s/0ocu85fvl5o3z8a/multistream.png

詳細な SFU の図

https://dl.dropbox.com/s/8z9q6wbbvd11zq7/webrtc_sfu_pubsub.png

SFU はサーバサイドで一切映像を加工しません。転送するだけのため、CPU リソースを MCU ほど消費しません。

SFU で一番 CPU リソースを消費するのは暗号です。受信したパケットを復号化して再度暗号化して送信するコストが一番高くなります。

なぜ WebRTC SFU を作ったのか

  • P2P では管理側がコントロールしにくいことから、結局サーバ経由になると考えたから
  • マシンパワーが弱い端末では中継サーバを介してのリアルタイム配信が必要になると考えた
  • サーバでの録画価値の需要が高いと考えた

シグナリング

WebRTC SFU でもシグナリングは必要です。ただ通常のシグナリングとは異なります。

通常シグナリングは自分が Offer を生成し、相手に届けて、相手から Answer をもらいます。 SFU の場合は SFU から Offer が送られてくる事が多いです。

SFU から Offer が送られてきて、 Answer をクライアントが送る図

https://dl.dropbox.com/s/8pjmz9ip5w0xct1/connect_offer_answer.png

DTLS

WebRTC は DTLS のハンドシェイクを使用し、 SRTP に使用するマスターシークレットを交換します。DTLS の Cipher Suite で定義された暗号で暗号化はされません。

そのため、 WebRTC の通信は DTLS を使いつつも、実際の暗号化は全て SRTP を使用します。

WebRTC SFU では、 DTLS-SRTP という特殊な暗号方式を話せる必要があります。よく誤解される TURN サーバでは暗号を解いている、というのがあります。実際は解いていませんが、サーバ経由にすると暗号をほどいているのだろうというイメージがあるのだと思います。 SFU はこれを実際に行っています。

SFU で暗号化の中身を見れているという図

https://dl.dropbox.com/s/g8tkt5qbesr4o91/webrtc_sfu_dtls.png

TURN は暗号を解いてない図

https://dl.dropbox.com/s/ngqvyq96r4dfkxj/turn.png

SRTP

WebRTC SFU のメディアチャネルで使われている DTLS-SRTP の SRTP は映像や音声のプロトコルである RTP に Secure を付けたプロトコルです。 Secure RTP の略。そのままです。

RTP/RTCP に対して、暗号化を行います。その暗号化を行うための鍵の生成については DTLS で交換したマスターシークレットを使います。

この部分は特に SRTP の RFC から逸脱しておらず、ほぼそのまま適用されており、 WebRTC だから何か特殊なことはしていません。 SIP で使われている SRTP と同様です。ただし暗号方式は AES-CTR が採用されています。

最近になって AES-GCM の RFC がリリースされました。 AES-CTR から AES-GCM に移行するのも時間の問題でしょう。もともとこの暗号方式は DTLS の ClientHello 拡張の use_srtp にて定義可能です。クライアントから提示するため、クライアントが AES-GCM に対応さえすれば、サーバは追従するだけです。

全てのブラウザが使用する暗号鍵は異なる

https://dl.dropbox.com/s/8z9q6wbbvd11zq7/webrtc_sfu_pubsub.png

この暗号部分ですが、 SFU の一番のリソースボトルネックになります。画質がよくなればなるほどパケットの量は増え、復号化と暗号化の量が増えます。つまり SFU は暗号化さえ耐えきれば良いのです。

SDP

WebRTC SFU では SDP を自前で生成する必要があります。現時点で SDP は Multiplexing をどうしていくかが課題になります。 Simulcast が来たタイミングでさらに意識する必要があります。

マルチストリーム

悲しいことですが Chrome と Firefox ではマルチストリーム時に生成される SDP が異なります。Unified Plan や Plan B と呼ばれるものです。これは Chrome のマルチストリームの実装が最新に追いついていないため発生している問題です。

Chrome は 2018 年中には Unfied Plan へ切り替える計画をしています。 Firefox は最新に追従しており、Unified Plan を実装しています。 Unified Plan は m= を複数定義するという仕組みです。音声や映像があれば、その分だけ m= を増やしていきます。同一の組み合わせかどうかは a=msid を使って判断します。

Plan B は m= は audio と video の二つに分けてしまい、そのなかで a=ssrc-group という仕組みを使って分割する仕組みです。Plan B は今では Chrome しか実装していません。もともと Plan B から Unified Plan への移行をするはずが、 Chrome は対応が遅れたのです。

私はマルチストリームは WebRTC のキラー機能だと考えています。 1 つのコネクションに動的に映像や音声を追加したり削除したりするのを気軽にできるからです。

特にマルチストリームは映像や音声を合成できない SFU にとっては、キモとなる技術です。ここをどのくらい自由度をつけつつも、気軽に使えるようにするかが SFU の差別化となるでしょう。

Multiplexing

WebRTC では RTP/RTCP のポートを節約するための Multiplexing に対応しています。

簡単に言うと RTP と RTCP に使用するポートを同じにするという仕組みです。もともとは RTP と RTCP のポートは別でした。 RTP のポートに +1 したものを RTCP として使うという感じです。

ただ、その場合はポート番号を使いすぎてしまうことから、一つに節約するという戦略がとられました。RTP/RTCP を一つにする、という仕組みです。さらに、音声と映像の RTP/RTCP も同じにしてしまうという仕組みも追加されました。

つまり本来は 音声で 2 つ、動画で 2 つ使っていたポートを 1 つにまとめるという仕組みです。これのおかげで複数の DTLS セッションを張る必要もなくなり、一つのセッションを使い回すことができるようになりました。判断は SSRC やペイロードタイプで十分判断できます。

現時点で Multiplexing は普通で、むしろ対応していない方が特殊な状況です。

Negotiating Media Multiplexing Using the Session Description Protocol (SDP)

STUN

WebRTC SFU で STUN は UDP ホールパンチング用に使用します。 基本的に WebRTC SFU 自体はグローバルにいることから、STUN がどうこうというのは基本的にはクライアントが判断するための材料になります。

SFU は送られてきた STUN パケットを送り返したり、クライアントからの Candidate に対して STUN パケットを送ったりします。判断自体は全てクライアントが行うのでサーバが判断する必要はありません。

RTCP

WebRTC SFU を開発する上でコアとなるのが RTCP の生成です。 SFU では RTP をコピーして複数の宛先に転送します。そのため、受け取り先が複数います。ただもともと WebRTC では相手は 1 人しか想定されていません。そのため、動作に支障が出てきます。

RTCP は主に Sender Report と Receiver Report があります。この中に Feedback Message や Bye などが含まれてきます。Sender Report は映像や音声を配信している側が、こんな感じで送ってるよと送ったパケット数や時間などを送る仕組みになっています。 Receiver Report は Report Block というのを使って、受け取ったパケットの総数やジッターなど実際にどんな状況なのかを伝えていきます。

Sender Report を送った側はそれをみて映像の品質を動的に変更していきます。動的に映像の品質を変えることで相手の通信状況を考慮した動作が可能になるのです。が、これが SFU ではとても邪魔です。

なぜなら相手が 1 人ではなく N 人になるからです。一人でも回線品質が悪い人がいるとその人に引っ張られてしまうことになります。

RTCP を動的に生成する図

https://dl.dropbox.com/s/fuzftdhzxhqpi0p/rtcp.png

Chrome と Firefox

2018 年 4 月の時点で、 WebRTC の最先端を突き進んでいるのは Chrome です。Chrome は積極的に新しい機能を積んできています。 ただマルチストリームの点では Firefox に遅れを取っていましたが 2018 年では同等までに並べそうです。

Firefox はマルチストリームや Simulcast といった部分では Chrome より先にいっています。さらにデフォルトのビデオコーデックを VP9 にしたりとかなり前のめりに進んでいる印象です。

マルチストリーム

SDP の部分でも説明しましたがここでもう一度説明します。

マルチストリームは全てのやりとりを一つのポート、つまり一つの DTLS でやりとりを行う技術です。映像や音声を後から追加したり、流れている映像や通信を削除したりすることもできます。

参加者を動的に追加したり、削除したりを新しいコネクションを張らずに実現できるわけです。

マルチストリームの図

https://dl.dropbox.com/s/0ocu85fvl5o3z8a/multistream.png

複数人数でのマルチストリームの図

https://dl.dropbox.com/s/3d5rlexon4b2y25/multistream3.png

視聴者との直接やりとり

Multistream を上手く活用することで、接続を増やすことなく、切断することなく、今視聴者として参加している人を配信者として動的に参加させることが可能になります。

Multistream は配信者と視聴者を動的に切り替えたりすることができるようになるのです。

最初は一人だけが配信してそれ以外は視聴者

https://dl.dropbox.com/s/937519c8z4d7xj9/multistream_1ton.png

その後、誰かが質問をしてきたので、見ている人全員にも見えるようになる

https://dl.dropbox.com/s/esvsbc6uwxchpdy/multistream_2ton.png

質問が終わったので質問した人は視聴している一人になる

https://dl.dropbox.com/s/937519c8z4d7xj9/multistream_1ton.png

このように動的に配信者を参加させることが可能になります。

Simulcast

Simulcast は複数ビットレートの映像を配信側が送りつける方式です。この方式を使う事で SFU では回線速度が遅い場合は低ビットレート、回線速度が速い場合は高ビットレートの映像を配信することができます。

https://dl.dropbox.com/s/6s0xxjwt6l7si40/simulcast.png

Firefox 47 にて Simulcast の最小限な実装が入りました。簡単なサンプルと Simulcast を指定したときの SDP を張っておきます。

Simulcast を指定するサンプルコード:

var pc = new RTCPeerConnection({});

navigator.mediaDevices.enumerateDevices()
.then( devices => {
  return navigator.mediaDevices.getUserMedia({audio: false, video: true})
})
.then( stream => {
  pc.addTrack(stream.getVideoTracks()[0], stream);
  var sender = pc.getSenders()[0];
  // active と priority は 2016-02-03 の時点で Firefox では未実装のようです
  sender.setParameters({encodings: [{ rid: "spam", active: true, priority: "high", maxBitrate: 4000 },
                                    { rid: "egg", active: true, priority: "medium", maxBitrate: 1000 }]})

  pc.createOffer()
  .then( offer => {
      console.log(offer.sdp);
  })
})
.catch( error => {
    // エラー
});

Simulcast 有効時の SDP:

v=0
o=mozilla...THIS_IS_SDPARTA-47.0a1 1807443757353422678 0 IN IP4 0.0.0.0
s=-
t=0 0
a=fingerprint:sha-256 9E:DC:41:7B:95:B1:71:5F:CE:1E:70:D6:7E:4D:D9:C5:EA:DB:63:03:8D:A9:B2:DA:4B:98:D4:FE:DF:23:B9:B6
a=ice-options:trickle
a=msid-semantic:WMS *
m=video 9 UDP/TLS/RTP/SAVPF 120 126 97
c=IN IP4 0.0.0.0
a=sendrecv
a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1
a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1
a=fmtp:120 max-fs=12288;max-fr=60
a=ice-pwd:c11d5e4cdcf0f78dd61652efa93fceaa
a=ice-ufrag:ea0d08fc
a=mid:sdparta_0
a=msid:{1a0c032c-5b9b-7a41-8fa2-a3634ff5ed1f} {873e039b-dc7e-fe48-bb07-c45356e4a7c6}
a=rid:spam send
a=rid:egg send
a=rtcp-fb:120 nack
a=rtcp-fb:120 nack pli
a=rtcp-fb:120 ccm fir
a=rtcp-fb:126 nack
a=rtcp-fb:126 nack pli
a=rtcp-fb:126 ccm fir
a=rtcp-fb:97 nack
a=rtcp-fb:97 nack pli
a=rtcp-fb:97 ccm fir
a=rtcp-mux
a=rtpmap:120 VP8/90000
a=rtpmap:126 H264/90000
a=rtpmap:97 H264/90000
a=setup:actpass
a=simulcast: send rid=spam;egg
a=ssrc:2394790163 cname:{2fd69058-6496-984a-abdd-0d67f5d1701e}

本来は rid の名前の後ろにビットレートの細かい profile みたいなのが入ります。まだ出力されていないようです。 また、 RTP の拡張も入るはずなのですが、この段階では有効になっていないようです。

Simulcast は SFU にとってとても重要な技術です。この技術を使う事で再変換なしで帯域による映像の画質を変更できるわけです。

思っているよりも早く仕様が入ったという印象です。 WebRTC 1.0 には Simulcast は含まれてリリースされていくようです。

録画

WebRTC をサーバ経由で扱うメリットの一つとしてあげられるのが録画機能です。クライアントで録画する機能はそもそも WebRTC が API の一つとして提供しています。

録画はその処理をサーバ側で行う機能です。サーバはパケットを受信したら暗号を復号して、暗号化してから送信しています。そのためサーバには暗号化されていないパケットが存在できます。

よく誤解されますが TURN サーバの場合は暗号化されたパケットをそのまま流れます、そのため TURN サーバは何が流れているかを把握できません。

録画は WebRTC で配信をするにあたり、様々可能性を持っています。リアルタイムという遅延の少ない世界で見る映像を後出しで見ることができるようになるためです。

遅延が少ない世界と、遅延を気にしない世界の両方に存在できるようになります。VP8/Opus で配信されている映像であれば WebM 形式として保存が可能です。それを MPEG-DASH 形式に変換しておけば、後は HLS のような配信することもできます。

ただし、録画はコーデックに依存するため、なかなかしんどいと言われています。今後コーデックが VP9 や H.264 が標準になってくる場合色々と課題が出てきそうです。

TURN

グローバルに存在する WebRTC SFU といえども、 TURN は必要になる場合があります。それは TCP しか使えない世界だったりする場合です。 もちろん UDP の場合でも TURN が必要になる場合もありますが、ほとんどの場合は TCP での配信が求められるために必要となるでしょう。

ただ、今後はわざわざ TURN サーバを立てる必要もなくなるかもしれません。

ICE-TCP

ICE というのは STUN と TURN を合わせた仕組みを指しますが、 ICE-TCP という技術があります。これは Chrome では有効になっています。 Firefox では実装はされていますがデフォルトでは無効になっています。

実は WebRTC SFU は ICE-TCP との相性がとても良いです。

TURN-TCP を使うにはまず STUN を TCP で繋げられるようにする必要があります。WebRTC SFU として ICE-TCP を採用すると TURN サーバが不要になります。ただし、実装としては TCP というワンクッションが一度入ります。

TCP で SFU がパケットを受け取ってその中のパケットをは DTLS-SRTP なのです。つまりただの TCP のトンネルを SFU にはるだけです。その中を普通に DTLS-SRTP パケットを投げつけてきます。

これで UDP が通れない場合や、TCP で 80 番しか空いていない場合とかも解決です。ちなみに ICE-TCP は TLS も含みますので DTLS-SRTP over TLS も可能になります。

https://dl.dropbox.com/s/bkvup0n2utmxzx0/ice_tcp_dtls.png

SFU に求められる機能

WebRTC SFU は MCU と比較すると様々な点で弱い点がありますが、そこを補うために MCU でも利用されている仕組みを求められます。

動的なビットレートの変更

配信者の回線が不安定だったり、マシンスペックが低いと判断したら、動的に配信するビットレートを変更する機能です。

もともと P2P では実現している機能を SFU でも実現する必要があります。

この機能を実現することで MCU が得意とする会議システムへでの採用が増えると考えています。

音量により配信する音声や映像を制限する機能

配信するクライアントが 10 名になると WebRTC SFU の場合は受信する音声や映像が 9 名分になります。 これは受信するクライアントの負荷がとても高くなります。

そのため直近の音声を発した人 3 名程度のみの音声や映像を配信し、それ以外を配信しない仕組みを導入することで負荷を大幅に下げることが可能になります。

この機能を実現することで MCU が得意とする会議システムへでの採用が増えると考えています。

外部連携機能

SFU は音声や映像を変換する機能を持っていません。そのため大規模な配信を行うことは難しくなります。

そこで外部のツールと連動し、 WebRTC to HLS/MPEG-DASH といった大規模への配信を実現するような仕組みが必要になります。

広告

WebRTC SFU Sora

WebRTC SFU Sora

商用の WebRTC SFU です。価格は同時 100 接続で年間利用料ライセンス 60 万円です。毎年かかります。製品のサポート料金込みです。200 接続だと年間 120 万円です。

複数人数での会議や、 数百人への配信、一対一の面談など様々な用途に利用可能です。

パッケージで提供しますので、自社で運用が可能です。 AWS だろうが GCP だろうが、オンプレだろうがなんでも好きな環境で動かすことができます。

サーバさえあれば起動までは 10 分です。デモ機能が内蔵しているので動かすまで 15 分です。

  • 大変多くのお客様に採用いただいております
  • とにかく 落ちないこと を目的に作っています
  • とにかく 繋がること を目的に作っています
  • とにかく 手間がかからないこと を目的に作っています
  • 最新ブラウザのアップデートに追従しています
  • シグナリングサーバ内蔵ですので別途立てる必要はありません
  • TURN サーバ内蔵ですので別途立てる必要ありません
  • 日本語によるサポート対応しています
  • フルスクラッチ自前実装なのですべて把握しています
  • 1:1 の双方向に対応しています
  • 1:300 の片方向に対応しています
  • 3:300 といった配信者が複数の片方向にも対応しています
  • スポットライトという機能を利用することで 50 人以上の会議に対応しています
  • 録画機能があります
  • Chrome / Firefox / Edge / Safari といった主要ブラウザ全てに対応しています
  • Apache 2.0 ライセンスで JavaScript と iOS と Android のクライアント SDK を公開しています
  • Apache 2.0 ライセンスで React Native 向け WebRTC ライブラリを公開しています
  • 既存システムとの連携を重視しており、Web フック機能を利用して簡単に連携が可能です
    • 認証や、クライアントの接続切断などもすべて HTTP での通知を既存のシステムに送ることができます

興味のある方はお気軽に sora at shiguredo.jp までお問い合わせください。

紹介や検討資料も公開しております。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment