PipeWire 音频设计与实现分析一——介绍

PipeWire 是一个基于图的媒体处理引擎,一个可以运行多媒体节点图的媒体服务器,是 Linux 的音频/视频总线,它管理 Linux 系统中,不同应用程序对音频和视频设备的共享访问。它提供了一个本地客户端音频 API,但也提供兼容层来支持使用常见的音频 API,包括 ALSA、PulseAudio 和 JACK。

在实现上,PipeWire 主要分为几部分:

  • PipeWire 主守护进程 pipewire
  • PipeWire 的本地 API 客户端库
  • PipeWire 的 PulseAudio 兼容层,主要是 pipewire-pulse 守护进程
  • PipeWire 的 JACK 兼容层,主要是一个 shell 脚本和几个 JACK 兼容客户端库
  • PipeWire 的 ALSA 兼容层,主要是 PipeWire 的 ALSA 插件
  • PipeWire 的媒体会话管理器,目前主要有两个实现,一个是 pipewire-media-session,另一个是 wireplumber

PipeWire 在媒体节点图的处理方面,采用机制与策略分离的设计。PipeWire 主守护进程运行媒体节点图,是机制而不是策略。外部媒体会话管理器构建它们自己的音视频流水线,然后让 pipewire 守护进程执行最终的结果。这包括监听系统中设备状态的变化,并根据需要改变音视频流水线。

媒体会话管理器不属于 pipewire 项目本身,它们是单独的项目,独立的应用程序,它们通过 pipewire 的本地客户端 API 库与 PipeWire 主守护进程通信。媒体会话管理器目前的两个实现中,pipewire-media-session 的源码位于 media-session,WirePlumber 的源码位于 wireplumber,在 GitHub 上有 wireplumber 项目的镜像。PipeWire 社区有逐渐将媒体会话管理器从 pipewire-media-session 切换到 WirePlumber 的趋势,pipewire-media-session 的代码已经很少更新 (2025-03-09),甚至没有做 GitHub 的镜像。PipeWire 相关各部分结构如下图:

PipeWire 中,大部分功能通过模块和插件实现,主守护进程 pipewire 和它的 PulseAudio 兼容层 pipewire-pulse 尽管运行在不同的进程中,但共用相同的可执行文件,这两个守护进程通过加载不同的配置文件,进而加载不同的模块和插件,来运行不同的逻辑,执行不同的功能,如:

复制代码
$ ls -al /usr/bin/pipewire-pulse 
lrwxrwxrwx 1 root root 8 11月  9  2022 /usr/bin/pipewire-pulse -> pipewire

在 Ubuntu 操作系统平台上,pipewire 守护进程的配置文件为 /usr/share/pipewire/pipewire.conf ,pipewire-pulse 的配置文件为 /usr/share/pipewire/pipewire-pulse.conf 。媒体会话管理器同样有它的配置文件,pipewire-media-session 的配置文件为 /usr/share/pipewire/media-session.d/media-session.conf ,此外,它还有用于监视 ALSA 音频设备、蓝牙音频设备和 V4L2 视频设备的配置文件 /usr/share/pipewire/media-session.d/alsa-monitor.conf/usr/share/pipewire/media-session.d/bluez-monitor.conf/usr/share/pipewire/media-session.d/v4l2-monitor.conf

本文分析应用程序接入 PulseAudio 本地 API 客户端库或 PipeWire 本地 API 客户端库的场景下,PipeWire 音频部分的设计和实现,这主要包括 PipeWire 的本地 API 客户端库、PipeWire 主守护进程 pipewire、PipeWire 的 PulseAudio 兼容层 pipewire-pulse 和媒体会话管理器 pipewire-media-sessionpipewire-media-session 主要与比较老的 pipewire 版本一起使用,尽管它有逐渐被替代的趋势,但它的实现逻辑更容易理解一点,这里仍然以它为例,来看 PipeWire 的会话管理器的实现。

代码获取及编译调试运行

安装 PipeWire 的首选方法是通过 Linux 系统发行版的包管理系统,比如通过 apt/dpkg 为 Debian/Ubuntu 等系统安装,通过 yum/rpm 为 Fedora/CentOS 等系统安装,然而,我们为了方便代码分析调试,从代码编译运行。

获取代码的方法有多种,从 pipewirepipewire-media-session 的源码仓库,自然可以通过 git 命令获得源码。

如果是 Ubuntu 系统,可以通过 apt 命令获得源码,具体方法如下:

  1. 编辑 /etc/apt/sources.list 文件,取消注释那些 deb src 行,随后执行 sudo apt-get update 命令更新包仓库信息。
  2. 执行 sudo apt build-dep pipewiresudo apt build-dep pipewire-media-session 命令分别安装编译 pipewire 和 pipewire-media-session 所需的所有依赖包。
  3. 执行 apt source pipewireapt source pipewire-media-session 命令分别获得 pipewire 和 pipewire-media-session 的源码及其 debian 编译配置文件。
  4. 执行 cd pipewire-0.3.48 && dpkg-buildpackage -b -uc -nc 命令以 dpkg-buildpackage 的方式编译 pipewire 的源码,这将生成多个 .deb 文件。可以以同样的方式编译 pipewire-media-session 的源码。其中 dpkg-buildpackage-nc 参数表示不清理之前编译结果 (或者 --no-clean)。

dpkg-buildpackage 构建工具,封装 pipewire 本身的构建工具,编译生成正式版的 deb 包。对于调试分析,更方便的是,直接使用 pipewire 本身的构建工具编译生成 debug 版的二进制文件。PipeWire 使用称为 Meson 的构建工具作为它的构建过程的基础。这个工具与 Autotools 和 CMake 有点像。Meson 生成构建配置文件,喂给更底层的构建工具 Ninja,后者大概与我们更熟悉的 GNU Make 工作在相同的抽象层次上。

Meson 使用一个用户指定的构建目录,Meson 生成的所有文件都会放在这个目录中。本文中,构建目录称为 builddir

对于构建依赖,可以通过各个发行版的包管理工具安装。切换到 PipeWire 源码目录,并为 Ninja 生成构建配置文件:

复制代码
$ cd pipewire-0.3.48
$ meson setup builddir

上面的命令完成之后,可以看一下配置的构建选项:

复制代码
$ meson configure builddir 
Core properties:
  Source dir /media/data/develop/pipewire_0.3.48/pipewire-0.3.48
  Build dir  /media/data/develop/pipewire_0.3.48/pipewire-0.3.48/builddir

Main project options:
  
  Core options                        Current Value                         Possible Values                       Description
  ------------                        -------------                         ---------------                       -----------
  auto_features                       auto                                  [enabled, disabled, auto]             Override value of all 'auto' features
  backend                             ninja                                 [ninja, vs, vs2010, vs2012, vs2013,   Backend to use
                                                                            vs2015, vs2017, vs2019,               
                                                                             vs2022, xcode]                       
  buildtype                           debugoptimized                        [plain, debug, debugoptimized,        Build type to use
                                                                            release, minsize, custom]             
  cmake_prefix_path                   []                                                                          List of additional prefixes for cmake to search
  debug                               true                                  [true, false]                         Debug
  default_library                     shared                                [shared, static, both]                Default library type
  force_fallback_for                  []                                                                          Force fallback for those subprojects
  install_umask                       0022                                  [preserve, 0000-0777]                 Default umask to apply on permissions of installed files
  layout                              mirror                                [mirror, flat]                        Build directory layout
  optimization                        2                                     [0, g, 1, 2, 3, s]                    Optimization level
  pkg_config_path                     []                                                                          List of additional paths for pkg-config to search
  strip                               false                                 [true, false]                         Strip targets on install
  unity                               off                                   [on, off, subprojects]                Unity build
  unity_size                          4                                     >=2                                   Unity block size
  warning_level                       3                                     [0, 1, 2, 3]                          Compiler warning level to use
  werror                              false                                 [true, false]                         Treat warnings as errors
  wrap_mode                           default                               [default, nofallback, nodownload,     Wrap mode
                                                                            forcefallback, nopromote]             
  
  Backend options                     Current Value                         Possible Values                       Description
  ---------------                     -------------                         ---------------                       -----------
  backend_max_links                   0                                     >=0                                   Maximum number of linker processes to run or 0 for no limit
  
  Base options                        Current Value                         Possible Values                       Description
  ------------                        -------------                         ---------------                       -----------
  b_asneeded                          true                                  [true, false]                         Use -Wl,--as-needed when linking
  b_colorout                          always                                [auto, always, never]                 Use colored output
  b_coverage                          false                                 [true, false]                         Enable coverage tracking.
  b_lto                               false                                 [true, false]                         Use link time optimization
  b_lto_threads                       0                                                                           Use multiple threads for Link Time Optimization
  b_lundef                            true                                  [true, false]                         Use -Wl,--no-undefined when linking
  b_ndebug                            false                                 [true, false, if-release]             Disable asserts
  b_pch                               true                                  [true, false]                         Use precompiled headers
  b_pgo                               off                                   [off, generate, use]                  Use profile guided optimization
  b_pie                               true                                  [true, false]                         Build executables as position independent
  b_sanitize                          none                                  [none, address, thread, undefined,    Code sanitizer to use
                                                                            memory, address,undefined]            
  b_staticpic                         true                                  [true, false]                         Build static libraries as position independent
  
  Compiler options                    Current Value                         Possible Values                       Description
  ----------------                    -------------                         ---------------                       -----------
  c_args                              []                                                                          Extra arguments passed to the c compiler
  c_link_args                         []                                                                          Extra arguments passed to the c linker
  c_std                               gnu99                                 [none, c89, c99, c11, c17, c18, c2x,  C language standard to use
                                                                            gnu89, gnu99, gnu11,                  
                                                                             gnu17, gnu18, gnu2x]                 
  cpp_args                            []                                                                          Extra arguments passed to the cpp compiler
  cpp_debugstl                        false                                 [true, false]                         STL debug mode
  cpp_eh                              default                               [none, default, a, s, sc]             C++ exception handling type.
  cpp_link_args                       []                                                                          Extra arguments passed to the cpp linker
  cpp_rtti                            true                                  [true, false]                         Enable RTTI
  cpp_std                             c++17                                 [none, c++98, c++03, c++11, c++14,    C++ language standard to use
                                                                            c++17, c++1z, c++2a, c++20,           
                                                                             gnu++03, gnu++11, gnu++14, gnu++17,  
                                                                            gnu++1z, gnu++2a,                     
                                                                             gnu++20]                             
  
  Directories                         Current Value                         Possible Values                       Description
  -----------                         -------------                         ---------------                       -----------
  bindir                              bin                                                                         Executable directory
  datadir                             share                                                                       Data file directory
  includedir                          include                                                                     Header file directory
  infodir                             share/info                                                                  Info page directory
  libdir                              lib/aarch64-linux-gnu                                                       Library directory
  libexecdir                          libexec                                                                     Library executable directory
  localedir                           share/locale                                                                Locale data directory
  localstatedir                       /var/local                                                                  Localstate data directory
  mandir                              share/man                                                                   Manual page directory
  prefix                              /usr/local                                                                  Installation prefix
  sbindir                             sbin                                                                        System executable directory
  sharedstatedir                      /var/local/lib                                                              Architecture-independent data directory
  sysconfdir                          etc                                                                         Sysconf data directory
  
  Testing options                     Current Value                         Possible Values                       Description
  ---------------                     -------------                         ---------------                       -----------
  errorlogs                           true                                  [true, false]                         Whether to print the logs from failing tests
  stdsplit                            true                                  [true, false]                         Split stdout and stderr in test logs
  
  Project options                     Current Value                         Possible Values                       Description
  ---------------                     -------------                         ---------------                       -----------
  alsa                                auto                                  [enabled, disabled, auto]             Enable alsa spa plugin integration
  audioconvert                        enabled                               [enabled, disabled, auto]             Enable audioconvert spa plugin integration
  audiomixer                          enabled                               [enabled, disabled, auto]             Enable audiomixer spa plugin integration
  audiotestsrc                        enabled                               [enabled, disabled, auto]             Enable audiotestsrc spa plugin integration
  avahi                               auto                                  [enabled, disabled, auto]             Enable code that depends on avahi
  bluez5                              auto                                  [enabled, disabled, auto]             Enable bluez5 spa plugin integration
  bluez5-backend-hfp-native           enabled                               [enabled, disabled, auto]             Enable HFP in native backend in bluez5 spa plugin
  bluez5-backend-hsp-native           enabled                               [enabled, disabled, auto]             Enable HSP in native backend in bluez5 spa plugin
  bluez5-backend-hsphfpd              enabled                               [enabled, disabled, auto]             Enable hsphfpd backend in bluez5 spa plugin (no dependency on hsphfpd)
  bluez5-backend-ofono                enabled                               [enabled, disabled, auto]             Enable oFono HFP backend in bluez5 spa plugin (no dependency on oFono)
  bluez5-codec-aac                    auto                                  [enabled, disabled, auto]             Enable Fraunhofer FDK AAC open source codec implementation
  bluez5-codec-aptx                   auto                                  [enabled, disabled, auto]             Enable AptX Qualcomm open source codec implementation
  bluez5-codec-ldac                   auto                                  [enabled, disabled, auto]             Enable LDAC Sony open source codec implementation
  control                             enabled                               [enabled, disabled, auto]             Enable control spa plugin integration
  dbus                                enabled                               [enabled, disabled, auto]             Enable code that depends on dbus
  docdir                                                                                                          Directory for installing documentation to (defaults to
                                                                                                                  pipewire_datadir/doc/meson.project_name() )
  docs                                disabled                              [enabled, disabled, auto]             Build documentation
  echo-cancel-webrtc                  auto                                  [enabled, disabled, auto]             Enable WebRTC-based echo canceller
  evl                                 disabled                              [enabled, disabled, auto]             Enable EVL support spa plugin integration
  examples                            enabled                               [enabled, disabled, auto]             Build examples
  ffmpeg                              disabled                              [enabled, disabled, auto]             Enable ffmpeg spa plugin integration
  gstreamer                           auto                                  [enabled, disabled, auto]             Build GStreamer plugins
  gstreamer-device-provider           auto                                  [enabled, disabled, auto]             Build GStreamer device provider plugin
  installed_tests                     disabled                              [enabled, disabled, auto]             Install manual and automated test executables
  jack                                auto                                  [enabled, disabled, auto]             Enable jack spa plugin integration
  jack-devel                          false                                 [true, false]                         Install jack development files
  libcamera                           auto                                  [enabled, disabled, auto]             Enable libcamera spa plugin integration
  libcanberra                         auto                                  [enabled, disabled, auto]             Enable code that depends on libcanberra
  libjack-path                                                                                                    Where to install the libjack.so library
  libpulse                            auto                                  [enabled, disabled, auto]             Enable code that depends on libpulse
  libusb                              auto                                  [enabled, disabled, auto]             Enable code that depends on libusb
  libv4l2-path                                                                                                    Where to install the libpw-v4l2.so library
  lv2                                 auto                                  [enabled, disabled, auto]             Enable loading of LV2 plugins
  man                                 auto                                  [enabled, disabled, auto]             Build manpages
  pipewire-alsa                       auto                                  [enabled, disabled, auto]             Enable pipewire-alsa integration
  pipewire-jack                       enabled                               [enabled, disabled, auto]             Enable pipewire-jack integration
  pipewire-v4l2                       enabled                               [enabled, disabled, auto]             Enable pipewire-v4l2 integration
  pw-cat                              auto                                  [enabled, disabled, auto]             Build pw-cat/pw-play/pw-record
  raop                                auto                                  [enabled, disabled, auto]             Enable module for Remote Audio Output Protocol
  roc                                 auto                                  [enabled, disabled, auto]             Enable code that depends on roc toolkit
  sdl2                                auto                                  [enabled, disabled, auto]             Enable code that depends on SDL 2
  session-managers                    [media-session]                                                             Session managers to build (can be [] for none or an absolute path)
  sndfile                             auto                                  [enabled, disabled, auto]             Enable code that depends on libsndfile
  spa-plugins                         enabled                               [enabled, disabled, auto]             Enable spa plugins integration
  support                             enabled                               [enabled, disabled, auto]             Enable support spa plugin integration
  systemd                             auto                                  [enabled, disabled, auto]             Enable systemd integration
  systemd-system-service              disabled                              [enabled, disabled, auto]             Install systemd system service file
  systemd-system-unit-dir                                                                                         Directory for system systemd units (defaults to /usr/lib/systemd/system)
  systemd-user-service                enabled                               [enabled, disabled, auto]             Install systemd user service file (ignored without systemd)
  systemd-user-unit-dir                                                                                           Directory for user systemd units (defaults to /usr/lib/systemd/user)
  test                                disabled                              [enabled, disabled, auto]             Enable test spa plugin integration
  tests                               enabled                               [enabled, disabled, auto]             Build tests
  udev                                auto                                  [enabled, disabled, auto]             Enable Udev integration
  udevrulesdir                                                                                                    Directory for udev rules (defaults to /lib/udev/rules.d)
  v4l2                                auto                                  [enabled, disabled, auto]             Enable v4l2 spa plugin integration
  videoconvert                        enabled                               [enabled, disabled, auto]             Enable videoconvert spa plugin integration
  videotestsrc                        enabled                               [enabled, disabled, auto]             Enable videotestsrc spa plugin integration
  volume                              enabled                               [enabled, disabled, auto]             Enable volume spa plugin integration
  vulkan                              disabled                              [enabled, disabled, auto]             Enable vulkan spa plugin integration
  x11                                 auto                                  [enabled, disabled, auto]             Enable code that depends on X11
  x11-xfixes                          auto                                  [enabled, disabled, auto]             Enable code that depends on XFixes

Subproject media-session:
  
  Core options                        Current Value                         Possible Values                       Description
  ------------                        -------------                         ---------------                       -----------
  default_library                     shared                                [shared, static, both]                Default library type
  warning_level                       3                                     [0, 1, 2, 3]                          Compiler warning level to use
  werror                              false                                 [true, false]                         Treat warnings as errors
  
  Project options                     Current Value                         Possible Values                       Description
  ---------------                     -------------                         ---------------                       -----------
  docdir                                                                                                          Directory for installing documentation to (defaults to
                                                                                                                  pipewire_datadir/doc/meson.project_name() )
  docs                                disabled                              [enabled, disabled, auto]             Build documentation
  installed_tests                     disabled                              [enabled, disabled, auto]             Install manual and automated test executables
  systemd                             auto                                  [enabled, disabled, auto]             Enable systemd integration
  systemd-system-service              disabled                              [enabled, disabled, auto]             Install systemd system service file
  systemd-user-service                enabled                               [enabled, disabled, auto]             Install systemd user service file (ignored without systemd)
  systemd-user-unit-dir                                                                                           Directory for user systemd units (defaults to /usr/lib/systemd/user)
  tests                               enabled                               [enabled, disabled, auto]             Build tests
  with-module-sets                    [jack, pulseaudio]                    [alsa, jack, pulseaudio]              Extra modules sets to enable on install (see media-session.conf)

定义安装目录前缀:

复制代码
$ meson configure builddir -Dprefix=/usr # Default: /usr/local

PipeWire 特有的构建选项在 Project options 部分展示,它们在 meson_options.txt 文件中定义。

最后,调用如下命令构建 PipeWire:

复制代码
$ ninja -C builddir

如果想要在不安装 PipeWire 的情况运行它,可以先运行一个脚本,它会创建一个 PipeWire 可以从构建目录运行的环境,其中 ALSA、PulseAudio 和 JACK 应用程序将自动地使用 PipeWire 仿真库。这个脚本具体的执行方法如下:

复制代码
$ ./pw-uninstalled.sh -b builddir

pw-uninstalled.sh 脚本内容如下:

复制代码
#!/usr/bin/env bash

set -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

while getopts ":b:v:" opt; do
	case ${opt} in
		b)
			BUILDDIR=${OPTARG}
			;;
		v)
			VERSION=${OPTARG}
			echo "Version: ${VERSION}"
			;;
		\?)
			echo "Invalid option: -${OPTARG}"
			exit 1
			;;
		:)
			echo "Option -${OPTARG} requires an argument"
			exit 1
			;;
	esac
done

if [ -z "${BUILDDIR}" ]; then
	BUILDDIR=${SCRIPT_DIR}/builddir
	echo "Using default build directory: ${BUILDDIR}"
fi

if [ ! -d "${BUILDDIR}" ]; then
	echo "Invalid build directory: ${BUILDDIR}"
	exit 1
fi

# the config file read by the daemon
export PIPEWIRE_CONFIG_DIR="${BUILDDIR}/src/daemon"
# the directory with SPA plugins
export SPA_PLUGIN_DIR="${BUILDDIR}/spa/plugins"
export SPA_DATA_DIR="${SCRIPT_DIR}/spa/plugins"
# the directory with pipewire modules
export PIPEWIRE_MODULE_DIR="${BUILDDIR}/src/modules"
export PATH="${BUILDDIR}/src/daemon:${BUILDDIR}/src/tools:${BUILDDIR}/src/media-session:${BUILDDIR}/src/examples:${PATH}"
export LD_LIBRARY_PATH="${BUILDDIR}/src/pipewire/:${BUILDDIR}/pipewire-jack/src/${LD_LIBRARY_PATH+":$LD_LIBRARY_PATH"}"
export GST_PLUGIN_PATH="${BUILDDIR}/src/gst/${GST_PLUGIN_PATH+":${GST_PLUGIN_PATH}"}"
# the directory with card profiles and paths
export ACP_PATHS_DIR="${SCRIPT_DIR}/spa/plugins/alsa/mixer/paths"
export ACP_PROFILES_DIR="${SCRIPT_DIR}/spa/plugins/alsa/mixer/profile-sets"
# ALSA plugin directory
export ALSA_PLUGIN_DIR="${BUILDDIR}/pipewire-alsa/alsa-plugins"

export PW_UNINSTALLED=1
export PKG_CONFIG_PATH="${BUILDDIR}/meson-uninstalled/:${PKG_CONFIG_PATH}"

if [ -d "${BUILDDIR}/subprojects/wireplumber" ]; then
	# FIXME: find a nice, shell-neutral way to specify a prompt
	"${SCRIPT_DIR}"/subprojects/wireplumber/wp-uninstalled.sh -b"${BUILDDIR}"/subprojects/wireplumber "${SHELL}"
elif [ -d "${BUILDDIR}/subprojects/media-session" ]; then
	# FIXME: find a nice, shell-neutral way to specify a prompt
	"${SCRIPT_DIR}"/subprojects/media-session/media-session-uninstalled.sh -b"${BUILDDIR}"/subprojects/media-session "${SHELL}"
else
	# FIXME: find a nice, shell-neutral way to specify a prompt
	${SHELL}
fi

pw-uninstalled.sh 脚本接受构建目录 builddir 作为参数,根据构建工具生成的二进制文件的目录结构,导出 PipeWire 各守护进程或客户端应用程序需要用到的环境变量,如 PIPEWIRE_CONFIG_DIRPIPEWIRE_MODULE_DIRSPA_PLUGIN_DIRLD_LIBRARY_PATHSPA_DATA_DIR 等。

pw-uninstalled.sh 脚本有个 bug:当传入的构建目录 builddir 是个相对路径时,各环境变量也会以相对路径的形式导出。在 PipeWire 的代码仓库根目录执行pw-uninstalled.sh 脚本并检查构建目录的有效性,随后基于构建目录路径导出环境变量,切换到构建目录运行 PipeWire 系统音频服务器,会发现各环境变量指向的路径为无效路径。

从构建目录运行 pipewire 的方法如下:

复制代码
$ cd builddir/
$ make run

这将会使用默认配置文件配置和启动 pipewire 守护进程。默认的配置文件也将自动启动 pipewire-media-session,一个默认的示例会话管理器,和 pipewire-pulse,一个 PulseAudio 兼容服务器。

正常安装的 PipeWire,它的 pipewire 守护进程、pipewire-media-session 和 pipewire-pulse 是作为 3 个系统服务,各自由 systemd 独立管理的。从构建目录运行的 PipeWire,pipewire 守护进程的配置文件为 pipewire-0.3.48/builddir/src/daemon/pipewire-uninstalled.conf,其中有如下这样一段配置:

复制代码
context.exec = [
    #{ path = <program-name> [ args = "<arguments>" ] }
    #
    # Execute the given program with arguments.
    #
    # You can optionally start the session manager here,
    # but it is better to start it as a systemd service.
    # Run the session manager with -h for options.
    #
    { path = "/media/data/develop/pipewire_0.3.48/pipewire-0.3.48/builddir/subprojects/media-session/media-session-uninstalled.sh" args = "pipewire-media-session" }
    #
    # You can optionally start the pulseaudio-server here as well
    # but it is better to start it as a systemd service.
    # It can be interesting to start another daemon here that listens
    # on another address with the -a option (eg. -a tcp:4713).
    #
    { path = "/media/data/develop/pipewire_0.3.48/pipewire-0.3.48/builddir/src/daemon/pipewire" args = "-c pipewire-pulse.conf" }
]

pipewire-media-session 和 pipewire-pulse 由 pipewire 守护进程启动。

也可以通过 PIPEWIRE_DEBUG 环境变量启用更多的调试日志,如:

复制代码
$ cd builddir/
$ PIPEWIRE_DEBUG="D" make run

还可以启动 gdb 来调试运行 pipewire,如:

复制代码
$ cd builddir/
$ PIPEWIRE_DEBUG="D" make gdb

如果系统中已经安装了 PipeWire,且以 pipewire-media-session 作为媒体会话管理器,则从构建目录运行之前,可能需要先通过 systemd 停掉 pipewire service/socket,如:

复制代码
$ systemctl --user stop pipewire.service \
                      pipewire.socket \
                      pipewire-media-session.service \
                      pipewire-pulse.service \
                      pipewire-pulse.socket

如果以 wireplumber 作为媒体会话管理器,需要将上面命令中的 pipewire-media-session.service 替换为 wireplumber.service

PipeWire 包含一些库和工具,在 builddir 目录中运行如下命令安装所有这些东西到系统的指定目录:

复制代码
$ sudo meson install

完整安装的 PipeWire 应该有一个 pipewire 进程,一个 pipewire-media-session (或 WirePlumber) 和一个 pipewire-pulse 进程。PipeWire 通常作为一个 systemd 系统服务来启动。PipeWire 守护进程的配置文件位于 /usr/share/pipewire/pipewire.conf,可以参考这个配置文件中的注释来了解更多关于配置选项的内容。通过 systemd 启动 pipewire 守护进程的方法如下:

复制代码
$ systemctl --user start pipewire.service pipewire.socket

如果没有在 pipewire.conf 配置文件中启动 media-session,也需要像下面这样启动它:

复制代码
$ systemctl --user start pipewire-media-session.service

要使它在系统启动时自动启动可以执行如下命令:

复制代码
$ systemctl --user enable pipewire-media-session.service

可以使用 enable --now 来立即启动它。

对于 wireplumber,启动它的方法与 media-session 类似,唯一的区别是 systemd 激活文件需要替换为 wireplumber.service

pipewire-pulse 进程是 PulseAudio 音频服务器协议的重新实现,并作为一个系统服务在 PipeWire 之上运行。它的二进制可执行文件通常位于 /usr/bin/pipewire-pulse 。pipewire-pulse 进程可以通过提供的 systemd 激活文件或 PipeWire 守护进程来启动,可参考 /usr/share/pipewire/pipewire.conf 文件。

通过 systemd 启动 pipewire-pulse 服务的方法如下:

复制代码
$ systemctl --user start pipewire-pulse.service pipewire-pulse.socket

pipewire-pulse 的二进制可执行文件实际为 pipewire 守护进程二进制可执行文件的符号链接。

PipeWire 的 ALSA 插件在 Fedora 发行版中通常安装在 /usr/lib64/alsa-lib/libasound_module_pcm_pipewire.so ,在 Ubuntu 发行版中通常安装在 /usr/lib/x86_64-linux-gnu/alsa-lib/libasound_module_pcm_pipewire.so ,还有一个配置文件安装在 /usr/share/alsa/alsa.conf.d/50-pipewire.conf ,当以下文件位于 /etc/alsa/conf.d/ 目录中时,插件将被 alsa 拾取:

复制代码
/etc/alsa/conf.d/50-pipewire.conf -> /usr/share/alsa/alsa.conf.d/50-pipewire.conf
/etc/alsa/conf.d/99-pipewire-default.conf

通过这样的设置,aplay -L 应该可以列出 pipewire: 设备,它们可以被用作普通的 alsa 设备来播放和录音。

PipeWire 音频服务器主程序

PipeWire 音频服务器守护进程 pipewire 和它的 PulseAudio 兼容层 pipewire-pulse 守护进程共用同一个二进制可执行文件,它们的主程序定义 (位于 pipewire/src/daemon/pipewire.c) 如下:

复制代码
int main(int argc, char *argv[])
{
	struct pw_context *context = NULL;
	struct pw_main_loop *loop = NULL;
	struct pw_properties *properties = NULL;
	static const struct option long_options[] = {
		{ "help",	no_argument,		NULL, 'h' },
		{ "version",	no_argument,		NULL, 'V' },
		{ "config",	required_argument,	NULL, 'c' },
		{ "verbose",	no_argument,		NULL, 'v' },

		{ NULL, 0, NULL, 0}
	};
	int c, res = 0;
	char path[PATH_MAX];
	const char *config_name;
	enum spa_log_level level = pw_log_level;

	if (setenv("PIPEWIRE_INTERNAL", "1", 1) < 0)
		fprintf(stderr, "can't set PIPEWIRE_INTERNAL env: %m");

	snprintf(path, sizeof(path), "%s.conf", argv[0]);
	config_name = basename(path);

	pw_init(&argc, &argv);

	while ((c = getopt_long(argc, argv, "hVc:v", long_options, NULL)) != -1) {
		switch (c) {
		case 'v':
			if (level < SPA_LOG_LEVEL_TRACE)
				pw_log_set_level(++level);
			break;
		case 'h':
			show_help(argv[0], config_name);
			return 0;
		case 'V':
			fprintf(stdout, "%s\n"
				"Compiled with libpipewire %s\n"
				"Linked with libpipewire %s\n",
				argv[0],
				pw_get_headers_version(),
				pw_get_library_version());
			return 0;
		case 'c':
			config_name = optarg;
			break;
		default:
			res = -EINVAL;
			goto done;
		}
	}

	properties = pw_properties_new(
				PW_KEY_CONFIG_NAME, config_name,
				NULL);

	loop = pw_main_loop_new(&properties->dict);
	if (loop == NULL) {
		pw_log_error("failed to create main-loop: %m");
		res = -errno;
		goto done;
	}

	pw_loop_add_signal(pw_main_loop_get_loop(loop), SIGINT, do_quit, loop);
	pw_loop_add_signal(pw_main_loop_get_loop(loop), SIGTERM, do_quit, loop);

	context = pw_context_new(pw_main_loop_get_loop(loop), properties, 0);
	properties = NULL;

	if (context == NULL) {
		pw_log_error("failed to create context: %m");
		res = -errno;
		goto done;
	}

	pw_log_info("start main loop");
	pw_main_loop_run(loop);
	pw_log_info("leave main loop");

done:
	pw_properties_free(properties);
	if (context)
		pw_context_destroy(context);
	if (loop)
		pw_main_loop_destroy(loop);
	pw_deinit();

	return res;
}

pipewire 和 pipewire-pulse 主程序的执行过程如下:

  1. 传入可执行程序执行时的命令行参数,调用 pw_init() 函数初始化 PipeWire,初始化一些基本配置和功能组件。pw_init() 函数在所有 PipeWire 程序中都会被调用,包括 PipeWire 守护进程及其 PulseAudio 兼容层,PipeWire 客户端程序,PipeWire 媒体会话管理器 pipewire-media-session 和 wireplumber。初始化 PipeWire 的具体含义稍后会更细致地来看。
  2. 解析命令行参数。-v 参数指示 PipeWire 进程在执行时输出更多日志,具体指输出日志等级加 1,而不是输出所有日志。-c 参数用来指定配置文件名,配置文件名默认为可执行文件名加上 .conf 后缀,如 pipewire 的为 pipewire.conf ,pipewire-pulse 的为 pipewire-pulse.conf,这个参数指定的配置文件名替换默认配置文件名。
  3. 调用 pw_main_loop_new() 函数创建主循环。以 struct spa_dict 对象的形式传递参数,这里参数主要是配置文件名。为主循环添加 SIGINTSIGTERM 信号的处理程序,收到这两个信号时,PipeWire 进程退出主循环。
  4. 基于主循环和配置信息创建上下文。
  5. 运行主循环。
  6. 主循环结束时清理上下文和主循环等对象。

pw_init() 函数初始化一些基本配置和功能组件,该函数定义 (位于 pipewire/src/pipewire/pipewire.c) 如下:

复制代码
#define MAX_SUPPORT	32

#define SUPPORTLIB	"support/libspa-support"

PW_LOG_TOPIC_EXTERN(log_context);
#define PW_LOG_TOPIC_DEFAULT log_context

static char *prgname;

static struct spa_i18n *_pipewire_i18n = NULL;

struct plugin {
	struct spa_list link;
	char *filename;
	void *hnd;
	spa_handle_factory_enum_func_t enum_func;
	struct spa_list handles;
	int ref;
};

struct handle {
	struct spa_list link;
	struct plugin *plugin;
	char *factory_name;
	int ref;
	struct spa_handle handle SPA_ALIGNED(8);
};

struct registry {
	struct spa_list plugins;
};

struct support {
	char **categories;
	const char *plugin_dir;
	const char *support_lib;
	struct registry registry;
	char *i18n_domain;
	struct spa_interface i18n_iface;
	struct spa_support support[MAX_SUPPORT];
	uint32_t n_support;
	unsigned int initialized:1;
	unsigned int in_valgrind:1;
	unsigned int no_color:1;
	unsigned int no_config:1;
};

static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t support_lock = PTHREAD_MUTEX_INITIALIZER;
static struct support global_support;
 . . . . . .
 static void init_i18n(struct support *support)
{
	/* Load locale from the environment. */
	setlocale(LC_ALL, "");
	/* Set LC_NUMERIC to C so that floating point strings are consistently
	 * formatted and parsed across locales. */
	setlocale(LC_NUMERIC, "C");
	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
	pw_set_domain(GETTEXT_PACKAGE);
}
 . . . . . .
SPA_EXPORT
void pw_init(int *argc, char **argv[])
{
	const char *str;
	struct spa_dict_item items[6];
	uint32_t n_items;
	struct spa_dict info;
	struct support *support = &global_support;
	struct spa_log *log;
	char level[32];

	pthread_mutex_lock(&init_lock);
	if (support->initialized)
		goto done;

	pthread_mutex_lock(&support_lock);
	support->in_valgrind = RUNNING_ON_VALGRIND;

	if (getenv("NO_COLOR") != NULL)
		support->no_color = true;

	if ((str = getenv("PIPEWIRE_NO_CONFIG")) != NULL)
		support->no_config = pw_properties_parse_bool(str);

	init_i18n(support);

	if ((str = getenv("SPA_PLUGIN_DIR")) == NULL)
		str = PLUGINDIR;
	support->plugin_dir = str;

	if ((str = getenv("SPA_SUPPORT_LIB")) == NULL)
		str = SUPPORTLIB;
	support->support_lib = str;

	spa_list_init(&support->registry.plugins);

	if (pw_log_is_default()) {
		char *patterns = NULL;

		n_items = 0;
		if (!support->no_color)
			items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_COLORS, "true");
		items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_TIMESTAMP, "true");
		if ((str = getenv("PIPEWIRE_LOG_LINE")) == NULL || spa_atob(str))
			items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_LINE, "true");
		snprintf(level, sizeof(level), "%d", pw_log_level);
		items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_LEVEL, level);
		if ((str = getenv("PIPEWIRE_LOG")) != NULL)
			items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_FILE, str);
		if ((patterns = parse_pw_debug_env()) != NULL)
			items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_PATTERNS, patterns);
		info = SPA_DICT_INIT(items, n_items);

		log = add_interface(support, SPA_NAME_SUPPORT_LOG, SPA_TYPE_INTERFACE_Log, &info);
		if (log)
			pw_log_set(log);

#ifdef HAVE_SYSTEMD
		if ((str = getenv("PIPEWIRE_LOG_SYSTEMD")) == NULL || spa_atob(str)) {
			log = load_journal_logger(support, &info);
			if (log)
				pw_log_set(log);
		}
#endif
		free(patterns);
	} else {
		support->support[support->n_support++] =
			SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Log, pw_log_get());
	}

	pw_log_init();

	n_items = 0;
	if ((str = getenv("PIPEWIRE_CPU")))
		items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_CPU_FORCE, str);
	if ((str = getenv("PIPEWIRE_VM")))
		items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_CPU_VM_TYPE, str);
	info = SPA_DICT_INIT(items, n_items);

	add_interface(support, SPA_NAME_SUPPORT_CPU, SPA_TYPE_INTERFACE_CPU, &info);

	add_i18n(support);

	pw_log_info("version %s", pw_get_library_version());
	support->initialized = true;
	pthread_mutex_unlock(&support_lock);
done:
	pthread_mutex_unlock(&init_lock);
}

PipeWire 程序用 struct support 类型的全局对象 global_support 维护一些基本配置和功能组件,如插件目录、支持库路径、i18n、日志等,以及所有加载的插件,其中所有加载的插件用 struct registry 对象维护,基本功能组件用 struct spa_support 数组维护。

pw_init() 函数初始化 global_support 对象,具体过程如下:

  1. 从环境变量 NO_COLOR 、 **PIPEWIRE_NO_CONFIG ** 中获取对应配置,初始化 i18n。从环境变量 SPA_PLUGIN_DIR 获取插件目录,失败时使用默认目录路径,如 /usr/lib/aarch64-linux-gnu/spa-0.2 。从环境变量 SPA_SUPPORT_LIB 获取支持库路径,失败时使用默认路径 support/libspa-support 。初始化 struct registry

  2. 初始化并添加日志组件。如果全局日志组件为默认日志组件,构造日志组件初始化时的配置参数,除了前面获取的 no_color,还有如下这些配置参数:

    • 是否打印时间戳配置 log.timestamp,为 true
    • 从环境变量 **PIPEWIRE_LOG_LINE ** 中获取的是否打印行号配置 log.line;
    • 打印的最低日志等级配置 log.level
    • 从环境变量 **PIPEWIRE_LOG ** 中获取的是否打印文件配置 log.file
    • 从环境变量 PIPEWIRE_DEBUG 中获取的日志模式配置log.patterns

    之后调用 add_interface() 函数从支持库 插件中加载日志组件,并设置全局日志组件为该日志组件。如果环境变量 PIPEWIRE_LOG_SYSTEMD 指示使用 systemd 日志,则调用 load_journal_logger() 函数加载 journal 日志组件替换 支持库 插件中加载的日志组件。

    如果全局日志组件不是默认日志组件,则向 global_support 添加全局日志组件为。

    最后,调用 pw_log_init() 函数基于新的全局日志组件,初始化各个日志 topic。

    日志是 PipeWire 配置的第一个组件。

  3. 初始化并添加 CPU 组件。从环境变量 PIPEWIRE_CPUPIPEWIRE_VM 获取配置参数,调用 add_interface() 函数从支持库插件中加载 CPU 组件。

  4. 添加 i18n 组件。

  5. 结束初始化。

global_supportstruct spa_support 数组中维护的这些组件,提供十分基础的全局功能,可以在其它一般插件的实现中使用。全局支持组件主要包括日志、CPU 和 i18n。

相关推荐
冰橙子id4 分钟前
linux——磁盘和文件系统管理
linux·运维·服务器
武子康16 分钟前
AI炼丹日志-28 - Audiblez 将你的电子书epub转换为音频mp3 做有声书
人工智能·爬虫·gpt·算法·机器学习·ai·音视频
无聊的小坏坏44 分钟前
环境变量深度解析:从配置到内核的全链路指南
linux·bash
才鲸嵌入式1 小时前
01 Ubuntu20.04下编译QEMU8.2.4,交叉编译32位ARM程序,运行ARM程序的方法
linux·c语言·单片机·嵌入式·arm·qemu·虚拟机
伤不起bb2 小时前
Kafka 消息队列
linux·运维·分布式·kafka
Hello.Reader3 小时前
Git 安装全攻略Linux、macOS、Windows 与源码编译
linux·git·macos
龙仔7253 小时前
华为云CentOS配置在线yum源,连接公网后,逐步复制粘贴,看好自己对应的版本即可,【新手必看】
linux·centos·华为云
tiging3 小时前
centos实现SSH远程登录
linux·centos·ssh
好多知识都想学4 小时前
Linux 文件处理器 sed 和 awk 详细讲解
linux·运维·ubuntu
Johny_Zhao4 小时前
阿里云数据库Inventory Hint技术分析
linux·mysql·信息安全·云计算·系统运维