#show tech >>log

Raspberry Pi 3B で自宅のテレビをどこでも見る ── 構築編・前編

>> はじめに - まず土台が使えるかを確かめる

構想編 で部品を決めたので,実際に組んでいく.

この構成の心臓部は h264_v4l2m2m,つまり Pi 3B に載っているハードウェアのH.264エンコーダだ.MPEG-2 で飛んでくる放送波を H.264 へ変換するのを,CPU ではなく専用回路にやらせる.ここが実時間に追いつかなければ,他が全部うまくいっても意味がない.

だから最初にやるのは受信でもチューナーでもなく,変換の実力を測ることにした.

結果から言うと,ここで半日溶かすことになる.測る前に,測れる状態になっていなかったからだ.

>> HW エンコーダは「あるか」ではなく「動くか」

>>> デバイスがあっても呼ぶと落ちることがある

まず /dev/video1x が見えるか,ffmpeg にエンコーダが入っているかを確かめる.

ls -l /dev/video1[0-9]
v4l2-ctl --list-devices
ffmpeg -hide_banner -encoders | grep v4l2m2m

ただし「存在」と「動作」は別物だ.デバイスノードがあって ffmpeg にコンパイルされていても,呼ぶと失敗する型の破損が報告されている.カーネルの版で当たり外れがある領域なので,実際にエンコードを通すところまでやる.

ffmpeg -hide_banner -f lavfi -i testsrc2=s=640x360:r=30 -t 5 \
  -c:v h264_v4l2m2m -pix_fmt yuv420p -b:v 1000k -f null - 2>&1 | tail -20

合成映像を5秒だけ食わせる.結果はこうだった.

OS        Debian GNU/Linux 13 (trixie)
カーネル   6.18.39+rpt-rpi-v8 / aarch64
デバイス   /dev/video10 11 12 あり(bcm2835-codec)

✅ [h264_v4l2m2m] Using device /dev/video11
✅ [h264_v4l2m2m] driver 'bcm2835-codec' on card 'bcm2835-codec-encode'
✅ frame=150  speed=7.92x   エラー・警告ゼロで完走

最新の Trixie / 6.18 系で問題なく動く.古い版に落とす必要はなかった.これは助かった.

>>> ところが,同時に出ていた値がまずかった

同じスクリプトで電源と温度も見るようにしていた.そこにこれが出た.

⚠️ throttled=0x50005    低電圧スロットリングが進行中
   temp=45.6'C          → 熱ではない

チューナーもカードリーダーも挿していない,ほぼ無負荷の状態で低電圧が出ている.

この時点で本来の検証(実際の放送に近い映像でどこまで変換できるか)に進みたかったのだが,ここを直さずに測るとクロックが下げられた状態の数字を「性能」として記録することになる.最大のリスクの判定を誤る.

先に電源を直すことにした.

>> 電源で半日溶かした話

>>> measure_volts core は入力電圧の指標にならない

最初にハマったのがこれだ.電圧を見ようとして vcgencmd measure_volts core を叩くと,1.2〜1.3V が返ってくる.正常に見える.

しかしこれは SoC 内部のコア電圧で,入力が 4.7V まで落ちていても正常値を保つ.見る場所が違う.

判断材料はこの3つになる.

vcgencmd get_throttled          # ★これが唯一の判定材料
vcgencmd measure_clock arm      # 1200000000 が正常 / 600000000 なら半減
dmesg -T | grep -iE 'under-voltage|voltage normalised' | tail -20

get_throttled のビットはこう読む.

0x0        正常
─────────────────────────────────────────────
現在の状態                     過去に発生した記録
bit0  0x00001  低電圧を検出中   bit16  0x10000  低電圧が発生した
bit1  0x00002  ARM 周波数制限中 bit17  0x20000  周波数制限が発生した
bit2  0x00004  スロットリング中 bit18  0x40000  スロットリングが発生した
bit3  0x00008  ソフト温度制限中 bit19  0x80000  ソフト温度制限が発生した

0x50005 は「今まさに低電圧でスロットリング中,かつ過去にも起きている」という意味だ.

>>> 判定の仕方を一度間違えた

ここで自分が引っかかった話を書いておく.

アイドル状態で measure_clock arm を見て 600MHz が出たので「制限されている」と判断した.これが間違いだった.600MHz はガバナーの最小値で,何もしていなければそれが正常だ.逆に 1200MHz が出ても,それは直前まで何か動いていて温まっていただけかもしれない.

正しくは負荷中に get_throttled の現在ビットを見る.

✕ アイドルで measure_clock arm を見る
    → 600MHz は «正常».判定材料にならん

○ 負荷中に get_throttled の現在ビット(bit0=0x1 / bit2=0x4)を見る
    → これが唯一の判定材料

○ さらに切り分けるなら scaling_max_freq を見る
    ガバナーは cur しか動かさん.max を削るのは firmware のスロットリングだけ

最後の一行が効く.ガバナー(省電力制御)は現在の周波数しか動かさない.上限そのものを削るのは firmware のスロットリングだけなので,scaling_max_freq が下がっていれば電源が原因だと確定する.

負荷をかけて観察すると,挙動がはっきりした.

アイドル時      throttled=0x50000(現在ビット無し)
負荷開始3秒後   throttled=0x50005 が立ち,ARM が 600MHz に張り付く
負荷を抜くと    現在ビットが消える
温度は最高 55.3℃ で bit3/bit19 は立たず = 熱ではない

負荷時に 5V が落ちている.本来 1200MHz 出るはずが半分に制限されていた.

>>> アダプタを替えても,ケーブルを替えても直らない

原因が電源だと分かったので,手元の部材で順に交換した.

電源アダプタ交換  → 変化なし
USB ケーブル交換  → 変化なし

どちらも単体では改善しない.ここで手詰まりになり,一度は懸案として持ち越した.ベンチも「600MHz の最悪条件で測った数値」として記録し,設計はその前提で進めることにした.

>>> 真因は「定格」ではなく「接点」だった

後日,ケーブル一体型の専用電源に交換したら,あっさり直った.

交換前   アイドル 4345℃ でも throttled=0x50005
         ARM が 600MHz に張り付く
交換後   4コア全負荷 60秒で throttled=0x0 を維持.ARM 1200MHz
         負荷解除後,過去ビットも含めて全ビット 0

アダプタもケーブルも個別には問題なかった.着脱式は接点が1つ増える.そこで電圧が落ちていたわけだ.

Pi の低電圧は「アダプタの定格」より「経路の接点」を疑う.これが今回いちばんの学びだった.5V/3A のアダプタを買っても,経路の途中で落ちていれば意味がない.

ついでに書いておくと,USB-PD や QC の急速充電器は逆に不利になりうる.あれはネゴシエーションで電圧を決める仕組みで,Pi 側は要求を出せない.結果として 5V の低電流に落とされることがある.「良い充電器を持っているから大丈夫」とはならない.

>> 直したら全段が 1.8倍になった

電源が直ったので測り直した.放送に近い負荷にするため,擬似フルセグ(1440x1080 インターレース / MPEG-2 17Mbps / 30秒)にノイズを混ぜて,復号の負荷を重い側へ寄せた素材を使っている.

出力設定 通信量/時 speed=(1200MHz) 旧(600MHz)
max 1280x720 / 2500k / 30fps 約 1.1 GB 1.91x 1.07x ⛔
high 848x480 / 1500k / 30fps 約 702 MB 2.41x 1.33x
mid(既定) 640x360 / 1000k / 30fps 約 482 MB 2.78x 1.52x
low 640x360 / 700k / 15fps 約 350 MB 2.91x 1.58x
eco 480x272 / 400k / 15fps 約 218 MB 3.15x 未実測

speed= が 1.0x を超えていれば実時間に追いつく,という指標だ.どの段も約1.8倍になった.600→1200MHz で線形にならないのは,復号以外の部分もあるためだ.

大きいのは max(720p)が 1.07x → 1.91x になって棚上げが解けたこと.半クロックの状態では「かろうじて追いつく」だったものが,倍の余裕を持つようになった.

半クロックで測った数値を「性能」と思い込んではならない.あのまま進めていたら,720p は「Pi 3B には無理な段」として最後まで捨てていたはずだ.

なお,ecolowdrop=448 と出るのは異常ではない.-r 15 で 30fps の素材を半分に間引いているので,30秒 × 15フレーム ≒ 450 が落ちるのが正しい挙動だ.30fps の3段は drop=0 になる.

>>> ベンチに入っていない負荷

正直に書いておくと,この数字には本番の負荷が3つ抜けている.

  • B-CAS のデスクランブル(Multi2 をソフトウェアで処理する.17Mbps を捬く見積りで1コアの10〜15%.未実測)
  • mirakc の常駐
  • ネットワーク越しのライブ入力(ファイル読みより重い)

max の 1.91x なら,これらを乗せても持ちこたえる見込みではある.ただし実測ではない.

>> 直った代わりに,温度が論点になった

めでたしで終わらないのが面白いところで,電源が直ったら別の数字が気になり始めた.

合成4コア全負荷 6045.1℃ → 71.4℃   (throttled は 0x0 のまま)
変換ベンチ5段の実施後     70.9℃
Pi 3B のスロットリング開始         80℃
    → 余裕は約 9

600MHz に縛られていた間は涼しかった.本来の速度で回るようになって,ちゃんと熱が出るようになった.

30秒のベンチで 71℃ なので,1〜2時間流し続けたときの挙動は分からない.夏場に密閉ケースなら 80℃ に届くかもしれない.ヒートシンクは数百円で買える安い保険だが,まず長時間流して測るのが順番だと思っている.死活監視のスクリプトで 70℃ 超に通知を出すようにしてあるので,視聴中に鳴ればそれが答えになる.

>> まとめ

前編でやったのは,結局「測る前に,測れる状態かを確かめる」という話だった.

  • エンコーダは「ある」ではなく「動く」まで確かめる.デバイスノードの存在は動作の保証にならない
  • measure_volts core は入力電圧を教えてくれない.見るのは get_throttled の現在ビット
  • アイドルの measure_clock で判断しない.600MHz は正常値だ
  • 低電圧は「定格」より「接点」を疑う.着脱式は接点が1つ増える
  • 半クロックで測った数値を性能と思い込まない.720p を捨てるところだった

ひとつだけ弁護すると,最悪条件で先に測れたのは悪いことばかりでもなかった.600MHz でも 848x480@30 が drop=0 で通ることが分かっていたので,「電源が直らなくても実用にはなる」という下限を握った状態で設計を進められた.直ったら全部よくなるだけ,という気楽さはあった.

後編 は映像を実際にブラウザまで届ける話になる.ここでもうひとつ,半日かけて掘り当てた罠が待っている.

📄Arrow icon of a page linkRaspberry Pi 3B で自宅のテレビをどこでも見る ── 構想編

📄Arrow icon of a page linkRaspberry Pi 3B で自宅のテレビをどこでも見る ── 構築編・前編

📄Arrow icon of a page linkRaspberry Pi 3B で自宅のテレビをどこでも見る ── 構築編・後編