点击下方👇关注 Android系统攻城狮
第209篇原创文章
持续迭代 | Android+Linux多媒体进阶体系
专栏·课程 知识体系持续更新
PipeWire Graph可以统一管理音频、视频和蓝牙设备,但是ALSA、V4L2、libcamera和BlueZ提供的接口并不相同,这些底层能力不能直接成为Graph里的Device和Node。
SPA负责把这些能力接入PipeWire,它使用插件库提供具体实现,再通过Factory找到实现类型、创建Handle实例并取得统一接口。那么,Factory、Handle和接口如何把ALSA播放能力变成PipeWire可以使用的Node?本篇以ALSA设备插件为例。

1


SPA全称Simple Plugin API,可以理解为PipeWire用来接入底层媒体能力的一套插件和接口层。ALSA负责音频设备访问,V4L2和libcamera负责视频设备,BlueZ负责蓝牙能力,SPA插件把这些后端能力整理成统一的Device、Node等接口,再交给PipeWire组织成媒体对象。
SPA解决的是底层能力如何被PipeWire使用,它不选择默认输出设备,也不决定播放流应该连接到哪个设备。WirePlumber负责设备和连接策略,PipeWire负责对象与Graph运行,SPA插件提供这些对象背后的底层实现。

2


一个SPA动态库可以提供多个实现,Factory描述其中一种实现类型。spa_handle_factory结构体保存name、get_size、init和enum_interface_info,其中name用于标识实现,其他成员负责计算实例空间、初始化实例并说明这个实例能够提供哪些接口。
Handle是按照Factory描述分配并初始化的实现实例,它保存当前实例的运行状态和接口入口。Factory保存的是实现描述,具体设备路径、循环接口和日志支持等信息会在Handle初始化时传入,因此同一个Factory可以根据不同属性创建不同实例。
interface是PipeWire真正使用的能力入口,Handle初始化完成后,调用方通过spa_handle_get_interface()按接口类型取得SPA Node或SPA Device,后续不需要理解ALSA、V4L2和BlueZ各自的内部实现,只需要使用对应interface的方法。

这条关系中,插件库是实现集合,Factory是实现说明,Handle是已经初始化的实例,接口是实例对外提供的能力。PipeWire取得SPA接口后,再创建自己的Node或Device,并把SPA实现绑定到对应对象上。

3


PipeWire创建pw_context时读取context.spa-libs配置,这个配置把Factory名称规则映射到SPA插件库。例如api.alsa.*对应alsa/libspa-alsa,api.v4l2.*对应v4l2/libspa-v4l2,api.libcamera.*和api.bluez5.*也分别指向自己的插件库。
当创建端给出factory.name=api.alsa.pcm.sink时,pw_context_load_spa_handle()先根据这个属性值找到alsa/libspa-alsa,再由pw_load_spa_handle()打开动态库并取得spa_handle_factory_enum()入口。这个入口会依次返回库中的Factory,PipeWire比较Factory的name成员,直到找到名称为api.alsa.pcm.sink的实现。
spa_handle_factory_enum()是插件库提供的枚举入口,它本身不是Factory。以ALSA插件为例,同一个库可以枚举录音、播放、设备监控和其他ALSA实现,调用方通过Factory名称选择当前需要的类型。

4


PipeWire找到api.alsa.pcm.sink对应的Factory后,先调用spa_handle_factory_get_size()取得实例需要的空间,再分配Handle并调用spa_handle_factory_init()完成初始化。设备路径等实例属性在这一步传入,所以api.alsa.pcm.sink表示ALSA播放实现类型,不表示某个具体PCM设备。
ALSA Sink Factory初始化Handle时,会建立SPA Node接口和播放端状态,并把Handle的get_interface入口设置好。Handle准备完成后,spa_handle_get_interface()取得SPA_TYPE_INTERFACE_Node对应的SPA Node,这个接口提供参数配置、Port能力和数据处理等统一方法。
此时得到的仍然是SPA Node接口。在常用的ALSA设备路径中,PipeWire的Adapter Factory把这个接口作为Follower交给pw_adapter_new(),再创建并注册PipeWire Node。到了这里,ALSA播放能力才进入PipeWire对象体系,后续才能作为设备Node参与Graph连接和运行。

ALSA设备监控使用的api.alsa.enum.udev也遵循同一套机制,只是Handle对外提供SPA Device接口,上层再根据设备信息组织Device和Node。
V4L2、libcamera和BlueZ虽然实现不同,但都可以通过插件库、Factory、Handle和接口进入PipeWire层。

5


SPA是PipeWire接入底层媒体能力的插件和接口层,PipeWire根据Factory名称找到插件库,从库中选择对应Factory,再由Factory分配并初始化Handle,最后从Handle取得SPA Device接口。
Factory标识一种实现,Handle表示已经创建的实例,interface提供统一能力,PipeWire再把SPA接口绑定到自己的Node或Device。理解这四层关系链路,可以判断问题发生在插件库加载、Factory匹配、Handle初始化,还是SPA接口进入PipeWire阶段。
若读者朋友发现有错误、疑问的地方,或者好的建议,欢迎拍砖!!!