Linux PipeWire深度解析之pw_properties_new_dict调用流程与实战(五十七)
简介:CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中……】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课🚀
专题四:Android15快速自定义与集成音效实战课🚀
专题五:Android15音频策略实战课🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例)🚀
人生格言:人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
🍉🍉🍉文章目录🍉🍉🍉
- 🌻1.前言
- 要点概括
- 🌻2.应用场景与用法
- 函数原型
- 参数说明
- 返回值
- 应用场景
- 🌻3.调用流程剖析
- 🌻3.1核心步骤
- 🌻3.2调用流程图
- 🌻3.3生命周期图
- 🌻4.实战应用案例
- 🌻5.一句话总结
🌻1.前言
本篇目的:
Linux PipeWire深度解析之pw_properties_new_dict调用流程与实战。
要点概括
核心功能:根据已有spa_dict创建一个新的pw_properties对象,用于承载PipeWire对象属性。
工作机制:读取spa_dict中的key/value条目,复制为pw_properties内部可管理的属性集合,并返回新的properties对象。
典型用途:把已有SPA字典转换成PipeWire属性对象,再传给Context、Stream、Filter、Node、Proxy等创建接口。
pw_properties_new_dict的本质是“属性对象构造函数”。它不负责连接PipeWireCore,不负责创建Stream,不负责解析完整配置文件,也不负责改变已有对象状态。它只做一件事:把一个spa_dict形式的只读键值集合,转换成PipeWire可拥有、可传递、可释放的pw_properties对象。
它和pw_properties_new的区别在于:pw_properties_new适合直接用变参方式手写少量key/value;pw_properties_new_dict适合已经有spa_dict的场景,尤其是在SPA、模块、回调、对象信息复制等链路中更自然。
它和pw_properties_update的区别在于:pw_properties_new_dict创建新对象,pw_properties_update更新已有对象。前者解决“从dict生成properties”,后者解决“把dict合并到已有properties”。
它和pw_properties_new_string也不同。pw_properties_new_string面向字符串解析,适合配置文本;pw_properties_new_dict面向结构化字典,适合C代码内部已有的spa_dict数据。
🌻2.应用场景与用法
pw_properties_new_dict
是PipeWire通用Properties API中用于从spa_dict创建pw_properties对象的接口。
它位于PipeWire对象创建链路的前置位置。PipeWire中大量对象都需要属性描述,例如应用名、媒体类型、媒体角色、节点名称、流名称、对象序列、设备属性、模块属性等。这些属性通常以key/value形式存在。pw_properties_new_dict把已有spa_dict转换成pw_properties之后,就可以继续传入PipeWire对象创建接口。
pw_properties_new_dict用于根据已有spa_dict构造新的pw_properties对象。
函数原型
structpw_properties*pw_properties_new_dict(conststructspa_dict*dict);参数说明
conststructspa_dict*dict;dict表示输入字典。
它通常由若干个spa_dict_item组成,每个item包含一个key和一个value。key/value都以字符串形式表达。该函数只读取dict内容,不修改输入dict本身。
典型spa_dict结构如下:
structspa_dict_item{constchar*key;constchar*value;};structspa_dict{uint32_tflags;uint32_tn_items;conststructspa_dict_item*items;};工程上要注意三点。
第一,dict只是初始化来源,不是输出对象本身。
第二,dict中的key/value应保持有效字符串语义,不要依赖NULLvalue表达复杂逻辑。
第三,dict中不要设计重复key。pw_properties是属性集合,不适合表达同名多值语义。
返回值
成功时返回:
structpw_properties*表示新创建的pw_properties对象。
返回NULL表示创建失败,常见原因是内存分配失败。调用者必须检查返回值,不能直接把NULL传给后续对象创建接口。
返回的pw_properties拥有自己的生命周期。未交给其他PipeWire对象接管时,调用者需要使用pw_properties_free释放。若传入某些PipeWire创建接口,并且该接口声明会接管properties所有权,则调用者不应再次释放。
应用场景
第一类场景是从静态属性表创建Stream属性。
例如播放器、录音器、测试音源、虚拟音频节点等应用,通常先准备一组media.type、media.category、media.role、application.name等属性,再创建PipeWireStream。
第二类场景是从已有对象属性复制一份可修改属性。
PipeWire对象信息中经常携带spa_dict形式的属性。如果业务需要基于已有属性派生新对象,可以先用pw_properties_new_dict复制,再通过pw_properties_set增加或覆盖部分字段。
第三类场景是模块加载和对象创建。
PipeWire模块、会话管理器、设备监控器、Filter、Node、Port、Metadata等对象创建时,通常都需要一组属性描述。pw_properties_new_dict可以把已有字典快速转换成创建接口可接收的properties对象。
第四类场景是测试代码和最小化示例。
当测试代码已经用spa_dict_item数组描述属性时,pw_properties_new_dict比变参形式更清晰,也更适合复用和封装。
🌻3.调用流程剖析
🌻3.1核心步骤
1.调用者准备spa_dict_item数组,每个条目包含一个key和一个value。
2.调用者用spa_dict封装items和n_items,形成输入字典。
3.调用pw_properties_new_dict并传入dict。
4.函数内部创建新的pw_properties对象。
5.函数读取dict中的每个spa_dict_item。
6.函数把key/value复制到pw_properties内部属性集合。
7.如果dict为空或没有条目,则返回一个空properties对象。
8.如果内存分配失败,则返回NULL。
9.调用者根据返回值判断是否创建成功。
10.调用者可以继续通过pw_properties_set修改属性。
11.调用者把properties传给Context、Stream、Filter、Node等对象创建接口。
12.如果properties没有被后续API接管,调用者负责释放。
🌻3.2调用流程图
🌻3.3生命周期图
🌻4.实战应用案例
下面以“从spa_dict创建Stream属性”为例,说明pw_properties_new_dict的典型用法。
这个场景常见于音频播放、音频采集、虚拟节点、测试音源、媒体桥接等PipeWire客户端代码中。应用先把属性写成spa_dict_item数组,然后调用pw_properties_new_dict生成pw_properties,最后交给pw_stream_new或pw_stream_new_simple创建Stream。
#include<pipewire/pipewire.h>structapp_data{structpw_core*core;structpw_stream*stream;};staticintcreate_audio_stream(structapp_data*data){staticconststructspa_dict_itemitems[]={{PW_KEY_MEDIA_TYPE,"Audio"},{PW_KEY_MEDIA_CATEGORY,"Playback"},{PW_KEY_MEDIA_ROLE,"Music"},{PW_KEY_APP_NAME,"pipewire-properties-demo"},{PW_KEY_NODE_NAME,"demo.playback.stream"},{PW_KEY_NODE_DESCRIPTION,"Demo Playback Stream"},};staticconststructspa_dictdict={.flags=0,.n_items=SPA_N_ELEMENTS(items),.items=items,};structpw_properties*props;props=pw_properties_new_dict(&dict);if(props==NULL)return-ENOMEM;pw_properties_set(props,PW_KEY_STREAM_IS_LIVE,"true");data->stream=pw_stream_new(data->core,"demo-playback",props);if(data->stream==NULL)return-EIO;return0;}这段代码的关键点不在Stream创建,而在properties构造方式。
items是静态属性表,适合表达一组固定的对象属性。dict只是对items的轻量封装。pw_properties_new_dict根据dict创建新的pw_properties对象。创建完成后,应用仍然可以调用pw_properties_set继续补充属性。
需要特别注意所有权问题。
在上面的场景中,props传给pw_stream_new之后,Stream创建接口会接管props所有权。调用者不应再对同一个props调用pw_properties_free,否则容易造成重复释放。
如果只是临时创建properties,并没有传给接管所有权的PipeWireAPI,就应由调用者释放:
staticintbuild_temp_properties(void){staticconststructspa_dict_itemitems[]={{"object.name","temporary-object"},{"object.serial","1001"},};staticconststructspa_dictdict={.flags=0,.n_items=SPA_N_ELEMENTS(items),.items=items,};structpw_properties*props;props=pw_properties_new_dict(&dict);if(props==NULL)return-ENOMEM;pw_properties_set(props,"object.description","Temporary Properties");/* * 这里可以读取、检查、修改或传递props。 * 如果没有把props交给会接管所有权的API,必须主动释放。 */pw_properties_free(props);return0;}还可以用它从已有对象属性派生新属性:
staticstructpw_properties*clone_and_extend_properties(conststructspa_dict*src){structpw_properties*props;props=pw_properties_new_dict(src);if(props==NULL)returnNULL;pw_properties_set(props,"application.name","derived-client");pw_properties_set(props,"node.description","Derived PipeWire Node");returnprops;}这个写法在对象代理、模块封装、调试工具和会话管理逻辑中很常见。它的优势是:原始spa_dict保持只读,新对象拥有独立pw_properties,后续修改不会反向污染原始属性来源。
工程实践中,pw_properties_new_dict最容易踩的坑主要有三个。
第一,把dict误认为properties。spa_dict只是轻量字典视图,pw_properties才是PipeWire可管理的属性对象。
第二,忽略返回值。该函数可能返回NULL,后续直接传入对象创建接口会放大错误。
第三,混淆所有权。props一旦交给会接管所有权的API,调用者就不应再释放;props如果没有被接管,调用者就必须释放。
🌻5.一句话总结
pw_properties_new_dict是PipeWire中把已有spa_dict转换成新pw_properties对象的属性构造接口,核心价值是完成结构化key/value属性复制,并为Context、Stream、Filter、Node等对象创建链路提供可拥有、可传递、可释放的properties对象。
