0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PipeWire徹底解説:LinuxマルチメディアアーキテクチャをOSエンジニアリングの観点から読み解く

Posted at

1. はじめに:なぜPipeWireはOSレベルのイノベーションなのか

PipeWireは、Linux向けの新しい低レベルマルチメディアフレームワークであり、オーディオとビデオの両方のストリームを高い効率と柔軟性で処理するように設計されています。従来のオーディオサーバー(例:コンシューマーオーディオ向けのPulseAudioやプロフェッショナルオーディオ向けのJACK)がサウンドのみに焦点を当てていたのとは異なり、PipeWireは最小限の遅延でオーディオとビデオの両方のキャプチャと再生を行うための統一されたメディアパイプラインを提供します。この広範なスコープにより、PipeWireは単なるユーザー空間のサウンドサーバーから、オペレーティングシステムのマルチメディアインフラストラクチャの中核をなす要素へと昇格しました。

PipeWireがOSレベルのイノベーションと見なされる理由の一つは、ポリシーよりメカニズムを重視している点です。PipeWireデーモンは、中核となるルーティングと処理エンジン(「配管」)を実装し、ポリシー決定(どのデバイスを使用するか、ストリームをどのようにリンクするかなど)は別のセッションマネージャーに委ねます。この設計は、関心の分離というOSの原則を反映しています。PipeWireは、マルチメディアに不可欠なカーネルのようなサービス(グラフスケジューリング、バッファ管理、デバイスアクセス)を提供し、より高レベルの決定をユーザー空間のポリシーモジュールに委任します。これにより、PipeWireはプロのオーディオワークステーションからサンドボックス化されたアプリケーションまで、デーモンに特定のポリシーをハードコーディングすることなく、さまざまなユースケースに対応できます。

PipeWireをOSの構造の一部のように感じさせるもう一つの側面は、システムサービスやセキュリティフレームワークとの統合です。ソケットアクティベーションとデーモン管理にはsystemdを使用し、デバイスアクセスを制御するためにPolkitのようなセキュリティモデルを採用しています。例えば、Waylandで画面やオーディオを録画する場合、ユーザーに許可を求めるポータルD-Busサービスを経由します。PipeWireのlibpipewire-module-portalはFlatpakサンドボックスと統合されており、サンドボックス化されたアプリがオーディオやビデオのキャプチャを要求すると、ポータルとPipeWireが連携して権限を強制します。Linuxのセキュリティとのこの緊密な連携(以前のオーディオシステムがaudiovideoのようなUNIXグループに依存していたのとは対照的)は、PipeWireがOS内で第一級の市民として動作し、ユーザーセッション、cgroup、サンドボックスの境界を尊重することを意味します。

最後に、PipeWireによるLinuxマルチメディアの統一は、OSアーキテクチャを簡素化します。歴史的に、Linuxデスクトップは複数のサウンドサーバー(デスクトップサウンド用のPulseAudio、低遅延オーディオ用のJACK、そしておそらくBluetoothオーディオやビデオキャプチャ用の別々のメカニズム)を実行していました。PipeWireはこれらを、PulseAudioとJACKのAPIをエミュレートできる単一のサービスに置き換え、同時にビデオ(以前はv4l2loopbackやカスタムGStreamerセットアップのようなソリューションに任されていた)も処理します。この統合は、OSカーネルが以前は別々だったドライバーを吸収するのに似ており、ユーザーと開発者の複雑さを軽減します。実際には、PipeWireは互換性レイヤーを提供するため、ALSA、PulseAudio、またはJACKを使用する既存のアプリケーションはPipeWire上でシームレスに動作します。例えば、PipeWireが実行されているとき、PulseAudioクライアントの呼び出しはpipewire-pulseサーバー(PulseAudioの代替として機能するPipeWireモジュール)によって処理され、JACKクライアントはpw-jackを介して起動してライブラリ呼び出しをPipeWireにリダイレクトできます。これは、ブラウザの通知音からプロフェッショナルなDAWまで、Linuxのオーディオエコシステム全体、さらには画面共有やカメラのためのビデオキャプチャまでもが、一つのまとまりのあるアーキテクチャを通じて流れることを意味します。このような広範な責任は通常、OSの中核コンポーネントにのみ与えられるものであり、これがPipeWireがLinux OSインフラストラクチャにおける重要な進化と見なされる理由を強調しています。

要約すると、PipeWireの広範な能力(オーディオビデオ)、メカニズムとポリシーを分離するアーキテクチャ的アプローチ、システムサービス/セキュリティとの統合、そして統一された互換性レイヤーとしての役割、これらすべてがOSレベルのイノベーションであることに貢献しています。それは、Waylandがディスプレイサーバーを近代化したのと同様に、孤立したユーザーランドアプリとしてではなく、システムレベルで設計を再考することによって、現代のLinuxマルチメディアの基盤を築いています。

2. PipeWireアーキテクチャの詳細

コアデーモンとセッションマネージャー(PipeWireとWirePlumber)

PipeWireの設計の中心には、別々のプロセスとして実行される2つの主要コンポーネント、PipeWire デーモンセッションマネージャーがあります。PipeWireデーモン(しばしば単にpipewireと呼ばれる)は、マルチメディアグラフを管理し、クライアントとデバイス間のデータフローを調整する中央サービスです。通常、ユーザーセッションごとに1つのPipeWireデーモンが存在し、Unixドメインソケット(例:${XDG_RUNTIME_DIR}/pipewire-0)でクライアント接続をリッスンします。デーモンは通常のユーザー権限で実行されますが、多くの場合、udevルールや付与された権限を通じてハードウェアデバイス(サウンドカード、カメラ)にアクセスできます。

重要なことに、PipeWireデーモンはポリシーフリーです。つまり、何を接続するか、いつノードを作成するかを決定しません。これらの決定はセッションマネージャーに委ねられています。初期にはpipewire-media-sessionというサンプルセッションマネージャーが存在しましたが、現在は非推奨となり、WirePlumberに取って代わられました。WirePlumberは、PipeWireデーモンにPipeWire IPC APIを介して接続する、別のユーザーランドプロセスとして実行されるモジュール式のセッション&ポリシーマネージャーです。その仕事は、グラフを監視し(新しいデバイスやクライアントストリームが出現したことを検出)、ポリシーを適用することです。例えば、新しいオーディオストリームをデフォルトの出力に自動的にリンクしたり、デバイスプロファイルを管理したり、ルーティングの変更を処理したりします。

コアデーモンとセッションマネージャーの分離は、コンポーネントの境界が明確に定義されていることを意味します。PipeWireデーモンはメカニズムを提供します。つまり、データグラフを実行し、リアルタイム制約を強制し、グラフを制御するためのAPIを公開します。WirePlumber(または任意のセッションマネージャー)は、そのAPIをクライアントとして使用してポリシーを実装します。本質的に、グラフ内のオブジェクトを作成および構成する権限を持つ特権クライアントです。これは、OSカーネル(PipeWireデーモン)とユーザー空間のポリシーデーモン(WirePlumber)が連携して動作するのに似ています。実際、PipeWireの設定による典型的な起動シーケンスでは、セッションマネージャーが自動的に起動されます。例えば、PipeWireの設定のcontext.execセクションには、デーモンが起動するとすぐにポリシー管理が開始されるように、WirePlumberを起動する記述が含まれていることがよくあります。

WirePlumber自体は、モジュール式でスクリプト可能な方法で設計されています。さまざまなポリシー決定を実装する一連のプラグインLuaスクリプトをロードします。例えば、WirePlumberには、ALSAデバイスを処理するための「ALSAモニター」スクリプトをロードするプラグインや、ストリームの接続方法を決定する「リンキングポリシー」スクリプトなどがあります。古いバージョンではこれらは純粋なLuaスクリプトでしたが、WirePlumber 0.5以降では、設定の多くが.confファイル形式に移行し、高度なロジックのためにスクリプトフックが引き続き利用可能です。重要な点は、セッションマネージャーは高度に設定可能であるということです。WirePlumberの設定を編集したり、新しいLuaスクリプトを作成したりすることで、PipeWireのCコードに触れることなく、ルーティングポリシーをカスタマイズしたり、新しい動作を追加したりできます。

例えば、WirePlumberのデフォルトスクリプトは次のようなタスクを処理します。新しいオーディオ出力ノード(例えばBluetoothヘッドセット)が表示されたときに、それがデフォルトのシンクになるべきかを決定する。または、クライアントストリームが開始されたときに、優先度ルールに基づいてそれを何らかのシンクに自動的にリンクする。スクリプトはイベント/フックシステムを使用します。実際、WirePlumberは「ノードが追加/削除された」や「デフォルトデバイスが変更された」などのイベントに対するフックを定義し、これらを使用してポリシーロジックをトリガーします。あるスクリプトはグラフを監視し、何かが変更されるたびに「最適」なデフォルトデバイスを見つけるために再スキャンをスケジュールします。別のスクリプトは、ノードのプロパティと保存されたユーザー設定を調べて、優先度が最も高いデバイスを新しいデフォルトとして選択するロジックを実装します(例えば、ヘッドフォンが接続された場合に切り替える)。WirePlumberのこのモジュール設計は、プロフェッショナルなディストリビューションや組み込みシステムが、PipeWire自体を変更することなくポリシーを調整できることを意味します(例えば、スタジオセットアップでマイク入力を常にエフェクトパイプラインにルーティングするなど)。

PipeWireのアーキテクチャはクライアントサーバー型ですが、PulseAudioほど重いものではないことに注意する価値があります。すべてのPipeWireクライアントはlibpipewireにリンクし、Unixソケット上のIPC(カスタムプロトコルを使用)を介してデーモンと通信します。各クライアントは、デーモンによって維持される利用可能なオブジェクト(デバイス、ノード、ポートなど)のレジストリを参照します。セッションマネージャーは、ユーザーポリシーに代わってオブジェクトを作成および破棄できる、単なる別のクライアントです(ただし、通常は完全な権限が付与されています)。PipeWireのPulseAudioおよびJACKのサポートでさえ、クライアントとして実行されます。例えば、pipewire-pulseは、PulseAudioプロトコルを実装し、それをPipeWire操作に変換するクライアントプロセスであり、各PulseAudioの「シンク」または「ソース」をグラフ内のPipeWireノードとして登録します。

要約すると、コアPipeWireデーモンはマルチメディアのデータフローグラフを実行するエンジンであり、セッションマネージャー(WirePlumber)はそのグラフを設定および調整する頭脳です。この分割により、PipeWireは柔軟性を維持し(特別なユースケースのためにWirePlumberをカスタムセッションマネージャーに置き換えることさえ可能)、集中できます(デーモンはリアルタイムI/Oのために高度に最適化でき、ポリシーロジックは別のプロセスで実行されます)。また、堅牢性も向上します。セッションマネージャー(ポリシー)のバグやクラッシュがストリーミングエンジンを直接クラッシュさせることはなく、その逆も同様です。これは、OS設計においてユーザー空間のドライバーのクラッシュが必ずしもカーネルをクラッシュさせないのとよく似ています。

コンポーネントの責任とオブジェクトモデル

PipeWireデーモンクライアントライブラリ内では、設計はマルチメディアの概念を反映したコアオブジェクトタイプを中心に構築されています。主なオブジェクトタイプには、CoreClientNodePortLinkDeviceFactory、およびセッション管理用のより高レベルのものがいくつか(Endpoint、Sessionなど)含まれます。各オブジェクトは一意のIDで識別され、タイプとプロパティを持ちます。これらのコンポーネント間の責任分担を理解することで、システムの動作が明確になります。

  • Core: 各PipeWireデーモンインスタンスには、正確に1つのCoreオブジェクト(ID 0)が存在します。Coreはグローバルな状態と、他のすべてのオブジェクトのレジストリを保持します。すべてのクライアントはCoreに接続し、それを通じてリクエスト(オブジェクトの作成、ポートのリンクなど)を発行します。Coreは、本質的にクライアントとグラフの間を仲介するハブです。

  • Client: クライアントプロセスからPipeWireデーモンへの接続を表します。アプリケーションがソケット経由で接続すると、デーモンはClientオブジェクトを作成します。このオブジェクトはクライアントの権限を追跡し、セキュリティポリシーを強制するために使用できます(例:クライアントがどのノードを表示または使用できるか)。クライアントが切断すると、関連するClientオブジェクト(およびそれが作成したすべてのNodeオブジェクト)は削除されます。

  • Node: Nodeは、メディアのプロデューサーまたはコンシューマー(あるいはその両方)を表す基本的な構成要素です。例えば、オーディオシンク(ハードウェアへの出力)、オーディオソース(マイクキャプチャ)、またはアプリケーションストリーム(音楽プレーヤーの出力やブラウザのオーディオなど)はすべてNodeです。各Nodeは、データが出入りする0個以上のPortを持つことができます。GStreamerの類推で言えば、Nodeは要素やフィルターに似ています。NodeはPipeWireデーモンプロセス内に存在することも(例えば、サウンドカード用の組み込みALSA再生ノード)、クライアントプロセス内に存在することもできます(例えば、アプリが生成しているオーディオ用にNodeを作成する)。Nodeがクライアント内にある場合、PipeWireは必要な共有メモリとIPCを設定し、Nodeのデータがデーモンのグラフに供給されるようにします。

  • Port: Portは、メディアの入力または出力のためのNode上のエンドポイントです。Portには方向があります。入力ポートはNodeにデータを受け取ります(シンクには再生するオーディオ用の入力ポートがあります)。出力ポートはNodeからデータを送信します(ソースには出力ポートがあり、アプリケーションストリームにはそのオーディオを提供する出力ポートがあるかもしれません)。各PortはNodeに属し、ネゴシエートされた特定のメディアタイプ/フォーマット(例:FLOAT32形式の48 kHzステレオオーディオ)を持ちます。GStreamerの用語では、Portは要素のパッドに似ています。

  • Link: Linkは、あるNodeの出力ポートを別のNodeの入力ポートに接続します。これがグラフを構築する方法です。例えば、アプリケーションのオーディオ出力ポートをシステムのスピーカーシンクの入力ポートにリンクすると、それらの間でオーディオが流れるようになります。Linkは明示的に作成されます(多くの場合、セッションマネージャーまたはクライアントによって)。確立されると、PipeWireデーモンはLinkを使用して、各処理サイクルでソースポートからシンクポートにデータを移動します。どちらかのポートまたはノードが消えると、Linkは破棄されます。

  • Device: ALSAサウンドカードやV4L2ビデオキャプチャデバイスなど、ハードウェアまたはシステムデバイスを表します。Deviceは直接オーディオストリームではありませんが、Nodeを作成するためのハンドルです。例えば、ALSA Deviceオブジェクトは物理的なサウンドカードに対応します。そこから、PipeWireは再生NodeまたはキャプチャNodeを作成できます(これらはそのデバイス上のオーディオストリームを表す子オブジェクトになります)。Deviceオブジェクトは、しばしばコントロールやプロファイル(サウンドカードの「ライン出力対HDMI」プロファイルなど)を公開します。PipeWireの設計では、「Device」はほぼALSAカードにマッピングされ、そのカード上のストリームがNodeになります。

  • Factory: Factoryは、他のオブジェクトを生成できるオブジェクトです。Factoryは通常、モジュールまたはプラグインによって提供されます。例えば、ALSA PCM再生ストリームをラップするNodeを作成する方法を知っているalsa-pcm-sinkというファクトリがあるかもしれません。Factoryには名前があり、pw-cli dump Factoryを介してクエリできます。セッションマネージャーまたはクライアントは、NodeまたはDeviceをインスタンス化するためにファクトリを呼び出します。Factoryにより、PipeWireは拡張可能になります。新しいモジュールは、新しい種類のNode用のファクトリを登録できます(例:「仮想ヌルシンク」ノード用のファクトリ)。

  • Endpoint/Session: これらは、セッション管理を支援するために導入された、より高レベルの抽象化です。Endpointは、ユーザーにとって意味のある方法で1つ以上のNodeをグループ化します(例えば、「ラップトップスピーカー」は出力ターゲットを表すEndpointであり、内部ではこのエンドポイントは特定のNodeまたはNodeのセットに対応します)。EndpointおよびEndpointStreamにより、セッションマネージャーは、すべての低レベルのNode/PortをUIに公開することなく、より人間にとって分かりやすい用語(「音楽をスピーカー対Bluetoothヘッドセットにルーティングする」など)でルーティングを管理できます。特に、WirePlumberは、ポリシーを実装するために内部でエンドポイントを使用します(例えば、スピーカー対ヘッドフォンジャックのように相互に排他的なデバイスを1つのエンドポイントの下のターゲットとしてグループ化する)。これらのセッションレベルのオブジェクトは直接メディアを運びません。セッションマネージャーがグラフについて推論するためのプロキシとして機能します。

これらの各コンポーネントには明確な責任があります。PipeWireデーモンの仕事は、これらのオブジェクトとグラフの関係を維持し、そして決定的に、グラフを実行することです。つまり、各サイクルでソースノードからデータを引き出し、リンクを介してシンクノードにプッシュします(スケジューリングについては後で説明します)。セッションマネージャーの仕事は、これらのオブジェクトの作成と構成を調整することです。例えば、起動時にWirePlumberはALSAデバイスをスキャンするプラグインをロードし(udevを使用)、各サウンドカードに対してPipeWireのDeviceオブジェクトと対応するNode(カードの再生およびキャプチャPCMストリーム用)を作成します。これは、PipeWireのSPAプラグインをアクティブ化することによって行われます。この場合、alsa-monitorプラグイン(PipeWireの一部)がWirePlumberによってロードされ、ALSAカードを自動的に検出し、デバイス+ノードオブジェクトを作成します。同様に、Bluetooth、ビデオ(libcameraまたはV4L2)など用のモニターがあり、すべてセッションマネージャーによって管理されます。セッションマネージャーはまた、これらのオブジェクトにプロパティ(デバイスの説明、初期音量など)を設定し、いつLinkを作成するかを決定します(例えば、アプリのNodeをDeviceのNodeに接続する)。

オブジェクトのライフサイクルは、アーキテクチャの重要な側面です。Node、Port、およびLinkの作成と破棄は、クライアントが出入りしたり、ハードウェアが表示/非表示になったりするにつれて動的に行われます。例えば、USBヘッドセットを接続する場合を考えてみましょう。シーケンスは次のようになるかもしれません。

  1. デバイスとノードの作成: カーネルがudev経由で新しいALSAカードを報告します。WirePlumberのALSAモニターがこれをキャッチし、PipeWireのファクトリを使用してUSBオーディオカード用の新しいDeviceオブジェクトを作成し、次にハードウェアエンドポイントを表す1つ以上のNodeオブジェクト(例えば、alsa_output.usb_deviceノードとおそらくalsa_input.usb_deviceノード)を作成します。各NodeにはPortがあります(出力ノードはそのデバイスに再生するオーディオ用の入力Portを取得し、入力ノードはキャプチャされたオーディオ用の出力Portを取得します)。

  2. リンキング(ルーティング): WirePlumberのポリシーは、ストリームを再ルーティングすべきかどうかを決定します。以前のデフォルトシンクをアンリンクし、代わりに既存のすべてのオーディオ生成Nodeを新しいUSB出力Nodeにリンクするかもしれません(それを新しいデフォルト出力にする)。これは、各ストリームのポートからUSBノードのポートへのcreate-linkリクエストを発行することによって行われます。デーモン内に新しいLinkが作成され、それらのPortが接続されます。ユーザーの視点からは、サウンドはUSBヘッドセットから出ています。内部では、各オーディオストリームNodeをヘッドセットNodeに接続する一連のLinkオブジェクトが追加されました。

  3. オブジェクトの破棄: ユーザーがUSBヘッドセットを抜くと、ALSAモニタープラグインがデバイスの削除を検出します。WirePlumberは、関連するDeviceおよびNodeオブジェクトの削除をトリガーします。PipeWireデーモンは、それらのNodeが削除されると、それらのPortと、それらのポートが関与していたすべてのLinkを自動的に削除します。(一方の端がなくなると、Linkは存在できません。)クライアントには、ストリームがアンリンクされたことが通知されます(そのため、アプリケーションはストリームが別の出力に戻るか、一時停止するのを見るかもしれません)。グラフは以前の状態に戻ります(例えば、内蔵スピーカーが別の一連のLink変更を介して再びデフォルトになります)。これらはすべてライブで発生し、PipeWireのグラフの動的な性質を反映しています。

このライフサイクル(検出、オブジェクト作成、リンキング、後のアンリンキングと破棄)は、セッションマネージャーとデーモンの連携によって管理されます。PipeWire IPCとレジストリメカニズムにより、オブジェクトが作成または削除されると、関心のあるすべてのクライアント(WirePlumberやpavucontrolwpctlのようなUIを含む)がそれに関するイベントを受け取ることが保証されます。これは、udevやHALがLinuxでデバイスイベントをブロードキャストする方法に似ていますが、マルチメディアグラフのレベルで行われます。

これを説明するために、Nodeとそのリンケージのライフサイクルの簡略化されたシーケンス図を以下に示します。

このようにして、PipeWireシステムは、セッションマネージャーのポリシーとクライアントの要求の管理下で、必要に応じてNodeとLinkのグラフを動的に構築および解体します。コンポーネントの境界により、コアデーモンは効率的なデータ処理に集中し、意思決定とセットアップをユーザー空間のポリシーコードに任せることができます。

実用的な例:ノードとリンクの作成

これを具体的にするために、PipeWireでオブジェクトを手動で作成およびリンクする方法を見てみましょう(これは通常、セッションマネージャーによって自動的に行われますが、ユーザーまたはスクリプトが提供されたツールを介して行うことができます)。PipeWireは、デーモンを内省および制御するための多機能なコマンドラインツール**pw-cliを提供しています。pw-cliを使用して、テストやカスタムセットアップのために仮想ノードを作成**したり、既存のノードをリンクしたりできます。

例えば、仮想シンクノード(オーディオを飲み込むか、ループバックとして機能するダミーのオーディオ出力)を作成したいとします。support.null-audio-sinkという組み込みのサポートファクトリを使用して、新しいNodeを作成できます。

# カスタム名で仮想オーディオシンク(ヌルシンク)を作成する
pw-cli create-node adapter { \
    factory.name=support.null-audio-sink \
    node.name="MyNullSink" \
    media.class="Audio/Sink" \
    object.linger=1 }

このpw-cliコマンドは、PipeWireデーモンにadapterファクトリを使用して新しいノードを作成するように要求します。このコンテキストでは、support.null-audio-sink(ヌル出力デバイスのように機能する提供されたファクトリ)をラップします。それにMyNullSinkという名前を付け、Audio/Sinkとして分類します。プロパティobject.linger=1は、クライアントが切断しても存続するように指示します(仮想デバイスに便利です)。これを実行すると、グラフに新しいNodeが表示されます(シンクなので入力Portがあります)。pw-cli list objectspw-topのようなツールはこの新しいノードを表示し、通常のデバイスのようにストリームをルーティングできます。

次に、ノードをリンクするために、既存のソースノードをシンクノードに手動で接続したいとします。「MyNullSink」にinput_FL(フロント左チャンネル入力)という名前の入力ポートがあり、「TestTone」という別のノードにoutput_FLという名前の出力ポートがある場合、それらをリンクできます。

pw-cli create-link "TestTone" output_FL "MyNullSink" input_FL

これにより、「TestTone」という名前のノード上のoutput_FLという名前のポートと、「MyNullSink」上のinput_FLという名前のポートが見つかり、リンクされます。すぐに、PipeWireは新しいLinkを介してTestToneのポートからMyNullSinkのポートにデータをストリーミングし始めます。pw-cliツールは、セッションマネージャーが使用するのと同じAPIを効果的に実行しています。実際、内部では、2つのポート間にリンクを作成するためにCore APIメソッドを呼び出します。同様に、実際のシンクのモニターポートを別のシンクにリンクしてオーディオを複製することもできます。

これらの例は、ノードの登録とリンキングが動的に行えることを示しており、PipeWireの柔軟性を物語っています。開発者やパワーユーザーは、ノード(例えば、エフェクトやフィルターノード)を注入し、接続をその場で再配線できます。これは、OSで実行時にカーネルモジュールやネットワークルートを追加/削除するのに似ています。PipeWireは、マルチメディアグラフを同様に順応性のあるものにします。

3. カーネルインターフェースとシステムプログラミングの側面

PipeWireはユーザー空間で動作しますが、ALSA(サウンド)やV4L2(ビデオ)のようなカーネルサブシステムと密接に連携します。本質的に、アプリケーションとマルチメディアデバイスのカーネルドライバーの間に位置します。PipeWireがこれらとどのようにインターフェースし、採用している基本的なシステムプログラミング技術(デバイス検出、イベントループ、共有メモリなど)、およびセキュリティへのアプローチについて見ていきましょう。

ALSAとV4L2の統合

ALSA (Advanced Linux Sound Architecture) はカーネルのサウンドサブシステムであり、サウンドカードにアクセスするためのデバイスドライバとユーザー空間ライブラリ (alsa-lib) を提供します。PipeWireはALSAを2つの方法で使用します:

  1. ALSAのクライアントとして – 再生/キャプチャのためにオーディオハードウェアと対話する。
  2. ALSAのプロバイダーとして – PipeWireのグラフにリダイレクトする仮想ALSAデバイスを提供する。

最初のケースでは、PipeWireには ALSA Source/Sink と呼ばれる SPA (Simple Plugin API) プラグインが含まれています。このプラグインは、他のオーディオアプリケーションと同様に alsa-lib を使用して、PCMデバイスを開き、サンプルレートを設定し、サンプルを読み書きします。PipeWireがハードウェアデバイス用のノードを作成したい場合、このプラグインをロードします。例えば、起動時に、ALSAモニター(WirePlumberによって設定されるコンポーネント)は api.alsa.enum.udev をロードし、これはudevを介してすべてのALSAカードを列挙し、各カードに対してDeviceオブジェクトと対応するPCMノードを作成します。内部では、各ノードはプラグインを介してALSAシステムコール(snd_pcm_readi / writeimmap など)を使用してドライバと対話します。本質的に、PipeWireノードはALSAクライアントと考えることができます。PipeWireを実行している場合、alsa-info を実行したり /proc/asound/ の統計情報でPCMストリームとしてリストされているのがわかります。

2番目の使用法は、仮想ALSAデバイスの提供です。PipeWireは、アプリケーションが開くことができるALSA PCMインターフェースを公開でき、これは実際にはPipeWireにルーティングされます。これは、pcm.pipewire デバイスを定義するALSA設定ファイル(多くの場合 /etc/alsa/conf.d/50-pipewire.conf としてインストールされます)を介して行われます。多くのディストリビューションでは、デフォルトのALSA PCMがPipeWireに設定されているため、ALSAを直接使用するレガシーアプリケーションは透過的にPipeWireのオーディオグラフにリダイレクトされます。これは互換性にとって非常に重要です:PulseAudioやより高レベルのAPI向けに書かれていないアプリケーションでも、PipeWireによってキャプチャできます。アプリが「デフォルト」のALSAデバイスを開くと、実際にはPipeWireと通信しています(PipeWireソケットと通信するプラグインを介して)。このようにして、PipeWireはすべてのオーディオの中心に位置します:純粋なALSAクライアント、PulseAudioクライアント、およびJACKクライアントを同様に処理します。

V4L2 (Video4Linux) についても、統合は類似しています。PipeWireのビデオキャプチャサポートは、歴史的にV4L2を直接利用するか、libcameraのようなより高レベルのライブラリを利用してきました。オーディオと同様に、SPAプラグインがV4L2デバイス(ウェブカメラなど)を監視し、それらのためのノードを作成します。例えば、ウェブカメラが存在する場合、セッションマネージャーは V4L2モニター プラグインをロードでき、これはlibudevを使用して /dev/video* デバイスを見つけ、カメラのビデオフィードを表すノードを作成します。ビデオをキャプチャしたいアプリケーション(画面録画やビデオ会議アプリなど)は、PipeWire APIを直接使用するか、より一般的にはPipeWireを使用してビデオフレームを取得する Portals (xdg-desktop-portal) を介して行います。実際、Waylandコンポジターとポータルは、PipeWireがフィードする仮想V4L2デバイスを作成することで画面キャプチャを可能にします(これにより、アプリは許可なく画面をグラブできず、承認されるとPipeWireのストリームを使用するポータルを経由する必要があります)。システム的な観点から見ると、PipeWireは 仲介者である という同じ手法を使用します:ノード内でV4L2 ioctl コールからフレームを取得し、それらを読み取る権限のあるクライアントにソケットプロトコル経由で利用可能にします。

デバイス検出とUDevモニタリング

PipeWireのデバイス検出処理は、主に SPA (Simple Plugin API) モニター とセッションマネージャーのロジックを組み合わせて実装されています。Linuxデスクトップは伝統的に、ハードウェアが追加または削除されたときにユーザー空間に通知するために udev(デバイスイベントシステム)に依存しています。PipeWireはこれを活用し、ALSA、V4L2、Bluetoothなどのモニタープラグインがudevイベントを購読するようにしています。

例えば、ALSAモニタープラグイン (api.alsa.enum.udev) はPipeWireのSPAライブラリの一部です。WirePlumberはこのプラグインを(デフォルトで設定されている通りに)ロードし、本質的に「ALSAデバイスを列挙してください」と指示します。プラグインはlibudevを使用してサウンドカードのリストを取得し、各カードのALSAコントロールインターフェースを開いて情報(デバイス数、名前など)を収集します。その後、検出された各デバイスに対してPipeWireの Device および Node オブジェクトを作成するためのイベントを発行します。また、udevイベントもリッスンしているため、新しい snd デバイスが出現または消滅すると、追加/削除イベントをトリガーします。WirePlumberの設定では、これらのイベントは前述のように関連オブジェクトの破棄につながります。

特筆すべきは、実際のALSAモニターはPipeWireデーモンのコンテキスト内で実行されるということです(プラグインはPipeWireの一部です)。WirePlumberは単に指示を出し、ポリシー(使用するデバイスプロファイルなど)を提供するだけです。ドキュメントには次のように明記されています:「モニターは、すべてのデバイスモニターと同様に、SPAプラグインとして実装されており、PipeWireの一部です。WirePlumberは単にプラグインをロードし、その作業を行わせます。プラグインはその後UDevを監視し、すべてのALSAカードに対してデバイスおよびノードオブジェクトを作成します」。この設計は、低レベルのデバイス処理がパフォーマンスのためにC言語で行われるが、より高レベルのセッションマネージャーの制御下にあることを意味します。

Bluetoothオーディオについても同様のことが起こります:BlueZ(Linux Bluetoothスタック)とD-Busを介して対話し、デバイスとその機能を検出し、Bluetoothオーディオソース/シンク用のノードを作成するSPAプラグインがあります。WirePlumberのBluetoothポリシーは優先コーデックなどを設定できますが、デバイス作成はBlueZを使用するPipeWireプラグインに委任します。

要約すると、PipeWireは、これらのデバイスのスマートクライアントとして機能し(サウンドカード、カメラなどを開く)、各ハードウェアデバイスがグラフ内のノードとして表現されるフレームワークを提供することで、カーネルデバイスと統合します。デバイス検出イベントはカーネルから(udev経由で)もたらされ、セッションマネージャーの指導の下、SPAプラグインによってPipeWireオブジェクトのライフサイクルイベントに変換されます。

非同期I/Oとイベントループアーキテクチャ

リアルタイムのマルチメディア処理には、応答性が高くノンブロッキングなI/Oモデルが必要です。PipeWireは、ほとんどの最新サーバーと同様に、単一スレッドでI/O準備完了、タイマー、その他のイベントを処理するイベントループを中心に構築されています。PipeWireイベントループは、Linuxの epoll APIの抽象化です。epoll を使用すると、多数のファイルディスクリプタ(FD)を監視し、それぞれを順番にポーリングすることなく、1つ以上が読み書き可能になるまで待機できます。これはスケーリングに不可欠です(一般的なPipeWireデーモンは、数十のクライアントソケット、デバイスFD、タイマーなどを同時にリッスンする場合があります)。

PipeWireコアは、pw_loop_add_io() のような関数を提供し、FDをイベントループに登録し、読み書きイベント用のコールバックを指定します。例えば、サーバーのリッスンソケットと各クライアントソケットは、読み取り可能性(受信データ)のためにループに追加され、デバイスファイルディスクリプタ(新しいオーディオピリオド用のALSA PCMのFDなど)も追加される場合があります。PipeWireは、デーモン内のメイン(非リアルタイム)タスクにシングルスレッドのイベントループを使用します。処理用の専用リアルタイムスレッドもありますが(スケジューリングで説明します)、スレッド間の通信もイベントFDとイベントループによって仲介されます。

epollに加えて、PipeWireはイベント処理のために他のいくつかのLinuxシステムコールを採用しています:

  • eventfd: FDを介してスレッド間またはプロセス間でイベントを通知するための軽量メカニズム。PipeWireは eventfd を使用して、特定の条件をメインループに通知します。例えば、リアルタイムスレッドは、ループが監視するeventfdに書き込むことでメインループを「ウェイクアップ」できます。これは、ノードプロセスが終了したことや注意が必要なことをメインループに伝えるために使用されます。
  • signalfd: PipeWireはまた、非同期シグナルハンドラを使用する代わりに、イベントループ内でUNIXシグナルを処理するために signalfd を使用します。これにより、シグナル(デバッグ用のSIGTERMやSIGUSR1など)を通常のイベントフローにクリーンに統合できます。
  • timerfd: 上記のスニペットでは明示的に言及されていませんが、一般的にこのようなループは timerfd_create を使用してタイマーイベントを処理します(PipeWireも定期的なウェイクアップのスケジューリングやウォッチドッグタイマーの監視のために使用している可能性があります)。

これらはすべて統合されているため、PipeWireループは 1つのepoll fd で待機し、すべてのタイプのイベントを統一された方法で処理できます。これは、複数のブロッキングスレッドをやりくりするのを避けるために、高性能デーモンでは一般的です。

PipeWireが持つ抽象化の1つに SPAループ があり、これはイベントループの概念を一般化します。これにより、さまざまなバックエンド実装が可能になります(例えば、posixタイマーやRTタスク用のXenomaiのような代替手段の使用をサポートします)。Linuxでの実際には、デフォルトはepollです。主なポイント:PipeWireのI/Oは非同期かつノンブロッキングであり、デーモン(および処理を実行する場合に独自のループを持つクライアントも)がどの操作でも待機状態でスタックしないことを意味します。すべてが準備完了イベントによって駆動され、デーモンの応答性が確保されます(低遅延には重要です。オーディオサーバーがあるストリームのブロッキング読み取りでスリープし、別のストリームのデッドラインを逃すことはできません)。

ALSAとの デバイスI/O も可能な限り非同期で行われます。ALSAは、PCMデバイスのFDを取得するための「poll」インターフェースを提供します。PipeWireはこれを使用するため、サウンドカードがより多くのデータの準備ができたとき、またはデータをキャプチャしたときに通知を受け取ります(snd_pcm_write でビジーウェイトしたりブロッキングしたりする代わりに)。したがって、ALSAノードはALSA PCMのFDをループに追加し、カーネルがバッファを充填できるか、読み取るデータがあることを示すときにコールバックを受け取ります。この設計により、PipeWireは複数のクライアントストリームを効率的にミキシングできます。オーディオハードウェアがデータを必要とするときにウェイクアップし、そのサイクルですべてのリンクされたクライアントストリームからデータを収集します(スレッド間でeventfdを介して調整)。非同期設計は、スケジューリングセクションで説明する「プルモデル」の前提条件です。

メモリマッピングとゼロコピーバッファ

高性能を達成するために、PipeWireは可能な限りデータコピーを回避します。グラフィックスコンポジターがクライアントとバッファを共有するのと同様に、PipeWireはプロセス間でオーディオおよびビデオバッファに 共有メモリ を使用します。これは、Unixソケットを介してファイルディスクリプタ(FD)を渡すことによって促進されます(UNIXドメインソケットの機能の1つはFDパッシングをサポートすることです)。

PipeWireクライアントが出力ポートを持つノードを作成する場合(例えば、オーディオを再生するアプリケーション)、1つ以上の バッファプール を割り当てます。通常、クライアントとサーバーの両方がアクセスできる共有メモリを取得するために memfd または shm_open を使用します。クライアントはその後、PipeWireプロトコルを介してメモリFDをデーモンに通信します(メッセージとともにFDを補助データとして送信します)。デーモンはそのメモリを自身のアドレス空間にマップします。これで、クライアントプロセスとPipeWireプロセスの両方が同じバッファ領域を読み書きできます。これは、クライアントがオーディオサンプルを生成するとき、それらを共有バッファに直接書き込むことができ、サーバーはそれらのバイトを別のバッファにコピーする必要がないことを意味します – 直接読み取ることができます。同様に、ビデオフレームの場合、PipeWireはさらに高度なゼロコピーをサポートします:DMA-BUF 共有を使用します。アプリケーションがフレーム用のDMABUF(GPUバッファ)を持っている場合、それをPipeWireと共有できるため、CPUでさえピクセルデータをコピーする必要がありません。

PipeWireデータ交換フォーマットは、SPAバッファ の概念を中心に構築されています。SPAバッファには、メタデータと1つ以上のデータチャンクが含まれます。例えば、オーディオバッファには、ポインタ、サイズなどを持つ1つのデータチャンクが含まれる場合があります。これらのチャンクは、多くの場合、共有メモリセグメントを指します。PipeWireはバッファ割り当てに関してクライアントと調整します:バッファのリング(ダブル/トリプルバッファリング用)を使用して、1つのバッファが再生されている間にクライアントが別のバッファに書き込めるようにします。

内部的には、効率のために、PipeWireは mmap ベースのアクセスと ロックフリー構造 を使用します。そのような構造の1つは、一部のノードでスレッド間通信に使用される ロックフリーリングバッファ です。SPAライブラリは、ロックなしで単一プロデューサー、単一コンシューマーパターンをサポートするリングバッファを実装しており、これはリアルタイムセーフな方法でスレッド間でオーディオを渡すのに役立ちます。典型的なシナリオでは、キャプチャノードがオーディオサンプルをミキサースレッドが読み取るリングバッファにプッシュする可能性があるため、わずかなタイミングの違いがあっても、どちらのスレッドもブロックする必要はありません – アトミックなインデックスを介して空/満杯の状態を見つけます。リングバッファAPIは、適切なラップアラウンドとアンダーランまたはオーバーランのインジケーターを使用して書き込みおよび読み取りを行う関数を提供します。ロックフリーであるため、それに書き込むリアルタイムスレッドはミューテックスを取得する必要がなく(優先度逆転やレイテンシのスパイクを引き起こす可能性があります)、コンシューマーも同様にロックせずに読み取ることができます。

例えば、pw_stream 高レベルAPIは、クライアントがバッファを取得し、それを満たし、キューに入れるための簡単な方法を提供します。内部的には、手順は次のとおりです:

  1. 空きバッファを デキュー します:pw_stream_dequeue_buffer()
  2. そのデータへの生のポインタを取得します:これはサーバーも知っている共有メモリ内のポインタです。
  3. バッファをオーディオ/ビデオデータで満たします(例えば、オーディオをデコードしたり、ビデオをそのメモリにキャプチャしたりします)。
  4. バッファのメタデータ(有効なデータのバイト数など)を設定します。
  5. バッファを キュー に戻します:pw_stream_queue_buffer() はそれを処理するためにPipeWireに渡します。

これらすべてが余分なデータコピーなしで行われます – メモリは同じままで、その瞬間だけ所有権がクライアントからサーバーに移動します。このプロセスは単純なパターンに従います:バッファをデキューし、それを満たし、そしてキューに戻します。この設計は、オーディオドライバやJACKのモデルを彷彿とさせ、サイクルごとのオーバーヘッドを最小限に抑えます。

さらに、PipeWireは、特にパイプ間でデータを移動する場合など、ユーザーランドにコピーせずにFD間でデータを移動するために、Linuxの splice(2) または tee(2) システムコールを利用することさえあります。ただし、主要なゼロコピーは共有メモリ技術によるものです。

要約すると、PipeWireのシステムプログラミング戦術には、共有バッファのための メモリマッピング の多用、応答性のための epoll による ノンブロッキングI/O、およびリソース共有のための fdパッシング が含まれます。これらを組み合わせることで、PipeWireは遅延が低く、CPU使用率が最小限に抑えられる設計を実現します – データが必要以上に動き回ることはありません。これはすべてCシステムコールとLinux固有の機能のレベルで動作しており、PipeWireをOSの機能を最大限に活用する非常に Linuxネイティブ なプロジェクトにしています。

セキュリティ:cgroups、PolicyKit、およびサンドボックス化の考慮事項

PipeWireはセキュリティを念頭に設計されており、特にマイク音声やカメラビデオのような機密データを扱うためです。いくつかのLinuxセキュリティメカニズムと統合されています:

  • cgroups: PipeWireはcgroups(コントロールグループ)を使用して、クライアントのリソース制限を管理できます。例えば、セッションマネージャーは、要求の多いクライアント(ビデオエディタなど)を特定のCPUまたはメモリ共有を持つcgroupに配置して、他のアプリケーションやシステムを飢餓状態に陥らせるのを防ぐことができます。これはPipeWireコア自体ではなく、セッションマネージャー(WirePlumberなど)によって管理されるシステム統合の側面ですが、PipeWireはクライアントを識別および管理するためのフックを提供します。

  • PolicyKit (polkit): 特権操作の場合、PipeWireはPolicyKitを使用します。例えば、ストリームに高いリアルタイム優先度を設定する(誤用するとシステムの安定性に影響を与える可能性がある)には、polkitの承認が必要になる場合があります。セッションマネージャーは通常、これらの要求を仲介します。クライアントが画面をキャプチャしたり、特定の高優先度オーディオデバイスを使用したりする場合、WirePlumberは、要求元のアプリケーション(PIDまたはその他の資格情報で識別される)がシステムポリシーに基づいてそうすることが許可されているかどうかをpolkitで確認する場合があります。これにより、任意のアプリケーションがリソースへの無制限のアクセスを取得するのを防ぎます。

  • Flatpakサンドボックス化とポータル: PipeWireは、サンドボックス化されたアプリケーション(例:Flatpakアプリ)がマルチメディアデバイスにアクセスする方法の中心です。Flatpakアプリは制限された環境で実行され、ALSAデバイスを直接開いたり、/dev/video* にアクセスしたりすることはできません。代わりに、ポータル を使用します。ポータルは、サンドボックス化されたアプリがファイル、カメラ、画面共有などのリソースへのアクセスを要求するために呼び出すことができる、ホスト上で実行されるD-Busサービスです。

    • カメラアクセスの場合、Flatpakアプリはカメラポータルを呼び出します。ポータルは、PipeWire(多くの場合セッションマネージャー経由)と通信して、利用可能なカメラを列挙し、ユーザーが承認した場合(通常はポータルサービスによって表示されるダイアログ経由)、特定のものへのアクセスを許可します。その後、PipeWireはサンドボックス化されたアプリにストリームを提供します。
    • 画面共有の場合も同様のメカニズムが使用されます。アプリはScreenCastポータル経由で画面共有を要求します。ポータルはPipeWire(および多くの場合Waylandコンポジター)と対話して、画面/ウィンドウを選択し、そのコンテンツをストリーミングします。PipeWireはビデオストリームを処理し、アプリで利用できるようにします。
      このポータルベースのアプローチにより、サンドボックス化されたアプリは直接ハードウェアアクセスを取得せず、仲介され、ユーザーが承認したチャネルを経由することが保証されます。PipeWireは、この安全なデバイス共有を可能にするバックエンドです。
  • Wayland統合: Wayland環境では、画面キャプチャと共有は特に機密性が高くなります。Waylandコンポジター自体が画面コンテンツを管理することがよくあります。PipeWireは、画面キャプチャのためにWaylandコンポジター(例:GNOMEのMutter、KDEのKWin)と統合します。コンポジターは画面コンテンツをキャプチャし、PipeWire経由で要求元のアプリケーションにストリーミングします(これも通常はポータルによって仲介されます)。これにより、アプリケーションが画面バッファを直接読み取ることを回避でき、セキュリティリスクとなります。

これらのメカニズムを組み合わせることで、PipeWireはメディアストリームを管理するだけでなく、マルチメディア用のより大きなOSセキュリティアーキテクチャのコンポーネントであることがわかります。特にサンドボックス化されたアプリケーションに対して、誰が何にアクセスできるかをきめ細かく制御でき、これは最新のデスクトップおよび組み込みLinuxシステムにとって不可欠です。この設計は、完全に新しいセキュリティモデルを発明するのではなく、既存のLinuxセキュリティプリミティブ(Unixパーミッション、FD、polkitなど)を活用しているため、堅牢で適切に統合されています。

4. グラフベースのストリームとスケジューリングメカニズム

PipeWireの中核的な強みの1つは、相互接続されたノードのグラフを管理し、そのグラフを介してリアルタイムでメディア処理をスケジュールする能力です。このセクションでは、グラフがどのように構造化され更新されるか、イベントループがどのようにグラフ実行を支えるか、そしてPipeWireがどのように低遅延スケジューリングを実現するか(リアルタイムスレッド、プルモデルスケジューリング、ロックフリー通信を活用)について詳しく説明します。また、ドライバーノードのような概念を説明し、グラフを介してオーディオをプッシュするために使用されるコードパターンでプロセスを説明します。

グラフのトポロジーと動的な変更

前述のように、PipeWireのメディアグラフは基本的に、リンクエッジで接続されたノードオブジェクトの有向グラフです。これを強力にしているのは、このグラフが 動的 であるということです – ノードとリンクはいつでも出現したり消えたりすることができます。この動的な性質は非常に重要です:オーディオを生成するアプリケーション(ノード)が自由に開始または停止したり、Bluetoothスピーカー(別のノード)が接続/切断したりすることを想像してみてください。PipeWireは、グラフの他の部分を中断することなく、オンザフライでグラフの再構成を処理する必要があります。

トポロジーは静的なパイプラインのように固定されていません。代わりに、レジストリで維持され、APIを介してクエリまたは変更できます。アプリケーションは既存のノードとそのポートを列挙できます(例えば、ユーザーにオーディオ出力のリストを提示するため)。新しいノードが(クライアントまたはセッションマネージャーによって)作成されると、グラフに追加され、通常はポリシーがどこに接続するかを決定するまでリンクされないままになります。セッションマネージャーは自動的に何かにリンクするかもしれません(自動接続ポリシー)、またはユーザーのルーティングに任せるかもしれません(プロオーディオのシナリオでは、ユーザーはJACKのように手動でノードを接続するかもしれません)。PipeWireは両方をサポートしています:「自動接続なし」ポリシー(JACKのような手動パッチベイモード)で実行することも、完全に自動的なルーティング(PulseAudioのような動作)で実行することもできます。

内部的には、ノードがリンクされると、PipeWireは コンポーネントサブグラフ を形成します。コンポーネントは、リンクによって(直接的または間接的に)接続されたノードのセットです。2つのノードグループ間にリンクがない場合、それらは別々のコンポーネントです – 本質的に、それぞれ独自に実行される独立したグラフです(例えば、オーディオグラフと完全に分離されたビデオグラフが同時に存在する可能性があります)。これはスケジューリングにとって重要です。なぜなら、各コンポーネントは独自の ドライバー(タイミングを決定するノード、下記参照)を持つからです。

トポロジーの動的な変更 – 例えばリンクの追加 – は、スケジューリング順序の再評価を引き起こす可能性があります。新しいリンクが以前は分離していた2つのコンポーネントを接続すると、それらは1つのコンポーネントになり、2つのドライバーのうちの1つがマージされたグラフの単一ドライバーとして選択されなければなりません。逆に、リンクが削除されてグラフが分割されると、各部分が独自のドライバーノードを昇格させる可能性があります。

開発者の観点から見ると、PipeWire APIと pw-cli を使用すると、動的なグラフ変更が容易に行えます。これは創造的なアプリケーションに活用できます。例えば、自動ミキサーが新しいストリームを監視し、音声通話ノードが表示されたときに音楽の音量を下げるために「ダッキング」ノードを間に挿入し、通話が終了したらそれを削除することができます。グラフモデルはそれをサポートするのに十分柔軟です – ノードイベントをリッスンし、それに応じてリンクやノードを追加/削除するスクリプト(WirePlumber Luaまたは外部経由)を書くことを想像できます。

PipeWireイベントループとオブジェクト依存関係

システムセクションで説明した イベントループ は、グラフの実行においても中心的です。しかし、純粋なepollの準備完了性だけではオーディオのスケジューリングには不十分です – オーディオには正確なタイミングループが必要です。PipeWireは 処理サイクル の概念を導入しています:基本的に、オーディオクォンタムによって決定される一定の間隔(例:5msまたは10msごと)で、グラフが処理されるべきです:各ノードは1「クォンタム」のデータ(256フレームのオーディオなど)を生成または消費する必要があります。これを達成するために、PipeWireはタイマーを設定するか、オーディオハードウェアのクロックを使用します。

ドライバーノード の概念が鍵となります:ノードの接続された各サブグラフにおいて、1つのノードが ドライバー として指定されます。ドライバーはスケジューリングサイクルをトリガーするものです。通常、ドライバーは固有のクロックを持つハードウェアエンドポイントです。例えば、ALSAシンクノード(サウンドカードへの再生)は、サウンドカードが割り込みを生成したり、「今すぐもっとデータが必要だ」というタイマーを持っていたりするため、ドライバーになることができます。同様に、ALSAソース(マイクからの録音)やシステムクロックを使用するJACKクライアントもドライバーになることができます。そのグラフ内の他のノードは フォロワー であり、いつ処理するかをドライバーに依存します。

アプリからスピーカーにオーディオを再生する簡単なケースでは、スピーカーノード(ALSAシンク) がドライバーです。これは通常 プル モードで動作します:サウンドカードのハードウェアバッファが特定のしきい値まで空になり、割り込みがデータの必要性を示します。PipeWireのALSAプラグイン/ノードはこの信号を(イベントループ経由で)受け取り、それに応じてサイクルを開始します:接続されたグラフに「データをプルする時間だ」と信号を送ります。リンクされているアプリのノードは(PipeWireプロトコルとeventfd経由で)起動され、より多くのデータを生成する(共有バッファを埋める)ように指示されます。データはリンクを通ってALSAシンクノードに流れ込み、それがハードウェアバッファに書き込みます。これで1サイクルが完了します。ドライバーが必要性を示すたびに(通常は固定期間、例えばバッファクォンタムが128フレームなら128フレームごと)、サイクルが繰り返されます。

複数のノードを持つより複雑なグラフ(例えば、直列のオーディオエフェクト)では、スケジューリングは各ノードが正しい順序で処理されるように保証する必要があります(アップストリームノードがダウンストリームノードより先に)。PipeWireは、ノードをトポロジカルソートすることによってこれを処理します(JACKが行うのと同様)。オブジェクトの依存関係(どのノードがどのノードに依存するかなど)は、リンクグラフ構造からわかります。各サイクル中に、PipeWireは依存関係を尊重する順序でノードのプロセスコールバックを呼び出します。例えば、ノードAがノードBにデータを供給し、Bがドライバーである(またはドライバーに間接的にリンクされている)場合、サイクル中にBのプロセスがAからのプルをトリガーします。実際には、PipeWireはしばしば プルモデル を排他的に使用します。つまり、ダウンストリームノード(ドライバーに近い)がアップストリームノードを呼び出して時間通りにデータを取得します。

これは、プロデューサーが自由に実行し、データがあるときにコンシューマーにデータをプッシュする プッシュモデル とは異なります。PipeWireは、低遅延のために明示的に プルモデル を選択しました。プルモデルでは、データは必要なときにジャストインタイムで生成されます。これにより、バッファの充填レベルが最小限に抑えられ、したがって遅延も最小限に抑えられます – データはキューで待機するのではなく、可能な限り遅く要求されます。このアプローチは、バッファ内のデータ量が最小化されたときに遅延が最適であることを保証し、プルすることによって、システムはバッファアンダーランの直前にプロデューサーに信号を送り、最新のデータを保証できます。PipeWireの実装はそれを反映しています:

  • グラフをいつ実行するかを知るために、ドライバーからのタイマー/割り込みを使用します。
  • イベント(eventfdとプロトコル経由)を使用して、クライアントスレッドに起動してオンデマンドでデータを提供するよう通知します。
  • 各ノードの処理関数(クライアント内または内部ノードの場合はデーモン内)は、通常、そのデータがダウンストリームリンクによって必要とされる場合にのみ呼び出されます。

具体的な例:エフェクトチェーンを仮定します:アプリノード -> リバーブノード -> ALSAシンクノード。ALSAシンクがドライバーです。時間になると(オーディオバッファがほぼ空):

  1. ALSAシンク(ドライバー)イベントが発生します。PipeWire(シンクノードのRTスレッド内)は「もうデータがないので、サイクルを実行しましょう」と言います。
  2. リバーブノードからのデータが必要です(リバーブの出力がALSAシンクの入力にリンクされているため)。そのため、リバーブのプロセスに実行を指示します。
  3. リバーブノードは次にアプリノードからリンクされているため、リバーブはアプリノードにより多くの入力を要求する可能性があります(プルモードで動作するか、データを要求するように設定できると仮定)。
  4. アプリノード(クライアントプロセス内)はIPC経由で起動され、データを生成します(これはクライアント自身のイベントループを通り、pw_streamon_process などの処理コールバックに入ります)。
  5. アプリノードはオーディオを出力バッファに書き込み、キューに入れます。
  6. リバーブノードはそのデータを受信し、エフェクトを適用し、出力をバッファに生成します。
  7. ALSAシンクはリバーブの出力から処理済みデータを受信し、ハードウェアに書き込みます。

これらすべては、スケジューリングサイクル内で協調的かつロックステップ方式で行われ、理想的にはサイクル期間のほんの一部で行われます(ハードウェアが実際にデータ切れになる前にマージンを確保するため)。いずれかのステップが遅すぎると、ハードウェアはアンダーラン(グリッチ)します。しかし、シングルスレッドまたは緊密に同期されたアプローチ(JACKのネットグラフアプローチなど)を使用することで、PipeWireはノード間のオーバーヘッドを最小限に抑えます。

グラフが独立している場合や、特定のノードが独自のスレッドを使用するように構成されている場合(PipeWireはクライアント用の スレッドループ をサポートしています)、PipeWireは複数のスレッドを使用できることに言及することが重要です。しかし、緊密に接続された1つのコンポーネントグラフ内では、ノード間の複雑なロックを必要としないように、オーディオ処理のために効果的にシングルスレッドで実行されることがよくあります(JACKはすべてのオーディオクライアントに対して単一のスレッドを持っていました。PipeWireはグラフごとにそれを模倣できます)。

PipeWireの設計では、同期が不要な場合に 非同期ノードスケジューリング も可能です(効率向上のため新しいバージョンで導入)。例えば、関連のないストリームがある場合、複数のCPUコアをより有効に活用するために独立してスケジュールされるかもしれませんが、ミキシングが必要な場合は1つのドライバーの下に収束します。

リアルタイムスケジューリング:RTKitとロックフリー通信

オーディオ処理は時間に非常に制約されます。PipeWireは、オーディオスレッドにリアルタイムスケジューリングを使用して、高い優先度と最小限のジッターで実行されるようにします。PipeWireデーモン(およびクライアントのプロセススレッドでさえも)は、グラフを実行するスレッドに対して高い優先度で SCHED_FIFO スケジューリングを取得しようとします。前述のように、モジュール libpipewire-module-rt がこれを処理します。ユーザータスクがRLIMIT経由で優先度を上げることができるシステム(オーディオを許可するために /etc/security/limits.conf などで設定されることが多い)では、PipeWireは単にスレッドを例えばFIFO優先度88に設定します。そうでない場合は、D-Busサービスである RealtimeKit (RTKit) にフォールバックし、適切な認証を得てスレッドの優先度を上げるように依頼します。ほとんどのディストリビューションでは、これが標準で設定されています(例えば、ユーザーを realtime グループに追加したり、PipeWireがRTスケジューリングを取得できるようにするPolkitルールを使用したりします)。これはJACKやPulseAudioが行う方法と非常によく似ています。

さらに、module-rt はいくつかの役立つパラメータを設定します:

  • niceレベルを設定できます(通常ポリシー下でもスケジューリングを改善するためにメインスレッドのnice値を下げます)。
  • ハングアップを避けるためにRLIMIT_RTTIME(RTスレッドの最大CPU時間)を設定します – オーディオスレッドは継続的に実行される可能性があるため、デフォルトでは無制限(ソフト/ハードで -1)のままにします。
  • 新しいカーネルではCPUスケジューラ使用率ヒント(util_clamp)も設定し、このスレッドが必要とする限り100%に近いものとして扱われるべきであることをスケジューラに知らせます。これは、RTタスクのCPU帯域幅をより正確に制御するためのcgroup v2機能に関連しています。

リアルタイム優先度を持つことにより、PipeWireのオーディオスレッドは通常のタスクをプリエンプトします。これはドロップアウトを避けるために不可欠です:何かCPU負荷の高い処理(コンパイラのビルドなど)が発生した場合でも、オーディオスレッドはサウンドカードにデータを供給するために時間通りに実行されるべきです。

しかし、RTスケジューリングはシステムのロックアップを避けるために慎重に使用する必要があります。PipeWireのリアルタイムスレッドは、ブロッキングなしで有界の作業 を行うように設計されています。これが、ロックフリーリングバッファなどが使用される理由です – リアルタイムスレッドは、優先度の低いスレッドが保持するミューテックスを待つことはできません。そうしないと、優先度逆転のリスクがあります(オーディオスレッドが優先度の低い何かを待つ – RTを無効にする)。PipeWireでは:

  • 処理スレッドは、クリティカルパスでmallocやその他の予測不可能な呼び出しを行いません(理想的には)。
  • メインスレッドとRTスレッド間の通信は、ロックではなくeventfdシグナルとリングバッファを介して行われます。

例えば、クライアントの on_process コールバック(クライアントのストリームスレッドで実行され、多くの場合 pw_thread_loop などで作成されたリアルタイムスレッド)は、バッファをデキューし、それを埋め、キューに入れます。これらの操作はすべてロックフリーであるか、アトミック操作を使用し、一定時間で完了します。PipeWireデーモンにバッファの準備ができたことを通知する必要がある場合、サーバーのepollループを起動するeventfdに書き込むかもしれませんが、これはカーネル内の単一のアトミック操作であり、非常に高速です。

ロックフリーFIFO の使用は、制御メッセージのような他のものにも及びます。PipeWireのプロトコルは、各小さなメッセージを個別のシステムコール経由で送信するのを避けるために、送信メッセージ用に共有メモリリングバッファを使用します – 代わりに複数のメッセージをマーシャリングしてからフラッシュすることができ、これはより効率的です(ただし、この部分はスペースを予約するためにミューテックスまたはアトミックなcompare-and-swapを使用する場合がありますが、オーバーヘッドが低くなるように設計されています)。また、PipeWireプロトコルエンコーディングを思い出してください:それはバイナリPOD(Plain Old Data)形式です。この選択(例えばXMLやJSONの代わりに)はパフォーマンスのために意図的なものです – エンコーディング/デコーディングは単に構造体をコピーするだけであり、これはCでは高速です。したがって、IPCでさえリアルタイムの考慮事項に合わせて最適化されています。

複数のリアルタイムスレッドで発生する1つの課題は、CPUの競合です。それぞれ独自のRTスレッドを持つ複数のオーディオクライアントがある場合(これはPipeWireで発生する可能性があります – 例えば、アプリがスレッドループを持つ pw_stream を作成し、サーバーもRTスレッドを持つ場合)、OSはそれらを適切にスケジュールする必要があります。理想的には、それらはすべてほぼ同じ優先度であるべきであり、それによって均等なシェアを得るか、1つが明確にドライバーであるため、他はその合図で起動し、それ以外はスリープします。

PipeWireは優先度を設定するために RTKit を使用します。デフォルトでは、サーバーのデータスレッドとクライアントを 同じ優先度(88など)に設定することがよくあります。これは、すべてのクライアントを1つのスレッドで実行していたJACKとは異なります – PipeWireでは、異なるCPUコア上にある場合、カーネルがそれらを同時にスケジュールする可能性があります。これにより、マルチコアシステムでの遅延を削減できます(独立したストリームの並列処理)が、同じ優先度のスレッドが多すぎると競合を引き起こす可能性もあります。これはPipeWireが進化しているトレードオフです(新しい明示的な同期および非同期処理機能は、同期を壊さずにマルチスレッドスケジューリングを改善することを目的としています)。

グラフスケジューリングアルゴリズム に関して言えば、PipeWireのアプローチの核心はJACKのものと似ています:各サイクルでバースト実行される非プリエンプティブなグラフです。しかし、PipeWireは複数のドライバーと異なるサイクルレートのブリッジング(例えば、必要に応じて変換ノードを使用して1つの48kHzオーディオグラフと別の44.1kHzを処理できる)により柔軟性を追加します。スケジューリングはある程度SPAライブラリレベルで処理されます – ノードにはグラフエンジンが呼び出す process() コールバックがあります。

もう1つのメカニズム:RTKitとcgroupsの相互作用。ArchWikiのメモで、RTKitの長年のバグにより、サスペンド/レジュームサイクルの後にRT優先度が取り消される可能性があることがわかりました。回避策は、サスペンド時にRTタスクを降格させようとするRTKitの内部「カナリア」メカニズムを無効にすることでした。これは、システムがサスペンドすると、RTKitが問題を回避するためにすべてのRTスレッドを下げるかもしれないが、その後それらを復元しないことを示しています。ユーザーはレジューム後にクラックリングに気づきました(つまり、スレッドは通常の優先度で実行され、デッドラインを逃していました)。修正はOSレベルのオーバーライドでした。これは、リアルタイムオーディオがOSポリシーに敏感であることの一例です – 継続的な改善の分野です(おそらくPipeWireは将来、この状態を再昇格または検出する独自の方法を実装するかもしれませんが、現時点では、ユーザーの介入または更新されたRTKitが必要です)。

全体として、PipeWireのリアルタイムスケジューリングとロックフリー設計は、OSエンジニアリングの考え方を反映しています:適切なカーネル機能(SCHED_FIFO、eventfds、epoll)をアルゴリズム戦略(プルスケジューリング、コンポーネントごとのシングルドライバー、オーディオパスにグローバルロックなし)と組み合わせて、信頼性の高い低遅延処理を実現します。正しく設定されていれば、PipeWireはJACKに匹敵するラウンドトリップ遅延(わずか数ミリ秒)を達成しつつ、より多様なワークロード(複数のストリーム、ビデオ同期など)を処理できます。

コード例:リアルタイムコールバックでのバッファ処理

これを実践的な例で具体的に説明するために、PipeWireクライアントが プルモデル でどのようにオーディオを生成するかを考えてみましょう。以下は、PipeWireのストリームAPIを使用したクライアントのプロセスコールバック(C言語)内で何が起こるかを示した簡略化されたスニペットです:

// グラフがより多くのデータを必要とするときにリアルタイムコンテキストで呼び出される
static void on_process(void *userdata) {
    struct data *d = userdata;
    struct pw_buffer *b;
    if ((b = pw_stream_dequeue_buffer(d->stream)) == NULL) {
        pw_log_warn("out of buffers"); // バッファ切れを警告
        return;
    }
    // バッファメモリへのポインタを取得
    float *buf = b->buffer->datas[0].data;
    uint32_t max_samples = b->buffer->datas[0].maxsize / sizeof(float); // 最大サンプル数
    uint32_t to_write = max_samples; // 書き込むサンプル数
    if (b->requested && b->requested < to_write) // 要求されたサイズがあればそれを優先
        to_write = b->requested;
    // バッファにオーディオサンプルを充填(例:ファイルから、またはトーンを生成)
    size_t n = read_audio_samples(d->file, buf, to_write); // オーディオサンプルを読み込む
    if (n < to_write) {
        // 必要に応じて巻き戻しまたはデータ終了処理
        rewind_file(d->file);
        // ... 残りを新しいデータで埋める ...
    }
    // バッファメタデータを設定
    b->buffer->datas[0].chunk->offset = 0;
    b->buffer->datas[0].chunk->stride = sizeof(float);    // チャンネルごとの1サンプルのサイズ
    b->buffer->datas[0].chunk->size   = n * sizeof(float); // 書き込まれたデータのサイズ
    // バッファをPipeWireに送り返す
    pw_stream_queue_buffer(d->stream, b);
}

この疑似コードは、前述の手順を示しています:

  1. バッファをデキュー – 書き込むための空きバッファを取得します(ない場合は警告をログに記録してスキップします)。
  2. メモリにアクセスbuf は、オーディオを配置すべき共有メモリへのポインタです。
  3. 書き込む量を決定 – バッファのフルサイズ、または要求されたサイズ(PipeWireは b->requested を介して必要なフレーム数を示すことができます)。
  4. データを生成 – ここで read_audio_samples はファイルから読み込むか、波形を生成することができます。バッファに to_write サンプルを書き込みます。
  5. データが不足している場合(EOFに達した場合など)、それを処理します(ファイルをループさせるなど)。
  6. メタデータを設定 – このバッファにどれだけの有効なデータが含まれているかをPipeWireに伝えます(バイト単位のサイズ、オフセットがあればそれ、インターリーブされている場合はストライドも使用できます)。
  7. バッファをキューに入れる – それをPipeWireに返します。PipeWireはそれをリンクされているもの(またはノードがリモートの場合はPipeWireサーバー)に渡します。

これはクライアントストリームの手順と一致します。すべてリアルタイムスレッドで行われます。このループの周りにはmallocもロックもないことに注意してください – これは単純な配列の充填です。複数のストリームのミキシングのような重い処理は通常サーバー側で行われますが、クライアントはできるだけ速くそのチャンクを提供するだけです。

これは「グラフがデータを必要としている」イベントによってトリガーされるため、クライアントはこれを常に実行しているわけではありません – PipeWireがスケジュールしたときだけです。クライアントが遅い場合(次のサイクルの前に戻らない場合)、アンダーランが発生します。しかし、通常、サイクルサイズ(クォンタム)は必要な作業に対してある程度の安全マージンを持って選択されます。

サーバー側では、ALSAシンクノードのプロセスに対して同様のコードがあります:入力バッファのリングからデキューし(クライアントの出力によって共有メモリ経由で充填されたもの)、次にハードウェアに送信するために snd_pcm_writei を呼び出すなどです。これはALSAのAPIによって大幅に簡略化されていますが、概念的には似ています。

結論として、PipeWireのグラフスケジューリングは、イベントとコールバックの細かく調整された連携であり、グラフの接続された各コンポーネントが順序通り、時間通りに実行され、デッドラインを満たすためにリアルタイムOS機能を使用することを保証します。この設計はJACK(プロオーディオのDNA)から多くを継承していますが、それをビデオや任意の処理をカバーするように一般化しており、これは重要なエンジニアリングの偉業です。

5. WirePlumberセッションマネージャーのソースレベルの洞察

PipeWireのデフォルトのセッションおよびポリシーマネージャーであるWirePlumberは、ソースレベルおよび設定の観点からより深く見ていく価値があります。エンジニアとして、WirePlumberがどのように意思決定を行い、どのようにカスタマイズできるかを理解することは、PipeWireを特定のニーズに合わせて調整するために不可欠です。このセクションでは、WirePlumberのスクリプトシステム(元々はLuaでしたが、現在はLuaと.conf設定が混在しています)、ルールの処理方法、および実装可能なカスタムポリシーの例について説明します。

WirePlumberのアーキテクチャ:プラグインとLuaスクリプト

WirePlumberはモジュラー設計で構築されています。コアデーモンがあり、モジュール(WirePlumber APIを使用してC言語で書かれたプラグイン)をロードし、ポリシーロジックのためにLuaスクリプトを実行することもできます。これは概念的に、Webブラウザのようなアプリケーションが拡張機能を許可する方法と似ています。コアはPipeWireオブジェクトを操作するためのAPIを提供し、スクリプトはそれらのAPIを使用して目的の動作を実装します。

WirePlumberが起動すると、設定ファイル(デフォルトでは /usr/share/wireplumber/wireplumber.conf)をロードします。バージョン0.4以前では、この設定自体がLuaコードでした。バージョン0.5以降では、どのプラグインとスクリプトをロードするかを指定する静的な設定(.conf構文)になっています。.confへの移行は、コードと設定を分離し、デフォルトポリシーとユーザーオーバーライドのパッケージ化を容易にするために行われました。

WirePlumberの主要なモジュールとスクリプトのいくつか:

  • デフォルトノードとデフォルトプロファイル: 先に見たように、デフォルトの入力/出力デバイスが何かを管理するスクリプトです。これには、ノードをスキャンし、優先度とユーザーの選択に基づいてどれをデフォルトにすべきかを選択することが含まれます。
  • ポリシーリンキング: ストリームを自動的に出力または入力にリンクするスクリプト(またはスクリプトのセット)です。WirePlumberには、新しいストリームを調べてエンドポイントに一致させるリンキングポリシーモジュールがあります。例えば、media.role=Music のストリームを「Music」エンドポイントまたは単にデフォルトのオーディオシンクにリンクすることがあります。
  • デバイスプロファイル管理: デバイス(サウンドカード)が出現したときに監視し、優先プロファイル(例えば、BluetoothのHigh Fidelity再生対ヘッドセットモノラルなど)に設定するコードです。また、ユーザーが開始したプロファイルの変更をリッスンし、それらを保存します。
  • 音量とルートの復元: アプリやデバイスの音量やルートを変更すると、WirePlumberはそれを記憶し(~/.local/state/wireplumber/ の状態ファイルに永続化されます)、次回に復元できます。これはPulseAudioの動作(アプリやデバイスごとの音量を記憶する)を模倣しています。
  • 特別ケースルール: 例えば、WirePlumberの設定には、特定のアプリケーションやデバイスを独自に処理するためのルールが含まれる場合があります。ArchWikiのスニペットは、音の問題を回避するためにDiscord専用に pulse.min.quantum をオーバーライドする例を示しています。WirePlumberは、アプリケーションのプロパティを照合することによって、そのようなルールを適用できます。

WirePlumberのLuaスクリプティングAPIは非常に包括的です。「ノード追加」、「ノード削除」、「リンク追加」などのイベントをリッスンし、node:link(target_port)node:set_volume(volume)などのメソッドを呼び出すことができます。スクリプトは通常、WirePlumberのイベントシステムにフックを登録します。

例えば、WirePlumberのリンキングスクリプトからのスニペットは次のようになるかもしれません:

core:connect("object-added", function(obj)
    if obj:is("PipeWire:Interface:Node") then
        props = obj:properties()
        if props["media.class"] == "Audio/Stream" and props["media.role"] == "Music" then
            -- デフォルトのAudio/Sinkエンドポイントを見つける
            local endpoint = find_default_endpoint("Audio/Sink")
            if endpoint then
                -- ストリームのノードをエンドポイントのノードにリンクする
                link_nodes(obj, endpoint.node)
            end
        end
    end
end)

(これは説明のための疑似コードです。)新しいノードが「Music」というロールを持つオーディオストリームであるかどうかを確認し、次にそれをリンクするエンドポイント(デバイス)を見つけます。WirePlumberは、PipeWireコアAPIをラップするこのような便利な関数とオブジェクトモデルを提供します。

もう1つの部分はルール設定です。すべてのシナリオに対してコードを書く代わりに、WirePlumberは設定ファイル内でプロパティのマッチングと設定を行うためのルール構文をサポートしています。以前のWirePlumber ALSA設定の抜粋では、ノードまたはデバイスのプロパティを変更するためにmatchesupdate-propsを使用していることが示されていました。例えば、node.name = \"~alsa_output.*\"(パターンを使用)に一致させ、session.suspend-timeout-seconds = 5を設定して、非アクティブなデバイスを5秒後に自動的にサスペンドすることができます。これは、新しいコードを書かずに、設定エントリだけで動作を調整できるため、強力な機能です。

実際の使用例:特定のアプリケーションのオーディオを常に特定のデバイスに出力したいとします(例えば、音楽プレーヤーの出力を常にUSBヘッドセットではなくHDMIにしたい場合など)。WirePlumberの設定にカスタムルールを記述できます:

wireplumber.policies = [
  {
    matches = [
      { application.name = "MyMusicApp" } // アプリケーション名で照合
    ]
    actions = {
      update-props = {
        target.node = "alsa_output.pci-0000_00_1f.3.hdmi-stereo" // ターゲットノードをHDMI出力に設定
      }
    }
  }
]

これは概念的なものですが、アイデアを示しています:アプリケーション名で照合し、プロパティ target.node(または新しいバージョンでは target.object)をHDMI出力の名前に設定します。先に見た linking.follow-default-target オプションが有効になっている場合(デフォルトで有効です)、WirePlumberはストリームのターゲットのメタデータを監視し、それに応じて移動します。したがって、target.node メタデータを設定することで、そのストリームを指定されたデバイスに移動させることができます。PulseAudioでは、これは default.pa 設定または pactl move-sink-input を介して行われていました。PipeWire/WirePlumberでは、メタデータとそれに対応するポリシーを使用して行うことができます。

WirePlumberはエンドポイントAPIも扱います:エンドポイントの概念を表面化し、そのより高いレベルでのルーティングを可能にします。wpctlのようなツールはエンドポイントを表示します(例えば、「内蔵スピーカー」対「USBヘッドセット」など)。内部的には、これらはPipeWireノードにマッピングされますが、WirePlumberは抽象化を維持しているため、例えば、相互に排他的なエンドポイントを1つにグループ化できます(スピーカー対ヘッドフォンジャックがターゲット選択を持つ1つのエンドポイントにグループ化されるなど)。先に見たデフォルトノードスクリプトは、そのようなロジックを使用しています – 特定のカテゴリのすべてのエンドポイントをスキャンしてデフォルトを選択します。

ソースの観点から、WirePlumberの拡張は次のように行うことができます:

  • カスタムLuaスクリプトを作成し、適切なディレクトリに配置します(そして設定ファイルにリストします)。例えば、電話中にミュートする特別なポリシーを実装するスクリプトを作成できます:media.role=Communication を持つアプリがアクティブになったことを検出し、音楽ストリームの音量を下げます。このスクリプトはAPIを使用してストリームの状態変化をリッスンし、音量を調整できます。
  • または、説明したように、より単純な照合/アクションのために設定ルールを追加します。

公式ドキュメントでは、カスタムスクリプトとフックが可能であることが言及されており、それらを配置する場所も示されています。また、既存のスクリプトも文書化されているため、ユーザーはそれらを模倣したり拡張したりできます。

注目すべき点:WirePlumber 0.5以降、一部の設定はスクリプトからコンパイル済みモジュールに移行しました(パフォーマンスと保守性のため)。例えば、Bluetoothポリシーは現在、主にC言語で記述されている可能性があります。しかし、Luaは依然としてよりポリシー的なロジックに使用されています。Collabora(メンテナー)は、スクリプトを介して物事を調整する機能を維持したいと述べています。

実世界の例:カスタムルーティングポリシー

実世界のシナリオを説明するために、スタジオセットアップで、すべてのシステムサウンド(通知音などのmedia.role=Eventとして分類される可能性のあるもの)を「システムスピーカー」出力に送信し、DAW(デジタルオーディオワークステーション)の出力(特定のアプリ名またはロールを持つ可能性があります)を外部モニター(別の出力デバイス)に送信したいとします。WirePlumberを使用すると、毎回手動でパッチを適用することなくこれを実現できます:

  • DAWのPipeWireストリームにプロパティをタグ付けします(アプリの起動時にカスタムプロパティを設定するために環境変数 PIPEWIRE_PROPS を使用できます。例:アプリ起動時に PIPEWIRE_PROPS=\"my.role=DAW\")。
  • WirePlumberのポリシー設定に以下を追加します:
rules = [
  {
    matches = [
      { my.role = "DAW" } // my.roleがDAWと一致する場合
    ]
    actions = {
      update-props = {
        target.object = "alsa_output.usb-PreSonus_Monitor" // ターゲットをPreSonusモニターに設定
      }
    }
  },
  {
    matches = [
      { media.role = "Event" } // media.roleがEventと一致する場合
    ]
    actions = {
      update-props = {
        target.object = "alsa_output.pci-0000_...built_in_speakers" // ターゲットを内蔵スピーカーに設定
      }
    }
  }
]

これで、my.role=DAW を持つノードが表示されると、WirePlumberはそのターゲットをPreSonusモニターに設定します。media.role=Event を持つストリームが表示されると、それを内蔵スピーカーにターゲットします。linking.follow-default-target はデフォルトでオンになっているため、アプリがデフォルトをターゲットしようとしても、私たちがデフォルトをオーバーライドした場合、ポリシーはそれを適切に処理することもできます。

この種のカスタマイズは、WirePlumberが複雑なワークフローに合わせて調整できることを示しており、これは特にプロフェッショナルまたは組み込みのコンテキストで役立ちます。

別の例:組み込みLinux(例えば車のIVIシステム)では、「ナビゲーション音声が再生中の場合、音楽の音量を50%下げる」といったカスタム動作をスクリプト化することが考えられます。これは、「音楽」と「ナビゲーション」用のエンドポイントを用意し、ナビゲーションエンドポイントのストリームがアクティブかどうかを監視するスクリプトを作成し、音楽エンドポイントのノードで音量変更を適用することで実現できます。実際、エンドポイントの概念はそのようなユースケースのために作られており、1つのエンドポイント上の複数のストリームが、緊急警報対音声プロンプトのように異なる優先度を持つことを可能にします。これを実装するには、エンドポイントストリームの開始/停止イベントにフックすることが重要です。

ソースレベルの洞察という点では、WirePlumberのコードはfreedesktopのGitLabにあり、C APIも提供しています。したがって、Luaが好みでない場合は、小さなCモジュールを作成して何らかのポリシーを実装することも可能です(おそらくパフォーマンスが重要なタスクや他のCコードとの統合のため)。

WirePlumberの興味深い点の1つは、PipeWireのメタデータオブジェクトをどのように活用しているかです。PipeWireにはメタデータインターフェースがあり、任意のキーと値のペアをグローバルまたはオブジェクトごとに保存できます。WirePlumberはこれを使用して、デフォルトノード(メタデータオブジェクトに default.audio.sink = <node id> のようなキーを書き込みます)やストリームターゲットなどの情報を保存します。このメタデータはクライアントからもアクセス可能であり、これが wpctl がデフォルトを表示したり変更したりできる理由です(wpctl set-default を実行すると、実際にはそのメタデータキーが更新されます)。セッションマネージャーはメタデータの変更をリッスンして動作します。これは巧妙な分離です:「デフォルトを設定する」というAPI呼び出しの代わりに、単にメタデータに書き込み、ポリシーが残りを処理します。このアプローチは、将来の研究で、より複雑なポリシーが同様に宣言的になる可能性を示唆しています。

要約すると、WirePlumberはPipeWireシステムのカスタマイズ可能な接着剤です。その設計は、ユーザーがコアデーモンをハッキングすることなくポリシーレイヤーを適応させることを奨励しています。典型的なユーザーにとっては、標準のポリシーにより、PipeWireはPulseAudioのように「ただ動作」します(サウンドの自動接続、音量の記憶など)。上級ユーザーにとっては、可能性が広がります:OS内にプログラム可能なオーディオ/ビデオルーターを効果的に持つことになります。

6. パフォーマンスとデバッグ技術

PipeWireのようなOSレベルのマルチメディアシステムを開発・保守するには、パフォーマンスチューニングとデバッグのための堅実なツールと技術が必要です。このセクションでは、PipeWireが提供(またはサポート)する監視およびデバッグ用のツールと、レイテンシの問題、アンダーラン(音切れ)、誤ったストリームルーティングなどの一般的な問題を診断するアプローチについて見ていきます。また、ゼロコピーバッファ処理をどのように検証または追跡できるか、どのようなプロファイリング手法を適用できるか(例:perfbpftraceの使用)についても触れます。

監視ツール:pw-top、pw-cli、その他

PipeWireには、コマンドラインツールのスイートが付属しています(多くの場合、pipewire-toolsパッケージと共にインストールされます)。これらは、PipeWireデーモンのライブ状態を検査するために非常に貴重です。

  • pw-cli:以前、オブジェクトを作成するために使用しましたが、これはPipeWireと対話するための汎用的な対話型ツールです。オブジェクトを一覧表示したり(pw-cli ls)、詳細を取得したり(pw-cli info <object-id>)、さらにはオブジェクトのメソッドを呼び出すこともできます(create-linkset-paramなど)。デバッグには、pw-cli infoが非常に役立ちます。ノードまたはデバイスのすべてのプロパティをダンプします。例えば、アプリケーションが音を再生しない場合、ストリームノードでpw-cli info <node-id>を実行して、シンクにリンクされているか、フォーマットは何かなどを確認できます。または、pw-cli dump Nodeを使用してすべてのノードを表示することもできます。この生の情報は、PulseAudioのpactl listに似ていますが、多くの場合より詳細です(ID、状態、プロパティが表示されます)。

  • pw-top:これはPipeWireのグラフ用のtopと考えてください。pw-topを実行すると、ノードとそのパフォーマンスのリアルタイムビューが表示されます。通常、各ノードの名前、現在のクアンタム(バッファサイズ)、レート、CPU使用率、XRun(アンダーラン)が表示されます。これは、どのノードが問題を引き起こしている可能性があるかを特定するのに非常に役立ちます。例えば、pw-topで特定のクライアントノードのCPU使用率が高いか、XRunが多い場合、そのクライアントがオーディオグリッチの原因である可能性が高いことがわかります。pw-topは、JACKのjack_topやPulseAudioにはそのような内部モニターがないことと対照的です。これはPipeWire独自のデバッグ追加機能です。

  • pw-dump:PipeWireの全体の状態をJSON形式で出力します。これをファイルにリダイレクトして、グラフのスナップショットを取得できます(状態の「コアダンプ」に似ています)。開発者はこれをバグレポートに使用します。状態を比較したり、どのノード、リンクなどが存在するかを正確に確認したりできます。リアルタイムではありませんが、包括的なスナップショットです。

  • pw-dot:PipeWireグラフのGraphviz「dot」グラフを生成します。ノードがどのように接続されているかの視覚的な図が必要な場合(複雑なプロオーディオ設定や、何が起こっているかを理解するのに最適です)、pw-dot.dotファイルを生成し、それを画像に変換できます。HelvumやQPwGraphのようなツールは、これの対話型GUIバージョンを提供しますが、pw-dotはドキュメントや分析用の図をすばやく取得する方法です。

  • pw-mon:PipeWireバス上のイベント(オブジェクトの作成、削除、プロパティの変更)を監視します。これはudev monitorに似ていますが、PipeWireオブジェクト用です。pw-monを実行すると、ノードが表示されたり、リンクが作成されたりするたびにログが出力されます。これは動的な動作のデバッグに役立ちます。例えば、デバイスを接続しても何も起こらない場合、pw-monを実行して、ALSAモニターが少なくともイベントを発行したかどうかを確認します。そうでない場合は、モニターが無効になっているか、誤動作している可能性があります。

  • wpctl:上記ほど低レベルではありませんが、WirePlumberのwpctlも開発者にとって役立ちます。wpctl statusはすべてのエンドポイントとデフォルトを一覧表示します。また、音量やその他の高レベルの情報も一覧表示します。オーディオが期待どおりの場所に送信されない場合、wpctl statusでデフォルトのターゲットが思ったものと異なることが明らかになる場合があります。wpctl inspect <id>は、オブジェクトのWirePlumberビューを表示できます(多くの場合pw-cli infoと重複しますが、セッションレベルでフィルタリングされます)。

  • PulseAudioおよびJACKツール:興味深いことに、PipeWireは互換性を提供するため、ある程度pactlpamixerを使用することもできます(これらはPipeWire Pulseサーバーと対話します)。例えば、pactl list sinksは、PipeWireシンクをPulseAudioシンクであるかのように一覧表示します。これらのツールに慣れている場合は便利です。同様に、pw-jack qjackctlを使用すると、JACKのパッチベイGUIを使用してPipeWire接続を操作できます(内部的には、qjackctlはJACKと通信していると考えていますが、pw-jackはそれをPipeWireのJACK APIにリダイレクトします)。

パフォーマンス分析について:

  • ツールにはpw-profilerが言及されており、これはデータ受け渡しのパフォーマンスメトリクス(バッファフィルレベルなど)を収集・表示する可能性があります。エンドユーザーが一般的に使用するものではありませんが、開発者は詳細なタイミングを確認するために使用するかもしれません。

  • 標準的なLinuxツール:**perf**は、例えば高CPUを引き起こすバグが疑われる場合にPipeWireをプロファイリングするために使用できます。PipeWireはマルチスレッドであるため、perf topを使用して特定の関数がホットになっていないか確認できます。perf topが、例えばalsa_poll_descriptors_reventsで多くの時間を費やしていることを示している場合、ALSAが頻繁すぎるウェイクを引き起こしている可能性があります。あるいは、何らかのspa_acquire_bufferで問題が発生している場合、バッファ処理のオーバーヘッドが問題である可能性があります。

  • strace は、PipeWireがシステムコールでブロックされているか、何かで無限ループに陥っている場合にデバッグするのに役立ちます。例えば、strace -p $(pidof pipewire)をアタッチし、システムコールを確認することで、特定のpollでスタックしているか、デバイスの読み取りでエラーをスローしているかどうかが明らかになる場合があります。

  • bpftrace/eBPF:eBPFを使用すると、より高度なことができます。例えば、スケジューリングのレイテンシをトレースできます。bpftraceスクリプトは、オーディオIRQハンドラとPipeWireスレッドのウェイクにフックして、ハードウェア割り込みとユーザースペース処理の間のレイテンシを測定できます。または、eventfdシグナルが送信される頻度を追跡することもできます。これらは高度なテクニックであり、通常、カーネルまたはパフォーマンスエンジニアによって使用されます。しかし、プロンプトで明示的にperf, bpftraceが言及されているため、これらの手法に関心があることが示唆されます。

例えば、perf schedperf latencyを使用して、PipeWireスレッドがデッドラインを逃したことがあるかどうか(例えば、スレッドが時間内にスケジュールされなかった回数に対応するXRunの数など)を確認できます。

レイテンシとアンダーランの診断

  • オーディオがパチパチ鳴ったり、定期的に途切れたりする場合、pw-topが最初の確認場所となるはずです。XRun(アンダーラン/オーバーランが発生すると増加します)が表示されます。出力デバイスまたはストリームのXRunカウンターが増加している場合は、問題があります。また、pw-topはDSP負荷(JACKのDSP負荷の概念に似ています)も表示します。DSP%が高いということは、処理が各サイクルの時間の大部分を占めていることを意味し、アンダーランのリスクがあります。
  • アンダーランには複数の原因が考えられます:クアンタム(バッファサイズ)が小さすぎる、CPUが不足している(またはCPUスロットリング)、スレッドの優先度の問題、またはバグのあるデバイス/ドライバー。バッファサイズを増やしてみることができます:PipeWireではdefault.clock.quantumを調整したり、PulseAudioクライアントの場合はpulse.min.quantumなどを調整したりできます。デフォルトの1024/48000(21ms)が重いシステム負荷に対して低すぎる場合は、レイテンシを犠牲にして2048(42ms)に上げると役立つ場合があります。ArchWikiの抜粋では、Discordの問題を修正するために最小クアンタムを700(フレーム)以上に上げることを提案していました。これは48kHzで約14.5msです。
  • 電源管理も考慮してください:ラップトップでは、CPUがオーディオのために時間内にランプアップしない場合、CPU周波数スケーリングがドロップアウトを引き起こす可能性があります。tlpのようなツールやガバナーがこれに影響を与える可能性があります。これはよりシステムレベルの問題ですが、レイテンシの問題に直面している場合は、CPUが超省電力モードになっていないことを確認する必要があるかもしれません。

リンク切れまたはルーティングの問題の診断

  • ストリームが再生されているのに何も聞こえない場合があります。間違ったシンクにルーティングされているか、まったくルーティングされていない可能性があります。pw-cli info <stream-id>を使用すると、次のようなものが表示されます:

    audio.stream { ... }
      target.node = "98"   # 何らかのノードID
    

    target.nodeが実際の出力デバイスIDではないIDに設定されている場合、問題である可能性があります(古い設定や変更されたデフォルトなど)。pw-cli set-param <stream-id> Props \"target.object\" = <new node id>で変更するか、より簡単にwpctl move <stream-id> <device-id>を使用できます。

  • または、リンクが確立されていないことを示す場合もあります。pw-cli ls Nodeでノードが表示されても、pw-cli ls Linkでそのノードのポートを含むリンクが表示されない場合、それは孤立しており、セッションマネージャーの問題である可能性が高いです。デバッグのコツの1つは、以前行ったようにpw-cli create-linkで手動でリンクしてみることです。これで解決する場合、セッションマネージャーが自動的に行うのに失敗したことになります。

  • ビデオストリームの場合:画面共有が機能していない場合、pw-cli ls Nodeでスクリーンキャスト用のノードが表示されるはずです。表示されない場合は、パーミッションのためにポータルが作成しなかった可能性があります。PipeWireを詳細ログ付きで実行すると(例:ターミナルでPIPEWIRE_DEBUG=4 pipewire。ただし、通常はsystemdによって管理されるため、環境変数を設定してサービスを再起動するか、journalctl --user -u pipewireでログを確認します)、手がかりが得られる場合があります。Flatpakアプリがブロックされた場合、ログに「permission denied」と表示されることがあります。

バッファのゼロコピーとメモリデバッグ

  • ゼロコピーが行われていない疑いがある場合(例えば、プロファイルでmemcpyのCPU使用率が高い場合)、PipeWireがコピーにフォールバックしたかどうかを確認したいと思うかもしれません。例えば、アプリケーションとサーバーがメモリを共有できない場合(コンテナの名前空間が異なるか、特殊なプラットフォームなど)、PipeWireはコピーを伴う代替方法を使用する可能性があります。しかし、通常のLinuxでは、可能な場合は常にmemfd共有メモリを使用します。
  • lsof -p $(pidof pipewire)のようなツールを使用して、PipeWireで開いているmemfdファイルを確認できます。/memfd:pulse-shm-XXXX/memfd:pipewire-XXXXのようなエントリが表示されます。これらは共有メモリセグメントを示しています。それらが存在する場合、データはおそらくそれらを介して渡されます。
  • perfは、pipewireまたはクライアントでmemcpyが時間を消費しているかどうかを示すことができます。もしそうなら、何らかの最適でないことが起こっています(フォーマット変換や最適化なしのリサンプリングなど)。
  • spa-acp(ALSA Card Profile)などは、予期しない場合にオーバーヘッドを追加する可能性のあるソフトウェアミキシングを引き起こすことがあります。プロオーディオの場合、ソフトウェア変換を無効にすることがよくあります(リサンプリングなどがないことを保証するためにnode.latencynode.lock-quantumを設定します)。特定のフィルターの有無でCPUを監視することで、ゼロコピーパスが維持されているかどうかを特定できます。

bpftraceの使用:カーネルレベルでアンダーランイベントをトレースしたいとします。ALSAドライバーが特定のALSA xrunイベントを発行するか、PipeWireユーザースペースがそれをログに記録する可能性があります。snd_pcm_period_elapsedカーネル関数(ピリオドが完了したときに呼び出されます)にeBPFをアタッチし、ユーザースペースでPipeWireのprocess()が呼び出されるまでの時間を測定できます。しかし、それにはユーザースペースの高度なトレースが必要です。あるいは、bpftraceを使用してPipeWireプロセスにフックし、特定の関数呼び出しを監視します。例:

usdt::pipewire:default:pw_stream_xrun
{
    printf("Xrun in stream %d at %d ms\n", arg0, nsecs/1000000);
}

PipeWireにxrun用のUSDT(DTraceスタイル)マーカーがある場合(あるかどうかは不明)、それらをキャッチできます。ない場合は、ユーザースペースで関数名でフックするには、xrunでトリガーされる既知の関数でuprobeを使用する必要があるかもしれません。

設定の問題のデバッグ

  • PipeWireが誤ったモジュールをロードすることがあります。設定ファイル(/etc/pipewire/pipewire.confおよびpipewire-pulse.conf)は、どのモジュールをロードするかを制御します。例えば、libpipewire-module-rtがロードされていなかった場合、RTスケジューリングは得られません。ロードされたモジュールはpw-cli dump Moduleで確認できます。module-rt、module-protocol-native、module-alsa-cardなどが一覧表示されます。期待されるものが欠落している場合、設定がそれをスキップしたか、ロードに失敗した可能性があります(ログを確認してください)。
  • また、pw-cli dump Factoryは利用可能なすべてのファクトリを表示します。特定のファクトリ(MIDI用のalsa.seqなど)が存在しない場合、プラグインが欠落している可能性があります。

バッファメトリクス

  • 各ノードにはlatencydelayのようなパラメータがあります。pw-cli info <node>は現在のレイテンシ(特にシンクの場合)を表示する場合があります。pw-topもクアンタム * レートの観点からレイテンシを表示する場合があります。アプリケーションがより低いレイテンシを必要とする場合、ストリーム作成時にlatency.targetプロパティを設定することで要求できます。不一致の場合、セッションマネージャーまたはPipeWireが妥協する可能性があります。pw-metadataのようなツールは、設定されていればグローバルに望ましいデフォルトレイテンシを表示できます。
  • プロオーディオを扱う場合、クアンタムを固定することがあります(動的なクアンタム変更を避ける)。JACKクライアントでnode.lock-quantum = 1を設定すると、PipeWireが予期せずバッファサイズを変更しないようにします(JACKアプリは静的なバッファサイズを期待します)。奇妙な変動が発生したり、あるアプリがより大きなクアンタムを強制したりする場合(小さなバッファを処理できないため)、全体のレイテンシが悪化する可能性があります。pw-topは、クアンタムが変更された場合に強調表示します(一部のクライアントは、追従できない場合にそれを押し上げる可能性があります)。

perfを使用したより詳細な分析

  • 負荷中に短時間perf record -g -p <pipewire_pid>を実行し、その後perf reportを実行すると、CPU時間がどこで費やされているか、およびコールグラフが表示されます。これにより、例えば、予期しないリサンプラーが頻繁に呼び出されている(CPUを消費している)かどうか、または何らかのロックが競合している(発生した場合、futexで時間が表示されます)かどうかが明らかになる場合があります。
  • メモリやクラッシュをデバッグする場合、AddressSanitizerを使用してPipeWireをコンパイルするか、gdbを使用することがあります。パフォーマンスではありませんが、これらは標準的なデバッグアプローチです。

要約すると、PipeWireはイントロスペクションツールを提供するという点で非常に開発者に優しいです。ルーティングの問題について当て推量や限られたログに頼らなければならなかった以前のシステムからの新鮮な変化です。これで、グラフを確認し、リアルタイムでパフォーマンスを測定できます。これらをLinuxの豊富なプロファイリングおよびトレースツールと組み合わせることで、マルチメディアパフォーマンスチューニングに体系的にアプローチできます:測定、調整、検証。

シナリオ例:
ユーザーが「音楽を再生中にZoomを開くと音声が途切れる」と報告したとします。エンジニアとして、あなたは次のことを行います:

  1. pw-topを実行します – Zoomの起動時にXRunが急増するのを確認するかもしれません。Zoomのノードのクアンタムが異なることに気づきます(他のすべてが1024フレームであるのに対し、おそらく256フレーム)。
  2. その異なるクアンタムにより、グラフ全体が256に切り替わる可能性があります(PipeWireは、1つのストリームが必要とする場合、ロックされていない限り、動的に小さく調整できます)。システムが256を処理できない場合、アンダーランが発生します。したがって、解決策は、Zoom/コミュニケーションロールがより大きなレイテンシを使用するように設定するか、他のものが低下しないようにロックすることかもしれません。あるいは、Zoomのエコーキャンセルモジュール(WebRTC)を追加するときのCPU使用率が高く、DSP負荷が約99%になっているだけかもしれません。これはpw-topで確認できます。
  3. 次にperfを使用してCPUがどこで消費されているかを確認します – おそらくSpeex DSP(エコーキャンセルに使用)で多く消費されています。
  4. おそらく、負荷を軽減するために別のエコーキャンセルを使用するか無効にするか、またはエコーキャンセルが別のスレッドで実行されるようにすることを決定します。

このようなエンドツーエンドのデバッグにより、PipeWireスタックの保守は、しばしばブラックボックスのように感じられた以前のオーディオスタックよりもはるかに簡単になります。これはOSエンジニアのアプローチです:計測、測定、調整。

7. 実環境での統合事例

PipeWireのLinuxディストリビューションへの導入と様々なユースケースへの適用は急速に進んでいます。PipeWireが実際にどのように使用され、特別なチューニングや設定がどのように行われているかを示す、いくつかの著名な統合シナリオについて説明します:

主要ディストリビューション(Ubuntu、Fedora)におけるPulseAudioとJACKの置き換え

Fedoraと(最近では)Ubuntuの両方が、PipeWireをデフォルトのサウンドサーバーとして採用しています。Fedoraは早期に移行しました:Fedora 34(2021年)では、PipeWireがオーディオを標準で管理し、ほとんどのユーザーにとってPulseAudioとJACK(PipeWireの互換レイヤーを使用)を置き換えました。Ubuntuは22.10/23.04頃からPipeWireをオーディオのデフォルトとして有効にし始めました(Ubuntu 22.04 LTSでは利用可能でしたが、オーディオのデフォルトではなく、ビデオのみでした)。他のディストリビューション(Debian testing、Arch、openSUSE)も切り替えるか、簡単なオプトインを提供しました。

移行には通常、以下が含まれていました:

  • PipeWireとWirePlumberのインストール。
  • PulseAudioデーモンの無効化または削除。
  • pipewire-pulseの実行(これは別のデーモンであるか、PulseAudioプロトコルサポート付きで起動されるPipeWireの特別なインスタンスです)。
  • 実際にはPipeWireの実装を指すJACKライブラリの提供(これにより、JACKアプリは起動時にPipeWireを使用します)。

Fedoraの場合、メンテナーがすべてを統合したため、これはスムーズでした。Ubuntuの場合、Bluetoothなどとの互換性を確保する必要がありました(PulseAudioのBluetoothスタックが十分に確立されていたため、PipeWireのBluetoothスタックが十分に成熟するまで、Ubuntuの切り替えは若干遅れました)。

エンジニアリングの観点からすると、大きな出来事はPipeWireがオーディオバックエンドを統一したことでした。そのため、デスクトップ用のPulseAudioとプロオーディオ用のJACKを使い分ける代わりに、1つのサービスで済みます。これにより、次のようなことが大幅に簡素化されます:

  • pactljack_connectを別々に使用する必要がなくなり、1つのインターフェースで両方を行うことができます。
  • PulseAudio用のアプリケーション(音量制御UIなど)は、pipewire-pulseを介して引き続き機能します。
  • JACKアプリケーションは、pw-jackで起動するか、PipeWireにリダイレクトする提供されたjackライブラリを介して機能します。ユーザーエクスペリエンスはほぼ同じです – CarlaやQJackCtlを開いてパッチベイを確認できますが、実際にはその下でPipeWireが動作しています。Phoronixの記事によると、2024年までにPipeWireはLinuxデスクトップ全体で広く見られ、PulseAudioとJACKの役割を置き換えており、この統合の成功を確認しています。

ディストリビューションにとって、課題の1つは設定でした:デフォルトでは、PipeWireはプロオーディオとコンシューマーオーディオの両方に対応しようとするため、さまざまなクライアントに対応するために動的なクアンタムとサンプルレートの切り替えを行いました。いくつかの初期の問題がありました(例えば、一部のプロオーディオユーザーは、アプリが突然非常に低いレイテンシを要求し、システムがそれを処理できない場合に、動的クアンタムメカニズムが不安定性を引き起こす可能性があることを見つけました)。ディストリビューションは、多くの場合、賢明な制限付きの設定を提供します:例えば、明示的に必要な場合を除き、バッファサイズが低くなりすぎないようにdefault.min.quantumを十分に高く設定します。

もう1つの問題はモジュールのパリティでした:PulseAudioには多くのモジュールがありました(RAOP/AirPlayストリーミング用、ループバック用、チャンネル結合用など)。PipeWireは多くを実装しましたが、一部の機能は当初遅れていました。時間が経つにつれて、不足していた部分(module-null-sinkのモニターの良い代替品など)が追加されました。PipeWire ~0.3.50以降、特に1.0までには、かなり機能が充実しています。

Ubuntuでは、注目すべき点は、ビデオの場合、21.04以降、Waylandでの画面共有にxdg-desktop-portalを介して既にPipeWireを使用していたことです。そのため、PulseAudioがまだオーディオを担当している間、PipeWireは1つのこと(ビデオ)に使用されていました。これはコンパートメント化を示しています:PipeWireを1つのドメイン専用にデプロイできました。最終的にそれらを組み合わせることで、スタックが簡素化されました。

システム管理者の観点から:Ubuntu 22.04でPipeWireオーディオを有効にするには、pipewire-audio-client-librarieswireplumberをインストールし、次にPulseAudioのsystemdサービスを無効にしてPipeWireのサービスを有効にする必要がありました。Ubuntu Wikiなどがガイドを提供しました。現在ではデフォルトになりつつあり、新規ユーザーにとってはシームレスになっています。

PulseAudioとJACKを置き換える利点:

  • 必要な人にとっては、標準でより低いレイテンシ(サーバーを切り替えることなくJACKレベルのレイテンシ)。
  • 維持するための統一されたインフラストラクチャ(障害点が少ない)。
  • より良いBluetoothサポート:PipeWireのBluetoothモジュールは最新のコーデック(LDAC、aptXなど)をサポートしており、PulseAudioは遅れてまたはパッチを介してのみ取得し、oFonoまたはhsphfpd統合を介したHFPプロファイルの処理が向上しています。2025年までに、PipeWireのBluetoothオーディオは非常に堅牢になり、より多くのコーデックと機能をサポートしています(将来の項目の1つは、Bluetooth LE Audioが登場していたため、改善されたBLEオーディオサポートであり、PipeWireはそれを統合する準備ができています)。

プロオーディオワークステーションのチューニング(PipeWire + JACKユースケース)

プロのオーディオユーザー(ミュージシャン、サウンドエンジニア)にとって、JACKはその超低レイテンシとアプリ間でオーディオ/MIDIをルーティングする機能のため、最適なツールでした。PipeWireの約束は、JACKのようなパフォーマンスとルーティングを、より多くの汎用性とともに提供することです。それは実現したのでしょうか?大部分はイエスですが、いくつかのチューニングが必要です。

レイテンシとXRun: プロオーディオでは、48kHzで128フレームのバッファ、あるいは64や32(3ms未満のレイテンシ)で実行することがあります。ハードウェアとCPUが許せば、PipeWireはこれを達成できます。しかし、デフォルトでは、ディストリビューションはそのような積極的な設定を行わないかもしれません。プロユーザーは、一貫性のためにpipewire.confを編集してdefault.clock.quantum = 128およびdefault.clock.min-quantum = 128(それで固定するため)を設定するかもしれません。また、レート切り替えを望まない場合は、default.clock.rate = 48000を固定にしてください。システムが負荷状態にある場合に最大のクアンタムを定義するquantum-limitもあります(PipeWireは必要に応じてxrunを回避するためにクアンタムを拡大できます)。スタジオユーザーは、デフォルトと等しいquantum-limitを設定することで、その動的リサイズを無効にするかもしれません(そのため変更されません)。

  • また、アダプティブリサンプリングをオフにします:可能であればリサンプリングを回避するプロパティがあります(PipeWireは変換を避けるためにすべてをグラフのレートで実行しようとします)。これは通常そのままで問題ありません。

JACKクライアント: ほとんどのJACKアプリはpw-jackを介してシームレスに動作します。しかし、1つのヒント:環境変数PIPEWIRE_LATENCYを使用して、クライアントに特定のレイテンシを要求できます。例えば、PIPEWIRE_LATENCY=128/48000 pw-jack guitarixは、PipeWireにGuitarixに48kHzで128フレームのバッファを与えるように指示します。あるいは、pw-jackは現在-p(ピリオド)パラメータを許可するかもしれませんが、環境変数が機能します。これは、jackdに-p 128 -n 2を渡す方法と似ています。PipeWireがそれに対応できない場合(おそらく他のストリームがより高いバッファサイズで開いているため)、正確にそれに到達しないかもしれませんが、設定でクアンタムをロックしていれば、そうなります。

MIDI: JACKはMIDI(JACK MIDIまたはALSA MIDI)も処理しました。PipeWire 0.3.30以降では、ALSAシーケンサーMIDIサポートが統合されました。MIDIポート用にPipeWireにMidiノードを作成します。PipeWireのJACKライブラリを介したJACK MIDIクライアントも動作するはずです。これは、プロオーディオセッションでは、MIDIコントローラーとシンセを同様にルーティングできることを意味します。実際には、初期の頃、MIDIのタイミングジッターに関する軽微な問題を見つけた人もいましたが、それは解決されつつあります。

パフォーマンス: PipeWireとJACKのパフォーマンスオーバーヘッドは、ほとんどのテストで最小限です。一部のユーザーはABテストを行い、CPU使用率がわずかに高いか同程度であることを見つけました。JACKはクライアントにとってインプロセスであったのに対し、PipeWireはアウトオブプロセスであるため、追加のコンテキストスイッチがあります。しかし、PipeWireは共有メモリを効果的に使用できるため、オーディオデータにとっては問題ありません。追加のオーバーヘッドは、主に柔軟なグラフとフォーマット変換にあります。変換を回避すれば(例えば、すべてのストリームが同じサンプルレートを共有するようにする、これはプロユーザーがとにかくよく行うことです)、本質的にJACKと同じくらい高速です。

バッファ管理の違い: JACKには「ピリオド」の概念があり、1つが変更されるとすべてのクライアントが適応する必要がありました(JACK1ではこれは静的でしたが、JACK2では変動する可能性がありましたが、通常は静的でした)。PipeWireは、デフォルトでは、必要に応じてクライアントごとに調整しようとします(リサンプリングまたは動的クアンタムを介して)。プロのワークフローでは、動的クアンタムをオフにすると、固定ピリオドが保証され、これが重要です(ミュージシャンは決定論的なレイテンシを望んでいます)。したがって、はい、プロの設定では、PipeWireをJACKの固定ピリオドモードのように動作するように設定します。

ツール: 多くのプロオーディオユーザーは、CarlaやCatia(JACKパッチベイGUI)などのツールに依存しています。Carlaは、それがPipeWireであることを知らずにPipeWireのJACKに接続できます。また、ネイティブのパッチベイ(Helvum、Qpwgraph)は代替手段を提供します – PulseAudioストリームとJACKクライアントを1つのビューで混在させることができ、これは非常に強力です。例えば、Firefoxのオーディオ(PulseAudioクライアント)をArdour DAW入力に簡単にルーティングできます – PulseとJACKが分離していたため、以前は簡単ではありませんでした(特別なブリッジが必要でした)。

プロオーディオとミキシング: JACKは伝統的にミキシングやリサンプリングを行いません – ユーザーは物事が整合するようにするか、追加のツールを使用する必要がありました。PipeWireは、必要に応じて(Pulseが行ったように)便宜のためにミキシングを行います。純粋なJACKシナリオでは、すべてを手動で管理することを好むかもしれません。PipeWireは、ミキシングモジュールを無効にして直接接続を行うだけで、同様に動作するように設定できます。しかし、それらをオンにしておく利点は、例えば、44.1kHzのシステムアラートサウンドを実行でき、メインプロジェクトが48kHzであっても再生されることです – PipeWireはその1つのサウンドをリサンプルします。これにより、「JACKは48kHzで実行されているため、異なるレートの他のアプリはオーディオを再生できない」という古い問題が解決されます。

本質的に、PipeWireはプロとコンシューマーの世界を融合しようとしています。適切に調整すれば、プロユーザーはJACKとほぼ同じパフォーマンスを得ることができ、さらにデスクトップオーディオを実行し続けることができます。これは、YouTubeビデオを見るためにJACKサーバーを停止しなければならなかった(または不格好なPulse->JACKブリッジを使用しなければならなかった)人々にとっては、かなりのゲームチェンジャーです。

: 小さなホームスタジオでは、録音用に5msのレイテンシでUSBオーディオインターフェースを使用してPipeWireを実行するかもしれません。ユーザーは、Ardour(DAW)、ソフトシンセ、およびブラウザのすべてを出力させることができます。Ardourとシンセは、パッチベイでインターフェース出力に接続できるノードとして表示されます。ブラウザの出力はデフォルトでシステム出力に送られますが、必要に応じて録音するためにArdourにルーティングすることもできます。これらすべてが、音声チャット(エコーキャンセル付き)も進行中である可能性があります。これは、Pulse + JACK + ブリッジが分離していると複雑でしたが、PipeWireでは統一されています。

サンドボックス化されたアプリケーション:Flatpakとポータルのユースケース(画面共有など)

前述のように、PipeWireはFlatpak/Waylandサンドボックスモデルで重要な役割を果たしており、特に画面キャプチャとリモートデスクトップ、そしてカメラとマイクへのアクセスにおいて重要です。それが実際にどのように機能するかを説明します:

  • Flatpakアプリがマイクを要求: アプリは、通常、PulseAudio APIを介して、または更新されていればPipeWire独自のAPIを介して録音しようとします。Flatpakの権限(.flatpakマニフェスト内)は、アプリがオーディオキャプチャにアクセスできるかどうかを制御します。そうでない場合、アプリが試行すると、PipeWireポータルモジュールが起動します。libpipewire-module-portalは、直接的な権限のないアプリが録音ストリームを作成しようとすると、承認のためにポータルに転送されるようにします。ユーザーには「アプリがマイクを使用しようとしています、許可しますか[はい/いいえ]」と表示される場合があります。はいの場合、ポータルはマイク用のノードを設定し、そのクライアントにアクセスを許可します(内部的にはPipeWireの権限システムを介して)。

  • Wayland画面共有: Waylandでは、アプリは画面コンテンツを見ることができません。代わりに、コンポジター(GNOME ShellやKDE KWinなど)が「リモートデスクトップ」ポータルを実装します。画面共有をトリガーすると(例えばFirefoxやZoomで)、アプリはxdg-desktop-portalのD-Busインターフェースを呼び出します。ポータルは次に、コンポジターに画面のPipeWireストリームを作成するように要求します(基本的には「モニターXを解像度Y、フレームレートZでキャプチャしてください」と言います)。コンポジターは、画面からのビデオフレームを持つPipeWireノードを作成します(PipeWireのAPIを直接使用するか、libportalのようなライブラリまたはプラグインを介して)。IDまたは何らかの資格情報をポータルに渡し、許可されていれば要求元のアプリに渡します。アプリは次にPipeWireに接続し、そのノードからフレームの受信を開始します。実際には、そのノードはコンポジタープロセス内のビデオ生成ノードです。仮想カメラに似ています。PipeWireはフレームを効率的に転送します(コピーを避けるために可能であればDMA-BUFを使用する可能性が高いです、GPUバッファを共有できるため)。

素晴らしいのは、複数のアプリが異なるものを共有でき、PipeWireが多重化を処理することです。例えば、あるアプリが画面全体をキャプチャし、別のアプリがウィンドウのみをキャプチャする場合、コンポジターはそれぞれに個別のストリームを作成できます。

  • セキュリティ: 実際のピクセルデータは、承認されない限りコンポジターのサンドボックスから出ることはありません。ポータルがなければ、ノードにはアクセスできないためです。アプリが回避してPipeWireノードを直接リストしようとしても、PipeWireの権限モデルがそれらを非表示にするため、利用可能なものは見つかりません(Flatpakサンドボックスは、デフォルトではポータルを介して明示的に許可されたものを除き、どのPipeWireノードにもアクセスできません)。これは、PipeWireコアの権限チェックにフックするポータルモジュールによって強制されます。

  • Flatpakオーディオ出力: 実際には、デフォルトでは、Flatpakアプリはポータルなしでサウンドを出力できます(通常は機密ではないため、スピーカーへのオーディオ再生は許可されています)。そのため、pipewire-pulseまたはPulseAudioインターフェースに直接接続します。一部のサンドボックスコンテキストでは、必要に応じてオーディオ出力さえ制限する場合がありますが、一般的にはオープンです。制御されるのはキャプチャ(マイク、画面、カメラ)です。

  • シナリオ例: Wayland GNOME上のFlatpakとしてOBS(Open Broadcaster)を使用してストリーミングするとします。OBSは画面キャプチャを要求します。ポータルがポップアップし、共有する画面またはウィンドウを尋ねます。ユーザーが選択すると、ポータルはPipeWireストリームを設定します。OBSはそれをビデオソースとして受信します。OBSはデスクトップからのオーディオも必要とします。PipeWireでは、OBSはAPIを介してシンクのモニターストリームをキャプチャできます(PulseAudio APIにはシンク用のモニターソースがあり、PipeWireは設定でnode.monitor = trueの場合、ノードにモニターポートを提供することで同様のことを行います)。Flatpakはそれを許可するか、再びポータルを介してルーティングする場合があります(オーディオポータルは理論的にはオーディオキャプチャの許可を要求できますが、デスクトップでは自動的に許可されるか、セッションマネージャーポリシーを介して管理される場合があります)。OBSは次にミキシングとエンコードを行い、おそらくPipeWireを介しても出力します(ストリーミングアウトする場合、ローカルに出力する必要はないかもしれません)。

  • ビデオコンポジション: もう1つの興味深い統合は、サンドボックス内のカメラデバイスにPipeWireを使用することです。生の/dev/videoアクセスを与える代わりに、ポータルは同様にPipeWireを介してカメラフィードを提供できます。これにより、仮想カメラを接続でき、アプリはPipeWireカメラノードをカメラとして認識します。例えば、カメラフィードにグローバルに何らかのエフェクトを適用したい場合、理論的には、カメラの出力がアプリに到達する前に処理するPipeWireノードを挿入できます。

  • Waylandローカル使用: サンドボックス化以外でも、PipeWireはDiscordやZoomなどのアプリでWayland上の画面を共有する方法です。それらはすべてPipeWireグラブを実装する必要がありました。これらのアプリ開発者にとっては当初少し作業がありましたが、現在は標準化されています。Xorgでは、アプリがX11 APIを介して画面をグラブすることが許可されていましたが、これは安全ではありませんでした。現在では、OS(コンポジター + PipeWire + ポータル)がそれを制御します。

「Tech Festa 2025」の文脈では、ユーザーにとって非常に目に見える機能(画面共有は現在リモートワークでどこでも使われています)にとって、PipeWireがいかに不可欠になったかを認識することが期待されていると思われます。

ポータルの制限: 当初、いくつかのオーバーヘッドと品質の問題がありました(フレームレートが制限されたり、カーソルをキャプチャできなかったりするなど)。これらは改善されました。将来の方向性としては、これをさらにシームレスにすること、おそらくストリームを圧縮すること(現在は生のフレームか軽く圧縮されていると思います)が含まれるでしょう。

コンテナとVM: PipeWireは、仮想マシンにオーディオ/ビデオを転送する方法としても注目されています(例えば、VMでPipeWireを実行し、ホストのPipeWireに簡単にオーディオを送信するなど)。「組み込み向けWirePlumber」というプロジェクトがあり、その一部を参照しており、自動車分野でも使用されています(複数のサンドボックス化されたプロセスがオーディオ/ビデオルーティングを一元的に必要とする場合)。

8. 将来の課題と研究の方向性

2025年現在、PipeWireは多くのシステムでデフォルトとなる成熟度に達しています。しかし、技術は決して立ち止まりません。PipeWireとLinuxマルチメディア周辺には、進行中の開発と潜在的な研究対象となるいくつかの分野があります:

  • リアルタイムA/Vストリームの進化: VR/ARや空間オーディオなどの新しい技術により、リアルタイムストリームへの要求が高まっています。課題の1つは、オーディオとビデオを正確に同期させることです(リップシンク、マルチルームオーディオ同期など)。PipeWireのグラフモデルは、メディア間の同期(オーディオとビデオのタイムラインの整合性を確保する)のために拡張または調整される可能性があります。これには、明示的な同期機能が含まれる場合があります – 実際、PipeWire 1.2では特定のノードに対する明示的な同期サポートが導入されました。研究では、同期グループを自動的に管理する方法(例えば、BluetoothスピーカーとWi-FiディスプレイのA/V同期を維持するために、遅延ノードを挿入したりクロックを調整したりするなど)を探求することができます。また、ネットワークが改善するにつれて、ローカルストリームとネットワークストリームの境界線が曖昧になります – PulseAudioのネットワークストリーミングやJACKのNetJACKに似た、PipeWireにおけるネットワーク透過性が見られるかもしれません。高度なアイデアとしては、PTP(Precision Time Protocol)を使用して、ネットワーク経由でデバイス間でオーディオをマイクロ秒精度で同期させ、家全体のオーディオを実現することがあります – 研究プロジェクトでは、ネットワーク出力用のPTP同期PipeWireドライバーノードを統合することができます。

  • VulkanとGPUオフロード: 将来の作業項目の1つとして、Vulkanベースのビデオコンバーターと処理フィルターの追加が挙げられています。これは、PipeWire内でGPUシェーダーを活用して、ビデオスケーリング、色変換、さらにはエフェクトを行うことを意味します。これにより、画面共有とビデオ処理が大幅に高速化される可能性があります(例えば、4K画面をキャプチャし、ストリーミング用に1080pにダウンスケールし、すべてGPU上で行い、その後DMA-BUFをエンコーダーに渡すなど)。研究では、これらのパイプラインの最適化、あるいはGPUでのオーディオ処理(OpenCLまたはCUDA DSP?オーディオには通常CPUで十分ですが、空間オーディオミキシングはGPUの並列処理の恩恵を受けるかもしれません)を掘り下げることができます。

  • Flatpak/コンテナ統合: PipeWireのポータル統合は優れていますが、課題の1つはよりきめ細かい制御です。おそらく将来のPolicyManagerでは、アプリごとの音量と優先度制御がネイティブに許可されるようになるでしょう。あるいは、SELinux/AppArmorとの統合:プロセスからのストリームにラベルを付け、セキュリティポリシーを適用する(例えば、特定のアプリがオーディオ出力にアクセスするのを拒否するなど)。これはセキュリティ研究の分野に入ります。

  • カーネルAPIのライフサイクル: ALSAは長年にわたりサウンドのカーネルAPIでした。時折、新しいカーネルインターフェース(例えば、ALSAのユーザー空間APIの現代的な後継または改善)についての話があります。PipeWireはカーネルの変更に適応する必要があります。また、PipeWireのドライバーのような役割の出現により、PipeWireの一部の側面がさらに低い遅延や電力効率のためにカーネルに移行するのではないかと考えられます。それは推測の域を出ませんが、ユーザー空間はより柔軟性を提供します。しかし、研究では、カーネルが最小限のオーバーヘッドで基本的なオーディオグラフスケジューリングを提供し(例えば、安全性が重要なオーディオのためのカーネル内ミキサーなど)、PipeWireがそれを調整するハイブリッドモデルを探求する可能性があります。また、Linuxはio_uringのような機能を追加しています – PipeWireはepollよりも効率的に非同期I/Oにio_uringを使用できるでしょうか? メディアのディスクI/Oや特定の非同期タスクに対して可能性があります。

  • コミュニティと採用: 今後の課題の1つは、すべてのアプリケーション開発者がより現代的なAPIに移行することを保証することです。例えば、多くのプロオーディオアプリは依然としてJACK APIを使用しており、その背後にJACKデーモンがあると考えていますが、PipeWireがそれを提供しているため問題ありません。しかし、おそらく直接的なPipeWire APIの使用は、JACK APIでは公開できないより多くの機能(ビデオやエンドポイントなど)を提供する可能性があります。PipeWireネイティブAPIの採用を奨励し(そしてドキュメントや言語バインディングを改善する)ことは、コミュニティの努力となるでしょう。既にRustバインディングがあり、他の言語もあるかもしれません。これにより、新しい用途の研究が促進される可能性があります(例えば、何かに反応してオーディオを合成するPipeWireノードを簡単に作成するPythonスクリプト – 楽しい教育ツールや迅速なプロトタイピングツールになるかもしれません)。

  • 未解決の問題: PipeWireは安定していますが、常にバグやエッジケースが存在します。トラッカーにある未解決の問題には、HDMI出力のプロファイル切り替えが一部のケースで正しく行われない、特定のUSBデバイスでクワーク処理が必要になる、といったものがあります。コミュニティはこれらの問題に積極的に取り組んでいます。もう一つの広範な問題はアクセシビリティです。PipeWireがスクリーンリーダーとどのように連携するか(例えば、視覚障碍者のために、PulseAudioにはスクリーンリーダーが話す際に音量を下げるためのフックがありました)。PipeWireがそのようなユースケースをサポートすること、または必要な機能を実装することは、取り組むべき良い分野でしょう。

  • 潜在的な研究トピック:

    • ネットワーク経由の超低遅延: PipeWireをAVB (Audio-Video Bridging) やWebRTCのようなプロトコルと組み合わせて、最小限の遅延でピアにオーディオを送信する。PipeWireはローカルとリモートを統合したメディアハブになれるでしょうか? PTP経由で同期し、ネットワーク経由でリモートのPipeWireインスタンスをデバイスとして扱うモジュールで可能になるかもしれません。
    • オーディオパイプラインにおける機械学習: リアルタイムのノイズ抑制(RNNoiseなどのモジュールは既にありますが、ノイズキャンセルや音声分離のためのMLモデルをグラフに統合するなど)。これはパフォーマンス(リアルタイムでのML実行)に関わってきます。
    • エネルギー効率の良いオーディオ: モバイルやラップトップで、PipeWireは電力使用量を最適化できるでしょうか? 例えば、複数のアプリが無音または静かな音を再生している場合、それを検出してダウンサンプリングしたり処理を一時停止してCPUを節約したりできるでしょうか? アダプティブ品質やバッチウェイクアップスケジューリング(オーディオウェイクをまとめる)によってCPUのスリープ時間を増やす研究が考えられます。
    • 動的なサービス品質 (QoS): ユーザーがDAWを起動した場合、PipeWireが自動的にスレッドの優先度を上げたり「プロモード」に切り替えたりし、終了したらリソースを節約するために元に戻すといったことができるかもしれません。一部は既に存在しますが(必要ない場合にRTスケジューリングを使用しないロジックなど)、より動的なポリシーが検討される可能性があります。
    • 新しいハードウェアサポート: マルチエンドポイントUSBデバイスやネットワークスピーカー(Sonosなど)のような新しいオーディオハードウェアについてはどうでしょうか? PipeWireはそれらをデバイスとして統合できるかもしれません。実際、RAOP (AirPlay) やChromecastのシンクとしてのサポートは素晴らしいでしょう(PulseAudioにはRAOPシンクがありました。PipeWireも可能かもしれません – 研究/実装プロジェクトになるかもしれません)。
    • マルチOS PipeWire: PipeWireの設計がLinux以外(FreeBSDや、さらにはmacOS/Windowsのユニバーサルサウンドサーバーとして)に移植できるかどうかに関心が寄せられています。研究として、どれだけLinux固有か(epoll、signalfdは明らかにLinuxですが、抽象化できるかもしれません)を見るのは興味深いでしょう。これによりプラットフォーム間でオーディオ管理を統一できるかもしれませんが、各OSにはネイティブなソリューションがあるため、これは大変な作業です。

短期的には(FOSDEM 2024での発表やPipeWireの1.0リリースノートなどによると)、開発者はビデオの改善(より良いルート管理、新しいフィルターインフラストラクチャ)に焦点を当て、MIDIセッション管理などの欠けている部分を追加し(おそらくALSAシーケンサーに類似したものですが、PipeWireの文脈で)、古いシステムからの移行が完全に完了することを確実にします。

最後に、コミュニティへの関与:2025年までには、PipeWireのコミュニティにはRed Hatエンジニアだけでなく、Arch、SUSE、Collabora、および独立した開発者からも貢献者が含まれています(WirePlumberのCollabora主導の開発で見られるように)。一部を標準化するための議論があります(例:アプリケーション開発者向けにより良い高レベルAPIを定義し、生データやSPA C APIという少し低レベルなものを使用せずとも済むように)。より使いやすいAPIは、学生がバッファ交渉の複雑さを学ぶことなく音声アプリケーションを構築するなど、更なる創造的な利用促進につながる可能性があります。

9. 結論:Linux OS進化におけるPipeWireの重要性

PipeWireの登場は、より統一された現代的なオペレーティングシステムへと向かうLinuxの軌跡における極めて重要な進歩を示しています。systemdがシステムの初期化とサービス管理を再編成したのと同様に、PipeWireはOSがマルチメディアをどのように扱うかを再構想します。オーディオとビデオのストリームを、認可されたコンポーネントが一貫した方法で管理できる第一級のルーティング可能なオブジェクトとして扱います。これは、複数のサウンドサーバーとカスタムソリューションが共存していた過去の断片化されたアプローチからの脱却です。

OSエンジニアリングの観点から、PipeWireは優れたシステム設計の多くの原則を具現化しています。

  • プラグ可能なコンポーネントを介して、さまざまなポリシーとユースケース(デスクトップ、プロオーディオ、組み込み、モバイル)に対応できる柔軟性を備えた共通コア(デーモンとグラフ実行エンジン)を提供します。
  • モジュール性と関心の分離を重視しています。メカニズム(データ転送、スケジューリング)はポリシー(セッションマネージャースクリプト)から分離されており、それぞれが独立して進化または調整できます。
  • 既存のカーネル機能(epoll、memfd、cgroups)を創造的な方法で活用し、専門的なカーネルサポートを必要とせずに、ハードウェアに近いパフォーマンスを実現します。これは、適切に設計された場合にユーザー空間がいかに強力であるかを示しています。
  • コンテナ化されサンドボックス化されたアプリが主流となる今日のOS環境においてますます重要になっている、セキュリティとサンドボックス化の統合を改善します。
  • 将来を見据えたAPIと機能を提供しつつ、PulseAudio、JACK、ALSA APIとの後方互換性を維持し、アプリケーションとユーザーの移行を容易にします。

PipeWireの重要性は、社会的および開発的側面にも及びます。それは努力の重複を減らします。PulseAudio、JACK、GStreamerルーティングなどに別々のコミュニティが取り組む代わりに、この中央インフラストラクチャに努力を集中できます。従来のJACKユーザーやプロオーディオディストリビューションがPipeWireを採用し、また、それについて考える必要のないデスクトップユーザー(サウンドが「ただ機能する」ようになり、今ではビデオキャプチャも同様です)も既に目にしています。この集約により、より多くのリソースが1つの堅固なスタックの改善に集中できるようになり、すべての人に利益をもたらします。

さらに、同じフレームワークでビデオを処理することにより、PipeWireは、あらゆる種類のリアルタイムデータストリーム(オーディオ、ビデオ、おそらく他のセンサー)を統一されたグラフで管理できる、より一般的な未来を示唆しています。これは、OSカーネルが複数のデバイスタイプに対応する汎用ドライバーフレームワークを持つ方法に似ています。2025年には主にオーディオとビデオが見られますが、将来的には、VR用のIMUセンサーストリームのようなものが、ビデオと同期してPipeWire経由で複数のクライアントに配信されるかもしれません。これは突飛なアイデアかもしれませんが、概念的には手の届く範囲にあります。

プラットフォームとしてのLinuxにとって、PipeWireは長年のギャップを埋めます。他のOS(macOSのCoreAudio/AV、WindowsのWASAPI/MediaFoundation)と同等か、おそらくそれ以上の一貫性のあるマルチメディアサブシステムです。このような複雑なソフトウェアが、実行中のシステムに(多くの場合、単純なアップデートによって)シームレスに導入され、具体的な改善(ユーザーが気づき評価した、はるかに低いBluetoothオーディオ遅延など)を即座に提供できたことは、オープンソース開発の証です。また、特定のドメインにとってLinuxをより魅力的なものにします。例えば、JACKセットアップから離れることをためらっていたプロのオーディオユーザーは、主流のディストリビューションでより簡単な道を見つけました。あるいは、優れた画面共有サポートを必要としていたWaylandデスクトップユーザーは、PipeWireのおかげでそれを手に入れました。

結論として、PipeWireはLinux OSにおける主要な進化的ステップとして位置づけられます。現在のニーズを解決するだけでなく、メディア処理における将来のイノベーションの基礎を築きます。それは、思慮深いOSエンジニアリングが、厄介な領域(無数のレガシーシステムを持つマルチメディア)を取り上げ、エレガントで拡張可能なソリューションを作り上げることができる方法を例証しています。開発が続くにつれて、PipeWireはLinuxのマルチメディアストーリーの中心であり続け、新たな課題に適応し、Linuxの最先端で汎用性の高いオペレーティングシステムとしての評判を確固たるものにする経験(インターネット経由でのグリッチのない低遅延ジャムから職場での安全なコンテンツ共有まで)を可能にすることが期待できます。

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?