Raspberry Pi4B で HDMIキャプチャ

やりたいこと

例えば、
 ミニファミコンの映像を入力してキャプチャしたり、
 RTSPを出力できないHDMIなカメラを繋げて、それを配信(RTMP)したり、
 普段はCUIなラズパイのコンソールに入力した映像映したり、
 AIの映像ソースとしてカメラじゃなくてHDMI機器の映像を使ったり、
そんなことがしてみたい。

ただし、0.5秒くらいの遅延は発生するのでアクションゲームみたいにリアルタイム性が求められる用途には全く向かない。

どうやって実現する?

普通にやるならUSB接続のHDMIキャプチャを用意したほうが手軽で無難で簡単。
だけど、今回は MIPI CSI-2 を使ってみようと思う。
このインターフェース、主にカメラを接続するために使われるけど、なぜかHDMI変換ボードが出ていて、無駄に試したい欲に駆られてしまって…

使ったもの

本体:Raspberry Pi4B
OS:Ubuntu Server 24.04 / Raspberry PiOS(64bit)-2024/10/22版
HDMIキャプチャ:Geeekpi HDMI to MIPI CSI-2 変換ボード

他に、映像ソースとして接続したい機器とHDMIケーブルは適当に。

接続

Amazon のレビューに MIPI DSI 側に接続していて、しかもラズパイ側の接続ではケーブルの裏表が逆になっていたりと、ちょっとよろしくなさそうな画像があるのが気になる…

DSIとCSIは物理層こそほぼほぼ同じだけど、プロトコルが違うから使えないし、そもそもケーブル裏表…

まぁ、販売元である Geeekpi がちゃんと接続イメージ画像を掲載しているので、そっちを見れば問題ない。

けど、ケーブルの差しが甘かったり、斜めになってたり、なんていう所には気を付けたい。

設定

ぶっちゃけ、これも販売元である Geeekpi のWiki を見るのが間違いない。
こちらにも接続イメージ画像があるので、こっち見ても良し。

EP-0214

で、「Getting Start」からようやく設定の案内が始まる。ページ中程よりちょっと下あたりから。

まずはお決まりの更新。

$ sudo apt-get update
$ sudo apt-get upgrade

続いて、次のファイルを編集して、dtoverlay を2つ追加する。

/boot/firmware/config.txt

dtoverlay=tc358743
dtoverlay=tc358743-audio

次に、メモリの割り当て状況確認する。

$ dmesg | grep cma

[    0.000000] cma: Reserved 256 MiB at 0x000000001ec00000

複数行表示されるが、この表示が含まれていなければ、次のファイルに設定を追記する。

/boot/firmware/cmdline.txt

cma=96M

で、リブート。

ここまでの設定内容確認

まずはデバイスとして認識されたかどうか。
次のように、/dev/video0 が出てくればOK。

$ ls /dev/video*

/dev/video0   /dev/video12  /dev/video15  /dev/video19  /dev/video22
(…以下省略)

続いて認識状況を見る。
unicam (platform:fe801000.csi): があればOK。

$ v4l2-ctl --list-devices

bcm2835-codec-decode (platform:bcm2835-codec):
        /dev/video10
        /dev/video11
        /dev/video12
        /dev/video18
        /dev/video31
        /dev/media4
 
bcm2835-isp (platform:bcm2835-isp):
        /dev/video13
        /dev/video14
        /dev/video15
        /dev/video16
        /dev/video20
        /dev/video21
        /dev/video22
        /dev/video23
        /dev/media2
        /dev/media3
 
unicam (platform:fe801000.csi):
        /dev/video0
        /dev/media0
 
rpivid (platform:rpivid):
        /dev/video19
        /dev/media1

EDIDファイルの準備

ここが面倒といえば面倒な部分かも。

HDMI機器はソース機器とシンク機器なんて分別されて呼ばれるけど、要はPCやゲーム機がソース機器で、ディスプレイがシンク機器に当たる。

で、今回はラズパイがシンク機器になるけど、シンク機器にはEDIDというデータがあって、それをソース機器とやり取りすることで解像度やフレームレートなどが決められていく。

てことで、とにかく、このEDIDという情報をラズパイに持たせるわけだが、単純にテキストファイルで良い。

以下のどちらかまたは両方を適当なディレクトリにテキストファイルとして保存しておく。

EDIDファイル:702p用

00 ff ff ff ff ff ff 00 52 62 88 88 00 88 88 88
1c 15 01 03 80 00 00 78 0a EE 91 A3 54 4C 99 26
0F 50 54 00 00 00 01 01 01 01 01 01 01 01 01 01
01 01 01 01 01 01 01 1d 00 72 51 d0 1e 20 6e 28
55 00 c4 8e 21 00 00 1e 8c 0a d0 8a 20 e0 2d 10
10 3e 96 00 13 8e 21 00 00 1e 00 00 00 fc 00 54
6f 73 68 69 62 61 2d 48 32 43 0a 20 00 00 00 FD
00 3b 3d 0f 2e 0f 1e 0a 20 20 20 20 20 20 01 00
02 03 21 43 4e 04 13 03 02 12 11 01 20 21 a2 3c
3d 3e 1f 23 09 07 07 66 03 0c 00 30 00 80 E3 00
7F 8c 0a d0 8a 20 e0 2d 10 10 3e 96 00 c4 8e 21
00 00 18 8c 0a d0 8a 20 e0 2d 10 10 3e 96 00 13
8e 21 00 00 18 8c 0a a0 14 51 f0 16 00 26 7c 43
00 13 8e 21 00 00 98 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

EDIDファイル:1080p 30fps用

00 FF FF FF FF FF FF 00 52 62 88 88 00 88 88 88
1C 15 01 03 80 A0 5A 78 0A 0D C9 A0 57 47 98 27
12 48 4C 00 00 00 01 01 01 01 01 01 01 01 01 01
01 01 01 01 01 01 01 1D 80 18 71 38 2D 40 58 2C
45 00 80 38 74 00 00 1E 01 1D 80 18 71 38 2D 40
58 2C 45 00 80 38 74 00 00 1E 00 00 00 FC 00 44
43 44 5A 2D 48 32 43 20 4D 4F 44 0A 00 00 00 FD
00 14 78 01 FF 10 00 0A 20 20 20 20 20 20 01 B9
02 03 1A 71 47 A2 22 22 22 22 22 22 23 09 07 01
83 01 00 00 65 03 0C 00 10 00 01 1D 80 18 71 38
2D 40 58 2C 45 00 80 38 74 00 00 1E 01 1D 80 18
71 38 2D 40 58 2C 45 00 80 38 74 00 00 1E 01 1D
80 18 71 38 2D 40 58 2C 45 00 80 38 74 00 00 1E
01 1D 80 18 71 38 2D 40 58 2C 45 00 80 38 74 00
00 1E 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03

これらをどう使うかはこの後で。

ラズパイ起動後や異なる解像度のソース機器を繋げ直した時に行う手順

ソース機器の解像度に合あったEDIDファイルを読み込む。
なぜって、要は信号検出を自動的にやってくれないから、手動でやるしかないのよ、これ…

例えば、ミニファミコンの場合は720pなので、上記で作成したEDIDファイルのうち720p用のものを、下記のようにして読み込む。

$ v4l2-ctl --set-edid=file=edid_720p.txt --fix-edid-checksums

尚、うちでは Ubuntu Server でも RaspberryPiOS でも、GeeekPiWiki に書かれているような表示は出なかったけど、結果として出なくても動いてるので、実質このコマンドの結果表示は出なくても問題ない。

次に、HDMIソース機器を繋げる。
繋げた後、次のコマンドで接続情報を確認する。
EDIDファイルの読み込みとソース機器の接続の順番が逆になると、正しく認識されないことがある。

$ v4l2-ctl --query-dv-timings

Active width: 1280
Active height: 720
Total width: 1650
Total height: 750
Frame format: progressive
Polarities: -vsync -hsync
Pixelclock: 74250000 Hz (60.00 frames per second)
Horizontal frontporch: 0
Horizontal sync: 370
Horizontal backporch: 0
Vertical frontporch: 0
Vertical sync: 30
Vertical backporch: 0
Standards:
Flags:

読み込んだEDIDファイルと繋げた機器の情報が一致しないと「Active width」や「Active height」が「0」になったりするので、その時は読み込んだEDIDファイルが正しいか確認をする。

内容に問題がなければ次のようにコマンドを続け、BT timings set 及び各種情報が表示されれば完了。

$ v4l2-ctl --set-dv-bt-timings query
BT timings set

$ v4l2-ctl -V
Format Video Capture:
        Width/Height      : 1280/720
        Pixel Format      : 'UYVY' (UYVY 4:2:2)
        Field             : None
        Bytes per Line    : 2560
        Size Image        : 1843200
        Colorspace        : SMPTE 170M
        Transfer Function : Default (maps to Rec. 709)
        YCbCr/HSV Encoding: Default (maps to ITU-R 601)
        Quantization      : Default (maps to Limited Range)
        Flags             :

音声は…???

個人的には音声が不要。

というのも、

コンソールで作業している画面の右下あたりに、映像を垂れ流しておきたいというのがひとつの大きな用途で、必要な時にキャプチャする、といった感じなので。

入力した映像はどう使う?

各サイト回っていると Gstreamer を使っているところが殆どだけど、個人的には慣れている ffmpeg にする。

単純にキャプチャしてファイル(out.mp4)に保存したいだけなら下記コマンド。

$ ffmpeg -f v4l2 -i /dev/video0 out.mp4

RTMPサーバーに向けるならこんな感じかな?

$ ffmpeg -f v4l2 -i /dev/video0 -f flv rtmp://[RTMP Server IP]:[Port]/[Key]

ちょっと変わった使い方かもしれないけど(個人的には主目的)、GUIは使わずにコンソールで使っている端末で、画面右下にちょこっと映像を垂れ流す場合は下記の通り。

$ ffmpeg -f v4l2 -i /dev/video0 -pix_fmt rgb565le -s 480:270 -xoffset 1000 -yoffset 500 -f fbdev /dev/fb0

尚、ffmpegを止めた時に映像は最後のフレームが表示されたままになるので、以下で消す。

dd if=/dev/zero of=/dev/fb0 > /dev/null 2>&1

実際に使ってみて

あれ…?
これ、圧倒的にUSB接続のHDMIキャプチャにしておいた方が、多くの用途では使いやすいぞ!?
(買う前からわかってはいても、試したくなってしまうダメな病気…)

確かに小さなボード1枚で済むから省スペースではあるけど、FFCケーブルは取り回しにも限りがある。

なので、やっぱりよっぽど小型化したいとかの事情がなければ…

この記事にコメントしてみる