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が返ってくることに注意

この便利な時代にファイルウォッチャーを自作した

f:id:puhitaku:20200615004836p:plain

コンピューターにまつわるたくさんの知見が存在する2020年。GoogleやGitHubを通じて世界の知見を見渡しても、意外と自分が欲しい道具が見つからないことがある。

今回自作したのは、既に星の数ほどありそうな道具、ファイルウォッチャー。名前から連想できるように、「ファイルの変化を監視して、変化をトリガーにコマンドを実行するソフト」だ。

開発の背景紹介や既存との比較は後の方でするとして、まずは今回作ったファイルウォッチャーその名も "r3build" の特徴をピッチしていく。

github.com

r3build紹介ピッチ

さて、あなたはいま組み込みLinuxのカーネルをいじっているとしよう(まあ、PythonでもGoでも対象は何でも良い)。試行錯誤のたびに make を手で起こすのは辛いので、makeを自動化したいとする。

ここでr3buildが登場する。r3buildの真髄は r3build.toml という設定ファイルで容易かつ直感的に設定できることにある。以下のような要求を、非常に簡潔に記述して監視させることができる。

  • コード(c, h, dts)を保存したら勝手にmake allしてほしい
  • defconfigを保存したら勝手にdefconfigしてほしい
  • 関係ないファイルの変更は無視して欲しい
  • make中のファイルの変化は無視して欲しい

この場合、r3buildに読ませる r3build.toml は以下のようになる。

[[job]]
  name = "All"
  type = "make"
  regex = [
    ".+\.[ch]$",
    ".+\.dts$",
  ]
  environment.ARCH = "arm"
  environment.CROSS_COMPILE = "arm-linux-gnueabi-"

[[job]]
  name = "Defconfig"
  type = "make"
  target = "mxs_defconfig"
  glob = [
    "./**/mxs_defconfig",
  ]
  environment.ARCH = "arm"
  environment.CROSS_COMPILE = "arm-linux-gnueabi-"

キーとバリューの説明は以下の通り。

name

Human-readableなJobの名前。処理には影響せず、ログでのみ登場する。

type

検知したファイルイベントを処理する実装を指定する。この「実装」はprocessorと呼ぶ。processorには make(makeのターゲットを実行), command(任意のコマンド実行), pytest(pytestを実行)がある。今回はターゲット指定(任意)がないので make がそのまま(= make all)実行される。

regex

ファイルパスをマッチするルール。名前の通りregexで指定する。これにマッチするファイルでイベントが検知されると、processorがトリガーされる。文字列を置くと単一ルールのマッチになり、文字列のArrayを置くとOR条件扱いでマッチする。同様に regex_exclude を指定すると、それにマッチするファイルは無視される。

glob

ファイルパスをマッチするルール。こちらはglobで指定する。使い方はexcludeも含めてregexと一緒、どちらでも好きな方を使える。一応両方同時に使うこともでき、OR条件としてマッチする。

environment

環境変数を指定する。今回は組み込みLinuxなのでARCHとCROSS_COMPILEを指定している。

target

makeのtarget。make processor特有のオプション。

その他

今回は盛り込んでいないが、みんな大好き -j オプションの数字は標準で自動推定される。自分で指定したい場合は、 jobs キーで設定できる。

[[job]]
...
type = make
target = hoge
jobs = 8  # 0にするとCPUコア数から推定させられる

このようにして設定を記述し、 r3build を常駐させておくと *.c, *.h, *.dts, mxs_defconfig の変更を検知次第makeが走る。r3buildを起動するには以下のように実行する。

$ r3build

もしくは

$ python3 -m r3build

r3buildのアーキテクチャ

r3buildでは、こういった「ファイルのマッチ条件」と「イベントを受け取るprocessor」と「processorの設定」をまとめた1単位を Job と呼んでいる。[[job]] はTOMLの Array-of-Tables文法を使ったもので、見た目にもスッキリと監視対象を列挙することができる。

また、Jobが実行中に検知したイベントは標準で捨てるようになっている(注: 並列実行はまだ実装していない)。これは event.ignore_events_while_run = false と指定することで無効化できる。

[event]
ignore_events_while_run = false

[[job]]
...

この例ではコードのコンパイルと自動configしかやっていないが、任意のコマンドをJobに指定することができるので、例えばコンパイルして実機に自動デプロイまでやるとか、もっと大掛かりな仕組みを作ることも可能なハズ。想像は無限大!

ドキュメント

r3build.toml の書き方についてはリファレンスやサンプルを色々用意した。

残念ながらSphinxで生成したような「いわゆるドキュメント」はまだない。疑問があれば随時Twitterで質問してもらって構わないし、バグや機能リクエストがあればCONTRIBUTING.mdに従ってIssueやPRを立てて欲しい。知り合いからのバグ報告であればASAPで直しにかかる…はず!

開発の背景

言うまでもなく、「ファイルの変化を監視して、変化をトリガーにコマンドを実行するソフト」は既にたくさんあるし、気軽に使えるCLIツールの範囲でいくつか試した。

例えば、joh/when-changed(gorakhargosh/watchdogのラッパー)。when-changedは、監視したいファイル名と実行したいコマンドを指定すればそれっぽく起動してくれる。秒で自動化できるのは良いが、複数の問題をはらんでいる。

  • exclude対象が作者の好みにハードコードされている
  • コマンドを発火させている間のファイルの変更も全部拾ってしまい、永遠に実行が終わらない
  • イベントの濁流みたいなことになる(debounceされていない)

別な例として、when-changedの中身である gorakhargosh/watchdog もまたCLIツール watchmedo を内蔵しているが、Tricksなどの便利そうな仕組みがどうも見た目に直感的でないというか、好みではなかった。ちなみにwatchdog自体はマルチプラットフォーム対応も手厚く使いやすいモジュールなので、r3build内部でイベントを拾うソースとして使用している。

上記では「そこが美しくない」とだけ書いているが、この中間処理の記述がめちゃくちゃめんどくさい。自分はただ「Linuxのソースコードをいじったらmakeしてほしい」だけなのに、なぜお手製Pythonスクリプトをわざわざ書かなければならないのか。これから先も似たような汚い手法の繰り返しは嫌だな、と早々にうんざりしてしまった。

結局の所、上に書いたことをそのままエレガントにしたツールが欲しいという結論に達した。

  • エレガントな設定ファイルで秒で自動化できて欲しい(自動化のための苦労をゼロに近づけたい)
  • 監視・除外対象をglobやregexでいい感じに指定したい
  • コマンドを発火させている間のファイルの変更は捨てたい(わざと拾うのも可能にしたい)
  • イベントの種類を絞り込みたい(作成・変更・削除など)
  • 謎の立て続けにくるイベントとかはdebounceしたい

2月の頭に「ぼくのかんがえたさいきょうの設定ファイル」の構想から着手し、だいたい4ヶ月半経った今日ついに完成した。せっかくなのでロゴも作った。

github.com

ちなみにr3buildという名前はどこかの国のプロテインと被ってるっぽいけど、それ以外はなかったのでGoogleabilityも良い。

今後

そもそも日頃の課題を解決したくて作ったものなので、これからもBrainハックとかで使いつつ改善を続ける。今のところ実装したいことは以下の通り。

  • 同じJobで設定を変化させながら何度も実行する
  • もっとエレガントなConfigの模索?
  • Coverageを上げてテストケースを増やす

あとはとにかくいろんな人に使って欲しい!これを呼んでいるあなたも、ファイルの変化をトリガーに自動化したい事があれば、是非r3buildを思い浮かべて欲しい。

フィードバック、お待ちしております!

TrelloのButler機能でマイルストーンからタスクをいい感じに生成する

f:id:puhitaku:20200427020916p:plain

tl;drな方はButler機能へジャンプしてください。

カンバンスタイルのTODO管理ツールとして、Trelloは結構な知名度で世界に普及している。

タスク管理、殊に「カンバン」というタームが絡むといつも「タスクカードは物理とデータどちらで管理すべきか」論争が起きる。2017年あたりに前々職でTrelloをひとしきり使った頃は、当時読んだ「Kanban in Action」の影響もあって「タスクカードは物理が最強!」とかのたまっていたものだった。

その勢いのままに、前職では完全物理カンバンを全社で2年間使い倒した*1。その末に「物理とデータ、どっちもどっち」という一番面白くない(?)結論に達したのは、タスク管理がいかに難しいかを体現しているといえる*2

ひと月前から家で独りで仕事をするようになり、物理かどうかはさておき「カンバンが欲しい」と思うのは自然な流れだった。「ものづくりのタスク管理」にはやっぱりカンバンが使い心地が良い。

人気を伸ばすAsanaやWrikeを横目に、「アカウントを持ってるから」という単純な理由で、苔むしたTrelloアカウントに再び足を踏み入れたのがつい二日前の事だ。

「マイルストーン」と「構成タスク」

Trelloでは、カード間の関係性はとてつもなく質素な「リンク」で表現する。カードに別なカードを "Attachment" として添付するか、Checklist itemやDescriptionの中にカードのリンクを置く。たったそれだけ。片方がリンクを張っていても、もう片方がリンクを張っていなければ相互に渡り歩くこともできない。

流石にこれが2017年からなにひとつ変わっていないのはちょっとガッカリした。

ではその「リンク」を使って自分が何をやりたいのかというと、割とデカい目標を「マイルストーンカード」として作っておき、それを分解した「サブタスクカード」を紐付けるというもの。

バカ正直に前者と後者の間でリンクを張り合うなど正直2020年のサービスとは思えない。なんとかならないものかと考えていたら、画面右上に見慣れない "Butler" ボタンを発見した。

Butler機能

"Butler" = 「執事」。要はボードで起きたイベントをトリガしていろんな操作をオートメーションするというもの。某CIおじさんっぽくていい名前だと思う。

前節で紹介した親子カード間のリンク貼りは、実は「親カードのチェックリストを子カードに変換する」という手段を使うと若干ショートカットすることができる。ここにButlerをくっつけて、以下のようなことが可能になる。

  1. 手動で "Milestones" リストにカードを作る
    (例: Implement API) f:id:puhitaku:20200427011336p:plain

  2. 「"Milestones" リストにカードを作った」をトリガにしてButlerが目を覚まし、 "Implement API" カードに "Tasks" という名前のチェックリストを自動で追加する f:id:puhitaku:20200427011512p:plain

  3. Butlerが追加したチェックリストに手動でアイテムを追加する(例: Create a repository) f:id:puhitaku:20200427011528p:plain

  4. 「"Milestones" リストにあるカードの "Tasks" という名前のチェックリストにアイテムを追加した」をトリガにしてButlerが目を覚まし

    • カードの名前から "Tasks of Implement API" というリストを自動でつくる
    • "Create a repository" をカードに変換し "Tasks of Implement API" に自動で追加する

f:id:puhitaku:20200427011600p:plain

自動でカードになっていることがわかる。さらに元のボード画面に戻ると、リストまでもが自動で作られていることがわかる。

f:id:puhitaku:20200427011616p:plain

子カードの詳細を覗くと、親へリンクされている。

f:id:puhitaku:20200427011623p:plain

このようにして、「マイルストーンカードを作ったら、タスクを書く場所が中に勝手にできて、さらにタスクを書くと勝手にカードになる」という嬉しいワークフローが実現する。

定義は直感的

実際の定義はというと、これまた直感的で好感が持てる。

「"Milestones" リストのカードにある "Tasks" チェックリストにアイテムを追加したらカード化」の定義の例でいうと、まずトリガが以下のようになる。

f:id:puhitaku:20200427012838p:plain

もう見た目通りという感じ。条件をポチポチで追加していくと英文法で記述されたトリガが完成する。今回はフィルタとして「in list "Milestones"」しか入れていないが、他にも「自分にアサインされている」とか「○○色のラベルが付いている」みたいな細かいフィルタが可能。

次にどういうアクションを起こすかの定義をする。

f:id:puhitaku:20200427013523p:plain

Tasks of {conditio と見切れているのは Tasks of {conditioncardname} と入っている。この {} で囲まれた文字は変数 (Variable)で、 {conditioncardname} の場合はトリガしたチェックリストを抱えるカードの名前がFormatされる。

このようにして、 "Tasks of {親カードの名前}" リストにチェックリストから変換した子カードを追加するという処理が実装できる。

保存したRuleは英文法で表示され、これまたわかりやすい。

f:id:puhitaku:20200427014200p:plain

無料プランだと 1 Rule のみ

現状、Butlerのトリガを2つ同時に登録することは無料ユーザーではできない。私の場合は、「MilestoneリストのカードにあるTasksチェックリストにアイテムを追加したら…」だけ有効にしている。そこが一番めんどくさくて省略したいところだからね!

しかしながら、この制限はかなーりきついので、もうちょっと緩和してくれたら超嬉しいなとは思う。

まとめ: Trelloがんばってね

ここまでTrello + Butlerを推しておきながら、正直なところ近頃人気のAsanaを見るとTrelloはもう空前の灯火なのではないかと心配になる。それくらいAsanaはよくできている。今回も、もしTrelloのアカウントを以前から持っていなければAsanaを採用していただろう。

まあButlerでできることが極度に目新しいかというとそうではないけども、Trelloの延命へとポジティブに働いてくれるのはきっと間違いない。「タスク管理といえばTrello」が過去のものにならないように、これからも強大なライバルたちとしのぎを削ってもらえたらと願っている。

*1:これは私がやりたいと言ったわけではなく、社長を筆頭にそういう思想だった

*2:スクラムとかチームの可視化みたいな話はもちろん背景にたくさんあるが、今回は割愛する