Skip to content

Instantly share code, notes, and snippets.

@voluntas
Last active March 16, 2024 15:30
Show Gist options
  • Star 63 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save voluntas/c4048b75e091bc5fae5b45f32a8f598d to your computer and use it in GitHub Desktop.
Save voluntas/c4048b75e091bc5fae5b45f32a8f598d to your computer and use it in GitHub Desktop.
時雨堂 WebRTC 入門 (講師資料) v2023-05

時雨堂 WebRTC 入門 (講師資料) v2023-05

これは時雨堂が開催しているオンラインイベントである WebRTC 入門の 講師用 の資料であり、 参加者用の資料ではありません。

時雨堂 WebRTC 入門 オンラインイベント

概要

ChatGPT がある今、学ぼうと思えば好きなだけ学べる時代がきています。 ただ「正しい情報」をなんとなく知っている事はとても重要だと考えています。

進め方

今回の WebRTC 入門はまず最後まで大まかに話をしていきます。 その後、残り時間を利用して、細かく話をしていきます。

資料表示用の画面と iPad を画面共有してホワイトボード的な使い方をしていきます。

お願い

是非 Discord にメモを残していってください。 後から振り返るとき、参加者の皆に有用だと思います。

ライセンス

Creative Commons — 表示 - 非営利 - 改変禁止 4.0 国際 — CC BY-NC-ND 4.0

参考書

はじめに | 好奇心旺盛な人のための WebRTC

とてもお勧めです。

O'Reilly Japan - ハイパフォーマンス ブラウザネットワーキング

少し古い本ですが大変お勧めです。

WebRTC でよくある間違い

  • WebRTC は簡単
    • ブラウザで利用するだけなら簡単です
    • 中身は本当に難しいです
  • WebRTC はブラウザだけで利用できる
    • WebRTC はプロトコルスタックなので、ブラウザ外でも利用できる
    • むしろ Discord や WhatsApp といった非ウェブアプリで利用されている
  • WebRTC は P2P でしか利用できない
    • WebRTC は P2P だけでなく、クライアント・サーバーモデルでも利用できる
  • WebRTC は P2P だから超低遅延を実現している
    • P2P といっても、いろいろなネットワークを経由して繋がる
    • WebRTC が超低遅延なのは P2P だからではなく、WebRTC プロトコルスタックのおかげ
  • WebRTC は UDP だから超低遅延を実現している
    • インターネットは UDP だろうが TCP だろうが速度一緒なのを忘れている
    • WebRTC が超低遅延なのは UDP だけでなく WebRTC プロトコルスタックのおかげ

WebRTC の歴史

WebRTC の歴史といっても、WebRTC ができた経緯とかではなく、 WebRTC 自体がどのように進化してきたかを話をしていきます。

ここで話すことは、基本的に役に立ちません。 ただ、歴史を知ることで、WebRTC がどのように進化してきたかを知ることができます。

libwebrtc

Google が主に開発している WebRTC ライブラリです。世の中の多くの製品はこのライブラリを利用しています。 性能、品質ともに一級品です。これに勝てるライブラリを実装する事はほとんど不可能でしょう。10 年以上大金をつぎ込んで開発されてきています。

ただし、4 週間毎にアップデートし、さらにセキュリティパッチは最新バージョンにしか反映されません。

ソースコード容量は約 16 GB です。

マルチストリーム

もともと WebRTC では P2P での通信を想定していますが、ビジネスでは P2P だけではなく、SFU のようにサーバー経由でうまいことやりたくなってきました。実際多くの WebRTC を使ったビジネスではサーバー経由である WebRTC SFU が採用されています。

この SFU を使った場合、必ず複数のストリームを送受信する必要があります。そこで登場したのがマルチストリームという概念です。 簡単に言えば複数の音声や映像を送信する仕組みです。マルチトラックとは異なります。

マルチトラックは 1 ストリームに複数の音声と映像を含む方式です。勘違いしやすいのでここは注意してください。

マルチストリームは 1 RTCPeerConnection に複数のストリームを追加します。基本的には ontrack コールバックを管理するだけで良くなります。配信者が 2 名いても、視聴者は 1 つの RTCPeerConnection を春だけで良くなるのがマルチストリームです。

ポート共通化

利用するポートを減らそうという取り組みがなされてきて、実際現在では 1 つのポートで複数のストリームを送受信することができます。

H.264

WebRTC は最初 H.264 には対応していませんでした。なぜなら H.264 コーデックにはパテントがあります。 ただこれを力業で解決したのが Cisco の OpenH264 です。MPEG-LA のライセンスを Cisco が負担するというものです。

これのおかげで WebRTC で H.264 を気軽に利用できるようになりました。

Cisco が提供している OpenH264 のバイナリを利用する限りは、無料で利用することができます。 ソースコードをコンパイルして使う場合は、ライセンスは自前で払う必要があります。

結局 H.264 は今も生きています。むしろ主流です。ライセンスが複雑ではなく、ハードウェアアクセラレーターが必ずと言っていいほど端末に乗っているというのは圧倒的です。

VP9

VP8 から始まった WebRTC のコーデックですが、 Chrome は早い段階で VP8 の次の VP9 に対応しました。 それに続き Firefox が対応し、今では Safari も対応しています。

ただ、Safari の VP9 対応はおまけ的なモノだと感じています。なぜなら Safari が VP9 に対応したのは YouTube のためです。 YouTube は VP9 に対応していないと 4K 以上の動画を再生できないからです。

サイマルキャスト

WebRTC のサイマルキャストは 1 つの映像ソースを、複数の画質で配信する仕組みです。1080p / 720p / 360p などの画質を同時に配信することができます。基本的には 3 本の映像ソースを同時に配信することになります。

libwebrtc の場合は解像度毎に配信可能な本数と、要求ビットレートがハードコードされています。

// These tables describe from which resolution we can use how many
// simulcast layers at what bitrates (maximum, target, and minimum).
// Important!! Keep this table from high resolution to low resolution.
constexpr const SimulcastFormat kSimulcastFormats[] = {
    {1920, 1080, 3, webrtc::DataRate::KilobitsPerSec(5000),
     webrtc::DataRate::KilobitsPerSec(4000),
     webrtc::DataRate::KilobitsPerSec(800)},
    {1280, 720, 3, webrtc::DataRate::KilobitsPerSec(2500),
     webrtc::DataRate::KilobitsPerSec(2500),
     webrtc::DataRate::KilobitsPerSec(600)},
    {960, 540, 3, webrtc::DataRate::KilobitsPerSec(1200),
     webrtc::DataRate::KilobitsPerSec(1200),
     webrtc::DataRate::KilobitsPerSec(350)},
    {640, 360, 2, webrtc::DataRate::KilobitsPerSec(700),
     webrtc::DataRate::KilobitsPerSec(500),
     webrtc::DataRate::KilobitsPerSec(150)},
    {480, 270, 2, webrtc::DataRate::KilobitsPerSec(450),
     webrtc::DataRate::KilobitsPerSec(350),
     webrtc::DataRate::KilobitsPerSec(150)},
    {320, 180, 1, webrtc::DataRate::KilobitsPerSec(200),
     webrtc::DataRate::KilobitsPerSec(150),
     webrtc::DataRate::KilobitsPerSec(30)},
    // As the resolution goes down, interpolate the target and max bitrates down
    // towards zero. The min bitrate is still limited at 30 kbps and the target
    // and the max will be capped from below accordingly.
    {0, 0, 1, webrtc::DataRate::KilobitsPerSec(0),
     webrtc::DataRate::KilobitsPerSec(0),
     webrtc::DataRate::KilobitsPerSec(30)}};

mDNS

WebRTC の仕様上ローカル IP アドレスが SDP 的に取得できてしまう問題を解決するために出てきた仕組みです。 そもそもローカル IP を知っても解決できることはそうそうないので mDNS の仕組みを使うことで隠蔽してしまおうという仕組みです。

プラン B とユニファイドプラン

SDP のフォーマットが変わった話です。

SDP の m= が増えていくとリソースを食うので m= の中でメディアトラックを増やしてくのが Plan B です。 WebRTC はそもそも P2P なのでこの方式だったのですが、 P2P はビジネスでは現実的ではない、 WebRTC SFU が流行ってきた、 マルチストリームが出てきたので、いろいろなコーデックにも対応したくなったと、まさに WebRTC の流れの影響を受けた変更でした。

最終的にはユニファイドプランと呼ばれる 1 メディアストリームトラック 1 m= モデルに切り替わりました。今はこれが普通です。

RTP 拡張枠の増加

RTP 拡張という独自に RTP に機能を付けられる仕組みがあります。これが増えすぎて 1 バイト枠 (16 個) だけで足りなくなったので、 2 バイト枠まで対応しました。もともと RTP 拡張自体 2 バイト枠が定義されていたのですが、今まで実装されていなかったという経緯があります。

ただいきなり 2 バイト枠に切り替えると破綻してしまうのと、1 バイト枠しか想定していない拡張があったりと、なかなかやっかいでした。

WebRTC を学ぶポイント

どこまで深追いしたほうがいい?

求めるレベルによりますが、 WebRTC は複雑な仕組みが多いので、深追いするとかなりの時間がかかります。 そのため、どこまで学ぶべきかを事前に決めるべきです。

SDP は読めた方がいいの?

利用するだけであれば P2P や SFU どちらかをつかい、ブラウザだけでなら読める必要はありません。 もしブラウザ以外から WebRTC を使う場合は必ず読めるようになった方がいいです。

RTP や SCTP について学ぶ必要性は?

基本的に必要ありません。ただ 知って損することはない です。実際 W3C の資料を読む際にも RTP や SCTP の知識があると良いこともあります。

TURN の仕組みは理解した方がいい?

多くの場合で通信関連の不具合や問題が起きるのが TURN がらみです。 うまく繋がらないとか、詰まるとか、色々な問題が起きます。 そのため TURN については、少し深追いしておくのが良いです。

この入門でも TURN についてはしっかり解説します。

WebRTC の仕組み

WebRTC は多くの技術の組み合わせです。ここではできる限りは取り上げていきます。 理解して詳しくなるのではなく なんとなく聞いたことがある を重視します。

W3C

W3C の WebRTC 関連の資料は本当に細かく、大量にありますがぜひ読むべきです。 タダ、どこから読めばいいかわからない、何を読めばいいか解らないというと思うので、 ここではざっと W3C の紹介をしていこうと思います。

メディアトランスポート (Media Transport)

RFC 8834: Media Transport and Use of RTP in WebRTC

今まではメディアチャネルと呼んできましたが、RFC 的にメディアトランスポートと書かれていることもあり、 そちらにあわせることにします。

メディアトランスポートは音声と映像をリアルタイムでやりとりする仕組みです。 プロトコルには RTP が利用されており、通信の暗号化には DTLS-SRTP が利用されます。

よく WebRTC P2P での例はこのメディアトランスポートを利用して、ブラウザで音声や映像をやりとりしています。

データチャネル (Data Channels)

メディアトランスポートが音声と映像専用ですが、データチャネルは なんでも です。 つまりバイナリやテキストをやりとりすることができます。

データチャネルは Data Channels over SCTP over DTLS over UDP というミルフィーユプロトコルです。

SDP では m=application で利用します。

この RFC 8864: Negotiation Data Channels Using the Session Description Protocol (SDP) の仕様は全て採用されていないので、忘れましょう。

データチャネルと DTLS

DTLS はデータグラム向けの TLS です。TLS と何が違うの?って思うと思いますが、ハンドシェイク部分に再送機能が付いています。よく誤解されるのですがアプリケーションデータでは再送はしません。

アプリケーションデータというのは暗号化されたトンネル通信部分です。

データチャネルの再送や順番保証の仕組みは DTLS ではなく SCTP が行っています。 TLS の場合は TCP がやってくれています。

DTLS は 1.2 が利用されています。1.3 は今のところ対応される様子はありません。 ちなみに、OpenSSL はまだ DTLS 1.3 を実装していません。

SCTP

SCTP は TCP と UDP のいいところ取りをした第 3 のプロトコルです。しかし一般的に使われることはありません。 理由は簡単で世のルーターが SCTP をうまくハンドリングできないことがあるからです。

そこで WebRTC ではまず SCTP を DTLS で隠蔽し、かつ UDP に載せています。これでルーターはただの UDP として SCTP を扱うことになります。

SCTP は本当に複雑なプロトコルです。また勘違いされやすいのが、データチャネルは SCTP 上に構築された プロトコルです。

SCTP は信頼性の高いプロトコルです。ただし SCTP の拡張を利用することで信頼性を下げることができます。 これは受信者に ここまで受け取ったことにする ということができるので、データチャネルのような信頼性の低いプロトコルを実現することができます。

SCTP は基本的には順番保証があり、かつ再送があります。これは TCP と同じです。しかし拡張する仕組みを追加することで、再送を諦める仕組みが入ります。データチャネルの最大再送信回数や最大パケットライフタイムはこの拡張の仕組みを使っています。

もともと SCTP は大きくても 64 KB 程度のメッセージを送信するための仕組みでした、そのため SCTP 標準では大きなメッセージを送ることができませんでした。 そのために拡張機能を使い大きなメッセージを送れるようにしています。

WebRTC プロトコルスタック

WebRTC は多くのプロトコルを利用しています。利用されているプロトコルは全て RFC になっています。

SDP

SDP は双方でやりとりをする際に、自分が利用可能な技術を教え合うプロトコルです。 ハンドシェイクのために Offer と Answer という 2 つのメッセージをやりとりします。

Offer に対して Answer を返します。

SDP は更新ができるプロトコルです。WebRTC の P2P ですと、一度 Offer/Answer のやりとりをすると終わりというイメージがあるため、 あまり知られていません。WebRTC SFU では Offer/Answer のやりとりを繰り返すことで、新たなピアを追加することができます。 この際の Offer/Answer は Re-Offer / Re-Answer と読んだりします。

v=0
o=- 3858642994894036120 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=extmap-allow-mixed
a=msid-semantic: WMS
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 0 8 13 110 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:Mvhj
a=ice-pwd:G9mloFZqj1EuYH6grVdTh9Ip
a=ice-options:trickle
a=fingerprint:sha-256 41:C0:9D:63:D9:07:64:7C:62:35:05:33:FD:4A:F9:48:31:84:A6:37:45:58:90:B6:98:2C:3D:7A:45:8E:E1:D9
a=setup:actpass
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=recvonly
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:126 telephone-event/8000
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 35 36 37 38 102 103 104 105 106 107 108 109 127 125 39 40 41 42 43 44 45 46 47 48 112 113 114 115 116 117 118 49
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:Mvhj
a=ice-pwd:G9mloFZqj1EuYH6grVdTh9Ip
a=ice-options:trickle
a=fingerprint:sha-256 41:C0:9D:63:D9:07:64:7C:62:35:05:33:FD:4A:F9:48:31:84:A6:37:45:58:90:B6:98:2C:3D:7A:45:8E:E1:D9
a=setup:actpass
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=recvonly
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:100 VP9/90000
a=rtcp-fb:100 goog-remb
a=rtcp-fb:100 transport-cc
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=fmtp:100 profile-id=2
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:35 VP9/90000
a=rtcp-fb:35 goog-remb
a=rtcp-fb:35 transport-cc
a=rtcp-fb:35 ccm fir
a=rtcp-fb:35 nack
a=rtcp-fb:35 nack pli
a=fmtp:35 profile-id=1
a=rtpmap:36 rtx/90000
a=fmtp:36 apt=35
a=rtpmap:37 VP9/90000
a=rtcp-fb:37 goog-remb
a=rtcp-fb:37 transport-cc
a=rtcp-fb:37 ccm fir
a=rtcp-fb:37 nack
a=rtcp-fb:37 nack pli
a=fmtp:37 profile-id=3
a=rtpmap:38 rtx/90000
a=fmtp:38 apt=37
a=rtpmap:102 H264/90000
a=rtcp-fb:102 goog-remb
a=rtcp-fb:102 transport-cc
a=rtcp-fb:102 ccm fir
a=rtcp-fb:102 nack
a=rtcp-fb:102 nack pli
a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
a=rtpmap:103 rtx/90000
a=fmtp:103 apt=102
a=rtpmap:104 H264/90000
a=rtcp-fb:104 goog-remb
a=rtcp-fb:104 transport-cc
a=rtcp-fb:104 ccm fir
a=rtcp-fb:104 nack
a=rtcp-fb:104 nack pli
a=fmtp:104 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f
a=rtpmap:105 rtx/90000
a=fmtp:105 apt=104
a=rtpmap:106 H264/90000
a=rtcp-fb:106 goog-remb
a=rtcp-fb:106 transport-cc
a=rtcp-fb:106 ccm fir
a=rtcp-fb:106 nack
a=rtcp-fb:106 nack pli
a=fmtp:106 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:107 rtx/90000
a=fmtp:107 apt=106
a=rtpmap:108 H264/90000
a=rtcp-fb:108 goog-remb
a=rtcp-fb:108 transport-cc
a=rtcp-fb:108 ccm fir
a=rtcp-fb:108 nack
a=rtcp-fb:108 nack pli
a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
a=rtpmap:109 rtx/90000
a=fmtp:109 apt=108
a=rtpmap:127 H264/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f
a=rtpmap:125 rtx/90000
a=fmtp:125 apt=127
a=rtpmap:39 H264/90000
a=rtcp-fb:39 goog-remb
a=rtcp-fb:39 transport-cc
a=rtcp-fb:39 ccm fir
a=rtcp-fb:39 nack
a=rtcp-fb:39 nack pli
a=fmtp:39 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f
a=rtpmap:40 rtx/90000
a=fmtp:40 apt=39
a=rtpmap:41 H264/90000
a=rtcp-fb:41 goog-remb
a=rtcp-fb:41 transport-cc
a=rtcp-fb:41 ccm fir
a=rtcp-fb:41 nack
a=rtcp-fb:41 nack pli
a=fmtp:41 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=f4001f
a=rtpmap:42 rtx/90000
a=fmtp:42 apt=41
a=rtpmap:43 H264/90000
a=rtcp-fb:43 goog-remb
a=rtcp-fb:43 transport-cc
a=rtcp-fb:43 ccm fir
a=rtcp-fb:43 nack
a=rtcp-fb:43 nack pli
a=fmtp:43 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=f4001f
a=rtpmap:44 rtx/90000
a=fmtp:44 apt=43
a=rtpmap:45 AV1/90000
a=rtcp-fb:45 goog-remb
a=rtcp-fb:45 transport-cc
a=rtcp-fb:45 ccm fir
a=rtcp-fb:45 nack
a=rtcp-fb:45 nack pli
a=rtpmap:46 rtx/90000
a=fmtp:46 apt=45
a=rtpmap:47 AV1/90000
a=rtcp-fb:47 goog-remb
a=rtcp-fb:47 transport-cc
a=rtcp-fb:47 ccm fir
a=rtcp-fb:47 nack
a=rtcp-fb:47 nack pli
a=fmtp:47 profile=1
a=rtpmap:48 rtx/90000
a=fmtp:48 apt=47
a=rtpmap:112 H264/90000
a=rtcp-fb:112 goog-remb
a=rtcp-fb:112 transport-cc
a=rtcp-fb:112 ccm fir
a=rtcp-fb:112 nack
a=rtcp-fb:112 nack pli
a=fmtp:112 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f
a=rtpmap:113 rtx/90000
a=fmtp:113 apt=112
a=rtpmap:114 H264/90000
a=rtcp-fb:114 goog-remb
a=rtcp-fb:114 transport-cc
a=rtcp-fb:114 ccm fir
a=rtcp-fb:114 nack
a=rtcp-fb:114 nack pli
a=fmtp:114 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=64001f
a=rtpmap:115 rtx/90000
a=fmtp:115 apt=114
a=rtpmap:116 red/90000
a=rtpmap:117 rtx/90000
a=fmtp:117 apt=116
a=rtpmap:118 ulpfec/90000
a=rtpmap:49 flexfec-03/90000
a=rtcp-fb:49 goog-remb
a=rtcp-fb:49 transport-cc
a=fmtp:49 repair-window=10000000

ICE

ICE は P2P での通信を確立するための仕組みです。実際に P2P でのデータをやりとりするのはまた別です。

ピアが利用可能な経路を収集(Gathering)して、その中から最適な経路を選択します。 最適な経路というのは優先度によって決められます。この優先度というのは、 ローカルネットワークが一番高く、リレー(TURN)が一番低くなります。

SDP と ICE はセットで利用されます。SDP に ice-uflag と ice-pwd が乗っかってきます。 この ice-uflag と ice-pwd は STUN パケットを送る際に Fingerprint や Message-Integrity に利用され、メッセージの認証メカニズムとして利用されます。

STUN

STUN は、NAT を経由するネットワーク上のデバイスが自分のパブリック IP アドレスとポートを発見することを支援しますためのプロトコルです。

グローバル環境にいる STUN サーバに STUN リクエストを飛ばして、自分の IP アドレスとポートを返してもらいます。 プロトコル自体はとてもシンプルなモノです。WebRTC で STUN を利用する場合は認証も暗号化も行いません。 STUN プロトコルは UDP を利用します。

TURN

TURN は NAT 越えができない場合にリレーするプロトコルです。 TURN があることで NAT 越えができない時に無理矢理サーバー経由で通信を行います。

TURN は通信の復号してしまうとよく勘違いされますが、 TURN はリレーサーバーなので、一切通信自体には手を加えません。

TURN には UDP / TCP / TLS over TCP / DTLS over UDP があり、さらに IPv4 / IPv6 も可能です。

通信方式

  • Allocate-Request
  • Permission-Request
  • ChannelBind-Request
  • Send-Indication / Data-Indication

優先順位

TURN には優先度という概念がありません。すべての TURN 経路に対して allocate-request を送信します。 allocate-request というのは TURN の経路を確保するためのリクエストです。

TURN Urls の払い出しに TURN-UDP と TURN-TCP と TURN-TLS を設定しているのであれば、 TURN-UDP と TURN-TCP と TURN-TLS のすべてに allocate-request を同時に送信します。 そして一番早く帰ってきた経路を利用します。

TURN-TCP (TURN-TLS 含む)

WebRTC で利用する TURN-TCP は厳密には TURN-TCP ではなく TURN-UDP over TCP です。 本来の TURN TCP 拡張の実装ではありません。TURN TCP 拡張はコントロールとデータにわけて TCP を張るのですが、WebRTC ではコントロールとデータを同じポートでやりとりします。つまり TCP 拡張は利用されていません。

RFC 6062 - Traversal Using Relays around NAT (TURN) Extensions for TCP Allocations

ICE Lite

ICE Lite はたまにでてきますが、仕様が RFC ドラフトしかないので理解している人が少ないです。 ICE Lite を利用する場合があるのは SFU 側です。クライアントは常に ICE Full を利用します。

draft-rescorla-mmusic-ice-lite-00

ICE Lite を利用すると、SFU から STUN Binding-Request が送られてきません。

RTP

RTP は音声と映像をリアルタイムでやりとりするためのプロトコルです。基本的に UDP を想定しています。 RTP は暗号化の機能は持っておらず、暗号化する場合は SRTP を利用します。

プロトコル自体もシンプルで、ヘッダーにはシーケンス番号やタイムスタンプなどが入っており、あとはペイロード (実際の音声や映像がエンコードされたバイナリ) が含まれているだけです。

RTP は分割の機能を持っています、これは MTU に収まらない場合に分割して送信するための機能です。 4K の映像を送る場合とかは 15 Mbps 必要なので、かなり細かく分割する必要があります。

RTP ヘッダー拡張

WebRTC では多くの RTP ヘッダー拡張が採用されています。これは主に libwebrtc が勝手に定義してます。 libwebrtc というか Google が、です。

RTP ヘッダーは決められた値以外の値を入れる場所がないので拡張を利用します。拡張は SDP でお互いが利用できる拡張をハンドシェイクします。 もし利用できない拡張が送られてきても無視する必要があります。

RTCP

RTP はデータを運ぶプロトコル、RTCP はそれをコントロールするプロトコルです。 WebRTC では統計情報や状態の通知、全画面要求や再送要求、帯域推定などに利用されています。

sequenceDiagram
    participant C1 as クライアント
    participant C2 as クライアント2
    note over C1,C2: WebRTC 確立
    C1-)C2: RTCP-SR
    C2-)C1: RTCP-RR
    C2-)C1: RTCP-RR / RTCP-RTPFB (Generic NACK)
    C2-)C1: RTCP-RR / RTCP-PSFB (REMB)
    C2-)C1: RTCP-RR / RTCP-PSFB (PLI)
    C1-)C2: RTCP-SR
    C2-)C1: RTCP-RR
    C2-)C1: RTCP-RR / RTCP-PSFB (REMB)
    C2-)C1: RTCP-RR / RTCP-PSFB (REMB)

SRTP/SRTCP

WebRTC で利用されている音声や映像の暗号化は SRTP というプロトコルを利用しています。これは RTP のセキュア版です。 ただ暗号化ということはどうにかして双方で同じ鍵を持つ必要があります。WebRTC ではこの双方で同じ鍵を持つ方法に DTLS を利用しています。

DTLS-SRTP

メディアトランスポートでは SRTP 向けの暗号鍵生成用に DTLS のハンドシェイクを利用しています。 メディアトランスポートだけを利用する場合はアプリケーションデータは利用されません。

DTLS で取得した MasterSecret を利用して SRTP/SRTCP の鍵を生成します。

sequenceDiagram
    participant C1 as クライアント1
    participant C2 as クライアント2
    C1-)C2: DTLS ClientHello
    C2-)C1: DTLS ServerHello...ServerHelloDone
    C1-)C2: DTLS Certificate...ClientVerify
    C2-)C1: DTLS ChangeCipherSpec...ServerVerify
    note over C1,C2: DTLS 確立
    C1-)C2: SRTP (MediaChannel)
    C1-)C2: SCTP over ApplicationData (DataChannel)
    C2-)C1: SRTP (MediaChannel)

WebRTC シグナリング

シグナリングというのは WebRTC を P2P で利用するにあたり SDP 情報を交換する手段です。 簡単に言うと Offer と Answer のやりとりします。

WebRTC でシグナリングの規格は定義されていません。

https://webrtcforthecurious.com/ja/docs/10-history-of-webrtc/#%e5%b7%a8%e4%ba%ba%e3%81%ae%e8%82%a9%e3%81%ae%e4%b8%8a%e3%81%ab%e7%ab%8b%e3%81%a4

シグナリングなどの再標準化も意識的に行いませんでした。 これはすでに SIP やその他の IETF 以外の取り組みで解決されており、非常に政治的な問題になりかねないと考えたからです。最終的には、この空間に加えるべき価値があまりないと感じたのです

Review of Signaling in different WebRTC applications

シグナリングサーバー

シグナリングを担当することをシグナリングサーバーと呼びます。クライアントから SDP を受け取ってもう一つのクライアントに SDP を渡し、その SDP に対応する SDP を受け取り、クライアントへ渡すという仕組みです。

sequenceDiagram
  participant C1 as クライアント1
  participant SS as シグナリングサーバー
  participant C2 as クライアント2
  C1->>+SS: type: offer
  SS->>+C2: type: offer
  C2->>-SS: type: answer
  SS->>-C1: type: answer
  note over C1,C2: ICE
  note over C1,C2: DTLS
  note over C1,C2: WebRTC 確立

HTTP

シグナリングに通常の HTTP を使うやり方もあります。ただし、offer を通知する仕組みが必ず必要になるので、 SSE を使うなどする必要があります。

sequenceDiagram
  participant C1 as クライアント1
  participant SS as シグナリングサーバー
  participant C2 as クライアント2
  C1->>SS: type: offer
  note right of SS: ここを SSE などでなんとかする必要がある
  SS->>C2: type: offer

WebSocket

WebSocket が利用されるのかというと、双方向のやりとりがとても簡単だからです。とはいえ XHR と SSE でも困りません。

WebSocket を利用すると、WebRTC でよく使われるメッセージングを実装することができます。そのため WebSocket が利用されることが多いためと、考えています。

sequenceDiagram
  participant C1 as クライアント1
  participant SS as シグナリングサーバー
  participant C2 as クライアント2
  par
    note over C1,SS: WebSocket 確立
  and
    note over SS,C2: WebSocket 確立
  end
  C1->>SS: type: offer
  SS->>C2: type: offer

WHIP

WebRTC ではシグナリングの規格が定義されていませんが、 これだと困る場合があります。それは 配信ツール を利用する場合です。

OBS というオープンソース (GPLv3) の配信ツールがあります。これから WebRTC を利用するにはシグナリングの規格が必要です。

そこで登場したのが WHIP です。WHIP はとてもシンプルな規格です。WHIP はマルチストリームには対応せず、あくまで音声トラックと映像トラックを 1 ずつの 1 ストリームを扱います。

WebRTC cracks the WHIP on OBS - webrtcHacks

sequenceDiagram
  participant OBS as OBS
  participant WE as WHIP Endpoint
  participant MS as Media Server
  participant WS as WHIP Resource
  OBS->>+WE: HTTP POST (SDP Offer)
  WE-->>-OBS: HTTP 201 Created (SDP Answer)
  note over OBS,MS: ICE
  note over OBS,MS: DTLS
  note over OBS,MS: WebRTC 確立
  OBS->>+WS: HTTP DELETE
  WS-->>-OBS: HTTP 200 OK

XMPP

基本的に利用されていません。Jitsi が使っています。

データチャネル

WebRTC を一度確立してしまえば、シグナリングにデータチャネルを使うこともできます。 一度確立したのにシグナリング使うのか?と思うかも知れません。これはマルチトラックやマルチストリームで再ハンドシェイクする必要があります。

再ハンドシェイク

マルチストリームを利用する場合は、再ハンドシェイクが必要になります。再ハンドシェイクは re-offer と re-answer と呼ばれています。

その際 SDP の m= の数を減らすことはできません。

WebRTC セキュリティ

WebRTC では様々なセキュリティの技術が利用されています。SRTP/SRTCP や DTLS 、さらには SDP を利用した証明書の Fingerprint チェック、STUN ice-ufrag / ice-pwd 、TURN の usrename / credential。

RFC 8826 - Security Considerations for WebRTC

WebRTC SFU の仕組み

WebRTC SFU は現時点で WebRTC をビジネスで利用する場合のデファクトスタンダードです。 WebRTC 本来の P2P の利用ではなく、クライアントサーバーモデルです。

SFU の課題

  • PLI ストーム (勝手に命名)
  • 帯域推定

WebRTC の今

現在の WebRTC のトレンドについて説明します。簡単に言えば、最近導入された技術です。

AV1

AV1 は Alliance for Open Media が開発しているコーデックで、VP9 よりも圧縮率が高いといわれています。 Chrome 90 から WebRTC でも AV1 が利用できるようになり、 Chrome 113 からサイマルキャストで AV1 が利用可能になりました。

AV1 はリリース初期は負荷がとても高かったのですが今はほとんど VP9 と変わりません。

H.265

H.265 は H.264 の後継ですが、パテントプールが複数に分かれてしまっている事から、色々やっかいな問題を抱えています。 ただ、ここに来て Chrome が H.265 に対応し始めています。

https://chromestatus.com/feature/5186511939567616

WebRTC ライブラリにも H.265 の対応が入り始めています。これは Chrome がソフトウェアの処理をせず、 ハードウェアアクセラレーターのみで処理するため、ライセンスが不要になるためです。

SVC

SVC は 1 ストリームに複数画質を押し込める仕組みです。

SFU + SVC にはパテントが存在するため、利用する場合は注意してください。

SFU

今は WebRTC SFU 戦国時代に入っています。MCU を採用しているサービスはなく、 すべてのサービスが SFU を採用しています。

P2P と SFU を併用しているサービスもありますが、Google Meet などは P2P を完全にすて、SFU のみに移行しています。

Google Meet の SFU の挙動は独特

1 ストリームに対してサーバ側で動的に切り替える。 最初から配信するストリーム先を決めてしまっている。

さらに音声を 3 本に絞ったりしています。

ICE Restart

ICE Restart はネットワークインターフェースの変更、たとえば LTE から Wifi への切り替えなどで発生する際に、必要な機能です。通常 IP アドレスが変わると、WebRTC は切断されてしまいますが、ICE Restart を利用し ICE で新規にペアを発見することで新しい接続を行います。

ICE Restart が発生すると DTLS はやり直しが入ります。ただペアリングがすぐ終われば DTLS 自体の通信は一瞬です。 そのため、一瞬音声や映像が止まると感じることはあります。

TURN を利用していたとしてもこれは同じで、新しく Allocate Request を利用してコネクションを張り直します。

WHIP

OBS 30.0 で WebRTC が配信で利用可能になります。 配信ツールへ WebRTC が利用される良いきっかけになると思っています。

実際に Twitch が対応し始めています。

Beta 版でのテスト始まってます。

Release OBS Studio 30.0 Beta 3 · obsproject/obs-studio

WHEP

WHIP とは逆の視聴用の仕組みです。

E2EE

あまり興味がある人は以内と思いますが、簡単にだけ触れていこうと思います。

E2EE というのは End to End Encryption の略で、クライアント同士のみで暗号化をする仕組みです。WebRTC P2P は E2EE といって問題無いです。

SFU で E2EE を行う場合はクライアント側で WebRTC とは別に暗号化をする必要があります。

WebRTC SFU と E2EE は多くのサービスで利用されています。WhatsApp や Signal や Facebook Messange。MS Teams や Google Meeting、 Facetime でも採用されています。

WebRTC の今後

WebRTC が複雑すぎたということで、 Media over QUIC (MoQ) というのが出てきています。今後はこちらが主流になっていくと思いますが、だからといって WebRTC が廃れることはありません。今後 10 年は WebRTC は続いていくと考えて問題ありません。

そもそも Media over QUIC も結果的に RTP over QUIC だったりしており、暗号処理部分が変わるだけで、WebRTC と同じような仕組みになっています。

WebTransport

  • QUIC
  • HTTP/3
  • HTTP/2 へのフォールバック

HTTP/3 上の WebTransport と HTTP/2 上の WebTransport を使用して、サーバーにデータを送信し、サーバーからデータを受信します。WebSocket と同様に使用できますが、複数のストリーム、一方向のストリーム、アウトオブオーダーデリバリー、信頼性のあるトランスポートと信頼性のないトランスポートをサポートしています。

WebCodecs

音声、映像、画像の符号化・復号化のためのコーデックへのインタフェースを定義する仕様です。 WebCodecs はあくまでインターフェースです。

Warp

Warp は次世代の超低遅延型片方向配信向けプロトコルです。QUIC 上に開発されています。WebRTC の課題である「レイテンシを優先しすぎる問題」を解決するために独自で開発しています。最初は Twitch の規格でしたが、今では色々な企業が参加してきています。

  • 片方向
  • Twitch / Meta / Cisco / Google

QUIC 上のライブメディアトランスポートプロトコルである Warp のコア動作を定義する。メディアは、基礎となるメディアエンコーディングに基づいてオブジェクトに分割され、QUIC ストリーム上で独立して伝送される。QUIC ストリームは、配信順序に基づいて優先順位を付けられ、輻輳時に重要度の低いオブジェクトを飢餓状態にしたり、ドロップしたりすることができる

draft-lcurley-warp-04

RTP over WebTransport (QUIC)

WebTransport で双方向はすでに試されていますが、結果的には RTP を載せるという話になっています。 WebTransport を使うメリットがあまりないように感じます。

libwebrtc の代わり

基本的に代わりになるモノはありません。圧倒的です。 それでもいくつか libwebrtc を使っていない実装があるので参考までに。

WebRTC の OSS

OSS を採用すべきかどうか

最近の WebRTC の OSS はとても良くできていますので、OSS を採用する事を躊躇する必要はなくなってきています。

OSS を選ぶときのポイント

正直もう WebRTC がでて何年もでており、ほぼ好み問題です。事業に合っている OSS を採用すれば良いです。

WebRTC の SaaS を選ぶときのポイント

サービスの名前を具体的には出しません。

料金体系と SDK があるかどうかが判断ポイントになると思います。 特定の機能が必要な場合はすぐ判断出来ると思います。

課金体系

  • 従量課金
  • 転送量課金
  • 利用時間課金
  • ユーザ数課金
  • ルーム数課金
  • 録画容量課金

SDK

  • iOS
  • Android
  • JavaScript
  • Windows
  • macOS
  • Linux
  • Unity
  • C++
  • Python
    • aiortc
  • Go
    • Pion
  • Flutter
  • React Native

参考

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