简介: CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中...... 】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中...... 】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀
专题四:Android15快速自定义与集成音效实战课 🚀
专题五:Android15音频策略实战课 🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉
🌻1.前言
本篇目的:
Linux PipeWire深度解析之pw_core_get_registry调用流程与实战。
要点概括
-
核心功能:从已连接的pw_core对象中获取Registry代理对象,用于发现PipeWire服务端已经注册的全局对象。
-
工作机制:客户端通过Core接口创建pw_registry代理,再给Registry注册监听器,由服务端异步回调global和global_remove事件。
-
典型用途:枚举Node、Device、Port、Link、Factory等全局对象,并根据对象id按需调用pw_registry_bind绑定成具体代理对象。
pw_core_get_registry的本质不是"直接获取所有对象列表",而是"创建一个Registry代理对象"。真正的对象枚举结果不会作为函数返回值同步返回,而是通过Registry事件异步通知给客户端。
它位于客户端连接PipeWire服务端之后。应用先通过pw_context_connect得到pw_core,然后通过pw_core_get_registry得到pw_registry,再通过pw_registry_add_listener接收服务端全局对象变化。
它和pw_registry_bind的职责不同。pw_core_get_registry负责获取"对象目录入口",pw_registry_bind负责根据Registry事件中的全局对象id绑定具体对象。它和pw_core_sync也不同,pw_core_sync用于做客户端和服务端的往返同步,不负责发现对象。
🌻2.应用场景与用法
pw_core_get_registry
是PipeWireCore API中用于获取Registry代理对象的接口。
它处于PipeWire客户端发现服务端对象的入口位置。客户端连接PipeWire服务端后,并不知道服务端当前有哪些Node、Device、Factory、Link等对象。应用需要先获取Registry,再监听Registry事件,才能拿到这些全局对象的id、type、version、permissions和properties。
pw_core_get_registry用于从pw_core获取pw_registry代理对象。
函数原型
c
struct pw_registry *pw_core_get_registry(struct pw_core *core,
uint32_t version,
size_t user_data_size);
参数说明
c
struct pw_core *core;
core表示已经连接成功的PipeWireCore代理对象。
该对象通常由pw_context_connect返回。只有拿到有效core之后,客户端才具备和PipeWire服务端通信的基础能力。pw_core_get_registry依赖这个core向服务端发起get_registry请求。
c
uint32_t version;
version表示客户端希望使用的Registry接口版本。
实际开发中通常传入:
c
PW_VERSION_REGISTRY
它用于声明客户端期望的pw_registry接口版本,便于协议层做版本匹配。
c
size_t user_data_size;
user_data_size表示为返回的Registry代理对象附加的用户数据大小。
如果不需要给Registry代理对象附加私有数据,可以传入0。它不是Buffer大小,也不是对象枚举结果大小,只是代理对象私有数据区域大小。
返回值
成功时返回:
c
struct pw_registry *
表示创建成功的Registry代理对象。
失败时返回NULL。
需要注意,返回非NULL只表示Registry代理对象创建成功,并不表示已经拿到了全部全局对象。全局对象信息需要通过pw_registry_add_listener注册监听器后,由global事件异步返回。
应用场景
第一类场景是枚举PipeWire服务端对象。
调试工具、图形化监控工具、设备管理程序通常需要获取服务端当前有哪些Node、Device、Port、Link和Factory。它们会先调用pw_core_get_registry获取Registry,再监听global事件构建设备和Graph视图。
第二类场景是查找目标播放或录音Node。
客户端可以通过Registry事件遍历所有Node,根据media.class、node.name、object.path等属性判断目标对象,再调用pw_registry_bind绑定该Node。
第三类场景是实现设备热插拔监听。
设备新增、删除、Link变化、Node变化都会通过Registry事件体现。客户端不需要轮询服务端,而是通过global和global_remove事件维护本地对象缓存。
第四类场景是开发PipeWire策略或诊断工具。
WirePlumber、调试程序、测试工具、对象分析工具都需要依赖Registry理解当前PipeWire服务端状态。Registry相当于服务端全局对象目录,是客户端理解PipeWire运行状态的入口。
🌻3.调用流程剖析
🌻3.1核心步骤
1.应用调用pw_init初始化PipeWire运行环境。
2.应用创建MainLoop和Context。
3.应用调用pw_context_connect连接PipeWire服务端,成功后得到pw_core对象。
4.应用调用pw_core_get_registry,从pw_core创建pw_registry代理对象。
5.应用调用pw_registry_add_listener,为Registry注册事件监听器。
6.PipeWire服务端通过Registry向客户端发送global事件,通知当前已经存在的全局对象。
7.应用在global回调中读取id、permissions、type、version和props。
8.应用根据type和props判断对象类型,例如Node、Device、Factory、Port、Link。
9.如果应用需要访问具体对象,则调用pw_registry_bind绑定目标id。
10.绑定成功后,客户端得到对应类型的代理对象,例如pw_node、pw_device或pw_proxy。
11.当服务端对象被移除时,Registry触发global_remove事件,应用同步清理本地缓存。
12.应用退出时销毁Registry代理对象,最后断开Core连接并释放Context。
🌻3.2调用流程图

🌻3.3生命周期图

🌻4.实战应用案例
下面以"枚举PipeWire服务端全局对象"为例,说明pw_core_get_registry在真实开发中的用法。
该案例的目标是:客户端连接PipeWire服务端后,通过Registry监听服务端对象,并筛选出Node对象。Node可能表示播放流、录音流、ALSA设备Node、蓝牙音频Node、视频Node等。
c
#include <pipewire/pipewire.h>
struct app_data {
struct pw_main_loop *loop;
struct pw_context *context;
struct pw_core *core;
struct pw_registry *registry;
struct spa_hook registry_listener;
};
static void on_registry_global(void *data,
uint32_t id,
uint32_t permissions,
const char *type,
uint32_t version,
const struct spa_dict *props)
{
const char *name;
const char *media_class;
if (props == NULL)
return;
name = spa_dict_lookup(props, "node.name");
media_class = spa_dict_lookup(props, "media.class");
if (type != NULL &&
spa_streq(type, PW_TYPE_INTERFACE_Node)) {
/*
* 这里可以记录Node对象:
*
* id : 服务端全局对象id
* permissions : 当前客户端权限
* type : PipeWire接口类型
* version : 对象接口版本
* props : 对象属性
*
* 如果后续需要真正访问该Node,
* 可以使用pw_registry_bind绑定这个id。
*/
(void)id;
(void)permissions;
(void)version;
(void)name;
(void)media_class;
}
}
static void on_registry_global_remove(void *data, uint32_t id)
{
/*
* 服务端全局对象被移除。
* 如果本地缓存了该id,需要在这里删除。
*/
(void)data;
(void)id;
}
static const struct pw_registry_events registry_events = {
PW_VERSION_REGISTRY_EVENTS,
.global = on_registry_global,
.global_remove = on_registry_global_remove,
};
static int init_registry(struct app_data *app)
{
app->registry = pw_core_get_registry(app->core,
PW_VERSION_REGISTRY,
0);
if (app->registry == NULL)
return -1;
pw_registry_add_listener(app->registry,
&app->registry_listener,
®istry_events,
app);
return 0;
}
这个案例只完成对象发现,不直接绑定对象。这样做的好处是边界清晰:Registry负责发现对象,bind负责访问对象。实际项目中可以先把global事件中的对象缓存到本地表,再按业务需求绑定目标对象。
如果要绑定Node对象,可以在global回调中进一步处理:
c
static void on_registry_global(void *data,
uint32_t id,
uint32_t permissions,
const char *type,
uint32_t version,
const struct spa_dict *props)
{
struct app_data *app = data;
struct pw_proxy *proxy;
const char *media_class;
if (props == NULL)
return;
if (!spa_streq(type, PW_TYPE_INTERFACE_Node))
return;
media_class = spa_dict_lookup(props, "media.class");
if (media_class == NULL)
return;
if (spa_streq(media_class, "Audio/Sink")) {
proxy = pw_registry_bind(app->registry,
id,
type,
version,
0);
/*
* proxy表示绑定后的Node代理对象。
* 后续可以根据需要注册Node事件监听器,
* 或者保存该proxy用于后续管理。
*/
(void)proxy;
}
}
这段代码体现了pw_core_get_registry和pw_registry_bind的典型配合方式。
pw_core_get_registry只创建Registry代理,不负责筛选对象,也不负责创建Node代理。global回调负责发现对象,业务逻辑负责判断对象是否符合条件,pw_registry_bind负责把目标id绑定成客户端可操作的代理对象。
工程使用中要注意三点。
第一,不能把pw_core_get_registry理解成同步枚举接口。它返回的是Registry代理对象,不是对象列表。对象列表来自后续global事件。
第二,global事件中的id是服务端全局对象id。这个id适合传给pw_registry_bind,但不能直接当成本地指针使用。
第三,global_remove事件必须处理。PipeWire服务端对象是动态变化的,设备拔出、流退出、Link销毁都会导致对象移除。如果本地缓存不清理,后续很容易访问过期对象。
🌻5.一句话总结
pw_core_get_registry是PipeWire客户端发现服务端全局对象的入口接口:它从pw_core创建pw_registry代理,通过Registry事件异步获得Node、Device、Port、Link等对象,再按需使用pw_registry_bind绑定具体对象。