ピア接続のスタートガイド

ピア接続は WebRTC 仕様の一部で、異なるコンピュータ上の 2 つのアプリケーションをピアツーピア プロトコルで通信するためのものです。ピア間の通信は、動画、音声、任意のバイナリデータです(RTCDataChannel API をサポートするクライアントの場合)。2 つのピアの接続方法を把握するには、両方のクライアントが ICE サーバー構成を提供する必要があります。これは STUN または TURN サーバーのいずれかで、各クライアントに ICE 候補を提供し、リモート ピアに転送します。このような ICE 候補の転送は、一般的にシグナリングと呼ばれます。

シグナリング

WebRTC 仕様には ICE(インターネット接続接続)サーバーと通信するための API が含まれていますが、シグナリング コンポーネントには含まれていません。2 台のピアが接続方法を共有するには、シグナリングが必要です。通常、ピア接続が開始される前に、ウェブ アプリケーションが必要な情報を中継できる通常の HTTP ベースのウェブ API(REST サービスまたは他の RPC メカニズム)によって解決されます。

次のコード スニペットは、この架空のシグナリング サービスを使用してメッセージを非同期で送受信する方法を示しています。これは、このガイドの残りの例で、必要に応じて使用します。

// Set up an asynchronous communication channel that will be
// used during the peer connection setup
const signalingChannel = new SignalingChannel(remoteClientId);
signalingChannel.addEventListener('message', message => {
    // New message from remote client received
});

// Send an asynchronous message to the remote client
signalingChannel.send('Hello!');

シグナリングはさまざまな方法で実装できます。WebRTC 仕様では特定のソリューションが好まれません。

ピア接続の開始

各ピア接続は、RTCPeerConnection オブジェクトによって処理されます。このクラスのコンストラクタは、単一の RTCConfiguration オブジェクトをパラメータとして受け取ります。このオブジェクトはピア接続の設定方法を定義します。このオブジェクトには、使用する ICE サーバーに関する情報を含める必要があります。

RTCPeerConnection を作成したら、ピアピアか受信ピアかに応じて、SDP オファーまたはレスポンスを作成する必要があります。SDP のオファーまたは回答を作成したら、別のチャネルを介してリモートピアに送信する必要があります。SDP オブジェクトをリモートピアに渡すことはシグナリングと呼ばれ、WebRTC 仕様の対象ではありません。

呼び出し側からピア接続の設定を開始するには、RTCPeerConnection オブジェクトを作成し、createOffer() を呼び出して RTCSessionDescription オブジェクトを作成します。このセッションの説明は、setLocalDescription() を使用してローカルの説明として設定され、Google のシグナリング チャネルを介して受信側に送信されます。また、提供されたセッションの説明に対する回答を受信側から受け取るタイミングを指定するリスナーを Google のシグナリング チャネルに設定します。

async function makeCall() {
    const configuration = {'iceServers': [{'urls': 'stun:stun.l.google.com:19302'}]}
    const peerConnection = new RTCPeerConnection(configuration);
    signalingChannel.addEventListener('message', async message => {
        if (message.answer) {
            const remoteDesc = new RTCSessionDescription(message.answer);
            await peerConnection.setRemoteDescription(remoteDesc);
        }
    });
    const offer = await peerConnection.createOffer();
    await peerConnection.setLocalDescription(offer);
    signalingChannel.send({'offer': offer});
}

受信側では、受信インスタンスを待機してから RTCPeerConnection インスタンスを作成します。完了したら、setRemoteDescription() を使用して、受け取ったクーポンを設定します。次に createAnswer() を呼び出し、受け取ったオファーに対する回答を作成します。この回答は、setLocalDescription() を使用してローカルの説明として設定され、シグナリング サーバー経由で呼び出し側に送信されます。

const peerConnection = new RTCPeerConnection(configuration);
signalingChannel.addEventListener('message', async message => {
    if (message.offer) {
        peerConnection.setRemoteDescription(new RTCSessionDescription(message.offer));
        const answer = await peerConnection.createAnswer();
        await peerConnection.setLocalDescription(answer);
        signalingChannel.send({'answer': answer});
    }
});

2 つのピアがローカル セッションとリモート セッションの両方の説明を設定すると、リモートピアの機能を把握します。これは、ピア間の接続の準備が完了していることを意味するわけではありません。これを行うには、各ピアで ICE 候補を収集し、(シグナリング チャネルを介して)他のピアに転送する必要があります。

ICE 候補

2 つのピアが WebRTC を使用して通信する前に、接続情報を交換する必要があります。ネットワークの状態はいくつかの要因によって異なる場合があるため、外部サービスは通常、ピアへの接続候補の候補を見つけるために使用されます。このサービスは ICE と呼ばれ、STUN サーバーまたは TURN サーバーを使用します。STUN は NAT のセッション走査ユーティリティの略で、通常はほとんどの WebRTC アプリケーションで間接的に使用されます。

TURN(リレー NAT を使用したトラバーサル)は、STUN プロトコルを組み込んだ高度なソリューションです。ほとんどの商用 WebRTC ベースのサービスは、ピア間の接続を確立するために TURN サーバーを使用します。WebRTC API は STUN と TURN の両方を直接サポートしており、より完全なインターネット接続の確立の下で収集されています。WebRTC 接続を作成するときには、通常、RTCPeerConnection オブジェクトの構成内に 1 つ以上の ICE サーバーが用意されています。

トリクルアイス

RTCPeerConnection オブジェクトが作成されると、基盤となるフレームワークは提供された ICE サーバーを使用して接続を確立する候補(ICE 候補)を収集します。RTCPeerConnection のイベント icegatheringstatechange は、ICE 収集の状態(newgatheringcomplete)を示します。

ピアが ICE の収集が完了するまで待つことは可能ですが、「トリック アイス」手法を使用して、各 ICE 候補が検出されたときにリモートピアに送信する方がはるかに効率的です。これにより、ピア接続のセットアップ時間が大幅に短縮され、遅延なくビデオ通話を開始できます。

ICE 候補を収集するには、icecandidate イベントのリスナーを追加します。そのリスナーで出力された RTCPeerConnectionIceEvent には、リモートピアに送信する必要がある新しい候補を表す candidate プロパティが含まれます(シグナリングを参照)。

// Listen for local ICE candidates on the local RTCPeerConnection
peerConnection.addEventListener('icecandidate', event => {
    if (event.candidate) {
        signalingChannel.send({'new-ice-candidate': event.candidate});
    }
});

// Listen for remote ICE candidates and add them to the local RTCPeerConnection
signalingChannel.addEventListener('message', async message => {
    if (message.iceCandidate) {
        try {
            await peerConnection.addIceCandidate(message.iceCandidate);
        } catch (e) {
            console.error('Error adding received ice candidate', e);
        }
    }
});

接続が確立されました

ICE 候補を受け取ったら、最終的にピア接続の状態が接続状態に変わるはずです。これを検出するために、connectionstatechange イベントをリッスンするリスナーを RTCPeerConnection に追加します。

// Listen for connectionstatechange on the local RTCPeerConnection
peerConnection.addEventListener('connectionstatechange', event => {
    if (peerConnection.connectionState === 'connected') {
        // Peers connected!
    }
});

RTCPeerConnection API のドキュメント