前回はUSBIPD-WINというツールを使って、WSL2のLinuxからWindows上のUSBデバイスを認識できるようにしました。
しかしまだWSL2でUSBカメラは使えません。今回はこの問題を解決したいと思います。
USBIPD-WINを導入していない場合は前編をご確認ください。
LinuxとUSBカメラ
LinuxでUSBカメラを利用する場合は、一般的にVideo for Linux 2(Video4Linux 2, V4L2)という仕組みを利用します。
V4L2では、様々なビデオカメラのドライバをサポートしています。
最近のUSBカメラはUSB Video Class(UVC)に対応しているものが多く、V4L2とUVCドライバという組み合わせでUSBカメラが利用できるケースが多いようです。
この「V4L2」と「UVCドライバ」はいずれもLinux Kernel内の機能であり、Linux Kernelがこれらの機能を有効にする設定で作られている(コンパイルされている)必要があります。
私が使っているWSL2のkernelでどうなっているか調べてみましょう。ちなみにLinuxカーネルのバージョンは次のようになっています。
$ uname -r 5.10.102.1-microsoft-standard-WSL2
まずV4L2の状況です。
$ zgrep CONFIG_VIDEO_V4L2 /proc/config.gz
何も表示されません。つまりV4L2は有効になっていないと言うことです。
UVCの状況も確認してみます。
$ zgrep CONFIG_USB_VIDEO_CLASS /proc/config.gz
こちらも何も表示されません。
従って、WSL2で提供されているこのLinux KernelではUSBカメラが利用できないことがわかります。
WSL2のLinux Kernelのコンパイル
WSL2のLinux KernelでV4L2とUVCを利用できるようにするためには、Linux Kernelの再コンパイルが必要です。
この作業はWSL2のLinux上で行います。
コンパイルのためのツールを入手
Linux kernelをコンパイルするためには様々なツールが必要です。
Ubuntuでは下記コマンドで必要なツール一式を導入することができます。
$ sudo apt-get update $ sudo apt-get install build-essential flex bison dwarves libssl-dev libelf-dev git libncurses-dev
これでツールの入手は完了です。
ソースの取得
次にLinux kernelのソースコードを取得します。
WSL2のLinux kernelのソースは下記で公開されています。
まずタグ名を調べましょう。
右上の①の部分を選択すると「Switch branches/tags」という欄が表示されるので、②のTagsを選択しましょう。
そして③のエリアに現在使用しているLinux Kernelのバージョン(数字の部分)を入力します。
するとそのカーネルに対応したタグが④に表示されます。このタグ名をメモしておきます。
次のWSL2上の適当なディレクトリでこのタグ名を指定してcloneをします。「-b」の次の「linux-msft-wsl-5.10.102.1」が今回取得するタグ名になります。
$ mkdir wsl-kernel $ cd wsl-kernel $ git clone https://github.com/microsoft/WSL2-Linux-Kernel.git -b linux-msft-wsl-5.10.102.1 --depth 1
これでWSL2-Linux-Kernelというディレクトリが作成され、その中に指定したバージョンのLinux Kernelのソースコードが展開されています。
設定(コンフィグレーション)
続いてLinux kernelの設定(コンフィグレーション)をしていきます。
これをゼロからやるのは大変なので、現状の設定をベースに行うのがオススメです。現状の設定はソースコードのMicrosoft/config-wslに記載されているのでこれを利用します。
/proc/config.gzを展開したものを利用しても構いません。
$ cd Microsoft/config-wsl $ make menuconfig KCONFIG_CONFIG=Microsoft/config-wsl
これで次のようなLinux kernelの設定画面が表示されます。
この設定画面ではカーソルキー・スペースキー・リターンキー・Tabキーを駆使して操作します。
まずはV4L2を有効にしましょう。
- 「Device Drivers」→「Multimedia support」→「Filter media drivers」を有効化
- 「Device Drivers」→「Multimedia support」→「Media device types」→「Cameras and video grabbers」を有効化
- 「Device Drivers」→「Multimedia support」→「Media core support」→「Video4Linux options」→「V4L2 sub-device userspace API」を有効化
を設定します。
有効化する際は、モジュール(<M>)でなくビルトイン(<*>)とする必要があります。
次はUVCを有効にしましょう。
- 「Device Drivers」→「Multimedia support」→「Media core support」→「Media drivers」→「Media USB Adapters」→「USB Video Class (UVC)」と「UVC input events device support」を有効化
とします。ここでもモジュールではなくビルトインにします。
UVC以外のUSBカメラを使いたい場合は「Media USB Adapters」で表示されるドライバを有効化しておくと使えるかもしれません。
特に「GSPA based webcams」では多くのWebカメラに対応しているようです。
ここまでの設定を行うと、いくつか勝手に有効になってしまう設定があります。これは無効化しておきましょう。私が気づいたのは下記の1つです。
- 「Device Drivers」→「Multimedia support」→「Media core support」→「Media drivers」→「Media USB Adapters」→「GSPCA based webcams」
これはそのままにして置いても問題ないですが、Linux kernelのサイズが無題に大きくなったり、コンパイル時間が延びたりするので、無効化するのがオススメです。
以上の設定が終わったらTabキーで<Exit>を選び続け、一番上の階層で<Exit>を選ぶと「Do you with to save your new configuration?」と聞かれるので<Yes>を選択して保存しましょう。
なお設定は「Microsoft/config-wsl」に上書き保存されます。
ビルド
先ほど保存した設定を使ってビルドします。
$ make -j $(nproc) KCONFIG_CONFIG=Microsoft/config-wsl
ビルドは結構時間がかかります。最終的に次のように「Kernel: arch/x86/boot/bzImage is ready」となればビルド成功です。
この最後の行に表示されたbzImageがビルドして生成されたLinux Kernelになります。
このファイルはWindows側にコピーする必要があります。
今回はWindowsのユーザディレクトリ(%UserProfile%)にwslというフォルダをつくってそこにコピーしておきます。
$ mkdir -p /mnt/c/Users/Windowsでのユーザ名/wsl $ cp arch/x86/boot/bzImage /mnt/c/Users/Windowsでのユーザ名/wsl/bzImage-v4l2-uvc
今回は作成したLinux KernelをbzImage-v4l2-uvcというファイル名でコピーしました。
作成したLinux Kernelでの起動
自分で作成したLinux KernelをWSL2で使用するためには、「.wslconfig」というファイルをWindowsのユーザフォルダ(%UserProfile%)に作成する必要があります。
このフォルダは「Windowsアイコンを右クリック」→「ファイル名を指定して実行」→「. (ピリオドを入力してOK)」で開くことができます。
ユーザフォルダを開いたら「テキストドキュメント」を新規作成し、ファイル名を「.wslconfig」に変更します。
そしてこの.wslconfigの中身を次のようにします。
[wsl2] kernel=C:\\Users\\Windowのユーザ名\\wsl\\bzImage-v4l2-uvc
つまりkernelというキーに先ほどコピーしたbzImageファイルを指定します。ただし、ファイルパスの「\」は「\\」とする必要があります。
.wslconfigファイルを作成したら、PowerShell(あるいはWindowsターミナルで)次のコマンドを入力し、いったんWSL2を停止させます。
wsl --shutdown
そしてWSL2を再度開始します。
WSL2が起動したらLinux Kernelの詳細情報を表示させてみましょう。
$ uname -a Linux baron 5.10.102.1-microsoft-standard-WSL2+ #1 SMP Sat Apr 23 20:43:18 JST 2022 x86_64 x86_64 x86_64 GNU/Linux
このように表示される日時がビルドした日時になっていれば、自分でビルドしたLinux kernelで起動しています。
またV4L2とUVCが有効になっていることも確認しておきましょう。
$ zgrep V4L2 /proc/config.gz CONFIG_VIDEO_V4L2=y CONFIG_VIDEO_V4L2_I2C=y CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_VIDEOBUF2_V4L2=y $ zgrep USB_VIDEO /proc/config.gz CONFIG_USB_VIDEO_CLASS=y CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y
USBカメラの利用
V4L2とUVCに対応したLinux KernelでWSL2が動いていることを確認したら、USBカメラを試してみましょう。
今回は下記のUSBカメラを利用しています。
USBカメラの接続
まず前回紹介した手順でUSBカメラをアタッチします。
Windows側でUSBカメラをアタッチすると、WSL2からはUSBカメラが接続された様に見えます。
そこでWSL2側でdmesgを実行してみます。次のように「uvcvideo」で始まる行があればUVCのカメラとして認識されています。
パーミッションの修正
USBカメラが無事に認識されると/dev以下にvideoで始めるファイル(デバイスノード)が作成されます。
$ ls -l /dev/video* crw------- 1 root root 81, 0 4月 23 23:06 /dev/video0 crw------- 1 root root 81, 1 4月 23 23:06 /dev/video1
このパーミッションではルート権限を持つユーザしかUSBカメラを利用できません。
これでは不便なのでグループをvideoに変更し、グループでも読み書き可能なようにしておきます。
$ sudo chgrp video /dev/video* $ sudo chmod g+rw /dev/video* $ ls -l /dev/video* crw-rw---- 1 root video 81, 0 4月 23 23:06 /dev/video0 crw-rw---- 1 root video 81, 1 4月 23 23:06 /dev/video1
このパーミッションの修正は毎回行う必要があります。
これでは不便なのでなんとかしたいのですが、今回は先送りにしておきます。
v4l-ctlでの確認
次にV4L2で今回接続したカメラが認識できるか確認してみます。この確認にはv4l2-ctlというツールを使いますのでv4l-utilsパッケージをインストールします。
$ sudo apt-get install v4l-utils
そしてv4l2-ctlでデバイスの情報を表示させてみます。
$ v4l2-ctl --all Driver Info: Driver name : uvcvideo Card type : Microsoft® LifeCam HD-3000: Mi Bus info : usb-vhci_hcd.0-1 Driver version : 5.10.102 Capabilities : 0x84a00001 Video Capture Metadata Capture Streaming Extended Pix Format Device Capabilities Device Caps : 0x04200001 Video Capture Streaming Extended Pix Format Priority: 2 Video input : 0 (Camera 1: ok) Format Video Capture: Width/Height : 640/480 Pixel Format : 'YUYV' (YUYV 4:2:2) Field : None Bytes per Line : 1280 Size Image : 614400 Colorspace : sRGB Transfer Function : Rec. 709 YCbCr/HSV Encoding: ITU-R 601 Quantization : Default (maps to Limited Range) Flags : Crop Capability Video Capture: Bounds : Left 0, Top 0, Width 640, Height 480 Default : Left 0, Top 0, Width 640, Height 480 Pixel Aspect: 1/1 Selection Video Capture: crop_default, Left 0, Top 0, Width 640, Height 480, Flags: Selection Video Capture: crop_bounds, Left 0, Top 0, Width 640, Height 480, Flags: Streaming Parameters Video Capture: Capabilities : timeperframe Frames per second: 30.000 (30/1) Read buffers : 0 brightness 0x00980900 (int) : min=30 max=255 step=1 default=133 value=133 contrast 0x00980901 (int) : min=0 max=10 step=1 default=5 value=5 saturation 0x00980902 (int) : min=0 max=200 step=1 default=83 value=83 white_balance_temperature_auto 0x0098090c (bool) : default=1 value=1 power_line_frequency 0x00980918 (menu) : min=0 max=2 default=2 value=2 0: Disabled 1: 50 Hz 2: 60 Hz white_balance_temperature 0x0098091a (int) : min=2800 max=10000 step=1 default=4500 value=4500 flags=inactive sharpness 0x0098091b (int) : min=0 max=50 step=1 default=25 value=25 backlight_compensation 0x0098091c (int) : min=0 max=10 step=1 default=0 value=0 exposure_auto 0x009a0901 (menu) : min=0 max=3 default=1 value=3 1: Manual Mode 3: Aperture Priority Mode exposure_absolute 0x009a0902 (int) : min=5 max=20000 step=1 default=156 value=156 flags=inactive pan_absolute 0x009a0908 (int) : min=-201600 max=201600 step=3600 default=0 value=0 tilt_absolute 0x009a0909 (int) : min=-201600 max=201600 step=3600 default=0 value=0 zoom_absolute 0x009a090d (int) : min=0 max=10 step=1 default=0 value=0
V4L2も問題なく動作しているようです。
USBカメラの映像の表示
ここまできたらUSBカメラの映像を表示してみましょう。
今回はUVCに対応したツールである「guvcview」を使って見ます。インストールは下記のコマンドです。
$ apt-get install guvcview
これでguvcviewというコマンドがつかるようになるので実行してみましょう。
$ guvcview
WSLgが使える環境であればこれで「Guvcview」と制御するウィンドウとカメラの映像を表示するウィンドウの2つのウィンドウが表示されます。
しかし、私の場合はカメラ映像を表示するウィンドウは真っ暗で何も表示されません。
guvcviewを起動したターミナルを確認すると
V4L2_CORE: Could not grab image (select timeout): リソースが一時的に利用できません
というメッセージが繰り返して表示されています。
このような場合はguvcviewの制御ウィンドウで解像度を下げてみましょう。
私の環境では解像度を320×240まで下げると、カメラ映像が表示されるようになりました。
WSL2でUSBデバイスを使う際に、USBをネットワーク(IP)に変換する処理が入っており、高い解像度ではデータ転送が間に合わないようです。
なお、フレームレートを下げると、VGA(640×480)ぐらいならなんとか表示させることができました。
ただ、映像にノイズが入ってしまっており、今子設定での実用性には少々難があると感じます。
この辺は現状の仕組みの限界なのかもしれません。
解像度の変更などをいろいろ試していると、guvcviewがエラーで落ちてしまうことがたびたびありました。
このような場合では、USBIPD-WINのアタッチが無効になっている(勝手にデタッチされてしまう)ことが多かったです。
まとめ
今回はWSL2でUSBカメラを使えるようにしてみました。
WSL2のLinux KernelはV4L2やUVCが有効になっていないため、USBカメラを利用するにはLinux Kernelを自分でコンパイルして差し替える必要があります。
Linux Kernelを変更すればUSBIPD-WINと組み合わせることによってWSL2でUSBカメラを利用することができます。ただ転送速度に難があるようなのが残念です。
次回はroot権限がなくてもUSBカメラを使えるようにしてみたいと思います。
コメント