WebRTCとは - P2Pでの動画・音声のリアルタイム通信

WebRTC（Web Real-Time-Communication）は、ブラウザやアプリ間での動画や音声などのリアルタイム通信を実現するオープンソースのプロジェクトです。
ブラウザ間、アプリ間で直接ピア・ツー・ピア（P2P）で通信します。開発者はプラグインを入れたりすることなく、ビデオチャットやファイル共有を作ることができます。

主要なブラウザに実装されているので、開発者はすぐにJavaScriptでWebRTCの機能を利用できます。iOS/Androidアプリではライブラリを導入することで使えます。

なお、P2Pとはいっても、いきなり通信相手を探すのは難しいので、最初にお互いが接続するサーバが必要です。（シグナリングサーバ）
また、相手がプライベートIPアドレスしか持っていないなど送り先を指定する術がない場合は、通信を中継するサーバも必要になります。（TURNサーバ）
このあたりは後述します。

WebRTCは主要な3つの機能を持っています。

1. カメラやマイクで、動画・音声をストリーミング式に取得する（MediaStream）
2. P2Pで動画・音声をやりとりする（RTCPeerConnection）
3. P2Pで任意のデータを双方向でやりとりする（RTCDataChannel）

# 1. MediaStream - 動画・音声の取得

以下のHTMLファイルをブラウザで開いてみてください。
カメラが起動し、カメラが受け取った映像がページ上で表示されると思います。

```index.html:
<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <video autoplay playsinline></video>
    <script>
      navigator.mediaDevices.getUserMedia({ video: true })
        .then(stream => {
          document.querySelector('video').srcObject = stream 
        })
    </script>
  </body>
</html>
```

書かれているJavaScriptは、getUserMedia()を呼び出し、取得したstreamをvideoタグにセットしています。
このstreamが、次々に流れてくる動画・音声データを表すMediaStreamオブジェクトです。

# 2. RTCPeerConnection - P2Pで動画・音声をやりとりする

RTCPeerConnectionは、P2Pの通信を確立するまでの手続き（シグナリング）を含め、お互いにメディアをストリーミング再生するまでのあらゆる機能を提供します。

## シグナリングサーバ - P2Pといってもサーバは必要

P2Pといっても、いきなり通信相手を探すのは難しいので、最初はサーバに接続します。
サーバに接続されたコンピューターの中から通信相手を探し、その後でP2Pの直接通信に切り替えます。

このサーバをシグナリングサーバと呼び、後述する通信経路の情報や、利用可能なメディアの形式などを、SDPという書式のテキストで交換します。

WebRTCは、シグナリングを行うまでに必要な機能（SDPの生成など）は提供しますが、実際にシグナリングを行う方法については関与しません。
シグナリングは、開発者がサーバやそのプロトコルについて決める必要があります。

## P2Pにおいて、通信をどのように相手に到達させるか

通常のグローバルIPアドレスを持ったサーバへのリクエストと違い、P2Pでは自分も相手もグローバルIPアドレスを持っていないことが多く、直接到達させることができません。
これを解決するためにICEというプロトコルが定められ、プライベートネットワークにいる相手にも到達させる仕組みを規定しています。RTCPeerConnectionがこのICEをサポートしています。
ICEでは、STUNとTURNという2つのサーバが登場します。

### STUN - インターネット側から見た時の自身のIPアドレスを知るためのサーバ

別のネットワークにいる相手があなたのプライベートIPアドレスを指定したところで、その通信はあなたに届きません。グローバルIPアドレスが必要です。
しかし、あなたの端末は自身のプライベートIPアドレスしか知りません。

そこで、インターネット上のサーバにリクエストを送り、その通信時にルーターによって交換されたグローバルIPアドレスを反射的にそのサーバに返してもらいます。
このサーバをSTUNサーバといい、インターネット側から見た時の自身のIPアドレスを知るために使います。

ここで知ったグローバルIPアドレスは、シグナリングサーバに送って相手と交換します。
相手はこのグローバルIPアドレスを指定するだけで自身に通信を到達できるケースがあります。
例えば、グローバルIPアドレス宛ての通信を自身に転送してくれるタイプのルーターを使っているケースです。（Full cone NAT）
これならSTUNだけでP2P通信が確立します。

しかし、外部から来た通信が自身に届かないルーターも多く使われています。（Symmetric NAT）
そのようなルーターでは、自身からのリクエストに対するレスポンスしか、自身に到達できません。
その時はTURNの出番です。

### TURN - STUNで到達できない場合に、通信をリレーするサーバ

外部の端末がスタートした通信が自身に届かないネットワーク環境の場合は、STUNサーバだけでは解決しないので、TURNサーバを使って通信を確立します。
TURNサーバは、自身も相手も通信できるインターネット上にいて、通信を中継してくれるサーバです。

WebRTCはICEをサポートしており、RTCPeerConnectionを生成する際に、ICEで使うSTUNサーバやTURNサーバのアドレスを設定できます。
あとは、通信経路が収集されたというイベントが発生するたびに、通信経路情報を相手と交換するためにシグナリングサーバに送るといった処理を書くことになるでしょう。

ちなみに、STUNで解決できないケースもあるなら最初からTURNを使えばいいじゃないかと思うかもしれません。
しかし、TURNは全ての通信をリレーするので、接続元が増えればそれだけリソースを消費します。
よってICEは、TURNはSTUNが使えない場合の手段としています。

# 3. RTCDataChannel - P2Pで任意のデータを双方向でやりとりする

RTCDataChannelを使うと、前述したような動画や音声などのメディアのストリーミングとは別に、アプリケーションで作った任意のデータをP2Pでやりとりできます。

プログラム的には、RTCDataChannelはWebSocketとよく似たインターフェースで提供されています。
send()メソッドでデータを送ったり、データを受け取った時にmessageイベントが発火するなどです。

WebSocketもリアルタイムでデータをやりとりしますが、こちらはサーバを経由しないブラウザ間・アプリ間のP2Pなので、接続元が増えてもサーバの負荷への影響が少なく、通信もあまり遅延しないといった利点があります。

RTCDataChannelはバイナリもやりとりできるので、例えばリアルタイムのファイル共有が実装できます。

# 参考
[WebRTC公式](https://webrtc.org/)
[Google Codelabs - WebRTC](https://codelabs.developers.google.com/codelabs/webrtc-web/index.html?index=..%2F..index#0)
[ネットワークアドレス交換 - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E5%A4%89%E6%8F%9B)
[TURN - Wikipedia](https://ja.wikipedia.org/wiki/TURN)
[WebRTCのICEについて知る](https://www.slideshare.net/iwashi86/webrtcice)
[WebSocket / WebRTCの技術紹介](https://www.slideshare.net/mawarimichi/websocketwebrtc)