がねこまさし

シグナリングサーバーを応用! 「WebRTCを使って複数人で話してみよう」

こんにちは! 前回はシグナリングサーバーを動かして、WebRTCでPeer-to-Peer通信をつなぐ処理を作りました。最後に書いた通り、前回の実装ではサーバーあたり2人だけしか同時に通知できません。今回はこれをもっと実用的にしていきましょう。 ※今回もNode学園祭2013で発表した内容と共通の部分が多いです。その時の資料も併せてご参照ください。

※こちらの記事は2014年に書かれました。2016年8月のアップデート記事がありますので、そちらもご参照ください。

複数会議室を作ろう

前回作ったのは、いわばカップル1組限定サイトのシングルテナントアプリでした(左)。これを複数組が共存できる、マルチテナント(複数会議室)のアプリに改造します(右)。
rtc11_multiroom
複数組が共存できない理由は、シグナリングの通信が同じシグナリングサーバーに接続している全員に飛んでしまうからです。これを混線しないように分離してあげる必要があります。シグナリングサーバーで利用しているsocket.ioでは、これを簡単に実現できるroom機能があります。

  • roomに入室する… socket.join()
  • roomから退室する… socket.leave()
  • room内だけにメッセージを送る… socket.broadcast.to.emit()

まずクライアント側を一部手直しします。

ソケット接続が確立したら、シグナリングサーバーに対して入室要求(enter)を送っています。ここでgetRoomName()はアプリケーション側で実装する部分で、何らかの方法で会議室名を取得して返します。 手抜きなサンプルとしてはこんな感じでしょうか。URLの?以降をそのまま切り出して返しています。

ついでにもう少し直しましょう。実は前回までのサンプルではカメラしかアクセスしていません。マイクはアクセスしていないので、声が聞こえません。今回はマイクも取得するように一カ所だけ修正します。※webkitGetUserMedia()の引数を変更

※同一PC上で複数の2つのウィンドウ/タブを開いて通信する場合、ハウリングしやすいので音量を絞るか、ヘッドフォンを利用してください。

今度はシグナリングサーバー側も修正しましょう。クライアントからの入室要求(enter)に対応するのと、会議室内だけに通信する部分です。

シグナリングサーバーを起動しなおして、HTMLをリロードすれば、複数会議室に対応したマルチテナントアプリの完成です。 URLの後ろに ?room1 や ?room2 などのように会議室名を指定すれば、 その部屋の人と通信できます。

複数人で通信してみたい

次は2人だけでなく、複数人で同時に話せるようにしてみたいと思います。こんな感じです。 rtc_nn

複数のPeer-to-Peer通信を扱うには

複数人と通信するには、クライアント側(ブラウザ側)に相手の数だけPeerConnectionが必要です。それを管理するための便宜上のクラスを作ります。 通信状況や、相手のID(socket.ioが割り振る)を保持します。

ついでに、複数のConnectionを格納する配列と、それを管理する関数群も用意します。ここに挙げた2つ以外に、getConnectionCount(), isConnectPossible(), deleteConnection(id), などなど。(詳細は最後に全ソースを掲載します)

シグナリングを手直し

シグナリングの流れも手直しが必要です。 今までのシグナリングでは、最初にOffer SDPを送る際に同じ部屋の全員に送っていました(broadcast)。すると全員からAnswer SDPが返ってきてしまうので、情報が衝突してしまいます。
sdp_nn_corrupt
そこで、まず部屋に誰が居るかを確認し(call-response)、一人ずつ個別にOffer-Answerのやり取りをする必要があります。
sdp_call_response
では、クライアント側のソースを直していきましょう。

call()で全員にbroadcastし、受け取った側はonMessage()の中でcallを受け取ると、responseを相手を特定して送り返します。発信側はreponseを受け取ると、その相手に対してoffer SDPを送っています。 sendOffer()の中身もちょっと変わります。

同様に、sendAnswer()もちょっと変えます。複数のコネクションに対応するのと、送る相手を指定するのが変更点です。


さらに、SDPを覚える処理も複数セッションに対応させます。


引き続きConnectionを生成する処理も修正します。今まではPeerConnectionを直接返していましたが、今回はConnectionのインスタンスを生成し、そこにPeerConnectionを保持させます。また、Candidateの送信時にも相手先を指定します。


Candidateの送信部分を変更したので、Candidateを受信した処理も変更しましょう。 onCandidate()も複数コネクションに対応させます。


さてさて、クライアント側の修正はいったん終わりにして、次はシグナリングサーバー側を修正します。前半では部屋の中だけに送信する機能を加えましたが、次は特定の相手にだけ送信できるようにします。

ここまででいったん動かしてみましょう。まだ映像が2人までしか出ませんが、通信はできるはずです。

複数の映像を扱えるようにしよう

ここまでで複数人相手に通信をできるようにしました。でも通信できても映像は見えていません。ちゃんと見えるようにしましょう。 まずHTMLに複数のvideoタグを配置します。

その複数のvideoタグを扱えるような関数群を追加します。※本当は動的にタグを作成、削除するのがかっこいいのですが…。

※ソース全体は最後に記載します。

これで準備が整いました。早速接続してみましょう。 [Start video]ボタンを押して、[Connect]を押す、という操作を一人ずつ行ってください。一人、また一人と接続され、最大4人まで通信できます。
rtc4

補足 (2014/03/09追記)

Twitter経由でご指摘をいただきました。User B/Cからresponseではなく、Offerを送れば良いのでは?
アドバイスに従うと、次のように改善されます。

  • 現状:User Aからcall, User B/Cからresponse, User AからOffer, User B/CからAnswer
  • 改善:User Aからcallme, User B/CからOffer, User AからAnswer

確かにその通りです。メッセージのやり取りが片道分少なくなり、すっきりしますね。 ご指摘ありがとうございました。

次回は

次回は最終回の予定です。NATやFirewallを越えて通信するための、STUN/TURNについて説明したいと思います。

今回のソースコード

シグナリングサーバー (node.js)

クライアント側 (HTML, JavaScript)

Powered byNTT Communications

tag list

アクセシビリティ イベント エンタープライズ デザイン ハイブリッド パフォーマンス ブラウザ プログラミング マークアップ モバイル 海外 高速化 Angular2 AngularJS Canvas Chrome Cordova CSS de:code ECMAScript Edge Firefox Google Google I/O Google I/O 2014 HTML5 Conference 2013 html5j IoT JavaScript Microsoft Node.js PhoneGap Polymer SkyWay spdy TypeScript UI UX W3C W3C仕様 Webアプリ Web Components WebGL WebRTC WebSocket