がねこまさし

WebRTCでキャスしよう!片方向リアルタイム映像配信を作ろう

連載: WebRTC (3)

こんにちは!がねこまさしです。「WebRTCを使ってみよう」シリーズの最新話をお送りします。今回は、簡易的な放送局を作ってみましょう。

片方向配信の特徴

WebRTCを使った音声通話、ビデオチャットのサンプルには、双方向のものが多く見られます。ライブラリもそれを前提とした作りのモノが多いようです。なので今回は、片方向配信を実際に動かしてみましょう。
multi_or_1tomany

片方向配信には、双方向通信とは異なる特徴があります。

  • 視聴側はカメラやマイクといった機器が不要なので、参加のハードルが下がる
  • Peer-to-Peerでもフルメッシュ構造にはならないので、より多くの人が同時に利用できる

特に同時接続数はは双方向では4~5人が実用範囲なのに対し、片方向では10~30人程度に対して1つのPCから配信できます。ちょっとした仲間内のイベントや、社内イベントであれば、十分にカバーできるのではないでしょうか?(社内で動かせば、社内ネットワーク内で完結するので、セキュリティ部門に怒られることもないでしょうし…)

片方向配信をつなぐまで

今回の記事は、技術的には過去の記事「WebRTCを使って複数人で話してみよう」の内容でカバーされています。違いはPeer-to-Peer接続までの手順にあります。

片方向配信では、話す側(talk)と見る側(watch)が非対称です。どちらから通信を始めるかで、2つのシナリオに分かれます。

(A) 配信中に、新たに見る人(watch)が現れて、視聴を開始する

begin_watch

この場合、Peer-to-Peer確立までのシグナリングの流れは、次のようになります。
signaling_new_watch

  • 新しい視聴者(watchA)が、同じ部屋の中にいる全員に「映像ちょうだい(talk_request)」を送る
  • 他の視聴者(watchB)は、単に無視する
  • 配信側(talk)は新しくOffer SDPを生成し、watchAに対して送信する
  • watchAはOffer SDPを受け取ったら、Answer SDPを生成してtalkに送り返す
  • その後 ICE Candidate が複数交換され、最終的にPeer-to-Peerで片方向の映像ストリームが流れる

(2)視聴者(watch)が待機しているところに、話す側(talk)が配信を開始する

begin_talk
こちらの場合は、Peer-to-Peer確立までのシグナリングの流れは、次のようになります。
signaling_new_talk

  • 配信側(talk)が、同じ部屋の中にいる全員に「始めるよー(talk_ready)」を送る
  • 各視聴者(watchA, watchB)は、「映像ちょうだい(talk_request)」を返す
  • 配信側(talk)は各視聴者ごとに別々のOffer SDPを生成し、それぞれ送信する
  • 各視聴者(watch)はOffer SDPを受け取ったら、Answer SDPを生成してtalkに送り返す
  • その後 ICE Candidate が複数交換され、最終的にPeer-to-Peerで片方向の映像ストリームが流れる

先ほどのシーケンスの前に、開始の合図(talk_ready)を加えただけですね。

シグナリングサーバーのソースコード

シグナリングサーバーは、過去の記事「WebRTCを使って複数人で話してみよう」と同じものが使えます。ただしこの時はSocket.IO v0.9を使っていたので、今回はSocket.IO v1.0/v1.1の場合を掲載しておきます。サーバー開始の部分と、部屋名の保持の仕方が少し異なります。

クライアント:配信側(talk)のソースコード

以前の複数人でのケースとの違いを見ていきましょう。配信側では、相手側の映像を受け取る必要がないので、複数のvideoを処理する部分はごっそり削れます。また配信側の通信処理は、複数人で話す場合ととても近いです。

Peer-to-Peerの管理

以前と同じく、複数のPeer-to-Peer接続を管理するために便宜上のクラスと、関連する関数を作っておきます。

※以前は自前の過剰なステータス管理フラグを使っていましたが、無用なのでやめました。

シグナリングサーバーへの接続とイベント処理

シグナリングサーバーに対して、socket.ioクライアントを使って接続しておきます。また、接続時(会議室への入室)、切断時、メッセージ受信時のイベントハンドラを設定します。

応答するメッセージは、talk_request, answer, candidate です。talk_ready, offerは来ないはずなので、処理はしていません。

映像、音声の取得開始

特に変わったところはありませんが、映像取得後に tellReady()を呼び、その中で「準備できたよ(talk_ready)」と通知しています。

配信要求への応答

視聴側(watch)から、配信要求(talk_request)があった場合、次の処理が呼び出されます。

複数人の双方向のケースと違い、映像や音声を受け取る必要がないので、mediaConstraintsの内容がどちらも受信不要(false)にしています。実際に通信を行うオブジェクトを用意するのは、prepareNewConnection()の中で行っています。

  • RTCPeerConnectionを生成
  • ICE candidate生成時のイベントハンドラを設定

配信開始時の通知

反対に、配信側(talk)から新たに配信開始を通知する処理はこちらです。単にsocket越しに部屋内にメッセージを投げるだけですね。

SDP、ICEのやり取り

SDPやICE Candidateも、socket越しに相手(1人)に送るだけです。

Answerや、Candidateを受け取った場合は、PeerConnectionに覚えさせます。

クライアント:視聴側(watch)のソースコード

視聴側は自分のユーザーメディアは取得しません。映像も1つだけ受け取るので処理はシンプルです。

Peer-to-Peerの管理

Peer-to-Peerは1つだけなので、本来便宜上のクラス、関数は不要です。とは言えtalk側と共通にするために(あるいは複数人からの変更を減らすため)、同じコードにしておきました。

※同じと言いましたが、1点違いがありました。 MAX_CONNECTION_COUNT = 1 にしています。

シグナリングサーバーへの接続とイベント処理

シグナリングサーバーへの接続はtalkと同じです。処理するメッセージの種類が異なります。

応答するメッセージは talk_ready, offer, candidate です。talk_requestやanswerには反応しません。

配信の要求

配信の依頼は、socket.ioで会議室内全員に(相手を特定せずに)投げます。

SDP受信時の処理、ストリーム受信時の処理

talkからOffer SDPを受け取ったら、Peer-to-Peer通信の準備をします。PeerConnectionを生成し、Offerを覚えて、Answerを返します。

PeerConnectionを準備しているのは、prepareNewConnection()の中です。talkとほとんど同じですが、一部異なる部分があります。

自分のストリーム(localStream)がない代わりに、相手のストリーム(RemoteStream)のハンドラを用意しています。

配信開始通知を受けた場合

talk側から talk_ready を受け取った場合、まだ接続が確立していなければ、talk_requestを返します。その後処理は配信の要求と同様に進みます。

動かしてみよう

配信前
before_onair
配信開始後
after_onair
このように、複数のブラウザに配信を行うことができます。ぜひ社内などで試してみてください。シグナリングサーバーを流用してテキストチャットなどを付けると、より楽しく利用できますよ!
※今回のソースには含まれていませんが、TURNサーバーを用意すれば、Firewall/NATの内側から外側に配信することも可能です。

クライアント側のソースコード(全体)

配信側と視聴側の主な違い

配信側(talk)と視聴側(watch)の仕組みは似通っていますが、違う部分もあります。改めて違う部分を整理しておきます。
(1) 配信側だけがユーザーメディアを取得する。PeerConnectionを生成したときに、そのストリームを追加する。

  • peerconnection.addStream(localStream)


(2) 受信側だけが、相手のメディアストリームの接続、除去イベントを処理する。

  • peer.addEventListener(“addstream”, onRemoteStreamAdded, false);
  • peer.addEventListener(“removestream”, onRemoteStreamRemoved, false);


(3) 配信側と受信側は異なるメッセージに応答する。

  • 配信側だけ:talk_request, answer
  • 受信側だけ:talk_ready, offer

配信側(talk.html)

視聴側(watch.html)

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