ビデオチャットをするに当たってスマホよりもPCの方が便利なことが多い。しかし所有しているWebカメラの性能が低く、何とかしてスマホのカメラを使えないかと考えた。調べれば既存のアプリケーションはあるだろうが、今回は自分で無理やり実装することで知識を深めることにした。要するに自己満足。
方針
まずスマホカメラで撮った画像をPCにリアルタイムで送らなければならない。RTMPやHTTPでのストリーム配信を検討したが、自宅の低速なLAN環境では1〜2秒のラグが生じた。これでは十分と言えない。そこでWebRTCを用いてP2P通信を行い画像取得のラグを減らすことにした。
次に取得した画像データはブラウザ上に表示されている前提で、これを仮想カメラに送る必要がある。仮想カメラはv4l2loopbackで作成し、obs-studioとobs-v4l2sinkを用いてストリーミングすることにした。またブラウザ上の映像領域はピクチャーインピクチャー(PIP)機能で独立したウィンドウに切り出せるため、これをOBSで指定する方針を立てた。
WebRTCを用いたP2P通信
現状推奨されてはいないらしいが今回は「無理やり」なのでPeerJSを利用することにする。
P2P通信に必要となるPeer ServerはNode.js環境でnpmからインストールできる。
npm install -g peerjs
Peer Serverを立てるコマンド例は以下の通りだ。
peer --port 9000
次に送り手と受け手の実装だ。ここではPC側のローカルIPを192.168.0.X
とする。
スマホ側(送り手)sender.html
<!doctype html> <html> <head> <title>Sender</title> <script src = "https://cdnjs.cloudflare.com/ajax/libs/peerjs/0.3.16/peer.min.js"></script> </head> <body> <video id="local" autoplay ></video> <script type="text/javascript"> const peer = new Peer('sender', {host: '192.168.0.X', port: 9000}); const startChat = async() => { const localStream = await navigator.mediaDevices.getUserMedia({ video: true }); document.querySelector('video#local').srcObject = localStream; const call = peer.call('receiver', localStream); call.on('stream', (remoteStream) => { }); } startChat(); </script> </body> </html>
PC側(受け手)receiver.html
<!doctype html> <html> <head> <title>Receiver</title> <script src = "https://cdnjs.cloudflare.com/ajax/libs/peerjs/0.3.16/peer.min.js"></script> </head> <body> <video id="remote" autoplay></video> <script type="text/javascript"> const peer = new Peer('receiver', {host: '192.168.0.X', port: 9000}); peer.on('call', call => { const startChat = async() => { const localStream = await navigator.mediaDevices.getUserMedia({ video: true, }); call.answer(localStream); call.on('stream', remoteStream => { document.querySelector('video#remote').srcObject = remoteStream; }) } startChat(); }) </script> </body> </html>
上記のコードは相互画像送信を行っている。一方向で問題ないのだが、この処理を省くと安定した通信ができなくなったため妥協した。
仮想カメラの作成
仮想カメラの作成にはv4l2loopbackを使うのだが、この機能のビルドにはLinuxカーネルのヘッダーファイルが必要になる。
まずはカーネルのバージョンを確認。
uname -r
筆者の場合、”4.19.118-1-MANJARO”だったため該当バージョンのヘッダーファイルをインストールする。
sudo pacman -S linux419-headers
以上の前準備でv4l2loopbackのビルドが可能になった。ソースファイルまたはパッケージマネージャーから導入する。
yay -S v4l2loopback-dkms
後は以下のコマンドで仮想カメラを作成できる。(ls /dev/video*
で追加されているか確認できる)
sudo modprobe v4l2loopback exclusive_caps=1
OBSによるストリーミング
obs-studioとobs-v4l2sinkを導入する。
sudo pacman -S obs-studio yay -S obs-v4l2sink
後はOBS StudioからPIPのウィンドウキャプチャを行い、【ツール】->【v4l2sink】から作成した仮想カメラデバイスを選択すれば完成だ。
操作まとめ
- Peer Serverを立てる
peer --port 9000
- PCでreceiver.htmlを開く
- スマホでsender.htmlを開く&カメラを許可
- PC側receiver.htmlもカメラを許可
- PCにスマホカメラ映像が映る
- ピクチャーインピクチャー機能で独立ウィンドウに切り出し
- OBS Studioでウィンドウを指定
- v4l2sinkで仮想カメラに出力
- ビデオチャットツールで仮想カメラを選択
動作デモ
- 動作時のスクリーンショット
- Discordでの動作テスト
まとめ
なんとか目的の動作をさせることができた。通信ラグも1秒未満であったため概ね満足である。
仮想デバイスやWebRTC技術を学ぶ良い機会にはなったが、タイトルの通り「無理やり」実装したものであり需要がない。
Androidカメラを仮想カメラとして扱うDroidCamというアプリケーションのクライアントがLinuxでも利用可能だが、導入過程が難解であったため次回はそれを解説しようと思う。
コメント