NikonのカメラをWebカメラにする方法(もちろんマルチOSで)

がなかったので、自分で開発した。

tl;dr

f:id:puhitaku:20200727221049j:plain
概観

github.com

  • カメラからライブビュー (LV) を取ってきてOBS経由で仮想カメラにするソフト mtplvcap をGoで書いた
  • PCやスマホからカメラのフォーカス等を制御できるリモコンもある
  • libusbのおかげで1つのコードで Windows/macOS/Linux 全対応!(ビルド済みバイナリはGitHubにある)(多分BSDとかでも動くはず)
  • あまり動作確認できてないので、皆さんの報告をお待ちしております
  • 他メーカーのカメラも原理上は対応可能(実験機を貸してくれるならやります)

最新情報(2020/7/31現在)

Version 1.0.3リリースしました。

  • D7000で動作確認 (id:takashi0314 さんありがとうございます!)
  • D610で動作確認(id:hazlitt さんありがとうございます!)
  • Z6で動作確認
  • D3200で動作確認
  • 初期化でミスった時にエラーメッセージがちゃんと出るようにした
  • 初期化で世代間の差がある部分を吸収した

使い方

日本語のREADMEを書いておいたのでそれを参照してください。

経緯

昨今のアレにより手持ちの一眼をWebカメラにするのが流行っている。CanonのWebカメラソフトはBetaながらMacとWindowsに対応してきたし、SIGMA fpはなんとUVCに対応している。しかしどうもそれ以外のベンダーは対応がしょっぱい。Nikon*1ユーザーな僕は、家で寂しく指をくわえるしかなかった。

4月以降はインカメが最高なPixel 3で快適にビデオ通話できてたし、一眼をわざわざ使う理由は正直ないのだが、

やっぱり…一眼で撮れたら嬉しいよね…。

ということで、開発に踏み切った。

日頃はほぼMac + ヘッドレスなLinuxの構成なので、Macで写したい。既存手法で出ているv002 Camera Live謎の海外ソフト頑張る方法だと、Macのみ・Windowsのみの対応となり汎用性がない。ソフトでライブビューを表示してそのウインドウをキャプチャするなんていう記事もあるけど、ゲーム配信じゃないんだし…とてもエレガントとはいえない。

ではどうやって画を取ってきて仮想カメラに流し込むか?その算段はついている。カメラからMTPで画を取り出し、WebSocketで配信、それをOBSのBrowserSource(OBSにエンベッドされたChromiumの画面をソースにできるやつ)で表示してOBSの仮想カメラに流すというものだ。

MTPとはMedia Transfer Protocolの略で、デバイスの制御とファイルのやり取りを網羅しているプロトコルだ。ベネッセ個人情報流出事件で用いられたテクニックはMTPと言えば思い出す人もいるかもしれない。USB Mass Storageとは異なるのでWindows以外では扱いにくくよく厄介者扱いされるが、機能的にはMass Storageよりも豊富なため携帯プレーヤーなどではデファクトスタンダードとなっている。

開発

要件は以下のように決めた。

  • Windows/macOS/Linux全対応
    • 昨今のカメラベンダーの対応に業を煮やしていたので
  • Goで書く
    • cgoが
    • リンクが楽
    • ランタイムがいらない
  • USB部分と仮想カメラ部分はOS間で差異があるためlibusbとOBSに任せる

色々探していると、これまたドンピシャなOSS go-mtpfs を発見した。AndroidなどのMTPデバイスとlibusb経由で低レベル通信をしつつ、FUSEを使ってマウントするものだ。

github.com

USBの低レベル通信とWebサーバーによる配信を1プロセスでやるにあたって、Go + libusbで書きたいと思っていたので、まさに願ったり叶ったり。macOSでデバイスの初期化が上手くいかない問題はあったもののGoLandのデバッガーのお陰でスムーズに解決し、forkした。

まず取り掛かったのはライブビューの開始。MTPではuint16の命令番号的なのを送りつけることで動作を命令することができる。例えば、ライブビューの開始は0x9201が対応している。これを送りつけたり応答をパースしたりする部分はgo-mtpfsの力を借りる。

次に取り掛かったのはライブビューの画の取得。ここでちょっと心拍数が上がってくる。これもまたライブビューの開始と同様に 0x9203 を送りつけるとキャプチャできる。

この画像をOBSのBrowserSourceで表示するには、フロントエンドを用意して、画像シーケンスを配信する必要がある。これはWebSocketでサクッと解決できた。

そしてそして、お待ちかねのOBS + Zoomの結合も難なく成功!やったー!

ひとまずのマイルストーンを達成したので、LinuxとWindowsでも動作確認。結論から言うと、WindowsのWSL 1では動作しなかったが、MinGW (MSYS2) では楽勝で動かすことが出来た。やはり年の功というか、歴史が長いだけあってのことか。MinGWだとネイティブのexeが出てきてくれるので、環境を用意する障壁がWSLよりも圧倒的に低い。これは超嬉しかった。

この後、紆余曲折を挟みつつも*2、ISOや絞りも変えることができた。

画像が安定して撮れるようになったところで、リモコンの実装に着手した。

リモコンはBootstrap + jQueryで書いた古式ゆかしいフロントエンド*3で、絞り・ISO・オートフォーカス(一定間隔でAFさせるのも設定可)・フレームレート制限を設定できる。カメラのダイヤルでモードをAUTOにすれば自動で輝度が調整されるので、それで十分であれば絞りやISOを手で変更する必要はない*4

f:id:puhitaku:20200727234015p:plain
リモコン on Mac

./mtplvcap -host 0.0.0.0 という風に外からのアクセスもListenするようにすれば、スマホからでも操作できる。

f:id:puhitaku:20200727234042j:plain
リモコン on Android

まとめ

まだ動作確認がとれている機種がD5300とD3300しかないので、ぜひお手元のNikonカメラで動作を確認してみて欲しい。もちろん一眼でなくても動作するはずだ。

なお、MTPで通信をするカメラであれば、原理上他メーカーでも対応が可能なはずだ。やり方としては、USBのVendor IDとProduct IDを見て動作を振り分ける方法を想定している。今はまだこの仕組みはないが、もしMTPで通信すると判明しているカメラがあって、私に貸していただけるならば実装を引き受けることも可能かもしれない。

*1:当初は非公式かつWinのみの仮想カメラソフトを公式が紹介する始末で、最近やっとWindows対応・macOS公開時期未定なWebカメラソフトの開発を発表した。

*2:Arrayなどを内含できるDevice Propertyなる可変な構造体があり、例えばISO値はこれで送られてくる。Goは可変な構造体は扱いにくくたいていreflect祭りになる。この割と複雑なエンコード・デコード処理が実装されておらず、go-mtpfsのコードを手で紐解く必要があった。

*3:いわゆるモダンさはないけど、これでいい。当然node.jsなどに触れることもないので、超シンプルになってくれるのが最高。

*4:現在はカメラの動作モードを見に行く処理がないので、AUTOモードで絞りやISOを変更しようとするとAccessDeniedが返ってくることに注意