Linux PipeWire深度解析之pw_factory_create_object调用流程与实战(七十二)

简介: CSDN博客专家、《Android系统多媒体进阶实战》作者

博主新书推荐:《Android系统多媒体进阶实战》🚀

Android Audio工程师专栏地址:Audio工程师进阶系列原创干货持续更新中...... 】🚀

Android多媒体专栏地址:多媒体系统工程师系列原创干货持续更新中...... 】🚀

专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀

专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀

专题四:Android15快速自定义与集成音效实战课 🚀

专题五:Android15音频策略实战课 🚀

专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉

🌻1.前言

本篇目的:

Linux PipeWire深度解析之pw_factory_create_object调用流程与实战。

要点概括

  • 核心功能:通过已经绑定到客户端的Factory对象创建一个新的PipeWire远端对象。

  • 工作机制:客户端拿到Factory代理后,调用pw_factory_create_object向服务端Factory发送创建请求,服务端根据type、version和props创建具体对象,并返回一个新的Proxy。

  • 典型用途:通过Factory动态创建Node、ClientNode、ClientDevice、Adapter等对象,理解PipeWire对象创建链路。

pw_factory_create_object的本质是"通过Factory创建对象"。它不是音频数据处理接口,也不是Buffer接口,更不是直接访问ALSA、V4L2、BlueZ这类底层设备的接口。

它工作在PipeWire对象模型层。PipeWire内部很多对象并不是应用直接new出来的,而是通过Factory创建。Factory可以理解为对象创建入口,客户端通过Registry发现Factory,再通过Factory创建对应类型的对象。

它和pw_core_create_object很接近,但关注点不同。pw_core_create_object是通过Core指定factory_name创建对象;pw_factory_create_object是已经拿到Factory对象之后,通过这个Factory创建对象。前者更像"按名字找工厂再创建",后者更像"已绑定工厂后直接创建"。

它也不同于pw_stream_new_simple。pw_stream_new_simple是上层Stream封装接口,适合普通播放、录音应用;pw_factory_create_object更靠近PipeWire底层对象创建机制,适合理解Factory、Proxy、Resource、Registry之间的关系。

🌻2.应用场景与用法

pw_factory_create_object

是PipeWireFactory API中用于通过Factory创建远端PipeWire对象的接口。

它位于PipeWire客户端对象创建链路中。客户端先连接Core,通过Registry发现服务端暴露的Factory全局对象,再bind得到本地Factory代理。随后调用pw_factory_create_object,向PipeWire服务端发送对象创建请求。服务端Factory收到请求后,根据目标type、接口version和属性props创建具体对象,并把新对象以Proxy形式返回给客户端。

pw_factory_create_object用于通过已经绑定的Factory创建一个新的PipeWire远端对象。

函数原型

c 复制代码
struct pw_proxy *pw_factory_create_object(struct pw_factory *factory,
                                          const char *type,
                                          uint32_t version,
                                          const struct spa_dict *props,
                                          size_t user_data_size);

参数说明

c 复制代码
struct pw_factory *factory;

factory表示已经绑定到客户端的Factory代理对象。

它通常来自Registry bind流程。也就是说,客户端不是凭空构造factory,而是先从PipeWire服务端Registry中发现Factory全局对象,然后根据global id绑定得到pw_factory对象。

c 复制代码
const char *type;

type表示要创建对象的接口类型。

它用于告诉Factory:客户端希望创建哪一类PipeWire对象。例如某些Factory可以创建Node类型对象,某些Factory可以创建Device类型对象,某些Factory可以创建ClientNode或ClientDevice类型对象。

c 复制代码
uint32_t version;

version表示客户端希望使用的目标接口版本。

PipeWire对象接口是有版本的。客户端创建对象时需要声明自己期望的接口版本,服务端会根据Factory能力和协议兼容性处理该请求。

c 复制代码
const struct spa_dict *props;

props表示创建对象时附带的属性字典。

它通常用于传递对象名称、媒体类型、Factory特定参数、节点属性、设备属性或模块需要的初始化配置。不同Factory识别的props不同,不能把所有属性都理解成通用参数。

c 复制代码
size_t user_data_size;

user_data_size表示为返回的Proxy附加分配的用户数据大小。

如果应用需要在Proxy后面附带私有数据,可以设置该值。如果不需要,通常传0。

返回值

成功时返回:

c 复制代码
struct pw_proxy *

表示新创建对象对应的本地Proxy。

这个Proxy是客户端访问远端对象的入口。后续可以根据对象类型把它转换成对应的高层封装,或者通过Proxy监听事件、发送方法、管理生命周期。

失败时返回NULL。

失败原因通常包括Factory无效、type不匹配、version不兼容、服务端拒绝创建、权限不足、props缺失或Factory内部创建失败。工程上不能只检查函数返回值,还要结合PipeWire日志、Registry对象、Factory名称、创建参数和服务端错误事件一起定位。

应用场景

第一类场景是理解PipeWireFactory机制。

PipeWire中的很多对象都来自Factory。比如client-node、client-device、adapter、link-factory、spa-node-factory等,本质上都可以从"Factory创建对象"这个角度理解。pw_factory_create_object正好处在这条链路的客户端侧入口。

第二类场景是创建底层PipeWire对象。

普通播放器一般不会直接调用pw_factory_create_object,而是使用pw_stream_new_simple、pw_stream_connect等更高层接口。但调试PipeWire对象模型、写底层客户端、研究Factory创建流程时,需要理解它。

第三类场景是分析pw_core_create_object和Factory绑定流程。

如果代码里使用pw_core_create_object,内部语义可以理解成"通过Core按factory_name请求创建对象"。如果代码里已经拿到了pw_factory对象,就可以通过pw_factory_create_object直接发起创建。

第四类场景是分析PipeWire模块如何导出对象能力。

PipeWire模块加载后会注册Factory。Factory对外暴露"我能创建什么对象"。客户端发现Factory后,再通过创建接口把这个能力转化为真实对象。这个过程是理解PipeWire模块、Factory、Registry、Proxy、Resource关系的关键。

🌻3.调用流程剖析

🌻3.1核心步骤

1.客户端连接PipeWireCore,建立与PipeWire服务端的通信通道。

2.客户端通过pw_core_get_registry获取Registry对象。

3.Registry向客户端同步当前服务端暴露的全局对象。

4.客户端在global事件中识别目标Factory对象。

5.客户端通过pw_registry_bind绑定Factory,得到本地pw_factory代理。

6.客户端准备type、version和props等创建参数。

7.客户端调用pw_factory_create_object向Factory发送创建请求。

8.PipeWire服务端收到Factory create_object请求。

9.服务端Factory检查对象类型、接口版本、客户端权限和属性参数。

10.Factory调用对应实现逻辑创建具体对象实例。

11.服务端为新对象建立Resource或Proxy关联关系。

12.客户端获得返回的pw_proxy,并通过该Proxy继续管理新对象。

13.对象正常运行期间,客户端和服务端通过Proxy/Resource进行方法调用和事件通知。

14.对象销毁时,Proxy、Resource和实现侧对象依次释放。

🌻3.2调用流程图

🌻3.3生命周期图

🌻4.实战应用案例

下面用一个简化案例说明pw_factory_create_object在PipeWire客户端侧的典型使用方式。

这个案例的重点不是播放音频,也不是写PCM数据,而是演示"发现Factory→绑定Factory→通过Factory创建对象"的主链路。

c 复制代码
#include <pipewire/pipewire.h>

struct app_data {
    struct pw_core *core;
    struct pw_registry *registry;
    struct pw_factory *factory;
    struct pw_proxy *created;
    struct spa_hook registry_listener;
};

static void on_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;
    const char *name;

    if (props == NULL)
        return;

    name = spa_dict_lookup(props, PW_KEY_FACTORY_NAME);
    if (name == NULL)
        return;

    /*
     * 这里只是示例。
     * 实际开发中应根据目标Factory名称选择,比如client-node、adapter等。
     */
    if (strcmp(name, "adapter") != 0)
        return;

    app->factory = pw_registry_bind(app->registry,
                                    id,
                                    type,
                                    version,
                                    sizeof(struct pw_factory));

    if (app->factory == NULL)
        return;

    {
        struct spa_dict_item items[] = {
            { PW_KEY_OBJECT_LINGER, "false" },
            { PW_KEY_NODE_NAME, "demo.factory.created.object" },
        };

        struct spa_dict props = SPA_DICT_INIT_ARRAY(items);

        app->created = pw_factory_create_object(app->factory,
                                                PW_TYPE_INTERFACE_Node,
                                                PW_VERSION_NODE,
                                                &props,
                                                0);
    }
}

static const struct pw_registry_events registry_events = {
    PW_VERSION_REGISTRY_EVENTS,
    .global = on_global,
};

这段代码的执行重点有三个。

第一,pw_factory_create_object之前必须先拿到Factory。

Factory不是普通C对象,不应该由应用自己malloc。客户端需要先通过Registry发现服务端暴露的Factory,然后调用pw_registry_bind获得本地代理。只有拿到pw_factory之后,才有条件调用pw_factory_create_object。

第二,type和version必须和Factory能力匹配。

不是所有Factory都能创建Node,也不是所有Factory都能创建Device。Factory导出的能力决定了它能创建什么对象。type写错,version不匹配,或者props缺少关键字段,都可能导致创建失败。

第三,返回值是pw_proxy,不是最终实现对象指针。

PipeWire采用客户端/服务端对象模型。客户端拿到的是Proxy,服务端才持有真实实现对象。应用后续对对象的操作,本质上是通过Proxy向服务端对象发送请求,或者接收服务端对象发回的事件。

从工程调试角度看,pw_factory_create_object失败时,建议从四个方向排查。

第一,确认Factory是否真的存在。

可以通过PipeWire对象枚举工具查看当前Registry里有没有目标Factory。没有Factory,后续创建一定失败。

第二,确认Factory名称是否正确。

很多Factory名称看起来像对象类型,但它们不是库名,也不是模块名,而是PipeWire暴露出来的对象创建入口。例如adapter是Factory名称,Node是对象接口类型,两者不是同一层概念。

第三,确认type和version是否匹配。

type描述"创建什么接口类型的对象",version描述"使用哪个接口版本"。它们必须和Factory支持的创建能力一致。

第四,确认props是否完整。

Factory创建对象时通常依赖属性字典。props中缺少媒体类别、节点名称、工厂参数或SPA相关配置时,服务端可能无法完成创建。

下面给出一个更接近真实工程的伪代码主链路:

c 复制代码
static void create_object_from_factory(struct app_data *app)
{
    struct spa_dict_item items[] = {
        { PW_KEY_NODE_NAME, "my-created-node" },
        { PW_KEY_MEDIA_CLASS, "Audio/Sink" },
    };

    struct spa_dict props = SPA_DICT_INIT_ARRAY(items);

    if (app->factory == NULL)
        return;

    app->created = pw_factory_create_object(app->factory,
                                            PW_TYPE_INTERFACE_Node,
                                            PW_VERSION_NODE,
                                            &props,
                                            0);

    if (app->created == NULL)
        return;

    /*
     * 后续可以给created添加listener,
     * 或根据实际对象类型继续完成参数配置。
     */
}

这个函数展示的是Factory对象创建的核心形态:调用方不直接创建服务端对象,而是把创建意图交给Factory。Factory内部再根据服务端模块注册的实现逻辑生成真实对象。

在PipeWire架构中,这种设计非常关键。它把"对象如何创建"从客户端代码中抽离出去,由模块和Factory统一管理。客户端只需要知道目标Factory、对象类型、接口版本和创建属性,不需要直接知道底层对象结构如何分配、如何初始化、如何接入PipeWire对象模型。

这也是PipeWire比传统单体音频服务更灵活的地方。播放流、采集流、设备、适配器、协议桥接、底层SPA插件,都可以通过Factory机制接入统一对象体系。理解pw_factory_create_object,就能更清楚地理解PipeWire为什么大量使用Factory、Registry、Proxy和Resource这些概念。

🌻5.一句话总结

pw_factory_create_object是PipeWireFactory对象创建入口:客户端先通过Registry绑定Factory,再通过type、version和props请求Factory创建远端对象,成功后拿到pw_proxy继续管理该对象。

相关推荐
lsh曙光1 小时前
playbook剧本(2)--Ansible变量
linux·运维·网络
lengjingzju1 小时前
编译与调试完全指南—第14章 系统配置与调优
linux
大鹏的NLP博客2 小时前
通用 Linux 嵌入式板端 C/C++ ABI 与 Glibc 依赖治理规范
linux·c++·交叉编译
tedcloud1233 小时前
book-to-skill 怎么部署?把技术书和项目文档变成 AI Agent 可复用知识
linux·运维·服务器·人工智能·开源
瀚高PG实验室3 小时前
SQL优化案例:使用分区表优化SQL查询性能
linux·服务器·数据库·sql·microsoft·postgresql
青禾8374 小时前
Linux 系统信息、权限管理与 Python 并发编程(协程与线程)完全指南
linux·python
2301_800954994 小时前
Linux 常用系统信息与进程管理命令速查
linux·运维·服务器
T型码农要学习5 小时前
eMMC_命令整理
linux·驱动开发·emmc
刚入门的大一新生6 小时前
Linux-进程4
linux·运维·服务器