1 概念
PipeWire 是一个由 Red Hat 主导、目前已成为 Linux 桌面音频/视频服务基座的现代多媒体框架,它在同一套体系内统一处理音频采集与播放、视频/摄像头流以及跨应用的实时信号路由,核心特点是以有向图(graph)方式组织节点和链路,让多个应用可以同时读写同一设备、自由组合输入输出,并天然支持低延迟的 Pro-Audio 实时调度(对比旧方案:它同时取代了 PulseAudio 的"通用混音"与 JACK 的"专业低延迟",且默认与 PulseAudio 和 JACK 兼容,可平滑接管二者的协议与客户端)。相比前辈,PipeWire 的优势在于:图式动态重连、强安全隔离(会话与设备权限由系统策略控制)、低延迟高精度时钟、以及对 Flatpak/Snap 等容器化应用的透明支持。它由 systemd 用户服务管理,现已是 Fedora、Debian、Ubuntu 等主流发行版桌面环境的默认音频服务(常配合 WirePlumber 作为会话管理器)。在具体应用上,若是嵌入式/音频开发者,PipeWire 常用作音视频中继、虚拟设备(如 virtual sink/source)以及连接蓝牙、USB、HDMI 等多路音频的统一枢纽。
核心功能就是:多路音视频流在 CPU 上的统一零拷贝混音/复用、图状路由与低延迟定时调度”。
如下:
可以看到,PipeWire往下其实不止ALSA,还有蓝牙,V4L2等。
具体拆解就是:
统一混音与复用(Mixing & Compositing):
音频:把多个播放软件(浏览器、播放器、游戏)产生的不同采样率、不同通道数的 PCM 音频流,在 CPU 上统一重采样、格式转换并混音成一路。
视频:把多个应用(如屏幕共享、摄像头捕捉、OBS)的视频 Frame 统一管理和分发(基于 DMA-BUF 零拷贝传输)。
图状路由(Graph Routing):
在系统内部构建一个动态节点图(Graph)。你可以像接音频电缆一样,任意把应用 A 的音频输出拉线路由给应用 B(比如做录音/效果器处理),再路由给硬件扬声器;或者把某个窗口的视频流路由给会议软件。
硬件调度输出(Scheduling & Sync):
作为用户态应用与 ALSA(音频驱动)/ V4L2(视频驱动)之间的中介,以极低的延迟(甚至达到毫秒级的专业 RT 实时调度)把混好或路由好的数据包送入硬件缓冲区,并严格保证音画同步(A/V Sync)。
2 安装
环境是之前的树莓派5+WM8960。(Audio学习4(WM8960的bringup)-CSDN博客)
来自:GitHub - mikeroyal/PipeWire-Guide: PipeWire Guide. Learn about how PipeWire gives your Linux system a Professional Audio/Video Processing workflow. · GitHub
Run these three commands as your regular user (not as root):
Check for new service files with:
systemctl --user daemon-reloadDisable and stop the PulseAudio service with:
systemctl --user --now disable pulseaudio.service pulseaudio.socketEnable and start the new pipewire-pulse service with:
systemctl --user --now enable pipewire pipewire-pulse
下面是运行结果:
tom@raspberrypi:~ $ systemctl --user daemon-reload tom@raspberrypi:~ $ systemctl --user enable --now pipewire.socket pipewire-pulse.socket Created symlink '/home/tom/.config/systemd/user/sockets.target.wants/pipewire.socket' → '/usr/lib/systemd/user/pipewire.socket'. Created symlink '/home/tom/.config/systemd/user/sockets.target.wants/pipewire-pulse.socket' → '/usr/lib/systemd/user/pipewire-pulse.socket'. tom@raspberrypi:~ $ systemctl --user enable --now pipewire.service wireplumber.service pipewire-pulse.service Created symlink '/home/tom/.config/systemd/user/default.target.wants/pipewire.service' → '/usr/lib/systemd/user/pipewire.service'. Created symlink '/home/tom/.config/systemd/user/pipewire.service.wants/wireplumber.service' → '/usr/lib/systemd/user/wireplumber.service'. Created symlink '/home/tom/.config/systemd/user/default.target.wants/pipewire-pulse.service' → '/usr/lib/systemd/user/pipewire-pulse.service'. tom@raspberrypi:~ $ systemctl --user is-active pipewire wireplumber pipewire-pulse active active active查看运行状态:
tom@raspberrypi:~ $ wpctl status -n PipeWire 'pipewire-0' [1.4.2, tom@raspberrypi, cookie:1359508960] └─ Clients: 33. pipewire [1.4.2, tom@raspberrypi, pid:2099] 35. WirePlumber [1.4.2, tom@raspberrypi, pid:2098] 47. WirePlumber [export] [1.4.2, tom@raspberrypi, pid:2098] 74. wpctl [1.4.2, tom@raspberrypi, pid:2175] Audio ├─ Devices: │ 48. alsa_card.platform-107c701400.hdmi [alsa] │ 49. alsa_card.platform-107c706400.hdmi [alsa] │ 50. alsa_card.platform-soc_107c000000_sound [alsa] │ ├─ Sinks: │ * 71. alsa_output.platform-soc_107c000000_sound.stereo-fallback [vol: 0.40] │ ├─ Sources: │ * 72. alsa_input.platform-soc_107c000000_sound.stereo-fallback [vol: 1.00] │ ├─ Filters: │ └─ Streams: Video ├─ Devices: │ 54. v4l2_device.platform-1000800000.codec [v4l2] │ 55. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 56. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 57. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 58. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 59. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 60. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 61. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 62. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 63. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 64. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 65. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 66. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 67. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 68. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 69. v4l2_device.platform-1000880000.pisp_be [v4l2] │ 70. v4l2_device.platform-1000880000.pisp_be [v4l2] │ ├─ Sinks: │ ├─ Sources: │ ├─ Filters: │ └─ Streams: Settings └─ Default Configured Devices:这里:
| ID | 对象 | 含义 |
|---|---|---|
| 50 | Device | WM8960 这张物理声卡 |
| 71 | Sink | 应用把声音送到这里,再通过耳机播放 |
| 72 | Source | 应用从这里获取麦克风录音 |
* | 默认标记 | 未指定设备的应用优先使用它 |
3 基本功能
3.1 录音
pw-record --rate=48000 --channels=2 ~/pipewire-recording.wav此时观察音频图(watch -n 0.5 'wpctl status -n')
此时查看端口连接:
tom@raspberrypi:~ $ pw-link -l alsa_input.platform-soc_107c000000_sound.stereo-fallback:capture_FL |-> pw-record:input_FL alsa_input.platform-soc_107c000000_sound.stereo-fallback:capture_FR |-> pw-record:input_FR pw-record:input_FL |<- alsa_input.platform-soc_107c000000_sound.stereo-fallback:capture_FL pw-record:input_FR |<- alsa_input.platform-soc_107c000000_sound.stereo-fallback:capture_FRWM8960:capture_FL → pw-record:input_FL
WM8960:capture_FR → pw-record:input_FR
这里pw-record是应用创建的流节点。
3.2 播放
pplay ~/pipewire-recording.wav观察结果:
此时查看端口连接:
tom@raspberrypi:~ $ pw-link -l alsa_output.platform-soc_107c000000_sound.stereo-fallback:playback_FL |<- pw-play:output_FL alsa_output.platform-soc_107c000000_sound.stereo-fallback:playback_FR |<- pw-play:output_FR pw-play:output_FL |-> alsa_output.platform-soc_107c000000_sound.stereo-fallback:playback_FL pw-play:output_FRpw-play:output_FL → WM8960:playback_FL
pw-play:output_FR → WM8960:playback_FR
4 高级应用
4.1 命令行
对于基本功能,看这几个常用的功能就行了。
wpctl status -n 看全局
wpctl inspect 看对象
pw-link -l 看连线
pw-link / -d 连线、断线
pw-record 录音
pw-play 播放
例子1:
应用节点存在时可以没有声音;建立到声卡的连接后,声音才能送出去。
//播放,此时只有播放没有任何路由
pw-play --target=0 ~/pipewire-test-tone.wav
//连接到左声道
pw-link pw-play:output_FL alsa_output.platform-soc_107c000000_sound.stereo-fallback:playback_FL
//查看连接
pw-link -l
alsa_output.platform-soc_107c000000_sound.stereo-fallback:playback_FL
|<- pw-play:output_FL
pw-play:output_FL
|-> alsa_output.platform-soc_107c000000_sound.stereo-fallback:playback_FL
例子2:
混音
//终端1播放
pw-play ~/pipewire-test-long.wav
//终端2播放
pw-play ~/pipewire-recording.wav
//此时能同时听到两个声音。
路径如下:
当两个播放流连接到同一个 Sink 时,PipeWire 会处理格式转换、音量和混音,再把结果送到声卡。此时PipeWire可以针对某一路单独控制。
例子3:
Monitor
//启动一个不自动连接麦克风的录音流
pw-record --target=0 ~/monitor-test.wav
//保持它运行,再把 Sink 的 monitor_FL/FR 分别连到录音程序的 input_FL/FR。此时播放测试音,录音文件就能收到播放链路中的声音。
4.2 图形工具
| 工具 | 主要用途 | 与已学命令的对应 |
|---|---|---|
| qpwgraph | 显示节点、端口和连线,鼠标连接/断开,支持 Patchbay 连接规则 | 类似图形版pw-link |
| Helvum | 较简洁的节点连线界面 | 也是图形化音频图 |
| pavucontrol | 应用音量、静音、输入输出选择、设备配置 | 覆盖部分wpctl操作,通过pipewire-pulse工作 |
qpwgraph
pavucontrol
4.3 低延迟
这个功能感觉大部分是硬件,不过软件也可以控制一部分。这里以麦克风实时监听为例:
麦克风 → ADC → 驱动缓冲 → PipeWire → 音效处理 → 驱动缓冲 → DAC → 耳机| 环节 | 主要由谁决定 | PipeWire 能做什么 |
|---|---|---|
| ADC/DAC 转换、芯片内部滤波 | 硬件 | 通常只能间接通过设备支持的参数影响 |
| 声卡传输与 DMA 缓冲 | 硬件+驱动 | 通过 ALSA 协商采样率、缓冲等参数 |
| 音频图每次处理多少数据 | PipeWire | 调整quantum |
| 节点唤醒、执行顺序 | 软件调度+操作系统 | 安排处理顺序、申请实时调度 |
| 音效、重采样等处理 | 软件算法 | 管理处理节点,但算法本身也可能引入延迟 |
这里PipeWire可以控制缓冲大小。 假设采样率为 48 kHz:
| 一批处理的帧数 | 对应音频时长 |
|---|---|
| 1024 | 21.33 ms |
| 256 | 5.33 ms |
| 128 | 2.67 ms |
基本上硬件决定了能力和下限;PipeWire 决定如何在这些条件下组织处理,在低延迟与稳定性之间取舍。 同一套硬件,用大缓冲和用小缓冲,实际延迟可以相差很大。
在之前的练习中,就尝试了采集麦克风的帧大小(Audio学习2(麦克风和喇叭)-CSDN博客)。
5 APP层的使用
一般来说要使用这一套功能就要用PipeWire的接口。不过PipeWire 在设计之初就考虑到了兼容性,它提供了完备的垫片(Shim/Emulation Library)和透明兼容层。
ALSA 应用(直连底层驱动的轻量应用/嵌入式程序): PipeWire 提供了
pipewire-alsa插件(libasound_module_pcm_pipewire.so)。应用以为自己是在直接向 ALSA 设备写 PCM 数据,实际数据被偷偷重定向到了 PipeWire 的 Graph 中。PulseAudio 应用(绝大多数桌面应用,如 Chrome、Firefox、网易云音乐): PipeWire 实现了
pipewire-pulse服务,提供了一个和 PulseAudio 一模一样的 UNIX Socket 接口。应用发给 PulseAudio 的音频指令和数据,直接由 PipeWire 接管解析。JACK 专业音频应用(如 Ardour、REAPER、FL Studio Linux 版): PipeWire 提供了
pipewire-jack替换库。应用调用的libjack.so被替换为 PipeWire 的实现,瞬间获得低延迟的专业路由能力。
直接使用pipewire当然是现在更好的选择,一个简单的播放例子如下:
#include <stdio.h> #include <math.h> #include <spa/param/audio/format-utils.h> #include <spa/pod/builder.h> #include <pipewire/pipewire.h> struct data { struct pw_main_loop *loop; struct pw_stream *stream; float phase; }; // 核心音频处理回调函数:当 PipeWire 需要新数据时会触发此函数 static void on_process(void *userdata) { struct data *data = userdata; struct pw_buffer *b; struct spa_buffer *buf; float *samples; int n_frames, n_channels; int i, c; // 1. 从管道中获取一个可写缓冲区 b = pw_stream_dequeue_buffer(data->stream); if (!b) return; buf = b->buffer; if (!buf->datas[0].data) return; samples = buf->datas[0].data; n_channels = 2; // 双声道(立体声) n_frames = buf->datas[0].maxsize / (sizeof(float) * n_channels); // 2. 实时生成 440 Hz 正弦波填充缓冲区 for (i = 0; i < n_frames; i++) { // 生成幅值为 0.2 的正弦波,防止声音过大 float val = sinf(data->phase) * 0.2f; // 步进相角:2 * pi * 频率 / 采样率 ># ~/.config/wireplumber/wireplumber.conf.d/50-my-card.conf monitor.alsa.rules = [ { matches = [ { node.name = "alsa_output.pci-0000_00_1f.3.analog-stereo" } ] actions = { update-props = { node.description = "我的内置扬声器" node.nick = "Speaker" priority.driver = 100 } } } ]7 PulseAudio
PulseAudio 是一个运行在应用和 ALSA 之间的音频服务,主要解决桌面上多个应用共享音频设备的问题。
它提供的典型功能包括:
- 多个应用同时播放并混音。
- 每个应用独立调音量、静音。
- 选择默认麦克风和输出设备。
- 将应用声音切换到耳机、HDMI、蓝牙等设备。
- 通过输出的 Monitor 录制系统声音。
不过目前PipeWire 覆盖的场景更广,又能承接原来的应用生态。所以基本上都升级到PipeWire了。这里也就不重点看了。