目標
UbuntuのUnity(デスクトップ環境じゃなくてゲームエンジンのほう)で, Webカメラからの入力をカメラに表示する.
環境
Ubuntu 20.04 LTS
Unity 2020.3.16f1
カメラ入力のフォーマットの変換
私が使用しているWebカメラのフォーマット, mjpegにUnityが対応していないようで, そのままではUnityで映像入力として扱うことができませんでした. そこで, ffmpegを使ってv4l2loopbackによる仮想カメラを経由してUnityに映像を渡すことにしました.
私はどちらも導入済みでしたが, まだの方は,
- ffmpeg:
sudo apt-get install ffmpeg
- v4l2loopback: https://qiita.com/skyfish20ch/items/b1a29dd03bf2808ea59c (追加でv4l-utilsも使うので,
sudo apt-get install v4l-utils
あたりで導入してください)
あたりで導入してください.
これで必要なものが揃いました.
では, まずは仮想デバイスを作ります.
sudo modprobe v4l2loopback devices=1 exclusive_caps=0
で仮想デバイスを作成し, v4l2-ctl --list-devices
で現在のデバイスの一覧を確認します. すると, 接続されているWebカメラの他に以下のようなデバイスが作成されていることが確認できると思います.
Dummy video device (0x0000) (platform:v4l2loopback-000):
/dev/video4
GENERAL - UVC : GENERAL - UVC (usb-0000:00:14.0-3):
/dev/video0
/dev/video1
環境によって/dev/video*
の数字などが違うかと思いますが, 私の場合は上の/dev/video4
が作成した仮想デバイス, /dev/video0
がUSB接続のWebカメラを表しています(なぜWebカメラのデバイスが2つあるのかは謎ですが).
次に, Webカメラの映像を仮想デバイスに流します.
$ ffmpeg -f v4l2 -i /dev/video0 -vf format=yuyv422 -f v4l2 /dev/video4
このように, -i
のあとにWebカメラ, 末尾に仮想デバイスを指定します. また, Unityが対応しているフォーマットを指定します.
Unityで映像を流す
シーンにCanvasを追加し, その子要素にRaw Imageを追加します. さらに, 以下のようなスクリプトを作成します.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class WebCamera : MonoBehaviour
{
public RawImage rawImage;
WebCamTexture webCamTexture;
WebCamDevice[] webCamDevice;
int selectCamera = 0;
void Start () {
this.webCamDevice = WebCamTexture.devices;
for (int i = 0; i < this.webCamDevice.Length; i++){
print(i.ToString() + ": " + this.webCamDevice[i].name.ToString());
}
this.webCamTexture = new WebCamTexture(webCamDevice[selectCamera].name, 1280, 720, 30);
this.rawImage.material.mainTexture = this.webCamTexture;
this.webCamTexture.Play();
}
}
スクリプトは, Raw Imageあたりにアタッチしましょう. そして, アタッチ先のInspectorからScriptのRaw Imageにシーン内のRaw Imageをアタッチします.
さていよいよ実行…ですが, おそらくエラーが出たり, canvasに何も表示されなかったり, 正しく動作しないと思います. 一度ログを見てみましょう. for文の中でのprintの出力は, 以下のようになると思います.
0: GENERAL - UVC : GENERAL - UVC (/dev/video0)
UnityEngine.MonoBehaviour:print (object)
1: GENERAL - UVC : GENERAL - UVC (/dev/video1)
UnityEngine.MonoBehaviour:print (object)
2: Dummy video device (0x0006) (/dev/video4)
UnityEngine.MonoBehaviour:print (object)
この中の, 2: Dummy video device
の2
が, Unity上での識別番号のようです. そこで, 先程のスクリプトのint selectCamera = 0;
をint selectCamera = 2;
に書き換えて実行してみましょう. これでcanvasに映像が出ると思います.
また, うまくやれば, この手動での書き換え操作を処理に組み込めそうですね.
感想
最初はOBSのv4l2-sinkで仮想カメラに接続していましたが, 煩わしさを感じたので重い腰を上げてffmpegのことを調べてみました. なかなか苦戦しましたが, シェルスクリプトにまとめておくことでスッキリさせることができて満足です.
ffmpegすげえ.
参考