積雲が映像制作したMV『RANGEFINDER』公開中
専門88IO

【Linux】スマホカメラを無理やりPCの仮想カメラに

専門

ビデオチャットをするに当たってスマホよりも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-studioobs-v4l2sinkを導入する。

sudo pacman -S obs-studio
yay -S obs-v4l2sink

後はOBS StudioからPIPのウィンドウキャプチャを行い、【ツール】->【v4l2sink】から作成した仮想カメラデバイスを選択すれば完成だ。

操作まとめ

  1. Peer Serverを立てる peer --port 9000
  2. PCでreceiver.htmlを開く
  3. スマホでsender.htmlを開く&カメラを許可
  4. PC側receiver.htmlもカメラを許可
  5. PCにスマホカメラ映像が映る
  6. ピクチャーインピクチャー機能で独立ウィンドウに切り出し
  7. OBS Studioでウィンドウを指定
  8. v4l2sinkで仮想カメラに出力
  9. ビデオチャットツールで仮想カメラを選択

動作デモ

  • 動作時のスクリーンショット
  • Discordでの動作テスト

まとめ

なんとか目的の動作をさせることができた。通信ラグも1秒未満であったため概ね満足である。

仮想デバイスやWebRTC技術を学ぶ良い機会にはなったが、タイトルの通り「無理やり」実装したものであり需要がない。

Androidカメラを仮想カメラとして扱うDroidCamというアプリケーションのクライアントがLinuxでも利用可能だが、導入過程が難解であったため次回はそれを解説しようと思う。

コメント

タイトルとURLをコピーしました