がねこまさし

シグナリングサーバーを応用! 「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)

週間PVランキング

新着記事

Powered byNTT Communications

tag list

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