Raspberry Piでフレームバッファを使ったデジタルサイネージ


TL; DR

X Windowを使った画像表示が重いとき(特にRaspberryPi ZERO),X Server等を立ち上げずにフレームバッファで画面表示をするとサクサクになりがち.

なにがしたいか

RaspberryPiを使ってデジタルサイネージっぽいものやってる例があったので(これとかこれとか)がうらやましかったので,ブラウザを全画面表示してやってみようとしたところ,RasPiZeroだとブラウザの処理が重く実用に耐えませんでした.

そこでX Windowサーバを立ち上げずに直接フレームバッファを使うことで,軽量化を図ってみました.

基本戦略

普通にブラウザを立ち上げるとブラウザがCPUを食い潰してしまうのでHTMLのレンダリングとレンダリング結果の表示を独立して行います.
具体的には

  1. ChromiumのHeadlessモードを使ってHTMLを画像に変換し
  2. 画像をfbiコマンドを使ってフレームバッファに表示
  3. 1から繰り返す

という流れになります.
(結論から言うと1.のHTMLのレンダリングが時間がかかりすぎてそんなに体感速度は出ませんでした・・・)

下準備

Chromium, fbiのインストールとfbiのパス無しsudoの許可

HTMLをPNGに変換するためにchromium-browserを,PNG画像をフレームバッファに表示するためにfbiを使うのでインストールします.

$sudo apt install chromium-browser fbi

また,fbiは実行時に/dev/ttyXにアクセスする必要があるのでsu権限での実行が必要ですが,
自動化する手前sudo時にパスワードを聞かれるのは困るのでパスワード無しでsudo可能にしておきます.
(参考:(sudo のパスワードを入力なしで使うには)[https://qiita.com/RyodoTanaka/items/e9b15d579d17651650b7])
visudoして以下の一行をsudoersに追加します.

pi ALL = NOPASSWD: /usr/bin/fbi

/sys/class/graphics/fb0/blankの権限変更

RaspberryPiではHDMI出力のOn/Offを切り替えは

HDMI出力ON
#echo 0 > /sys/class/graphics/fb0/blank
HDMI出力OFF
#echo 1 > /sys/class/graphics/fb0/blank

のようにすればいいっぽいんですが(画面スリープからの復帰も同様),一般ユーザからも操作したいので

$sudo chmod 666 /sys/class/graphics/fb0/blank

する必要があります.
ただ,毎回起動後にsudoするのは現実的ではないので/etc/rc.localに以下を追加して起動後に権限を変更するようにしておきます.(セキュリティ的にどうなの?的な問題はあるとは思いますが,たかが画面のON/OFFだし・・・)

chmod 666 /sys/class/graphics/fb0/blank

動作

HeadlessなChromiumを使ってHTML→PNG

ChromeのHeadlessモードの詳細は本家のヘッドレス Chrome ことはじめ に紹介をゆずりますが,端的に言えばコマンドライン上で動作が完結するモードです.
例えば,以下のようにすることで1920x1080のサイズのヤフー天気のページが取得できます.
--hide-scrollbarを指定することでスクロールバーが表示されず見栄えが良くなります.
(なぜかコマンドを間違えた時にエラーが出ずに無視されるだけなので非常にtypoに気付きにくいです)

$chromium-browser --headless --disable-gpu --screenshot --hide-scrollbar --window-size=1920,1080 https://weather.yahoo.co.jp/weather//weather/

とすると,実行したディレクトリにYahooのお天気ページがscreenshot.pngとして保存されます.
(↓こんな感じ)

HDMI出力の有効化

イマイチ条件がわかってないんですが,放っておくとHDMI出力がOFFになりがちなので,

$echo 0 > /sys/class/graphics/fb0/blank

してHDMI出力を有効化します.

フレームバッファへの画像出力

以下のコマンドを実行することで,先ほど保存した画像が全画面表示されます.

$sudo fbi -T 1 -d /dev/fb0 -noverbose screenshot.png

以上の手順をスクリプトにまとめて無限ループさせることでX無しにデジタルサイネージっぽいことが可能になります.

感想

Chromiumのheadlessモードの利用法としてはかなり邪道な予感がするせいか,全くパフォーマンス的には改善が見られませんでした.
ただ,フレームバッファに画像を表示する動作自体は高速なので,サーバサイドで表示する画像を作ってあげてwgetして表示とか言う流れならもっとフレームレートが上げられる気はします.
まぁそもそもHTML→PNGが重いので,表示したい内容をSVGで作ってconvert(imagemagic)するとか,もっと他の画像作成方法を使うと言う手は十分に考えられそうです.
ただ,「Webベースのダッシュボードを作ってそれを定期的に更新しながら画面に表示する」的なアプリケーションなら今回の構成で十分実用可能そうです.
あとモニタを起動後に接続しても上手いこと解像度等設定してくれないので,プラグアンドプレイな状態を実現したいものです.