WebRTCって何?
WebRTCはブラウザとブラウザ直結で動画や音声、データのやりとりをする規格です。
つまり、WebRTC使うとブラウザだけでテレビ電話が出来てしまいます。
実装状況は?
色々幸せそうな未来が見えますが、例によってブラウザにより実装状況が違います。
まあ、多分そのうち主要なブラウザで実装されそうですが、動画の規格で実装状況が違うという、Videoタグで聞いたような話が。
一応、両方実装してねみたいなことにはなっているみたいです。
今回やること
そんなわけで、明るい未来が見えるので、今のうちにWebRTCを触ってみましょう。
ゴールは
- 1対1でテレビ電話をする、
- 1対1でチャットをする
です。
WebRTCのAPIをそのまま使うのはキツイのと、ブラウザとブラウザを直結すると言いながら実は サーバーが必要 なのでSkyWayを利用します。
なぜサーバーが必要かというと、だって、接続先わからないからそれを斡旋してくれる人が必要じゃんってことです。
その他、自分のIPアドレスを教えてくれるサーバーもいたりしますが、詳しいことはこのスライドが詳しいです。
つながってしまえば間にサーバーが入ることなくP2Pになります。
SkyWayのサイトにはこのサンプルもあるのですが、テレビ電話をするのではなく、WebAPIのプログラムを知りたいので自分で書いてみます。
ついでに、 Promiseの練習 もしてみます。
Promiseって何?
非同期処理でコールバック地獄を回避するための、いい感じなJavascriptのネイティブ関数です。
ES6からの実装ですがjQueryやその他それっぽい感じの実装は今までも色々ありました。
最新のブラウザは使えるので安心です!(というか、さっさとIEなくしちゃえばいいのに・・・・。関係無いけどMacでもEdge出せばいいのに。)
やってみよう
SkyWayにあるPeerJSのドキュメントを読みながらテキトーに実装するとあっという間にチャットだけは出来ましたがPromise使ったり動画表示したりで躓きました。
ソースは下記URLにあります。
https://github.com/pman-taichi/webrtc
###SkyWayのAPIキーを取得
SkyWayのサーバーを利用するにはAPIキーを取得する必要があるので取得します。
あとは、WebRTCのAPIラッパーであるPeerJSをSkyWay用にカスタマイズしたライブラリを使います。
PeerJS: https://skyway.io/dist/0.3/peer.min.js
###処理の順番
Promise使っているのでどう言う順番で何をするのかわかりやすくなっています。
getUserMedia() //カメラやマイクへの接続を取得
.then(connectToSkyway) //SkyWayのサーバーへ接続してpeerオブジェクトを取得する
.then(setPeerEvent) //Peerオブジェクトのイベントにアクションをセットする。
.then(function(peer){
return getUserList(peer) //ユーザーIDの一覧を取得
.then(showUserList(peer)); //ユーザーIDの一覧を表示
})
.catch(function(message){
console.log(message);
});
###カメラやマイクへの接続を取得(getUserMedia)
Peerオブジェクトを取得してからでもいいんですが、getUserMediaでカメラとマイクへの接続を取得しています。
このときブラウザには確認のアラートがでます。ここの部分のコードはここのコードまんまです。
ユーザーが許可してくれるとlocalMediaStreamを引数にsuccessCallbackがよばれるので、そこでPromiseのresolveを呼んでいます。
この関数を呼び出すとこの処理は正常に終わったよとなってthenでつなげた次の処理へ移ります。
localMediaStreamを通信したいユーザーに渡すっぽい。
var getUserMedia = function(){
return new Promise(function(resolve, reject) {
navigator.getUserMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
if (navigator.getUserMedia) {
navigator.getUserMedia(
// constraints
{
video: true,
audio: true
},
// successCallback
function (localMediaStream) {
mediaStream = localMediaStream;
resolve(localMediaStream);
},
// errorCallback
function (err) {
console.log("The following error occured: " + err);
reject(err);
}
);
} else {
var message = "getUserMedia not supported";
console.log(message);
reject(message);
}
});
};
###SkyWayのサーバーへ接続してpeerオブジェクトを取得する
var connectToSkyway = function (){
return new Promise(function(resolve, reject){
var peer = new Peer({
key: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
debug:0
});
peer.on('open', function(id) {
resolve(peer);
});
});
};
SkyWayのサーバーにつないで相手と通信するのにつかうPeerオブジェクトを作ります。
生成されるとopenイベントがよばれるのでコールバックを設定しておきます。
コールバックの引数にはサーバでふられたIDが入っています。これは相手と接続するために使うIDです。
Peerオブジェクトを作るときに任意のIDを設定することもできます。その場合重複したりしたらどうなるのかなんかまではやっていません。
次の処理へはIDじゃなくてPeerオブジェクトを渡していますが、PeerオブジェクトからIDは取得出来るので大丈夫です。
このときサーバーとの間でなにが行われているかはdebugのレベルを上げてあげるとコンソールに表示されます。
どうやら、websocketのコネクションが生成されているみたいです。
###Peerオブジェクトのイベントにアクションをセットする。
ここはなんかイマイチな実装です(言い訳するなら、とりあえず出来ればいいや的な)。
var setPeerEvent = function (peer) {
//データ用の接続イベントを受信したら
peer.on('connection', function(conn){
openConn(conn);
});
//呼び出しされたら
peer.on('call', function(call) {
// 呼び出されたら自分のメディアストリームを渡して応答する。
call.answer(mediaStream);
streamCall(call);
});
return peer;
};
これは簡単にいうと、電話がかかってきたら何をするかってことです。
相手からの呼び出しはデータ用と動画、音声用の呼び出しがあるのでそれぞれに処理を記述します。
データ用の呼び出しがあったらその接続のイベントに対してまたアクションを設定します。
その内容はopenConnに記述されています。
簡単に説明してしまうと、
- 接続が開いたら、チャットに必要なフォームやボタンを作る。
- 接続にデータが届いたらそのデータを表示する。
だけです。
そして、動画・音声で呼び出されたらまず相手に自分のメディアストリームを渡します。
これを使って相手はこちらのカメラからのデータを受け取ります。
そのあとの処理はstreamCallに記述してありますが、こちらも簡単に
- 相手のメディアストリームが届いたら
- videoのDOMを作る
- それにメディアストリームを設定する
- ブラウザに表示する
- 再生開始
だけです。
setPeerEventでは非同期で処理する部分もないのでPromiseオブジェクトを返さずにPeerをreturnして次へ渡しています。
###ユーザーIDの一覧を取得
peerオブジェクトを使ってSkyWayのサーバーから使用しているAPIキーで接続している全ユーザーのIDを取得します。
これも非同期処理なのでPromise使っています。全ユーザーIDを受信したらそれをリストにして次の処理へ渡しています。
###ユーザーIDの一覧を表示
前の処理から受け取ったIDリストを表示します。
そのときに各IDをクリックしたらそのIDのユーザーへ接続する処理を仕込んでおきます。
ここは他と違って下記の様に。他にも色々やり方はありますが、peerとlistの両方を渡したかったためです。
var showUserList = function(peer){
return function(list) {
..............
};
};
以上でおしまいです。テストするにはカメラ付きのPCが2台ないと出来ないと思いきや、同じブラウザ内でも出来ます。もしくはAndroidでも動くのを確認しました。iOSはダメです・・・。
複数拠点でもできますが、3つくらい開くとめちゃくちゃ遅いです。複数の場合はなんかやらなきゃいけないみたいなので、本格的に使うことが出てきたらやってみたいと思います。