自宅のオーディオ環境は、複数のPCからVBANプロトコルでRaspberry Piに音声を集約し、PipeWireでミキシングした後、FiiO BTA30 Pro経由でBluetoothヘッドホンに送る構成になっている。ある日、この音声が「バチバチ」と途切れ始めた。
CPU負荷も高くない。温度も52°C前後で正常。VBANのストリームも生きている。原因がどこにあるのか——PipeWireのスレッドスケジューリングまで辿り着くまでの記録。
症状:ストリーム個別ではなく全部で発生
重要な観察は「特定のストリームではなく、どのストリームでも発生する」という点。これにより、VBANプロトコル固有の問題ではなく、受信側のオーディオスタック全体の問題に絞り込めた。
根本原因:PipeWireスレッドがリアルタイム優先度で動いていなかった
確認すると、PipeWireのデータループスレッドのスケジューリングクラスが TS(Time Sharing)——つまり通常のプロセスと同じ優先度——になっていた。CPU負荷が上がると、音声処理スレッドが他のプロセスにプリエンプト(割り込み)され、バチバチという途切れが発生していた。
PipeWireは本来 module-rt プラグイン経由でSCHED_FIFO(リアルタイムスケジューリング)を取得する設計だが、これが機能していなかった。
原因の3層構造
設定ファイルは全部正しく書いてあった。それでも動かなかった。原因は2層のメカニズムが絡んでいた。
層1:PAM limits
/etc/security/limits.d/25-pw-rlimits.conf で @pipewire グループに rtprio 95 を設定済み。piadmin ユーザーも pipewire グループに追加済み。
しかし、グループ追加が起動後だったため、PAMがlimitsを適用していなかった。
層2:systemd user managerのRLimit
さらに重要な問題。systemdのユーザーマネージャー(user@1000.service, PID 691)自体の LimitRTPRIO が 0 だった。
Linuxの setrlimit は、現在のhard limitを超えて設定できない。ユーザーマネージャーのRLIMIT_RTPRIOが0なので、PipeWireがmodule-rtで95を要求してもカーネルが弾く。
つまり、設定ファイルに正しく書いてあっても、それを継承する親プロセスのRLimitが0では意味がない。
修正:システムレベルのdrop-in
システムレベルの user@1000.service にdrop-inを作成した。
sudo mkdir -p /etc/systemd/system/user@1000.service.d sudo tee /etc/systemd/system/user@1000.service.d/rt-limits.conf << 'EOF' [Service] LimitRTPRIO=95 LimitNICE=39 LimitMEMLOCK=4194304 EOF sudo systemctl daemon-reload
再起動後、PipeWireのスレッド状態を確認:
ps -Lo tid,cls,rtprio,ni,comm -p $(pgrep -x pipewire) TID CLS RTPRIO NI COMMAND 795 TS - -11 pipewire 839 FF 88 - data-loop.0
データループスレッドが FF(SCHED_FIFO)88 になった。pipewire-pulse と wireplumber も同様。RLimitも通った(rtprio=95, nice=39)。
なぜこの問題が難しいのか
「設定は全部正しく書いてあるのに動かない」という状態。PAMのlimits適用タイミングと、systemd user managerのRLimit継承という、2層のメカニズムが重なった落とし穴だった。
よくあるガイドでは「pipewireグループに追加してlimits.confに書けばよい」と説明されるが、これはログインセッションに対しては正しい。しかし、systemd --user サービスとして動くPipeWireの場合、ユーザーマネージャー自体のRLimitがボトルネックになる。ここをdrop-inで引き上げないと、module-rtが呼んでもカーネルが拒否する。
まとめ
- VBAN経由オーディオのノイズは、PipeWireのRTスケジューリング未適用が原因
- 直接の原因は
user@1000.serviceのLimitRTPRIO=0 /etc/systemd/system/user@1000.service.d/rt-limits.confにdrop-inで解決- ログインセッションとsystemd user sessionは、RLimitの継承パスが違うことに注意
Raspberry Piでオーディオを扱う方は、PipeWireが本当にRTで動いているか、一度 ps -Lo cls,rtprio,comm -p $(pgrep -x pipewire) で確認してみるとよい。CLS列が「TS」ではなく「FF」になっていれば正常。