Write and Run

it's a simple way, but the only way.

OBS Studioで使えるHDMIキャプチャを5000円で手に入れる

ヤァヤァ、KOBA789 であるぞ。

今回はおもしろハードウェア活用術のご紹介です。

安い HDMI キャプチャデバイスが欲しい

欲しい。欲しいじゃないですか。

ゲームにしろカメラの映像にしろ、なんらかの映像をオンラインで生配信したいと思ったとき、真っ先に必要になる機材が HDMI キャプチャデバイスです。
日常的にパソコンやってると USB Audio IF とか、ウェブカムとか、そういうのは案外持ってるもんですが、HDMI キャプチャは持ってないがち。

しかし HDMI キャプチャデバイスというのはあまり一般的なデバイスではないので案外高い。
いくら安くても1万は切れないことが多くて、まぁ2〜3万は覚悟が必要になるわけです。

映像ソースが1本しかないのであれば、まぁ買い切りだし1個くらい買ってもいいか、となるもんですが、これが2本、3本となると話は別です。
ちょっときつい。
ロスレスなんて言わないから、なんなら60fpsも諦めるから、安いやつが欲しい、そういうケースはあります。

たとえば勉強会の配信とか。
スライドなんて数十秒に1回しか切り替わらんし、演台の人間もほとんど動かないので60fpsは要らない。
大したディティールも要らんのでキャプチャデバイスロスレスだろうとロッシーだろうと、配信を見てる人にはほとんど差がわからない。

あと、登壇者のマシンとカメラが離れてるとか、そういうこともあって映像を数十メールくらい伝送する必要があったりします。
20m の HDMI のケーブルとか結構いいお値段するし、太くて取り回しはだるいし、かといって安いケーブルだと信号がロスってちゃんと伝送できないこともあります。

こういう願いを全部叶えてくれる、ナイスなデバイスないかな〜〜と思ってたら、あったんです。

LKV373

自称「120m HDMI extender over CAT5/5e/6」
自称というか、ちゃんと HDMI 延長器として銘を受けて生まれてきたデバイスです。本来は送信側と受信側をペアにして使います。

ただこのデバイス、over CAT5/5/6 とか言いながら、実は LAN ケーブルに律儀に IP を流しています。
中に流れているパケットの正体は細切れになった JPEG の断片を含む独自プロトコルUDP のパケットなので、受信側を外して代わりに一般的な NIC を搭載したコンピュータを繋ぐと映像を容易に受信することができます。
nJPEG に圧縮されているので原理的にはロッシーですが、言うほど劣化は目立たなくて、許せるレベル。

当然 CAT5 とかのいわゆるふつーの LAN ケーブルを使えるわけので、数十メール伝送するくらい楽勝ですし、ケーブルも安いしどこでも買える。

詳しいことはこの記事に書いてあるので、興味のある人はぜひ。

Reverse engineering Lenkeng HDMI over IP extender | danman's blog

一般的な NIC で容易に受信可能とはいえ、中身は独自のプロトコルなので、既存のソフトウェアにそのまま食わせることはできません。
生配信用のデファクトスタンダードである OBS Studio なんかに食わせられると最高なんですが、もうひと手間必要です。

OBS Studio 用のプラグインを書く

OBS Studio にはプラグイン機構があり、仕様に沿った動的リンクライブラリを用意してやると機能を拡張することができます。
拡張できる機能にもエンコーダとか映像入力とか、いくらか種類があります。

今回は映像を入力したいので、Source という機能を持つプラグインを実装しました。

実装の方法は至って簡単で、 libobs/obs-module.h というヘッダファイルのインターフェースに従って必要な関数を実装、export するだけです。
だいたいのことはこのページを読めばわかります。

Plugins — OBS Studio 20.1.0 documentation

普通なら実装言語は当然C言語ですが、2018年も末になって新規でC言語を書きたくなかったので、Rust で書くことにしました。

Rust は C++ などと同様、C言語の呼び出し規約と互換性のある動的リンクライブラリを生成することができるので、better C として使うことができます。

まずは libobs とリンクする必要があるため、Rust のビルドシステムである cargo に、libobs のビルドシステムを組み込んでやる必要があります。
libobs はビルドシステムとして cmake を利用しているのですが、cmake という crate を使うとシュッと連携させることができます。ほんと便利。
さらに、バインディングは頑張って手で書かなくても bindgen という crate を使うことでヘッダファイルから自動生成できます。マジで優秀。
というわけで、だいたい全部自動で準備が整います。

動画の JPEG フレームをデコードするため、同様の方法で、libjpeg-turbo もリンクしてやります。
Pure Rust な実装の JPEG デコーダもあるのですが、インターフェースの兼ね合いなどもあって断念しました。
libjpeg-turbo は SIMD 命令などを活用することで JPEG の高速な圧縮と伸張ができるライブラリです。
このライブラリを選んだ一番の理由は、出力形式として libobs と互換性のある BGRX フォーマットが使えたことです。
ピクセルエンディアンJPEG デコーダと libobs でズレていると、毎フレームごとにフォーマットを揃える必要があり、実行速度上のペナルティが大きくなります。
動画のフレームのデコードは速度が命なので、これはとても大切でした。

で、できあがったものがこちらです。

github.com

今は macOS でしかビルドを確認していませんが、ちょっと編集すれば Linux でもビルドできるんではないかと思っています。

使い方

  1. ビルドできたライブラリを、OBS Studio のプラグインディレクトリに入れる
  2. PC に NIC を刺す
  3. NIC の IP アドレスを 192.168.168.0/24 のうちの任意に設定
  4. OBS を起動
  5. 入力ソースとして "LENKENG" を追加
  6. Interface Address に3で設定した IP アドレスを設定
  7. たぶんこの時点では動かないので OBS を再起動
  8. 動く

7 でちゃんと動かないのは、俺が実装をサボったからです。 誰か source_info.update をちゃんと実装して Pull Request を投げてください。

おわり