Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions source/_posts/pipewire-architecture-00-roadmap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
---
title: PipeWire 架构学习路线:从 Linux 声音栈到源码模块地图(系列导读)
date: 2026-10-23 13:30:00
tags: [PipeWire, Linux音频, 架构, 源码阅读, 教程]
categories: [教程]
---

很多人第一次接触 PipeWire,是在终端里看到一句 `Server Name: PulseAudio (on PipeWire 0.3.x)`。
于是问题马上变成:它到底是 PulseAudio 的替代品、ALSA 的上层、JACK 的竞品,还是一个通用的多媒体图框架?

这些说法都只对了一部分。PipeWire 同时承担了**用户态媒体服务器、图调度器、进程间传输层和兼容层后端**四个角色。
如果只从 `pactl` 或 `pw-play` 的使用角度切入,很容易把“会话策略”和“数据搬运”混在一起,后面读源码就会一直打结。

这个系列按源码结构拆成 10 篇,目标是让一个会 C、了解 Linux 基础概念的读者,能够建立完整的 PipeWire 心智模型,
并且知道遇到具体问题时该去看哪个目录、哪个对象、哪条协议消息。

---

## 一、先固定事实边界

本系列基于 [PipeWire/pipewire](https://github.com/PipeWire/pipewire) 的一个源码快照:

| 项目 | 值 |
| --- | --- |
| 提交 | `110e1a42c` |
| `git describe` | `1.6.0-1315-g110e1a42c` |
| `meson.build` 中的项目版本 | `1.7.0` |
| 顶层模块源文件 | 50 个 `src/modules/module-*.c` |
| SPA 插件目录 | 19 个 `spa/plugins/*` |

这不等于某个发行版安装的版本。Linux 桌面上的 PipeWire、WirePlumber、ALSA、PulseAudio 兼容层和内核驱动版本可能各不相同,
排障时第一步永远是确认**实际运行的二进制和配置**,而不是只看源码树。

本文不编造性能数字。涉及调度、延迟和吞吐的结论来自仓库内的设计文档与代码;
所有命令都应以 Linux 环境为准。Windows 上可以读源码,但不能直接运行 PipeWire 守护进程。

## 二、PipeWire 在系统中的位置

先用一张图把它放回 Linux 多媒体栈:

```text
应用 / 浏览器 / 游戏 / DAW / 会议软件
│
├── 原生 PipeWire API ───────────────┐
├── PulseAudio 客户端库 ── pipewire-pulse
├── JACK 客户端库 ──────── pipewire-jack
├── ALSA 客户端库 ──────── pipewire-alsa
└── V4L2 客户端库 ──────── pipewire-v4l2
│
libpipewire / native protocol
│
PipeWire daemon(pipewire 进程)
├── modules(协议、节点、虚拟设备)
├── SPA plugins(ALSA/V4L2/BlueZ/转换/混音)
└── graph scheduler(driver + nodes)
│
Linux 内核与硬件
ALSA / V4L2 / Bluetooth / DRM / 网络
```

这里最容易混淆的是三层:

1. **PipeWire daemon**:真正持有全局对象图、打开设备、调度节点、在客户端之间搬数据。
2. **WirePlumber**:独立进程,通常作为 session manager,负责“发现设备、选默认设备、决定谁连谁、保存策略”。
3. **兼容层**:让 PulseAudio、JACK、ALSA、V4L2 应用不用改代码,仍然连接到 PipeWire。

一句话记忆:**PipeWire 提供机制,WirePlumber 提供策略。**

PipeWire 本身可以创建节点、端口和链路,但它默认不会因为插入一副耳机就自动决定“音乐应该走耳机”。
这个决策由 session manager 完成。源码文档也明确把 PipeWire daemon 描述为框架,把策略交给 session manager。

## 三、必须建立的五个二分法

### 1. 控制面与数据面

控制面走 Unix socket 和原生协议,传输的是对象创建、参数协商、状态变化等消息。
数据面通常走共享内存或 `memfd`,视频场景也可以使用 DMA-BUF;音频不复制整段 PCM 到 socket 里。

所以“`pw-dump` 很快”不代表音频处理也走 JSON;“socket 很忙”也不一定意味着音频卡顿。
排障时要先分清是控制面问题还是数据面问题。

### 2. 本地对象与远端代理

`pw_context` 是进程内的资源管理器;`pw_core` 是到另一个 PipeWire 实例的代理。
客户端看到的 `pw_node`、`pw_port`、`pw_link` 多数是远处对象的代理,不是内核对象,也不是 daemon 内部结构体本身。

### 3. 模块与插件

- **PipeWire module**:加载进 PipeWire 进程的 `.so`,操作 `pw_context`、创建全局对象、扩展协议。
- **SPA plugin**:更底层的组件,提供 factory 和 interface,例如 ALSA PCM、音频转换、日志、循环。
- **Extension**:协议和对象模型的扩展,通常同时有客户端侧与服务端侧实现。

### 4. 静态配置与运行时状态

配置写在 `pipewire.conf`、`client.conf`、`pipewire-pulse.conf` 等文件中;
运行时状态则通过 registry、metadata、对象参数和 session manager 的数据库维护。
修改配置不等于修改当前图,很多变化需要重载或重启对应进程。

### 5. 同步节点与异步节点

大多数音频节点按 driver 的周期同步运行。异步节点可以脱离当前周期的严格依赖,代价是至少多一个周期的缓冲和延迟。
这个区别会直接影响延迟、卡顿和音画同步。

## 四、系列文章地图

| 篇目 | 主题 | 读完应能回答的问题 |
| --- | --- | --- |
| [00](/2026/10/23/pipewire-architecture-00-roadmap/) | 导读与全局图景 | PipeWire、WirePlumber、ALSA 各自负责什么? |
| [01](/2026/10/23/pipewire-architecture-01-core-model/) | 核心对象模型 | Context、Core、Registry、Proxy、Resource 怎么对应? |
| [02](/2026/10/23/pipewire-architecture-02-spa/) | SPA 插件架构 | 插件如何加载?Node、POD、Buffer、IO 是什么? |
| [03](/2026/10/23/pipewire-architecture-03-graph-scheduling/) | 图调度与实时数据流 | driver、runnable、activation、xrun 怎么工作? |
| [04](/2026/10/23/pipewire-architecture-04-daemon-protocol-security/) | 守护进程、配置与协议 | 启动时加载什么?客户端如何连上并创建对象? |
| [05](/2026/10/23/pipewire-architecture-05-module-catalog/) | 模块全目录 | 50 个 `src/modules` 模块分别做什么? |
| [06](/2026/10/23/pipewire-architecture-06-spa-plugin-catalog/) | SPA 插件全目录 | 19 个插件目录提供哪些 factory? |
| [07](/2026/10/23/pipewire-architecture-07-session-integrations/) | 会话管理与兼容层 | WirePlumber、Pulse、JACK、ALSA、V4L2 如何接进来? |
| [08](/2026/10/23/pipewire-architecture-08-tools-debugging/) | 工具与排障 | 用 `pw-dump`、`pw-top`、`pw-cli` 怎么定位问题? |
| [09](/2026/10/23/pipewire-architecture-09-code-reading-contribution/) | 源码阅读与贡献 | 从哪个入口开始读?写客户端或模块要看哪里? |

如果你是第一次系统学习,建议按 00 → 01 → 02 → 03 顺序读。
如果你只想排障,可以先读 01、03、08,再按需查 05 和 06。
如果你准备写客户端或修改模块,01、02、04、09 是主线。

## 五、源码目录怎么读

```text
pipewire/
├── src/pipewire/ libpipewire 公共 API 与对象实现
├── src/daemon/ pipewire、pipewire-pulse 等配置模板与入口
├── src/modules/ PipeWire 运行时模块
├── src/tools/ pw-cli、pw-dump、pw-top 等工具
├── src/examples/ 客户端示例
├── spa/include/spa/ SPA 头文件 API:node、pod、buffer、support
├── spa/plugins/ SPA 插件实现:ALSA、V4L2、BlueZ、audioconvert
├── pipewire-alsa/ ALSA 兼容插件
├── pipewire-jack/ JACK 兼容库
├── pipewire-v4l2/ V4L2 兼容库
├── doc/dox/ 设计文档、教程、模块文档
└── test/ 测试与辅助程序
```

读源码时不要一上来从 `pipewire.c` 的 `main()` 顺着读。
更有效的路线是:

1. 从 `src/pipewire/context.c` 和 `src/pipewire/core.c` 理解进程内资源与远端代理。
2. 从 `spa/include/spa/node/node.h` 理解节点处理接口。
3. 从 `doc/dox/internals/scheduling.dox` 理解数据线程如何被驱动。
4. 再回到 `src/modules` 看一个具体模块如何把对象挂进图里。

## 六、几个常见误解

**误解一:PipeWire 就是 PulseAudio 的重写。**
不是。PipeWire 的图模型、协议和调度层独立于 PulseAudio;
`pipewire-pulse` 只是在其上实现 PulseAudio 服务端协议。

**误解二:WirePlumber 是 PipeWire 的插件。**
它是独立进程,通过 PipeWire 客户端 API 控制系统。它可以退出,PipeWire 仍然存在,只是设备发现和自动路由会失去策略来源。

**误解三:有 Link 就一定有数据流。**
链路存在只说明图结构存在。节点是否 runnable、是否有 driver、链路是否 passive、对象是否 active,都会影响实际调度。

**误解四:延迟只由 buffer size 决定。**
调度周期、驱动时钟、异步链路、重采样、图深度、客户端唤醒时间都会叠加。后文会把 latency 的传播路径单独展开。

## 七、小结

先记住三句话:

1. **PipeWire 是用户态媒体图,不是单一音频 API。**
2. **控制面负责“图长什么样”,数据面负责“这一周期谁先跑、谁读谁写”。**
3. **PipeWire 是机制,WirePlumber 是策略,兼容层是入口。**

下一篇从最核心的对象模型开始:一个客户端连上 daemon 之后,到底拿到了哪些对象,它们又如何跟服务端资源一一对应。


Loading
Loading