WebRTC アプリのバックエンド サービスを構築する

公開日: 2013 年 11 月 4 日

WebRTC を使用すると、ピアツーピア通信が可能になります。シグナリングと呼ばれるプロセスを通じて、クライアントがメタデータを交換し、通信を調整できるように、サーバーが必要です。サーバーは、クライアントがネットワーク アドレス変換(NAT)やファイアウォールに対処する際にも役立ちます。

ここでは、シグナリング サービスを構築する方法と、STUN サーバーと TURN サーバーを使用した実際の接続の特性に対処する方法について説明します。また、WebRTC アプリがマルチパーティ通話を処理し、VoIP や PSTN(電話とも呼ばれる)などのサービスとやり取りする方法についても説明します。

シグナリングとは

シグナリングとは、通信を調整するプロセスです。WebRTC アプリで通話をセットアップするには、クライアントが次の情報を交換する必要があります。

  • 通信の開始または終了に使用されるセッション制御メッセージ
  • エラー メッセージ
  • メディア メタデータ(コーデック、コーデック設定、帯域幅、メディアタイプなど)
  • 安全な接続を確立するために使用される鍵データ
  • ネットワーク データ(外部から見たホストの IP アドレスやポートなど)

このシグナリング プロセスでは、クライアントがメッセージをやり取りする方法が必要です。このメカニズムは WebRTC API では実装されていません。代わりに、自分でビルドする必要があります。

シグナリングが WebRTC で定義されていないのはなぜですか?

冗長性を回避し、確立されたテクノロジーとの互換性を最大化するため、シグナリングの方法とプロトコルは WebRTC 標準では指定されていません。このアプローチは、JavaScript Session Establishment Protocol(JSEP)で概説されています。

JSEP のアーキテクチャでは、ブラウザが状態を保存する必要、つまりシグナリング ステートマシンとして機能する必要もありません。たとえば、ページが再読み込みされるたびにシグナリング データが失われると、問題が発生します。代わりに、シグナリング状態をサーバーに保存できます。

JSEP アーキテクチャ図

JSEP では、ピア間でメディア メタデータのオファーとアンサーを交換する必要があります。オファーとアンサーは、Session Description Protocol(SDP)形式で通信されます。この形式は次のようになります。

v=0
o=- 7614219274584779017 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS
m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:W2TGCZw2NZHuwlnf
a=ice-pwd:xdQEccP40E+P0L5qTyzDgfmW
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=mid:audio
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:9c1AHz27dZ9xPI91YNfSlI67/EMkjHHIHORiClQe
a=rtpmap:111 opus/48000/2
...

この SDP の難解な用語が実際に何を意味するのかを知りたい場合は、インターネット技術特別調査委員会(IETF)の例をご覧ください。

WebRTC は、SDP テキストの値を編集することで、ローカルまたはリモートの説明として設定する前にオファーまたはアンサーを調整できるように設計されています。たとえば、appr.tc の preferAudioCodec() 関数を使用して、デフォルトのコーデックとビットレートを設定できます。SDP は JavaScript で操作するのがやや面倒であり、WebRTC の将来のバージョンでは代わりに JSON を使用すべきかどうかについて議論されていますが、SDP を使い続けることにはいくつかの利点があります。

RTCPeerConnection API とシグナリング: オファー、アンサー、候補

RTCPeerConnection は、WebRTC アプリがピア間の接続を作成し、音声と動画を通信するために使用する API です。

このプロセスを初期化するために、RTCPeerConnection には次の 2 つのタスクがあります。

  • 解像度やコーデック機能など、ローカル メディアの条件を確認します。これは、オファーとアンサーのメカニズムに使用されるメタデータです。
  • アプリのホストの候補となるネットワーク アドレス(候補)を取得します。

このローカルデータが確認されたら、シグナリング メカニズムを介してリモートピアと交換する必要があります。

アリスがイブに電話をかけようとしているとします。以下に、応答メカニズムの全容を詳しく説明します。

  1. アリスが RTCPeerConnection オブジェクトを作成します。
  2. アリスは、RTCPeerConnection createOffer() メソッドを使用してオファー(SDP セッションの説明)を作成します。
  3. アリスは setLocalDescription() を呼び出してオファーを渡します。
  4. Alice はオファーを文字列化し、シグナリング メカニズムを使用して Eve に送信します。
  5. Eve は Alice のオファーを使用して setRemoteDescription() を呼び出し、Eve の RTCPeerConnection が Alice の設定を認識できるようにします。
  6. Eve が createAnswer() を呼び出し、これに対する成功コールバックにローカル セッションの説明(Eve の回答)が渡されます。
  7. Eve は setLocalDescription() を呼び出して、自分の回答をローカル記述として設定します。
  8. Eve はシグナリング メカニズムを使用して、文字列化された回答を Alice に送信します。
  9. アリスは、setRemoteDescription() を使用して、イブの回答をリモート セッションの説明として設定します。

アリスとイブは、ネットワーク情報も交換する必要があります。「候補の検索」という表現は、ICE フレームワークを使用してネットワーク インターフェースとポートを検索するプロセスを指します。

  1. アリスは、onicecandidate ハンドラを使用して RTCPeerConnection オブジェクトを作成します。
  2. このハンドラは、ネットワーク候補が利用可能になったときに呼び出されます。
  3. ハンドラで、Alice はシグナリング チャネルを介して文字列化された候補データを Eve に送信します。
  4. Eve は Alice から候補メッセージを受け取ると、addIceCandidate() を呼び出して、リモートピアの説明に候補を追加します。

JSEP は ICE Candidate Trickling をサポートしています。これにより、発信者は最初のオファーの後、候補を着信者に段階的に提供できます。また、着信者はすべての候補が到着するのを待たずに、通話を開始して接続を設定できます。

シグナリング用の WebRTC をコーディングする

次のコード スニペットは、シグナリング プロセス全体をまとめた W3C コードの例です。このコードは、シグナリング メカニズム SignalingChannel が存在することを前提としています。シグナリングについては後ほど詳しく説明します。

// handles JSON.stringify/parse
const signaling = new SignalingChannel();
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{urls: 'stun:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);

// Send any ice candidates to the other peer.
pc.onicecandidate = ({candidate}) => signaling.send({candidate});

// Let the "negotiationneeded" event trigger offer generation.
pc.onnegotiationneeded = async () => {
  try {
    await pc.setLocalDescription(await pc.createOffer());
    // send the offer to the other peer
    signaling.send({desc: pc.localDescription});
  } catch (err) {
    console.error(err);
  }
};

// After remote track media arrives, show it in remote video element.
pc.ontrack = (event) => {
  // Don't set srcObject again if it is already set.
  if (remoteView.srcObject) return;
  remoteView.srcObject = event.streams[0];
};

// Call start() to initiate.
async function start() {
  try {
    // Get local stream, show it in self-view, and add it to be sent.
    const stream =
      await navigator.mediaDevices.getUserMedia(constraints);
    stream.getTracks().forEach((track) =>
      pc.addTrack(track, stream));
    selfView.srcObject = stream;
  } catch (err) {
    console.error(err);
  }
}

signaling.onmessage = async ({desc, candidate}) => {
  try {
    if (desc) {
      // If you get an offer, you need to reply with an answer.
      if (desc.type === 'offer') {
        await pc.setRemoteDescription(desc);
        const stream =
          await navigator.mediaDevices.getUserMedia(constraints);
        stream.getTracks().forEach((track) =>
          pc.addTrack(track, stream));
        await pc.setLocalDescription(await pc.createAnswer());
        signaling.send({desc: pc.localDescription});
      } else if (desc.type === 'answer') {
        await pc.setRemoteDescription(desc);
      } else {
        console.log('Unsupported SDP type.');
      }
    } else if (candidate) {
      await pc.addIceCandidate(candidate);
    }
  } catch (err) {
    console.error(err);
  }
};

オファー / アンサーと候補交換のプロセスを実際に確認するには、simpl.info RTCPeerConnection にアクセスし、シングルページのビデオチャットの例のコンソールログをご覧ください。WebRTC のシグナリングと統計情報の完全なダンプは、Google Chrome の about://webrtc-internals ページまたは Opera の opera://webrtc-internals ページからダウンロードできます。

ピアの検出

これは、「話しかける相手をどうやって見つけるか」を言い換えたものです。

電話の場合は、電話番号と電話帳があります。オンラインのビデオチャットとメッセージングには、ID とプレゼンスの管理システム、およびユーザーがセッションを開始するための手段が必要です。WebRTC アプリでは、クライアントが通話を開始または参加したいことを互いに通知する方法が必要です。

ピア検出メカニズムは WebRTC で定義されておらず、ここではオプションについて説明しません。このプロセスは、URL をメールやメッセージで送信するだけの簡単なものです。Talky、tawk.to、Browser Meeting などのビデオチャット アプリでは、カスタムリンクを共有してユーザーを通話に招待します。デベロッパーの Chris Ball 氏は、serverless-webrtc という興味深い実験を行いました。これは、WebRTC 通話の参加者が、IM、メール、伝書鳩など、好きなメッセージ サービスでメタデータを交換できるようにするものです。

シグナリング サービスを構築するにはどうすればよいですか?

繰り返しになりますが、シグナリングのプロトコルとメカニズムは WebRTC 標準では定義されていません。どちらを選択する場合でも、クライアント間でシグナリング メッセージとアプリデータを交換するための中間サーバーが必要です。残念ながら、ウェブアプリはインターネットに向かって「友だちに接続して」と叫ぶことはできません。

幸いなことに、シグナリング メッセージは小さく、ほとんどが通話の開始時に交換されます。ビデオチャット セッションの appr.tc を使用したテストでは、シグナリング サービスによって合計 30 ~ 45 件のメッセージが処理され、すべてのメッセージの合計サイズは約 10 KB でした。

WebRTC シグナリング サービスは、帯域幅の要件が比較的緩いだけでなく、メッセージをリレーし、接続されているクライアントなどの少量のセッション状態データを保持するだけでよいため、処理やメモリをあまり消費しません。

サーバーからクライアントにメッセージを push する

Browser Support

  • Chrome: 6.
  • Edge: 79.
  • Firefox: 6.
  • Safari: 5.

Source

シグナリング用のメッセージ サービスは、クライアントからサーバー、サーバーからクライアントの双方向である必要があります。双方向通信は HTTP クライアント / サーバーのリクエスト / レスポンス モデルに反しますが、ウェブサーバーで実行されているサービスからブラウザで実行されているウェブアプリにデータを push するために、ロング ポーリングなどのさまざまなハックが長年にわたって開発されてきました。

EventSource API を使用すると、サーバー送信イベント(HTTP を介してウェブサーバーからブラウザ クライアントに送信されるデータ)を有効にできます。EventSource は一方向のメッセージング用に設計されていますが、XHR と組み合わせて使用することで、シグナリング メッセージを交換するサービスを構築できます。シグナリング サービスは、XHR リクエストによって配信された呼び出し元からのメッセージを、EventSource を介して呼び出し先に push することで渡します。

Browser Support

  • Chrome: 5.
  • Edge: 12.
  • Firefox: 11.
  • Safari: 5.

Source

WebSocket は、全二重のクライアント サーバー通信用に設計されています。つまり、メッセージは同時に双方向に流れることができます。純粋な WebSocket またはサーバー送信イベント(EventSource)で構築されたシグナリング サービスの利点の 1 つは、これらの API のバックエンドを、PHP、Python、Ruby などの言語のほとんどのウェブホスティング パッケージに共通するさまざまなウェブ フレームワークに実装できることです。

WebRTC をサポートするすべてのブラウザは、WebSocket もサポートしています。メッセージが暗号化されずに傍受されるのを防ぎ、プロキシ トラバーサルの問題を軽減するために、すべての接続に TLS を使用する必要があります。WebSocket とプロキシ トラバーサルの詳細については、Ilya Grigorik 氏の High Performance Browser Networking の WebRTC の章をご覧ください。

WebRTC クライアントが Ajax を介してメッセージング サーバーを繰り返しポーリングするようにすることで、シグナリングを処理できますが、その場合、冗長なネットワーク リクエストが多数発生します。これは、特にモバイル デバイスで問題になります。セッションが確立された後も、ピアは、他のピアによる変更やセッションの終了に備えて、シグナリング メッセージをポーリングする必要があります。WebRTC Book アプリの例では、ポーリング頻度を最適化してこのオプションを使用しています。

シグナリングをスケーリングする

シグナリング サービスは、クライアントあたりの帯域幅と CPU の消費量が比較的少ないものの、人気のあるアプリのシグナリング サーバーは、さまざまな場所からの大量のメッセージを高い同時実行レベルで処理しなければならない場合があります。トラフィックの多い WebRTC アプリには、かなりの負荷を処理できるシグナリング サーバーが必要です。ここでは詳しく説明しませんが、大容量で高性能なメッセージングには、次のようなオプションがあります。

  • eXtensible Messaging and Presence Protocol(XMPP)は、シグナリングに使用できるインスタント メッセージ用に開発されたプロトコルです。サーバーの実装には、ejabberd や Openfire などがあります。Strophe.js などの JavaScript クライアントは、BOSH を使用して双方向ストリーミングをエミュレートしますが、BOSH は WebSocket と比較して効率が悪く、スケーラビリティが低い可能性があります。

  • ZeroMQ(TokBox が Rumour サービスで使用)や OpenMQ などのオープンソース ライブラリ(NullMQ は、WebSocket 経由で STOMP プロトコルを使用して ZeroMQ のコンセプトをウェブ プラットフォームに適用します)。

  • WebSocket を使用する商用クラウド メッセージング プラットフォーム(ただし、ロング ポーリングにフォールバックする場合があります)。たとえば、Pusher や Kaazing などです。

  • vLine などの商用 WebRTC プラットフォーム

Node で Socket.io を使用してシグナリング サービスを構築する

以下は、Node 上の Socket.io で構築されたシグナリング サービスを使用するウェブアプリのコードです。Socket.io の設計により、メッセージを交換するサービスを簡単に構築できます。また、Socket.io にはルームのコンセプトが組み込まれているため、WebRTC シグナリングに特に適しています。この例は、本番環境での使用ではなく、比較的少数のユーザーを対象として設計されています。

Socket.io は、WebSocket をフォールバック(AJAX ロング ポーリング、AJAX マルチパート ストリーミング、Forever Iframe、JSONP ポーリング)とともに使用します。さまざまなバックエンドに移植されていますが、この例で使用されている Node バージョンが最もよく知られています。

この例では WebRTC は使用されていません。これは、ウェブアプリにシグナリングを組み込む方法を示すことを目的としています。コンソール ログを表示すると、クライアントがルームに参加してメッセージを交換する際の動作を確認できます。この WebRTC Codelab では、これを完全な WebRTC ビデオチャット アプリに統合する方法について、手順ガイドを提供します。

クライアント index.html は次のとおりです。

<!DOCTYPE html>
<html>
  <head>
    <title>WebRTC client</title>
  </head>
  <body>
    <script src='/socket.io/socket.io.js'></script>
    <script src='js/main.js'></script>
  </body>
</html>

クライアントで参照される JavaScript ファイル main.js を次に示します。

const isInitiator;

room = prompt('Enter room name:');

const socket = io.connect();

if (room !== '') {
  console.log('Joining room ' + room);
  socket.emit('create or join', room);
}

socket.on('full', (room) => {
  console.log('Room ' + room + ' is full');
});

socket.on('empty', (room) => {
  isInitiator = true;
  console.log('Room ' + room + ' is empty');
});

socket.on('join', (room) => {
  console.log('Making request to join room ' + room);
  console.log('You are the initiator!');
});

socket.on('log', (array) => {
  console.log.apply(console, array);
});

完成したサーバーアプリは次のようになります。

const static = require('node-static');
const http = require('http');
const file = new(static.Server)();
const app = http.createServer(function (req, res) {
  file.serve(req, res);
}).listen(2013);

const io = require('socket.io').listen(app);

io.sockets.on('connection', (socket) => {

  // Convenience function to log server messages to the client
  function log(){
    const array = ['>>> Message from server: '];
    for (const i = 0; i < arguments.length; i++) {
      array.push(arguments[i]);
    }
      socket.emit('log', array);
  }

  socket.on('message', (message) => {
    log('Got message:', message);
    // For a real app, would be room only (not broadcast)
    socket.broadcast.emit('message', message);
  });

  socket.on('create or join', (room) => {
    const numClients = io.sockets.clients(room).length;

    log('Room ' + room + ' has ' + numClients + ' client(s)');
    log('Request to create or join room ' + room);

    if (numClients === 0){
      socket.join(room);
      socket.emit('created', room);
    } else if (numClients === 1) {
      io.sockets.in(room).emit('join', room);
      socket.join(room);
      socket.emit('joined', room);
    } else { // max two clients
      socket.emit('full', room);
    }
    socket.emit('emit(): client ' + socket.id +
      ' joined room ' + room);
    socket.broadcast.emit('broadcast(): client ' + socket.id +
      ' joined room ' + room);

  });

});

このアプリを localhost で実行するには、Node.js、Socket.IO、node-static が必要です。Socket.IO と node-static をインストールするには、アプリ ディレクトリのターミナルから npm を実行します。

npm install socket.io
npm install node-static

サーバーを起動するには、アプリ ディレクトリのターミナルから次のコマンドを実行します。

node server.js

ブラウザで localhost:2013 を開きます。任意のブラウザで新しいタブまたはウィンドウを開き、localhost:2013 を再度開きます。何が起きているのかを確認するには、コンソールを確認します。Chrome と Opera では、Ctrl+Shift+J(Mac の場合は Command+Option+J)を使用して Google Chrome デベロッパー ツールからコンソールにアクセスできます。

シグナリングにどのアプローチを選択しても、バックエンドとクライアント アプリは、この例と同様のサービスを提供する必要があります。

シグナルの注意点

  • RTCPeerConnection は、setLocalDescription() が呼び出されるまで候補の収集を開始しません。これは JSEP IETF ドラフトで義務付けられています。
  • Trickle ICE を活用する。候補が到着したらすぐに addIceCandidate() を呼び出します。

既製のシグナル サーバー

独自のサーバーを構築したくない場合は、いくつかの WebRTC シグナリング サーバーを利用できます。これらは、前の例のように Socket.IO を使用し、WebRTC クライアント JavaScript ライブラリと統合されています。

  • webRTC.io は、WebRTC の最初の抽象化ライブラリの 1 つです。
  • Signalmaster は、SimpleWebRTC JavaScript クライアント ライブラリで使用するために作成されたシグナリング サーバーです。

コードをまったく記述したくない場合は、vLine、OpenTok、Asterisk などの企業から、商用の WebRTC プラットフォームが提供されています。

ちなみに、Ericsson は WebRTC の初期に Apache 上で PHP を使用してシグナリング サーバーを構築しました。これはやや古いものですが、同様のものを検討している場合は、コードを確認する価値があります。

シグナルのセキュリティ

すべての WebRTC コンポーネントで暗号化が必須です。

ただし、シグナリング メカニズムは WebRTC 標準で定義されていないため、シグナリングを安全にするかどうかはユーザー次第です。攻撃者がシグナリングをハイジャックすると、セッションの停止、接続のリダイレクト、コンテンツの記録、変更、挿入が可能になります。

シグナリングを保護するうえで最も重要な要素は、HTTPS や WSS(TLS など)といった安全なプロトコルを使用することです。これにより、メッセージが暗号化されずに傍受されることがなくなります。また、同じシグナリング サーバーを使用している他の発信者がアクセスできる方法でシグナリング メッセージをブロードキャストしないように注意してください。

NAT とファイアウォールに対応する

メタデータ シグナリングの場合、WebRTC アプリは中間サーバーを使用しますが、セッションが確立された後の実際のメディアとデータ ストリーミングの場合、RTCPeerConnection はクライアントを直接またはピアツーピアで接続しようとします。

もっと単純な世界であれば、すべての WebRTC エンドポイントに一意のアドレスがあり、他のピアと交換して直接通信できるでしょう。

シンプルなピアツーピア接続
NAT とファイアウォールのない世界

実際には、ほとんどのデバイスは 1 つ以上の NAT レイヤの背後にあり、一部のデバイスには特定のポートやプロトコルをブロックするウイルス対策ソフトウェアがインストールされています。また、多くのデバイスはプロキシや企業のファイアウォールの背後にあります。ファイアウォールと NAT は、実際には、家庭用 Wi-Fi ルーターなどの同じデバイスによって実装される場合があります。

NAT とファイアウォールの背後にあるピア
現実の世界

WebRTC アプリは、ICE フレームワークを使用して、現実世界のネットワーキングの複雑さを克服できます。これを有効にするには、アプリで ICE サーバーの URL を RTCPeerConnection に渡す必要があります。

ICE は、ピアを接続するための最適なパスを見つけようとします。すべての可能性を並行して試し、最も効率的なオプションを選択します。ICE はまず、デバイスのオペレーティング システムとネットワーク カードから取得したホストアドレスを使用して接続を試みます。それが失敗した場合(NAT の背後にあるデバイスの場合)、ICE は STUN サーバーを使用して外部アドレスを取得します。それも失敗した場合は、トラフィックは TURN リレーサーバーを介してルーティングされます。

つまり、STUN サーバーは外部ネットワーク アドレスを取得するために使用され、TURN サーバーは直接(ピアツーピア)接続が失敗した場合にトラフィックをリレーするために使用されます。

すべての TURN サーバーは STUN をサポートしています。TURN サーバーは、リレー機能が組み込まれた STUN サーバーです。ICE は、NAT 設定の複雑さにも対応します。実際には、NAT ホール パンチングにはパブリック IP:ポートアドレス以上のものが必要になる場合があります。

STUN サーバーと TURN サーバーの URL は、RTCPeerConnection コンストラクタの最初の引数である iceServers 構成オブジェクトで WebRTC アプリによって(必要に応じて)指定されます。appr.tc の場合、この値は次のようになります。

{
  'iceServers': [
    {
      'urls': 'stun:stun.l.google.com:19302'
    },
    {
      'urls': 'turn:192.158.29.39:3478?transport=udp',
      'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
      'username': '28224511:1379330808'
    },
    {
      'urls': 'turn:192.158.29.39:3478?transport=tcp',
      'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
      'username': '28224511:1379330808'
    }
  ]
}

RTCPeerConnection がその情報を取得すると、ICE のマジックが自動的に行われます。RTCPeerConnection は、ICE フレームワークを使用して、必要に応じて STUN サーバーと TURN サーバーと連携し、ピア間の最適なパスを特定します。

気絶

NAT は、プライベート ローカル ネットワーク内で使用する IP アドレスをデバイスに提供しますが、このアドレスは外部では使用できません。パブリック アドレスがないと、WebRTC ピアが通信する方法はありません。この問題を回避するため、WebRTC は STUN を使用します。

STUN サーバーはパブリック インターネット上に存在し、NAT の背後で実行されているアプリからの受信リクエストの IP:port アドレスを確認し、そのアドレスをレスポンスとして返すという 1 つのタスクを実行します。つまり、アプリは STUN サーバーを使用して、パブリックな観点から IP:ポートを検出します。このプロセスにより、WebRTC ピアは自身に一般公開アドレスを取得し、シグナリング メカニズムを介して別のピアに渡して、直接リンクを設定できます。

STUN サーバーは、多くの処理や記憶を行う必要がないため、比較的低スペックの STUN サーバーでも大量のリクエストを処理できます。

ほとんどの WebRTC 通話は STUN を使用して正常に接続を確立しますが、ファイアウォールと複雑な NAT 構成の背後にあるピア間の通話では、接続が確立されないことがあります。

STUN サーバーを使用したピアツーピア接続
STUN サーバーを使用してパブリック IP:ポートアドレスを取得する

TURN

RTCPeerConnection は、UDP 経由でピア間の直接通信を設定しようとします。失敗した場合は、RTCPeerConnection は TCP にフォールバックします。それが失敗した場合、TURN サーバーをフォールバックとして使用し、エンドポイント間でデータを中継できます。

繰り返しになりますが、TURN はピア間の音声、動画、データ ストリーミングを中継するために使用され、シグナリング データは中継しません。

TURN サーバーにはパブリック アドレスがあるため、ピアがファイアウォールまたはプロキシの背後にある場合でも、ピアから接続できます。TURN サーバーのタスクは、ストリームをリレーすることです。STUN サーバーとは異なり、TURN サーバーは本質的に大量の帯域幅を消費します。

STUN サーバーを使用したピアツーピア接続
フルモンティ: STUN、TURN、シグナリング

この図は、TURN の動作を示しています。純粋な STUN は成功しなかったため、各ピアは TURN サーバーを使用することになります。

STUN サーバーと TURN サーバーのデプロイ

テスト用に、Google は appr.tc で使用されている公開 STUN サーバー stun.l.google.com:19302 を実行しています。本番環境の STUN / TURN サービスには、rfc5766-turn-server を使用します。STUN サーバーと TURN サーバーのソースコードは GitHub で入手できます。また、サーバーのインストールに関するいくつかの情報源へのリンクも GitHub で確認できます。アマゾン ウェブ サービス(AWS)用の VM イメージも利用できます。

代替の TURN サーバーとして restund があります。これはソースコードとして、また AWS でも利用できます。Compute で restund を設定できます。

  1. 必要に応じて、tcp=443、udp/tcp=3478 のファイアウォールを開きます。
  2. 4 つのインスタンスを作成します。各パブリック IP に 1 つずつ、標準の Ubuntu 12.06 イメージを使用します。
  3. ローカル ファイアウォールを構成します(ANY から ANY を許可)。
  4. ツールをインストールします。 shell sudo apt-get install make sudo apt-get install gcc
  5. creytiv.com/re.html から libre をインストールします。
  6. creytiv.com/restund.html から restund を取得して解凍します。
  7. wget hancke.name/restund-auth.patch をダウンロードし、patch -p1 < restund-auth.patch で適用します。
  8. libre と restund の make、sudo make install を実行します。
  9. restund.conf を必要に応じて調整し(IP アドレスを置き換え、同じ共有シークレットが含まれていることを確認します)、/etc にコピーします。
  10. restund/etc/restund を /etc/init.d/ にコピーします。
  11. restund を構成します。
    1. LD_LIBRARY_PATH を設定します。
    2. restund.conf を /etc/restund.conf にコピーします。
    3. restund.conf を右側の 10 を使用するように設定します。外部IPアドレスは不要です
  12. restund を実行する
  13. リモートマシンから stund クライアントを使用してテストします。./client IP:port

マルチパーティ WebRTC

また、Justin Uberti 氏が提案した TURN サービスにアクセスするための REST API の IETF 標準もご覧ください。

メディア ストリーミングには、1 対 1 の通話以外にもさまざまなユースケースがあります。たとえば、同僚のグループ間のビデオ会議や、1 人のスピーカーと数百人または数百万人の視聴者が参加する公開イベントなどです。

WebRTC アプリは複数の RTCPeerConnection を使用できるため、すべてのエンドポイントがメッシュ構成で他のすべてのエンドポイントに接続します。これは、talky.io などのアプリで採用されているアプローチであり、少数のピアに対しては非常にうまく機能します。それ以上になると、特にモバイル クライアントでは、処理と帯域幅の消費が過剰になります。

メッシュ: 小規模な N 方向通話
フルメッシュ トポロジ: すべてのユーザーがすべてのユーザーに接続

または、WebRTC アプリで 1 つのエンドポイントを選択し、スター構成ですべてのエンドポイントにストリームを配信することもできます。サーバー上で WebRTC エンドポイントを実行し、独自の再配布メカニズムを構築することも可能です(サンプル クライアント アプリは webrtc.org から提供されています)。

1 つの RTCPeerConnection の MediaStream を別の の入力として使用できます。これにより、ウェブアプリは接続先のピアを選択して通話ルーティングを処理できるため、より柔軟なアーキテクチャを実現できます。実際の動作については、WebRTC サンプルのピア接続リレーと WebRTC サンプルの複数のピア接続をご覧ください。

マルチポイント制御ユニット

エンドポイントの数が多い場合は、多地点制御装置(MCU)を使用することをおすすめします。これは、多数の参加者間でメディアを配信するためのブリッジとして機能するサーバーです。MCU は、ビデオ会議でさまざまな解像度、コーデック、フレームレートに対応し、コード変換を処理し、選択的なストリーム転送を行い、音声と動画をミキシングまたは録画できます。マルチパーティ通話では、特に複数の動画入力を表示する方法や、複数のソースからの音声をミックスする方法など、考慮すべき問題が多数あります。vLine などのクラウド プラットフォームも、トラフィック ルーティングの最適化を試みます。

MCU ハードウェア パッケージ一式を購入することも、自分で製作することもできます。

Cisco MCU5300 の背面図
Cisco MCU の背面

オープンソースの MCU ソフトウェア オプションがいくつかあります。たとえば、Licode(旧称 Lynckia)は、WebRTC 用のオープンソース MCU を作成しています。OpenTok には Mantis があります。

ブラウザ以外: VoIP、電話、メッセージ

WebRTC は標準化されているため、ブラウザで実行されている WebRTC アプリと、電話やビデオ会議システムなどの別の通信プラットフォームで実行されているデバイスまたはプラットフォームとの間で通信を確立できます。

SIP は、VoIP システムとビデオ会議システムで使用されるシグナリング プロトコルです。WebRTC ウェブアプリと SIP クライアント(ビデオ会議システムなど)間の通信を有効にするには、WebRTC にシグナリングを仲介するプロキシ サーバーが必要です。シグナリングはゲートウェイを通過する必要がありますが、通信が確立されると、SRTP トラフィック(動画と音声)はピアツーピアで流れることができます。

公衆交換電話網(PSTN)は、すべての「従来型」アナログ電話の回線交換ネットワークです。WebRTC ウェブアプリと電話間の通話では、トラフィックが PSTN ゲートウェイを通過する必要があります。同様に、WebRTC ウェブアプリは、IM クライアントなどの Jingle エンドポイントと通信するために、仲介役となる XMPP サーバーを必要とします。Jingle は、メッセージ サービスで音声と動画を有効にするために、XMPP の拡張機能として Google によって開発されました。現在の WebRTC 実装は、C++ の libjingle ライブラリに基づいています。これは、当初 Talk 用に開発された Jingle の実装です。

多くのアプリ、ライブラリ、プラットフォームが、外部との通信に WebRTC の機能を利用しています。次に例を示します。

  • jsSIP: JavaScript SIP ライブラリ
  • Phono: プラグインとして構築されたオープンソースの JavaScript 電話 API
  • Twilio: 音声とメッセージ

詳細

350 ページの書籍 WebRTC: APIs and RTCWEB Protocols of the HTML5 リアルタイム Web には、データとシグナリングの経路に関する詳細が記載されており、ネットワーク トポロジの詳細な図が多数含まれています。