>> はじめに - まず土台が使えるかを確かめる
構想編 で部品を決めたので,実際に組んでいく.
この構成の心臓部は 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 の最悪条件で測った数値」として記録し,設計はその前提で進めることにした.
>>> 真因は「定格」ではなく「接点」だった
後日,ケーブル一体型の専用電源に交換したら,あっさり直った.
交換前 アイドル 43〜45℃ でも 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 には無理な段」として最後まで捨てていたはずだ.
なお,eco と low で drop=448 と出るのは異常ではない.-r 15 で 30fps の素材を半分に間引いているので,30秒 × 15フレーム ≒ 450 が落ちるのが正しい挙動だ.30fps の3段は drop=0 になる.
>>> ベンチに入っていない負荷
正直に書いておくと,この数字には本番の負荷が3つ抜けている.
- B-CAS のデスクランブル(Multi2 をソフトウェアで処理する.17Mbps を捬く見積りで1コアの10〜15%.未実測)
- mirakc の常駐
- ネットワーク越しのライブ入力(ファイル読みより重い)
max の 1.91x なら,これらを乗せても持ちこたえる見込みではある.ただし実測ではない.
>> 直った代わりに,温度が論点になった
めでたしで終わらないのが面白いところで,電源が直ったら別の数字が気になり始めた.
合成4コア全負荷 60秒 45.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 で通ることが分かっていたので,「電源が直らなくても実用にはなる」という下限を握った状態で設計を進められた.直ったら全部よくなるだけ,という気楽さはあった.
後編 は映像を実際にブラウザまで届ける話になる.ここでもうひとつ,半日かけて掘り当てた罠が待っている.
📄Raspberry Pi 3B で自宅のテレビをどこでも見る ── 構想編