Raspberry Pi 4B RTSPビューア for CUI

やりたいこと

特定の動画を流し続けたり、画像を表示させたい。
表示先は Raspberry Pi4B の DSI に接続された3.5~5インチあたりのモニタ。
(DSIにしたのは、GPIOを使いたくなかったわけではなく、感圧式タッチパネルばかりというのが
 NGだったため。静電容量式だとDSI接続のほうになるのかな、と)

要はサイネージや監視カメラ映像の常時出力、といった感じ。

そして、GUIはリソースの無駄なので使わない。

使ったもの

本体:Raspberry Pi4B
OS:Ubuntu Server 22.04
モニタ:OSOYOO 4.3インチTFT タッチスクリーン

モニタの接続

特に何も難しいことはしなかった。
マニュアルにある通り、フラットケーブルをモニタとRaspberryPI4Bにつなげるだけ。

OS等、設定は必要なし。

CUIなのに動画や画像といった映像をモニタに映せるの?

フレームバッファに書き込むことでできる

CUIというと、黒い画面に白や緑の文字だけが表示されて、マウスは使わず、キーボードでの入力のみでカタカタ作業するイメージですよね。

ただ、RaspberryPiの場合、モニタとのデータのやり取りにはフレームバッファというものを挟んで行っている。

この、フレームバッファに直接データを書き込んでやれば、文字に限らず、動画も問題なく表示させることができる。

書き込むにはffmpegを使う

他にもいろいろ手段はあるけれど、今回の目的、rtspで受けた映像をフレームバッファに書き出して、モニタに映像を映し出す、という目的にはffmpegが最適。

基本コマンド

映像の表示

ffmpeg -i rtsp://[id]:[password]@[IPアドレス]/ -pix_fmt rgb32 -s 800:480 -f fbdev /dev/fb0

-i 映像ソースとなるrtspのパスを指定。
-pix_fmt ピクセルフォーマットの指定。他にもyuv420pやrgb565leな場合もある。
-s 出力する映像の解像度を指定。
-f 出力先を指定。/dev/fb0 でフレームバッファとなる。

尚、/dev/fb0のほかにも、HDMIにもモニタを接続していると/dev/fb1が現れる。
HDMI側のモニタに映像を出力したければ/dev/fb1を指定してあげればよい。

フレームバッファの消去

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

カーソルの消去

setterm -cursor off

ただし、sshでアクセスしていたりすると、このコマンドを打ってもsshで接続している端末のカーソルしか消えない。

その場合は、下記のように明示してあげればよい。
ただし、ttyには管理者権限が必要だね。

setterm -cursor off > /dev/tty1

さて、実際にモニタに映像を流すのはいいけど、必ずしも30fpsで表示しなくてはならないというわけではなく、場合によっては1fpsでも構わないし、5秒に1回の更新でも構わないというところがある。

さらに、画面いっぱいに映像を表示するのではなく、右下にワイプのようにちょっと表示させたいなんてこともできる。
ただし、他のプログラムがフレームバッファを書き換えると、書き換わった部分だけデータが置き換わるので、表示が歯抜けになったりすることがあるので、ほんと用途に寄りけり。

例)解像度800×450、1fps、右に1000pxオフセット、下に500pxオフセット

ffmpeg -i rtsp://[id]:[password]@[IPアドレス]/ -pix_fmt rgb32 -r 1 -s 800:450 -xoffset 1000 -yoffset 500 -f fbdev /dev/fb0

5秒に1回にしたい場合は-rを1/5にする。30秒に1回なら1/30

とりあえず、以上でモニタへの表示は行えるようになった

あとは起動時に上記コマンドを実行させるスクリプトを書いて、バックグラウンドで実行させるとか、好き内容にいろいろできるんじゃないかな。

<蛇足1>うちはブラウザでアクセスして設定変えられるようにようにしている

apacheでもnginxでもwebサーバを立ち上げて、phpなんかでフォームの入力を受けてスクリプトに流し込む。

そんな感じで、rtsp映像ソースや更新時間、解像度を任意に指定できるようにしている。

この時、ちょっと気になった点をメモ代わりに。

ffmpegに引数を与えて実行するスクリプトについて。

このスクリプト(仮称:ffmpegScript.sh)自体をバックグラウンドで実行する。
ffmpegがずっと実行されっぱなしになるので。
ついでに、標準出力も標準エラー出力も捨てる。
(これをしないと呼び出し元が入力待ちで待機しっぱなしになる)

nohup ./ffmpegScript.sh [引数] > /dev/null 2>&1 &
仮称:ffmpegScript.sh を終了させるにあたって

スクリプト内で実行されたプロセスは親プロセスがkillされたら同時に終了してほしい。
けれど、その子プロセスは親プロセスのさらに親プロセスにぶら下がりなおす。
つまり、停止しない。
ずっとffmpegは動きっぱなしになる。

ffmpegScript.sh が終了したら、その中で実行された子プロセスも終了するように、下記をスクリプトの先頭に加えた。

#!/usr/bin/bash 
killChldProc(){
        kill $(jobs -p)
}
 
trap killChldProc EXIT

…これはまぁ、下記にしてもいいか。

trap 'kill $(jobs -p)' EXIT

スクリプト終了時にやっておきたい処理ができたら追記が簡単にできるようになっていた方がいいかな。

<蛇足2>コンソール出力はDSI側ではなくHDMI側にしたい

何の設定もせずともDSI接続のモニタとして使用することもできる。
けれど、RaspberryPi4Bとこのモニタのみでちょこっと映像を流しつつ、メンテナンス時にはHDMIのモニタを見ながらコマンドをポチポチしたい。

3インチとかだと、コンソールだろうGUIだろうが目に優しくないので。

しかし、標準ではDSIに接続されたモニタにコンソールが出力される。
これをHDMI側のモニタに変更してあげればいいわけだ。

が、ちょっとこの設定変更探すのに苦労した。

/boot/firmware/cmdline.txt に

fbcon=map:1

を追記することで、上記を実現。

まぁ、わかってしまえばなんてことない。
標準ではコンソール出力がフレームバッファの「/dev/fb0」にマッピングされている。
これを「/dev/fb1」に指定してあげればよい。

これでDSIにつなげた小画面で映像をちょいちょい流しながら、メンテ時はHDMIでそこそこのサイズで、ってのができるようになった。

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