グレンジ Advent Calendar 2019 6日目担当の raitome です。
グレンジでクライアントエンジニアをやっています。
最近はちょっと違う技術を触ってみたくて、Vue.jsを使って、ビデオ通話機能を作ってみました。
ネットの資料が多いですが、古いものも多くて、すぐ使えるコードが少ないので、
メモのためでもあり、必要なものを簡単にまとめました。
##WebRTCについて
詳細は下記でご覧いただけます。
https://ja.wikipedia.org/wiki/WebRTC
https://qiita.com/yusuke84/items/286f569d110daede721e
##使ったライブラリ
とりあえず手軽にビデオ通話機能を作りたくて、下記のライブラリを使いました。
・simple-peer: https://github.com/feross/simple-peer
WebRTCベースで、p2pのビデオ通話を簡単に実装できるライブラリ。ファイルの転送などもできるらしい
・ simple-signal-clientとsimple-signal-server: https://github.com/t-mullen/simple-signal
WebRTCでPeer to Peer通信を行う場合、シグナリングサーバと呼ばれる仲介サーバを用意する必要がありますので、simple-peerのためのシグナルサーバー用ライブラリsimple-signalを使いました。simple-signal-clientはクライアント側で、simple-signal-serverはサーバー側で使います。
他に、シグナルサーバーはSocket通信を使うため、socket.ioとクライアントとサーバー側のライブラリも使います。
(Skyway: https://webrtc.ecl.ntt.com/ というサードパーティのものを使えばもっと早く構築できるらしいが、有料らしいので、今回は自前で色々作ってみました)
##クライアント側の準備
今回は必要な処理だけ紹介します。完成版のコードを割愛します。
###1.必要なもののimport
import SimplePeer from 'simple-peer'
import SimpleSignalClient from 'simple-signal-client'
import io from 'socket.io-client'
###2.Peerの作成
const socket = io("シグナルサーバーのurl" , {secure: true})
let signalClient = new SimpleSignalClient(socket)
// 下記の方法でチャットルームの感じで作れる
signalClient.discover("チャットルームID的な何か") // これを使ってユーザーリストとかを取得できる
signalClient.on('discover', receiveUserList) // ユーザーリスト処理のfunctionをここに入れる
###2.Peerへの接続リクエスト処理
ビデオ通話したい相手のソケットIDが分かれば、下記のfunctionを使えばシグナルサーバーを経由して接続できます。
const { peer } = await signalClient.connect("自分のソケットID", "チャットルームID的な何か"})
###3.Peerの受信処理
signalClient.on('request', async (request) => {
const { peer } = await request.accept() // 接続リクエストを承認
peer.on('connect', () => {
peer.send("connected")
})
peer.on('stream', (remoteStream) => {
// 向こうから受け取ったPeerのストリームを画面に表示させる
const videoElement = document.createElement('video')
videoElement.autoplay = true
videoElement.srcObject = remoteStream
container.appendChild(videoElement) // 事前に用意したコンテナに新しく作ったElementを追加する
})
peer.on('close', () => {
// 停止時の必要処理
})
// 自分のカメラの映像も向こうに送信するので、ストリームを取得して画面に追加する。下記の関数はあとで説明します
addLocalCameraStream()
})
###4. カメラの扱い
下記の処理で、ブラウザがカメラの使用権限を取得して、カメラのストームを扱います。
ブラウザによって挙動が少し違うので、最新版のChromeかFireFoxを使うことをオススメします。
一つ注意点としては、Chromeの場合は、セキュリティの関係で、サーバー側はSSL対応をしないと、下記のメソッドはうまく動きません。
addLocalCameraStream() {
var mediaDevices = navigator.mediaDevices || ((navigator.mozGetUserMedia || navigator.webkitGetUserMedia || navigator.msGetUserMedia) ? {
getUserMedia(c) {
return new Promise(((y, n) => {
(navigator.mozGetUserMedia || navigator.webkitGetUserMedia).call(navigator, c, y, n);
}));
}
} : null)
mediaDevices.getUserMedia({ audio: true, video: true }).then((localStream) => {
peer.currentPeer.addStream(localStream) // これで接続した相手にストリームを送る
// ここでストリームを画面に表示させる処理を追加してもいい
}).catch(err => {
});
}
ちなみに、mediaDevices.getDisplayMediaを使えば、画面共有機能も作れます。
上記のコードを使えば、クライアント側の最低限の機能が作れます。
##シグナルサーバーの準備
シグナルサーバーのコードは割とシンプルになります。
主に必要に応じてユーザーの管理処理をカスタマイズします。
const io = require('socket.io')(server)
var SimpleSignalServer = require('simple-signal-server')
var signal = new SimpleSignalServer(io)
signal.on('request', (request) => {
request.forward() // リクエストをそのまま進ませる
})
signal.on('discover', (request) => {
// ここでユーザーのソケットIDやルームIDを管理して、request.discoverを使って、全てのpeerの必要な情報を送る
})
signal.on('disconnect', (socket) => {
// 接続切れる時の処理
})
##最後に
以上でコードを使えば、必要最低限のビデオ通話機能が作れます。少し調整すれば、画面共有機能も作れます。実際にテストしてみたところ、P2Pの関係でもありますが、割とスムーズなビデオ通話ができました。
また、何か不明なところがありましたら、simple-signalのサンプルコードも一緒にご覧いただければ。