三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Linux PipeWire深度解析之pw_properties_serialize_dict调用流程与实战(六十六)

Linux PipeWire深度解析之pw_properties_serialize_dict调用流程与实战(六十六)

简介: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_properties_serialize_dict调用流程与实战。

要点概括

  • 核心功能:将PipeWire中的spa_dict键值表序列化输出到FILE流中。

  • 工作机制:遍历dict中的每个key/value,根据flags决定换行、包裹、数组、递归、颜色等输出格式,并把结果写入指定FILE。

  • 典型用途:调试打印属性、保存配置、导出对象属性、把pw_properties转换成可读文本。

pw_properties_serialize_dict的本质不是“创建属性”,也不是“解析属性字符串”,而是把已经存在的spa_dict以文本形式写出去。它关注的是属性的输出表达,不负责属性对象的生命周期管理。

PipeWire中大量对象都会携带properties,例如Core、Client、Node、Port、Device、Stream等。properties内部最终表现为key/value键值集合,而pw_properties_serialize_dict就是把这组键值集合转成可读、可保存、可传递的文本形式。

它和pw_properties_new_dict不同。pw_properties_new_dict是根据dict创建新的pw_properties对象;pw_properties_serialize_dict只是读取dict并输出,不复制、不新增、不修改属性。

它和pw_properties_update_string也不同。pw_properties_update_string是把字符串解析后更新到properties中;pw_properties_serialize_dict是反方向操作,把dict序列化为文本输出。

🌻2.应用场景与用法

pw_properties_serialize_dict

是PipeWire Properties API中用于将spa_dict键值表序列化写入FILE的接口。

它位于PipeWire属性系统的输出路径中。应用或PipeWire内部模块先通过pw_properties_new、pw_properties_set、pw_properties_update等接口构建和维护属性集合,随后可以通过&props->dict取得内部dict视图,再调用pw_properties_serialize_dict把属性内容写入文件、日志、stdout或内存流。

pw_properties_serialize_dict用于把spa_dict中的key/value属性序列化写入FILE流。

函数原型

intpw_properties_serialize_dict(FILE*f,conststructspa_dict*dict,uint32_tflags);

参数说明

FILE*f;

f表示输出目标。

它必须是一个有效的FILE指针,可以来自fopen、tmpfile、open_memstream,也可以是stdout或stderr。该函数只负责向FILE写入内容,不负责打开和关闭FILE。

conststructspa_dict*dict;

dict表示待序列化的属性字典。

它通常来自pw_properties对象内部:

&props->dict

dict中的每一项是一个spa_dict_item,包含key和value。PipeWire properties中的key和value本质上都是字符串。该函数会读取这些字符串并输出,不会修改dict本身。

uint32_tflags;

flags用于控制序列化输出格式。

常见取值包括:

PW_PROPERTIES_FLAG_NL PW_PROPERTIES_FLAG_RECURSE PW_PROPERTIES_FLAG_ENCLOSE PW_PROPERTIES_FLAG_ARRAY PW_PROPERTIES_FLAG_COLORS

也可以传入0,表示使用默认输出格式。

返回值

返回int类型。

非负值表示序列化过程完成。负值通常表示参数无效、写入失败或底层输出异常。工程代码中建议保留返回值检查,尤其是在配置保存、状态导出、调试转储等场景中。

典型处理方式如下:

intres;res=pw_properties_serialize_dict(f,&props->dict,PW_PROPERTIES_FLAG_NL);if(res<0){/* 处理序列化失败 */}

应用场景

第一类场景是调试打印对象属性。

PipeWire对象通常携带大量属性,例如media.class、node.name、application.name、object.id、device.api等。开发者在分析对象创建、路由选择、设备匹配时,经常需要把这些properties完整打印出来。

第二类场景是配置保存。

某些模块或工具会把当前属性状态保存为文本,后续再根据这些文本恢复配置或辅助排查问题。pw_properties_serialize_dict适合把内部dict转换成可读的配置片段。

第三类场景是模块参数输出。

PipeWire模块加载时经常会携带args或properties。模块内部可以把属性序列化输出,方便观察模块最终接收到的配置。

第四类场景是工具类程序导出属性。

类似对象检查、容器导出、配置转储等工具,通常不需要修改properties,只需要把dict转换成文本并写入指定输出目标。

🌻3.调用流程剖析

🌻3.1核心步骤

1.调用者先创建或获得一个pw_properties对象。

2.调用者通过pw_properties_set、pw_properties_setf、pw_properties_update等接口写入key/value属性。

3.pw_properties内部维护一个struct spa_dict,用于统一保存属性字典视图。

4.调用者通过&props->dict取得待序列化dict。

5.调用者准备FILE输出目标,例如文件、stdout、stderr或内存流。

6.调用者根据输出需求设置flags,例如是否换行、是否包裹、是否递归输出。

7.调用pw_properties_serialize_dict,把FILE、dict和flags传入。

8.函数遍历dict中的每个spa_dict_item,依次读取key和value。

9.函数根据value类型和flags决定输出格式,对普通字符串进行必要编码。

10.函数把序列化后的属性内容写入FILE。

11.调用者根据返回值判断序列化是否成功。

12.调用者继续使用properties,或者关闭FILE并释放properties。

🌻3.2调用流程图

🌻3.3生命周期图

🌻4.实战应用案例

下面以“导出Stream属性到文本文件”为例,说明pw_properties_serialize_dict的典型用法。

在PipeWire开发中,很多对象创建时都会传入properties。比如创建Stream时,开发者通常会设置media.type、media.category、media.role、application.name、node.name等属性。调试时,如果只靠单个pw_properties_get逐项读取,效率很低;如果直接把dict序列化输出,就能快速看到当前对象携带的完整属性集合。

示例代码如下:

#include<stdio.h>#include<pipewire/pipewire.h>staticintdump_properties_to_file(constchar*path,conststructpw_properties*props){FILE*f;intres;if(path==NULL||props==NULL)return-EINVAL;f=fopen(path,"w");if(f==NULL)return-errno;res=pw_properties_serialize_dict(f,&props->dict,PW_PROPERTIES_FLAG_NL|PW_PROPERTIES_FLAG_ENCLOSE);fclose(f);returnres;}

这个函数的重点不在文件操作,而在这一句:

res=pw_properties_serialize_dict(f,&props->dict,PW_PROPERTIES_FLAG_NL|PW_PROPERTIES_FLAG_ENCLOSE);

这里传入的是&props->dict,不是props本身。因为pw_properties_serialize_dict面向的是底层spa_dict,而不是pw_properties对象。

如果开发者想导出一个播放Stream的属性,可以先创建properties,再序列化输出:

structpw_properties*props;intres;props=pw_properties_new(PW_KEY_MEDIA_TYPE,"Audio",PW_KEY_MEDIA_CATEGORY,"Playback",PW_KEY_MEDIA_ROLE,"Music",PW_KEY_APPLICATION_NAME,"pipewire-demo-player",PW_KEY_NODE_NAME,"demo.playback.stream",NULL);if(props==NULL)return-ENOMEM;res=dump_properties_to_file("/tmp/stream-props.conf",props);pw_properties_free(props);

这个过程可以拆成三层理解。

第一层是属性构建。

props=pw_properties_new(...);

这一层负责创建properties对象,并把业务属性写进去。

第二层是dict暴露。

&props->dict

这一层把pw_properties内部的键值集合暴露给序列化函数。dict只是视图,不代表新的属性对象,也不应该脱离props生命周期长期保存。

第三层是序列化输出。

pw_properties_serialize_dict(f,&props->dict,flags);

这一层把dict内容写入FILE。写入完成后,原来的props仍然可以继续使用、修改或释放。

再看一个调试场景。假设模块内部已经拿到一组dict,不一定来自pw_properties,也可以直接序列化:

staticvoiddebug_dump_dict(conststructspa_dict*dict){if(dict==NULL)return;pw_properties_serialize_dict(stderr,dict,PW_PROPERTIES_FLAG_NL|PW_PROPERTIES_FLAG_ENCLOSE);}

这种写法适合临时排查对象属性、模块参数、节点属性和设备属性。它不改变dict内容,只是把当前状态打印出来。

工程中使用pw_properties_serialize_dict时要注意四个边界。

第一,dict必须在序列化期间保持有效。如果dict来自pw_properties,那么pw_properties不能提前释放。

第二,FILE由调用者管理。函数不会替你打开文件,也不会替你关闭文件。

第三,flags只影响输出形态,不影响原始properties内容。换行、包裹、数组、颜色等只是显示策略。

第四,不要把它当成反序列化接口。把字符串重新解析成properties,应使用pw_properties_new_string、pw_properties_update_string或带checked版本的接口。

🌻5.一句话总结

pw_properties_serialize_dict是PipeWire属性系统中的dict序列化接口:它读取spa_dict中的key/value属性,根据flags格式化后写入FILE,适合用于属性调试、配置导出和对象状态转储。

← 返回列表