公司动态
Linux PipeWire深度解析之pw_core_get_registry调用流程与实战(九十一)
简介CSDN博客专家、《Android系统多媒体进阶实战》作者博主新书推荐《Android系统多媒体进阶实战》Android Audio工程师专栏地址Audio工程师进阶系列【原创干货持续更新中……】Android多媒体专栏地址多媒体系统工程师系列【原创干货持续更新中……】专题一 二AAOS车载系统AOSP14系统攻城狮入门视频实战课专题三Android14 Binder之HIDL与AIDL通信实战课专题四Android15快速自定义与集成音效实战课专题五Android15音频策略实战课专题六Android15音频性能实战课(无声/杂音/断音/爆音实战案例)人生格言人生从来没有捷径只有行动才是治疗恐惧和懒惰的唯一良药.更多原创,欢迎关注Android系统攻城狮文章目录1.前言要点概括2.应用场景与用法函数原型参数说明返回值应用场景3.调用流程剖析3.1核心步骤3.2调用流程图3.3生命周期图4.实战应用案例5.一句话总结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代理对象。函数原型structpw_registry*pw_core_get_registry(structpw_core*core,uint32_tversion,size_tuser_data_size);参数说明structpw_core*core;core表示已经连接成功的PipeWireCore代理对象。该对象通常由pw_context_connect返回。只有拿到有效core之后客户端才具备和PipeWire服务端通信的基础能力。pw_core_get_registry依赖这个core向服务端发起get_registry请求。uint32_tversion;version表示客户端希望使用的Registry接口版本。实际开发中通常传入PW_VERSION_REGISTRY它用于声明客户端期望的pw_registry接口版本便于协议层做版本匹配。size_tuser_data_size;user_data_size表示为返回的Registry代理对象附加的用户数据大小。如果不需要给Registry代理对象附加私有数据可以传入0。它不是Buffer大小也不是对象枚举结果大小只是代理对象私有数据区域大小。返回值成功时返回structpw_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等。#includepipewire/pipewire.hstructapp_data{structpw_main_loop*loop;structpw_context*context;structpw_core*core;structpw_registry*registry;structspa_hookregistry_listener;};staticvoidon_registry_global(void*data,uint32_tid,uint32_tpermissions,constchar*type,uint32_tversion,conststructspa_dict*props){constchar*name;constchar*media_class;if(propsNULL)return;namespa_dict_lookup(props,node.name);media_classspa_dict_lookup(props,media.class);if(type!NULLspa_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;}}staticvoidon_registry_global_remove(void*data,uint32_tid){/* * 服务端全局对象被移除。 * 如果本地缓存了该id需要在这里删除。 */(void)data;(void)id;}staticconststructpw_registry_eventsregistry_events{PW_VERSION_REGISTRY_EVENTS,.globalon_registry_global,.global_removeon_registry_global_remove,};staticintinit_registry(structapp_data*app){app-registrypw_core_get_registry(app-core,PW_VERSION_REGISTRY,0);if(app-registryNULL)return-1;pw_registry_add_listener(app-registry,app-registry_listener,registry_events,app);return0;}这个案例只完成对象发现不直接绑定对象。这样做的好处是边界清晰Registry负责发现对象bind负责访问对象。实际项目中可以先把global事件中的对象缓存到本地表再按业务需求绑定目标对象。如果要绑定Node对象可以在global回调中进一步处理staticvoidon_registry_global(void*data,uint32_tid,uint32_tpermissions,constchar*type,uint32_tversion,conststructspa_dict*props){structapp_data*appdata;structpw_proxy*proxy;constchar*media_class;if(propsNULL)return;if(!spa_streq(type,PW_TYPE_INTERFACE_Node))return;media_classspa_dict_lookup(props,media.class);if(media_classNULL)return;if(spa_streq(media_class,Audio/Sink)){proxypw_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绑定具体对象。