panda's tech note

WebRTC

WebRTCは,オーディオ・ビデオ通信のようなリアルタイム性の高い通信をブラウザで行うために標準化されています。

WebRTCは,オーディオ・ビデオ通信にはReal-time Transport Protocol (RTP)にセキュリティ機能を追加したSecure Real-time Transport Protocol (SRTP)、データ通信にはStreaming Control Transmission Protocol (SCTP)をDatagram TLS (DTLS)/UDP上で利用します。P2P通信の接続を確立するために,Session Description Protocol (SDP)を使用します。このSDPの生成や受信はWebRTCのAPIで定義されていますが,その交換(シグナリング)はWebSocketやHTTPサーバなどを用いて別途作成する必要があります。また,NATを超えるためにSTUN/TURNサーバを必要とする例が多いため,少々複雑な処理が必要となります。

WebRTCの技術構造

WebRTCのブラウザAPIはW3Cで策定され,その通信プロトコル仕様はIETF rtcweb WGで標準化が行われていました。IETF rtcweb WGは,仕様策定が一段落したため,2019年にConcludedとなりました。ブラウザAPIの詳細はW3C WebRTC仕様書を適宜確認してください。

WebRTCを構成する技術要素・通信プロトコル要素は,以下の通りです。

  • Session Description Protocol (SDP): 通信セッションを記述するプロトコル。セッションを確立するための情報(IPアドレスやポート番号,暗号化通信を行うための公開鍵情報など)やビデオやオーディオストリーム,データチャネルなどの通信チャネル情報を記述します。映像配信で用いられるReal Time Streaming Protocol (RTSP)などでも用いられてきたプロトコルです。
  • Interactive Connectivity Establishment (ICE): 現在のIPv4インターネットは,特にクライアント側にNetwork Address Translation (NAT)機器などが入っており,エンド・ツー・エンドで透過的に通信できない(IPアドレスで直接通信できない)ことが多いため,端末間での通信を行うにはNATを超える処理が必要になります。この「NAT越え」のために,Session Traversal Utilities for NAT (STUN)やTraversal Using Relays around NAT (TURN)など,ポート番号を空けたり,通信を媒介(リレー)するサーバを用います。このSTUNやTURNを相互で使いながら,端末間で接続を行う技術がICEです。
  • Datagram Transport Layer Security (DTLS): HTTPSなどで用いられているTransport Layer Security (TLS)と同様の機能をデータグラムで実現するためのプロトコル。暗号化・認証・改ざん防止を実現する。WebRTCの通信は,UDP上にDTLSを用いている。このDTLS上に,後述のSRTPやSCTPを用いてオーディオ・ビデオ通信やデータ通信を実装しています。
  • Secure Real-time Transport Protocol (SRTP): 映像などリアルタイム性の高い通信をUDP上で行うためのプロトコルであるReal-time Transport Protocol (RTP)にセキュリティ機能(暗号化・認証・改ざん防止)を加えたもので,RFC 3711で定義されています。WebRTCでは,DTLS上で鍵交換を行い,その鍵を用いてSRTPで暗号化通信を行います。
  • Streaming Control Transmission Protocol (SCTP): SCTPはTCPの次世代プロトコルとして,マルチホーミングやメッセージ単位のデータ転送をサポートし,Head-of-Lineブロッキングを解消するプロトコルとして提案され,RFC 4960で定義されています。SCTPはIP上に実装するプロトコルとして定義されていましたが,高度化する中継装置(ミドルボックス)により,TCPやUDP以外を通さない機器が多かったため,あまり普及しませんでした。WebRTCでは,SCTPの輻輳制御やメッセージ単位でのデータ転送機能やHead-of-Lineブロッキング解消の利点を活かし,DTLS/UDP上に実装されたSCTPをデータチャネルの通信に用いています。

WebRTCのプロトコルスタックをまとめると以下の図の通りとなります。 WebRTC Protocol Stack

WebRTCのピア確立とシグナリング

WebRTCのピアを確立するには,異なる端末(同一ブラウザの異なるタブでも良いです)間でSDPとICE candidatesを交換することで行います。このSDPとICE candidatesの交換をシグナリングと呼びます。WebRTCではシグナリングの手法は定義されていないので,WebSocketsやHTTPを使って行うことが一般です。

WebRTCのピアを確立するには,まずJavaScriptで RTCPeerConnection のインスタンスを作成します。このインスタンスで通信を行うメディアやデータチャネルを設定します。次に,このインスタンスの RTCPeerConnection.createOffer() を呼ぶことで,メディアやデータチャネルに合わせたSDPが生成されます。このSDPを RTCPeerConnection.setLocalDescription() でこのインスタンスで設定します。また, RTCPeerConnection.createOffer() の実行と同時に,NAT越えのためのICEの処理が開始されます。ICEの処理は後述する通り Vanilla ICETrickle ICE の二種類がありますが,ここではICEに関係する処理については説明を省略します。

RTCPeerConnection.createOffer() で生成したSDPを別の端末に送ることで,別の端末からの接続確立を試みます。このSDPを受信した側は,RTCPeerConnection インスタンスを作成し,RTCPeerConnection.setRemoteDescription() によりこのSDPを設定します。その後,RTCPeerConnection.createAnswer() により,受信したSDPに対応する応答SDPを生成します。RTCPeerConnection.createOffer() と同様に,これと同時にICEの処理が実行されます。この生成したSDPを元の端末に送信することで,WebRTCのピア確立を行います。

Vanilla ICEとTrickle ICE

WebRTCでピア(他の端末)に接続するには,Interactive Connectivity Establishment (ICE)を用います。ICEには,Vanilla ICEとTrickle ICEがあります。

Vanilla ICEでは,RTCPeerConnection.createOffer() または RTCPeerConnection.createAnswer() で生成したSDPを RTCPeerConnection.setLocalDescription() で設定し,全てのICE candidatesが揃うのを待ちます。全てのICE candidatesが揃うと RTCPeerConnection.icecandidate イベントハンドラの引数 evtevt.candidate が空になるため,そこで RTCPeerConnection.localDescription からSDPを取得し,相手に通知します。このSDPにはICE candidatesが含まれているため,受信した側は RTCPeerConnection.setRemoteDescription() でSDPを設定することで,SDPが offer であれば answer SDPの準備を,answer であれば接続の確立を試みます。つまり,Vanilla ICEのシグナリングシーケンスは下図のようになります。下図青破線の通り,Vanilla ICEでは全てのICE candidatesが揃ってからWebRTCのピア接続を確立するため,STUN/TRUNサーバとの遅延が大きいと,接続確立までに時間がかかります。 Vanilla ICE

Trickle ICEでは,RTCPeerConnection.createOffer() または RTCPeerConnection.createAnswer() で生成したSDPを RTCPeerConnection.setLocalDescription() で設定するところまではVanilla ICEと同じです。しかし,Trickle ICEでは,このSDPを即座に相手に通知し,このSDPの受信側は RTCPeerConnection.setRemoteDescription() でSDPを設定します。このSDPにはICE candidatesが含まれていないため,この時点ではWebRTCのピア接続ができません。解決できたICE candidateはICE candidateが RTCPeerConnection.icecandidate イベントハンドラの引数 evtevt.candidate で通知されるため,このICE candidateを相手に通知します。このICE candidateを受信した側は RTCPeerConnection.addIceCandidate() により追加します。つまり,Trickle ICEのシグナリングシーケンスは下図のようになります。Trickle ICEでは,準備のできたICE candidateによりWebRTCのピア接続確立を試みることができるため,接続確立までの遅延を最小限にすることができます。 Trickle ICE