これは時雨堂が 2024 年 2 月 8 日 (木) 14:00-18:00 で開催を予定しているオンラインイベント WebRTC 入門 v2024-02 の 講師用 の資料であり、 参加者用の資料ではありません。
時雨堂 WebRTC 入門 オンラインイベント v2024-02
ChatGPT がある今、学ぼうと思えば好きなだけ学べる時代がきています。 ただ「正しい情報」をなんとなく知っている事はとても重要だと考えています。
今回の WebRTC 入門はまず最後まで大まかに話をしていきます。 その後、残り時間を利用して、細かく話をしていきます。
資料表示用の画面と iPad を画面共有してホワイトボード的な使い方をしていきます。
是非 Discord にメモを残していってください。 後から振り返るとき、参加者の皆に有用だと思います。
Creative Commons — 表示 - 非営利 - 改変禁止 4.0 国際 — CC BY-NC-ND 4.0
この本を積極的に利用します。
O'Reilly Japan - ハイパフォーマンス ブラウザネットワーキング
少し古い本ですが大変お勧めです。
- 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 では P2P での通信を想定していますが、ビジネスでは P2P だけではなく、SFU のようにサーバー経由でうまいことやりたくなってきました。 実際多くの WebRTC を使ったビジネスではサーバー経由である WebRTC SFU が採用されています。
この SFU を使った場合、必ず複数のストリームを送受信する必要があります。そこで登場したのがマルチストリームという概念です。 簡単に言えば複数の音声や映像を送信する仕組みです。マルチトラックとは異なります。
マルチトラックは 1 ストリームに複数の音声と映像を含む方式です。勘違いしやすいのでここは注意してください。
- WebRTC in Firefox 38: Multistream and renegotiation - Mozilla Hacks - the Web developer blog
- Firefox multistream and renegotiation for Jitsi Videobridge - Mozilla Hacks - the Web developer blog
- Multistream を勝手にまとめる. Firefox 38 で実装された WebRTC の Multistream… | by V | Medium
利用するポートを減らそうという取り組みがなされてきて、実際現在では 1 つのポートで複数のストリームを送受信することができます。
- RTP/RTCP
- RFC 5761: Multiplexing RTP Data and Control Packets on a Single Port
- RFC 8858: Indicating Exclusive Support of RTP and RTP Control Protocol (RTCP) Multiplexing Using the Session Description Protocol (SDP)
- RFC 8872: Guidelines for Using the Multiplexing Features of RTP to Support Multiple Media Streams
- Multiplexing RTP and RTCP on a Single Port
- MULTIPLEXING RTP SESSIONS ON A SINGLE TRANSPORT
WebRTC は最初 H.264 には対応していませんでした。なぜなら H.264 コーデックにはパテントがあります。 ただこれを力業で解決したのが Cisco の OpenH264 です。MPEG-LA のライセンスを Cisco が負担するというものです。
これのおかげで WebRTC で H.264 を気軽に利用できるようになりました。
Cisco が提供している OpenH264 のバイナリを利用する限りは、無料で利用することができます。 ソースコードをコンパイルして使う場合は、ライセンスは自前で払う必要があります。
- cisco/openh264: Open Source H.264 Codec
- OpenH264
- Open-Sourced H.264 Removes Barriers to WebRTC
- archive です
- RFC 7742: WebRTC Video Processing and Codec Requirements
VP8 から始まった WebRTC のコーデックですが、 Chrome は早い段階で VP8 の次の VP9 に対応しました。 それに続き Firefox が対応し、今では Safari も対応しています。
ただ、Safari の VP9 対応はおまけ的なモノだと感じています。なぜなら Safari が VP9 に対応したのは YouTube のためです。 YouTube は VP9 に対応していないと 4K 以上の動画を再生できないからです。
WebRTC のサイマルキャストは 1 つの映像ソースを、複数の画質で配信する仕組みです。1080p / 720p / 360p などの画質を同時に配信することができます。基本的には 3 本の映像ソースを同時に配信することになります。
- simulcast.cc - Chromium Code Search
- RFC 8853: Using Simulcast in Session Description Protocol (SDP) and RTP Sessions
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)}};
ブラウザの場合は encodings の設定から自由に変更が可能です。
- https://w3c.github.io/webrtc-pc/#dom-rtcrtpencodingparameters
- https://w3c.github.io/webrtc-extensions/#rtcrtpencodingparameters
-
WebRTC の仕様上ローカル IP アドレスが SDP 的に取得できてしまう問題を解決するために出てきた仕組みです。 そもそもローカル IP を知っても解決できることはそうそうないので mDNS の仕組みを使うことで隠蔽してしまおうという仕組みです。
- https://webrtcforthecurious.com/ja/docs/03-connecting/#mdns
- draft-ietf-mmusic-mdns-ice-candidates-03
SDP のフォーマットが変わった話です。
SDP の m=
が増えていくとリソースを食うので m=
の中でメディアトラックを増やしてくのが Plan B です。
WebRTC はそもそも P2P なのでこの方式だったのですが、 P2P はビジネスでは現実的ではない、 WebRTC SFU が流行ってきた、
マルチストリームが出てきたので、いろいろなコーデックにも対応したくなったと、まさに WebRTC の流れの影響を受けた変更でした。
最終的にはユニファイドプランと呼ばれる 1 メディアストリームトラック 1 m=
モデルに切り替わりました。今はこれが普通です。
- Plan B: a proposal for signaling multiple media sources in WebRTC
- A Unified Plan for Using SDP with Large Numbers of Media Flows
- Unified Plan SDP 形式 - 移行計画 | WebRTC
- “Unified Plan” Transition Guide (JavaScript) - Google ドキュメント
RTP 拡張という独自に RTP に機能を付けられる仕組みがあります。これが増えすぎて 1 バイト枠 (16 個) だけで足りなくなったので、 2 バイト枠まで対応しました。もともと RTP 拡張自体 2 バイト枠が定義されていたのですが、今まで実装されていなかったという経緯があります。
ただいきなり 2 バイト枠に切り替えると破綻してしまうのと、1 バイト枠しか想定していない拡張があったりと、なかなかやっかいでした。
- RFC 8285 - A General Mechanism for RTP Header Extensions
- PSA: a=extmap-allow-mixed will be offered by default
求めるレベルによりますが、 WebRTC は複雑な仕組みが多いので、深追いするとかなりの時間がかかります。 そのため、どこまで学ぶべきかを事前に決めるべきです。
利用するだけであれば P2P や SFU どちらかをつかい、ブラウザだけでなら読める必要はありません。 もしブラウザ以外から WebRTC を使う場合は必ず読めるようになった方がいいです。
基本的に必要ありません。ただ 知って損することはない です。 実際 W3C の資料を読む際にも RTP や SCTP の知識があると良いこともあります。
特にデータチャネルを利用する場合は SCTP を学んでおくことで、適切な利用ができます。
多くの場合で通信関連の不具合や問題が起きるのが TURN がらみです。 うまく繋がらないとか、詰まるとか、色々な問題が起きます。 そのため TURN については、少し深追いしておくのが良いです。
この入門でも TURN についてはしっかり解説します。
WebRTC は多くの技術の組み合わせです。ここではできる限りは取り上げていきます。 理解して詳しくなるのではなく なんとなく聞いたことがある を重視します。
- WebRTC: Real-Time Communication in Browsers
- WebRTC API - Web APIs | MDN
- Real-Time Communication in WEB-browsers (rtcweb)
W3C の WebRTC 関連の資料は本当に細かく、大量にありますがぜひ読むべきです。 タダ、どこから読めばいいかわからない、何を読めばいいか解らないというと思うので、 ここではざっと W3C の紹介をしていこうと思います。
- MediaStreamTrack Insertable Media Processing using Streams
- Region Capture
- Screen Capture
- Scalable Video Coding (SVC) Extension for WebRTC
- WebRTC Encoded Transform
- Media Capture and Streams
- Audio Output Devices API
- Identifiers for WebRTC's Statistics API
- Media Capabilities
- MediaStreamTrack Content Hints
- WebRTC Priority Control API
RFC 8834: Media Transport and Use of RTP in WebRTC
今まではメディアチャネルと呼んできましたが、RFC 的にメディアトランスポートと書かれていることもあり、 そちらにあわせることにします。
メディアトランスポートは音声と映像をリアルタイムでやりとりする仕組みです。 プロトコルには RTP が利用されており、通信の暗号化には DTLS-SRTP が利用されます。
よく WebRTC P2P での例はこのメディアトランスポートを利用して、ブラウザで音声や映像をやりとりしています。
メディアトランスポートが音声と映像専用ですが、データチャネルは なんでも です。 つまりバイナリやテキストをやりとりすることができます。
データチャネルは Data Channels over SCTP over DTLS over UDP というミルフィーユプロトコルです。
- RFC 8831: WebRTC Data Channels
- RFC 8832: WebRTC Data Channel Establishment Protocol
- RFC 8841: Session Description Protocol (SDP) Offer/Answer Procedures for Stream Control Transmission Protocol (SCTP) over Datagram Transport Layer Security (DTLS) Transport
- "sctp-port" Attribute は使われていません
SDP では m=application
で利用します。
この RFC 8864: Negotiation Data Channels Using the Session Description Protocol (SDP) の仕様は全て採用されていないので、忘れましょう。
- RFC 6083: Datagram Transport Layer Security (DTLS) for Stream Control Transmission Protocol (SCTP)
- RFC 6347: Datagram Transport Layer Security Version 1.2
DTLS はデータグラム向けの TLS です。TLS と何が違うの?って思うと思いますが、ハンドシェイク部分に再送機能が付いています。よく誤解されるのですがアプリケーションデータでは再送はしません。
アプリケーションデータというのは暗号化されたトンネル通信部分です。
データチャネルの再送や順番保証の仕組みは DTLS ではなく SCTP が行っています。 TLS の場合は TCP がやってくれています。
DTLS は 1.2 が利用されています。1.3 は今のところ対応される様子はありません。 ちなみに、OpenSSL はまだ DTLS 1.3 を実装していません。
SCTP は TCP と UDP のいいところ取りをした第 3 のプロトコルです。しかし一般的に使われることはありません。 理由は簡単で世のルーターが SCTP をうまくハンドリングできないことがあるからです。
そこで WebRTC ではまず SCTP を DTLS で隠蔽し、かつ UDP に載せています。これでルーターはただの UDP として SCTP を扱うことになります。
SCTP は本当に複雑なプロトコルです。また勘違いされやすいのが、データチャネルは SCTP 上に構築された プロトコルです。
SCTP は信頼性の高いプロトコルです。ただし SCTP の拡張を利用することで信頼性を下げることができます。 これは受信者に ここまで受け取ったことにする ということができるので、データチャネルのような信頼性の低いプロトコルを実現することができます。
- RFC 3758: Stream Control Transmission Protocol (SCTP) Partial Reliability Extension
- RFC 6525: Stream Control Transmission Protocol (SCTP) Stream Reconfiguration
- RFC 7765: TCP and Stream Control Transmission Protocol (SCTP) RTO Restart
- RFC 8261: Datagram Transport Layer Security (DTLS) Encapsulation of SCTP Packets
- RFC 9260: Stream Control Transmission Protocol
SCTP は基本的には順番保証があり、かつ再送があります。これは TCP と同じです。しかし拡張する仕組みを追加することで、再送を諦める仕組みが入ります。データチャネルの最大再送信回数や最大パケットライフタイムはこの拡張の仕組みを使っています。
もともと SCTP は大きくても 64 KB 程度のメッセージを送信するための仕組みでした、そのため SCTP 標準では大きなメッセージを送ることができませんでした。 そのために拡張機能を使い大きなメッセージを送れるようにしています。
- RFC 6096: Stream Control Transmission Protocol (SCTP) Chunk Flags Registration
- これは SCTP の最新の RFC では統合されています。
WebRTC は多くのプロトコルを利用しています。利用されているプロトコルは全て RFC になっています。
SDP は双方でやりとりをする際に、自分が利用可能な技術を教え合うプロトコルです。 ハンドシェイクのために Offer と Answer という 2 つのメッセージをやりとりします。
Offer に対して Answer を返します。
SDP は更新ができるプロトコルです。WebRTC の P2P ですと、一度 Offer/Answer のやりとりをすると終わりというイメージがあるため、 あまり知られていません。WebRTC SFU では Offer/Answer のやりとりを繰り返すことで、新たなピアを追加することができます。 この際の Offer/Answer は Re-Offer / Re-Answer と読んだりします。
v=0
o=- 3358184816123717626 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:xVwm
a=ice-pwd:hsi24iz/FODRl0rmMFHzAHAA
a=ice-options:trickle
a=fingerprint:sha-256 9A:2A:81:5E:F8:24:5D:CF:53:8C:90:71:B7:22:F1:49:8D:B2:BA:9A:B8:65:49:5B:33:D6:2A:38:77:6D:F7:F2
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:xVwm
a=ice-pwd:hsi24iz/FODRl0rmMFHzAHAA
a=ice-options:trickle
a=fingerprint:sha-256 9A:2A:81:5E:F8:24:5D:CF:53:8C:90:71:B7:22:F1:49:8D:B2:BA:9A:B8:65:49:5B:33:D6:2A:38:77:6D:F7:F2
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 は P2P での通信を確立するための仕組みです。実際に P2P でのデータをやりとりするのはまた別です。
ピアが利用可能な経路を収集(Gathering)して、その中から最適な経路を選択します。 最適な経路というのは優先度によって決められます。この優先度というのは、 ローカルネットワークが一番高く、リレー(TURN)が一番低くなります。
- RFC 8445: Interactive Connectivity Establishment (ICE): A Protocol for Network Address Translator (NAT) Traversal
- RFC 8839: Session Description Protocol (SDP) Offer/Answer Procedures for Interactive Connectivity Establishment (ICE)
- ゲームでよくある「NAT タイプ」はどう判定しているの?
- How NAT traversal works · Tailscale
SDP と ICE はセットで利用されます。SDP に ice-uflag と ice-pwd が乗っかってきます。 この ice-uflag と ice-pwd は STUN パケットを送る際に Fingerprint や Message-Integrity に利用され、メッセージの認証メカニズムとして利用されます。
STUN は、NAT を経由するネットワーク上のデバイスが自分のパブリック IP アドレスとポートを発見することを支援しますためのプロトコルです。
グローバル環境にいる STUN サーバに STUN リクエストを飛ばして、自分の IP アドレスとポートを返してもらいます。 プロトコル自体はとてもシンプルなモノです。WebRTC で STUN を利用する場合は認証も暗号化も行いません。 STUN プロトコルは UDP を利用します。
TURN は NAT 越えができない場合にリレーするプロトコルです。 TURN があることで NAT 越えができない時に無理矢理サーバー経由で通信を行います。
TURN は通信の復号してしまうとよく勘違いされますが、 TURN はリレーサーバーなので、一切通信自体には手を加えません。
TURN には UDP / TCP / TLS over TCP / DTLS over UDP があり、さらに IPv4 / IPv6 も可能です。
-
RFC 6062: Traversal Using Relays around NAT (TURN) Extensions for TCP Allocations
-
Allocate-Request
-
Permission-Request
-
ChannelBind-Request
-
Send-Indication / Data-Indication
WebRTC で利用する TURN-TCP は TURN-UDP over TCP であって、TURN-TCP over TCP ではありません。
TURN には優先度という概念がありません。すべての TURN 経路に対して allocate-request を送信します。 allocate-request というのは TURN の経路を確保するためのリクエストです。
TURN Urls の払い出しに TURN-UDP と TURN-TCP と TURN-TLS を設定しているのであれば、 TURN-UDP と TURN-TCP と TURN-TLS のすべてに allocate-request を同時に送信します。 そして一番早く帰ってきた経路を利用します。
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 はたまにでてきますが、仕様が RFC ドラフトしかないので理解している人が少ないです。 ICE Lite を利用する場合があるのは SFU 側です。クライアントは常に ICE Full を利用します。
draft-rescorla-mmusic-ice-lite-00
ICE Lite を利用すると、SFU から STUN Binding-Request が送られてきません。
RFC 8445 に対応した処理は ice2
という SDP を利用する事ができるようになります。
- RFC 8445 - Interactive Connectivity Establishment (ICE): A Protocol for Network Address Translator (NAT) Traversal
- RFC 8839 - Session Description Protocol (SDP) Offer/Answer Procedures for Interactive Connectivity Establishment (ICE)
v=0
o=jdoe 2890844526 2890842807 IN IP4 203.0.113.141
s=
c=IN IP4 192.0.2.3
t=0 0
a=ice-options:ice2
a=ice-pacing:50
a=ice-pwd:asd88fgpdd777uzjYhagZg
a=ice-ufrag:8hhY
RTP は音声と映像をリアルタイムでやりとりするためのプロトコルです。基本的に UDP を想定しています。 RTP は暗号化の機能は持っておらず、暗号化する場合は SRTP を利用します。
プロトコル自体もシンプルで、ヘッダーにはシーケンス番号やタイムスタンプなどが入っており、あとはペイロード (実際の音声や映像がエンコードされたバイナリ) が含まれているだけです。
RTP は分割の機能を持っています、これは MTU に収まらない場合に分割して送信するための機能です。 4K の映像を送る場合とかは 15 Mbps 必要なので、かなり細かく分割する必要があります。
- RFC 3550 - RTP: A Transport Protocol for Real-Time Applications
- RFC 5761 - Multiplexing RTP Data and Control Packets on a Single Port
- RFC 8108 - Sending Multiple RTP Streams in a Single RTP Session
- RFC 8860 - Sending Multiple Types of Media in a Single RTP Session
WebRTC では多くの RTP ヘッダー拡張が採用されています。これは主に libwebrtc が勝手に定義してます。 libwebrtc というか Google が、です。
RTP ヘッダーは決められた値以外の値を入れる場所がないので拡張を利用します。拡張は SDP でお互いが利用できる拡張をハンドシェイクします。 もし利用できない拡張が送られてきても無視する必要があります。
- RFC 6464 - A Real-time Transport Protocol (RTP) Header Extension for Client-to-Mixer Audio Level Indication
- RFC 6465 - A Real-time Transport Protocol (RTP) Header Extension for Mixer-to-Client Audio Level Indication
- docs/native-code/rtp-hdrext/abs-send-time - src - Git at Google
- docs/native-code/rtp-hdrext/abs-capture-time - src - Git at Google
- RFC 8852 - RTP Stream Identifier Source Description (SDES)
- https://www.rfc-editor.org/rfc/rfc5450.txt
- docs/native-code/rtp-hdrext/video-content-type - src - Git at Google
- docs/native-code/rtp-hdrext/video-timing - src - Git at Google
- docs/native-code/rtp-hdrext/video-layers-allocation00 - src - Git at Google
- RTP Payload Format For AV1
- docs/native-code/rtp-hdrext/video-frame-tracking-id - src - Git at Google
- WebRTC header extensions review
- Video BFrame RTP Header Extension
RTP はデータを運ぶプロトコル、RTCP はそれをコントロールするプロトコルです。 WebRTC では統計情報や状態の通知、全画面要求や再送要求、帯域推定などに利用されています。
- RFC 3550: RTP: A Transport Protocol for Real-Time Applications
- RFC 5506 - Support for Reduced-Size Real-Time Transport Control Protocol (RTCP): Opportunities and Consequences
- RFC 7022: Guidelines for Choosing RTP Control Protocol (RTCP) Canonical Names (CNAMEs)
RTP 拡張と RTCP を利用した輻輳制御の仕組みです。
WebRTC で利用されている音声や映像の暗号化は SRTP というプロトコルを利用しています。これは RTP のセキュア版です。 ただ暗号化ということはどうにかして双方で同じ鍵を持つ必要があります。WebRTC ではこの双方で同じ鍵を持つ方法に DTLS を利用しています。
メディアトランスポートでは SRTP 向けの暗号鍵生成用に DTLS のハンドシェイクを利用しています。 メディアトランスポートだけを利用する場合はアプリケーションデータは利用されません。
DTLS で取得した MasterSecret を利用して SRTP/SRTCP の鍵を生成します。
シグナリングというのは WebRTC を P2P で利用するにあたり SDP 情報を交換する手段です。 簡単に言うと Offer と Answer のやりとりします。
WebRTC でシグナリングの規格は定義されていません。
シグナリングなどの再標準化も意識的に行いませんでした。 これはすでに SIP やその他の IETF 以外の取り組みで解決されており、非常に政治的な問題になりかねないと考えたからです。最終的には、この空間に加えるべき価値があまりないと感じたのです
シグナリングを担当することをシグナリングサーバーと呼びます。クライアントから 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 を使うやり方もあります。ただし、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 が利用されるのかというと、双方向のやりとりがとても簡単だからです。とはいえ 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
WebRTC ではシグナリングの規格が定義されていませんが、 これだと困る場合があります。それは 配信ツール を利用する場合です。
OBS というオープンソース (GPLv3) の配信ツールがあります。これから WebRTC を利用するにはシグナリングの規格が必要です。
そこで登場したのが WHIP です。WHIP はとてもシンプルな規格です。WHIP はマルチストリームには対応せず、あくまで音声トラックと映像トラックを 1 ずつの 1 ストリームを扱います。
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
基本的に利用されていません。Jitsi が使っています。
WebRTC を一度確立してしまえば、シグナリングにデータチャネルを使うこともできます。 一度確立したのにシグナリング使うのか?と思うかも知れません。これはマルチトラックやマルチストリームで再ハンドシェイクする必要があります。
マルチストリームを利用する場合は、再ハンドシェイクが必要になります。再ハンドシェイクは re-offer と re-answer と呼ばれています。
その際 SDP の m= の数を減らすことはできません。
WebRTC では様々なセキュリティの技術が利用されています。SRTP/SRTCP や DTLS 、さらには SDP を利用した証明書の Fingerprint チェック、STUN ice-ufrag / ice-pwd 、TURN の usrename / credential。
RFC 8826 - Security Considerations for WebRTC
- STUN と TURN
- Fingerprint
- Message-Integrity
- DTLS-SRTP
- SCTP over DTLS
WebRTC SFU は現時点で WebRTC をビジネスで利用する場合のデファクトスタンダードです。 WebRTC 本来の P2P の利用ではなく、クライアントサーバーモデルです。
- WebRTC SFU とは
- WebRTC SFU が出てきた背景
- クライアント側のスペックが上がった
- クライアント側の帯域が出るようになった
- サーバーマシンのスペックが上がった
- MCU が衰退した背景
- なぜ WebRTC SFU を採用するのか
- WebRTC SFU のユースケース
- WebRTC SFU における TURN
- 必要かどうかと言われると必要です
- TURN-TLS を使いたい場合は必要で す
- PLI ストーム (勝手に命名)
- 帯域推定 / 輻輳制御
- 大規模
現在の WebRTC のトレンドについて説明します。簡単に言えば、最近導入された技術です。
AV1 は Alliance for Open Media が開発しているコーデックで、VP9 よりも圧縮率が高いといわれています。
- Chrome 90 から WebRTC でも AV1 が利用できるようになり、 Chrome 113 からサイマルキャストで AV1 が利用可能になりました
- Edge 121 から WebRTC が AV1 が利用できるようになっています
- Safari は Technology Preview で AV1 が利用できるようになっています
- Firefox は対応していません
AV1 はリリース初期は負荷がとても高かったのですが今はほとんど VP9 と変わりません。
- Google Developers Japan: Chrome 90 ベータ版: WebRTC 用 AV1 エンコーダ、新しいオリジン トライアルなど
- RTP Payload Format For AV1
- Chrome M90 で WebRTC の AV1 が利用できるようになった
- Safari Technology Preview で WebRTC AV1 が利用可能になった
H.265 は H.264 の次のコーデックです。ただパテント周りが色々たいへんなことになっています。 ただ Apple の Safari が実験的機能として対応しています。さらにブラウザ外では積極的に利用されています。 これは RTSP などの監視カメラ向けのコーデックがほぼ H.265 が採用されているためのようです。
libwebrtc でも H.265 の対応が進んでいます。
- Added HEVC parser for WebRTC
- RFC 7798: RTP Payload Format for High Efficiency Video Coding (HEVC)
- draft-aboba-avtcore-hevc-webrtc-01
SVC は 1 ストリームに複数画質を押し込める仕組みです。
- Scalable Video Coding (SVC) Extension for WebRTC
- WebRTC SVC 拡張と AV1 コーデック
- Mastering the AV1 SVC chains - Dolby.io
SFU + SVC にはパテントが存在するため、利用する場合は注意してください。
今は WebRTC SFU 戦国時代に入っています。MCU を採用しているサービスはなく、 すべてのサービスが SFU を採用しています。
P2P と SFU を併用しているサービスもありますが、Google Meet などは P2P を完全にすて、SFU のみに移行しています。
- 1 ストリームに対してサーバ側で動的に切り替える
- 最初から配信するストリーム先を決めてしまっている
- x-goog- という専用の SDP を利用したりしている
ICE Restart はネットワークインターフェースの変更、たとえば LTE から Wifi への切り替えなどで発生する際に、必要な機能です。通常 IP アドレスが変わると、WebRTC は切断されてしまいますが、ICE Restart を利用し ICE で新規にペアを発見することで新しい接続を行います。
ICE Restart が発生すると DTLS はやり直しが入ります。ただペアリングがすぐ終われば DTLS 自体の通信は一瞬です。 そのため、一瞬音声や映像が止まると感じることはあります。
TURN を利用していたとしてもこれは同じで、新しく Allocate Request を利用してコネクションを張り直します。
OBS Studio v30 で利用可能になります、 配信ツールへ WebRTC が利用される良いきっかけになると思っています。
まずは H.264 から始まり、今は AV1 への対応が進められています。サイマルキャストへの対応も予定されています。 AV1 への対応は OBS Studio v30.1 で利用可能になる予定です。現在 v30.1 beta が提供されています。
WHIP 自体は Twitch が対応し始めています。おそらくこれから多くの配信サービスで WHIP への対応が始まると思います。
WHIP とは逆の視聴用の仕組みです。
- draft-murillo-whep-02
- Example setup of a combined MPEG-DASH and WebRTC distribution | by Eyevinn Technology | Medium
- DASH and webRTC-based Streaming | webRTC
あまり興味がある人はいないと思いますが、簡単にだけ触れていこうと思います。
E2EE というのは End to End Encryption の略で、クライアント同士のみで暗号化をする仕組みです。WebRTC P2P は E2EE といって問題無いです。
SFU で E2EE を行う場合はクライアント側で WebRTC とは別に暗号化をする必要があります。
WebRTC SFU と E2EE は多くのサービスで利用されています。WhatsApp や Signal や Facebook Messange。MS Teams や Google Meeting、 Facetime でも採用されています。
WebRTC が複雑すぎたということで、 Media over QUIC (MoQ) というのが出てきています。今後はこちらが主流になっていくと思いますが、だからといって WebRTC が廃れることはありません。今後 10 年は WebRTC は続いていくと考えて問題ありません。
IoT 向けの仕様ですが、DTLS のハンドシェイクコストを減らすための仕組みです。 CID を利用する事でハンドシェイクをスキップします。
RFC 9146: Connection Identifier for DTLS 1.2
WebRTC の後継として進められてる MOQT はパブリッシュ/サブスクライブモデルによる統合メディアストリーミングプロトコルです。
特徴としては規格にリレー機能を組み込む事で、 数百万人への配信といったような超大規模な配信を目的としています。
-
moq:// という URL も定義されています
-
CMAF
- QUIC
- HTTP/3
- HTTP/2 へのフォールバック
HTTP/3 上の WebTransport と HTTP/2 上の WebTransport を使用して、サーバーにデータを送信し、サーバーからデータを受信します。WebSocket と同様に使用できますが、複数のストリーム、一方向のストリーム、アウトオブオーダーデリバリー、信頼性のあるトランスポートと信頼性のないトランスポートをサポートしています。
音声、映像、画像の符号化・復号化のためのコーデックへのインタフェースを定義する仕様です。 WebCodecs はあくまでインターフェースです。
MOQT とは異なる戦略として WebTransport で双方向はすでに試されていますが、結果的には RTP を載せるという話になっています。
基本的に代わりになるモノはありません。圧倒的です。 それでもいくつか libwebrtc を使っていない実装があるので参考までに。
- libdatachannel
- MPLv2
- OBS で利用されている
- 通信部分だけに特化したライブラリ
- libdatachannel
- pion
- MIT
- Pion
- aiortc
- 修正 BSD
- aiortc
- webrtc-rs
- APL 2.0
- WebRTC.rs
- GStreamer
ここはざっくりお話しします
最近の WebRTC の OSS はとても良くできていますので、OSS を採用する事を躊躇する必要はなくなってきています。
正直もう WebRTC がでて何年もでており、ほぼ好み問題です。事業に合っている OSS を採用すれば良いです。
サービスの名前を具体的には出しません。
料金体系と SDK があるかどうかが判断ポイントになると思います。 特定の機能が必要な場合はすぐ判断出来ると思います。
- 従量課金
- 転送量課金
- 利用時間課金
- ユーザ数課金
- ルーム数課金
- 録画容量課金
- 配信者数課金
- iOS
- Android
- JavaScript
- Windows
- macOS
- Linux
- Unity
- C++
- libdatachannel
- Python
- aiortc
- Go
- Pion
- Rust
- webrtc-rs
- Flutter
- React Native