news 2026/8/26 18:25:41

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux PipeWire深度解析之pw_stream_queue_buffer调用流程与实战(八十二)

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

要点概括

  • 核心功能:把应用已经处理完成的pw_buffer重新提交给PipeWireStream。

  • 工作机制:应用先通过pw_stream_dequeue_buffer取得Buffer,读写媒体数据后,再通过pw_stream_queue_buffer结束本次持有,让PipeWire继续调度该Buffer。

  • 典型用途:播放流提交已填充PCM数据,采集流归还已读取PCM数据,视频流提交或回收帧Buffer。

pw_stream_queue_buffer的本质是“提交Buffer”或“归还Buffer”,不是“分配Buffer”,也不是“拷贝Buffer”。Buffer本身来自Stream连接后的Buffer协商阶段,应用只是临时取得并处理它。

它和pw_stream_dequeue_buffer是一对接口。dequeue负责取出,queue负责交回。应用在process回调中拿到Buffer后,必须在合适时机把Buffer重新交给PipeWire,否则Buffer会长期停留在应用侧,影响后续调度。

它和pw_stream_return_buffer也不同。pw_stream_return_buffer表示“这个Buffer没有被使用,直接退回队列”;pw_stream_queue_buffer表示“这个Buffer已经完成本周期处理,可以进入正常播放提交或采集复用路径”。

它和pw_stream_flush也不同。flush用于清理Stream队列中的残留数据,而queue_buffer只处理一个具体pw_buffer的提交或回收。

🌻2.应用场景与用法

pw_stream_queue_buffer

是PipeWireStream API中用于提交或回收Stream Buffer的接口。

它位于PipeWire客户端数据路径的末端。应用在process回调中调用pw_stream_dequeue_buffer取得Buffer,完成PCM数据写入、采集数据读取或视频帧处理后,再调用pw_stream_queue_buffer把Buffer交还给PipeWire。之后PipeWire可以把播放数据送入下游Sink,也可以把采集Buffer重新放回复用队列。

pw_stream_queue_buffer用于把已经处理完成的pw_buffer提交回PipeWireStream。

函数原型

intpw_stream_queue_buffer(structpw_stream*stream,structpw_buffer*buffer);

参数说明

structpw_stream*stream;

stream表示当前处理链路对应的PipeWireStream对象。

这个Stream必须与Buffer属于同一条数据链路。也就是说,Buffer应该来自同一个Stream的pw_stream_dequeue_buffer调用,不能把一个Stream取出的Buffer提交给另一个Stream。

structpw_buffer*buffer;

buffer表示应用已经处理完成的Buffer。

对于播放流,这个Buffer通常已经写入PCM数据,并设置了spa_chunk中的offset、size、stride等字段。

对于采集流,这个Buffer通常已经被应用读取完成,可以交还给PipeWire继续复用。

返回值

成功时返回0。

失败时返回负错误码。常见原因包括stream无效、buffer不属于该Stream、Buffer状态不符合当前提交路径等。

工程上通常不在实时process回调中做复杂错误恢复。如果queue失败,应记录错误状态,并在控制线程或状态回调中处理Stream重建、断开或退出。

应用场景

第一类场景是音频播放。

应用创建PW_DIRECTION_OUTPUT方向的Stream。process回调触发后,应用取出Buffer,填充PCM数据,设置chunk有效数据范围,然后调用pw_stream_queue_buffer提交。PipeWire后续把这个Buffer送入Graph,由下游Sink继续处理。

第二类场景是音频采集。

应用创建PW_DIRECTION_INPUT方向的Stream。process回调触发后,应用取出包含采集数据的Buffer,读取PCM数据,处理完成后调用pw_stream_queue_buffer归还。PipeWire后续可以复用这个Buffer承载新的采集数据。

第三类场景是视频帧处理。

摄像头采集、屏幕共享、视频源输出、视频DSP处理等场景,也通过dequeue/queue模式交换帧Buffer。queue_buffer负责结束应用侧对该帧Buffer的持有。

第四类场景是低延迟实时链路。

实时音频、虚拟声卡、回声消除、DSP处理等链路要求Buffer快速流转。应用应尽量在process回调中完成dequeue、处理、queue,减少额外线程切换和缓存积压。

🌻3.调用流程剖析

🌻3.1核心步骤

1.应用创建pw_stream对象,并注册process事件回调。

2.应用调用pw_stream_connect连接PipeWire图中的目标对象。

3.Stream完成格式协商和Buffer协商后,PipeWire通过add_buffer事件把pw_buffer交给Stream管理。

4.Graph调度开始后,PipeWire触发Stream的process回调。

5.应用在process回调中调用pw_stream_dequeue_buffer取得一个可处理Buffer。

6.播放流中,应用把PCM数据写入Buffer,并设置chunk->offset、chunk->size、chunk->stride。

7.采集流中,应用从Buffer读取PCM数据,并完成录音、编码、算法处理或网络发送。

8.应用调用pw_stream_queue_buffer(stream, buffer)提交该Buffer。

9.对于播放流,Buffer进入提交路径,后续由PipeWireGraph送往下游Sink消费。

10.对于采集流,Buffer进入回收路径,后续重新参与下一轮Buffer复用。

11.Buffer回到PipeWire调度体系后,应用不应继续访问该Buffer中的媒体数据。

12.Stream断开或销毁时,PipeWire通过remove_buffer事件结束Buffer生命周期。

🌻3.2调用流程图

🌻3.3生命周期图

🌻4.实战应用案例

下面以“播放流提交PCM数据”为例,说明pw_stream_queue_buffer在真实开发中的位置。

播放流的核心逻辑是:process回调触发后,应用取出空Buffer,写入PCM数据,设置有效数据范围,然后提交给PipeWire。

structapp_data{structpw_stream*stream;uint32_tframe_size;};staticuint32_tfill_pcm_data(void*dst,uint32_tmax_bytes){/* * 这里通常从解码器、环形缓冲区、音频算法输出、 * 网络接收缓存或测试音源中获取PCM数据。 */return0;}staticvoidon_process(void*userdata){structapp_data*app=userdata;structpw_buffer*b;structspa_buffer*buf;structspa_data*data;uint32_tn_bytes;b=pw_stream_dequeue_buffer(app->stream);if(b==NULL)return;buf=b->buffer;data=&buf->datas[0];if(data->data==NULL||data->chunk==NULL){pw_stream_return_buffer(app->stream,b);return;}n_bytes=fill_pcm_data(data->data,data->maxsize);data->chunk->offset=0;data->chunk->size=n_bytes;data->chunk->stride=app->frame_size;pw_stream_queue_buffer(app->stream,b);}

这个案例中,pw_stream_queue_buffer是播放数据进入PipeWireGraph前的关键提交点。

在调用它之前,Buffer仍然处于应用侧处理阶段,应用可以写入PCM数据并设置chunk信息。

调用它之后,Buffer重新交给PipeWire调度体系,应用不应继续修改该Buffer的数据区。否则可能破坏下游Sink正在消费的数据。

对于采集流,代码结构类似,但语义相反。应用不是写入Buffer,而是读取Buffer中的采集数据,读取完成后再调用pw_stream_queue_buffer归还。

staticvoidhandle_capture_data(constvoid*src,uint32_tsize){/* * 这里通常把采集数据送入录音文件、编码器、 * 语音识别前处理、回声消除算法或网络发送模块。 */}staticvoidon_capture_process(void*userdata){structapp_data*app=userdata;structpw_buffer*b;structspa_buffer*buf;structspa_data*data;constuint8_t*src;uint32_tsize;b=pw_stream_dequeue_buffer(app->stream);if(b==NULL)return;buf=b->buffer;data=&buf->datas[0];if(data->data==NULL||data->chunk==NULL){pw_stream_return_buffer(app->stream,b);return;}src=SPA_PTROFF(data->data,data->chunk->offset,uint8_t);size=data->chunk->size;handle_capture_data(src,size);pw_stream_queue_buffer(app->stream,b);}

播放和采集都调用pw_stream_queue_buffer,但它们的含义不同。

播放流中,queue_buffer表示“我已经把数据写好,请PipeWire继续播放”。

采集流中,queue_buffer表示“我已经把数据读完,请PipeWire继续复用”。

工程上要注意四点。

第一,dequeue成功后,正常路径必须queue或return。长期不归还Buffer会减少可用Buffer数量,最终导致卡顿、延迟升高或数据流停顿。

第二,播放流必须正确设置chunk->size。size表示本次Buffer中的有效字节数。设置过大可能导致播放脏数据,设置过小可能导致声音断续。

第三,process回调要保持轻量。尤其启用实时处理时,不要在回调中执行阻塞IO、长时间锁等待、复杂内存分配或耗时日志输出。

第四,queue之后不要继续访问Buffer数据。Buffer已经重新进入PipeWire调度体系,后续可能被下游消费、复用或在其他周期重新分配给应用。

🌻5.一句话总结

pw_stream_queue_buffer是PipeWireStream数据路径中的提交接口:播放流用它提交已填充Buffer,采集流用它归还已读取Buffer,它结束应用侧对Buffer的持有,让PipeWire继续完成Graph调度和Buffer复用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/26 18:24:07

MySQL主从复制:核心原理与优化策略

MySQL 主从复制原理MySQL 主从复制是一种常见的数据同步机制,通过将主库(Master)的写操作记录在二进制日志(binlog)中,从库(Slave)远程拉取并重放这些日志,最终实现数据的…

作者头像 李华
网站建设 2026/8/26 18:22:30

Java AI转型实战(七):Output Parser,让AI输出结构化数据

从“自然语言”到“结构化数据”,让程序真正读懂AI的回复01 引子经过前六篇的实战,我们的AI已经能聊天、能记住上下文、能流式输出、能调用Java方法。但有一个问题始终存在:AI返回的是自然语言文本,程序无法直接使用。比如让AI“提…

作者头像 李华
网站建设 2026/8/26 18:20:40

c语言续4(比特)

memecpy(void*dest,const void* source,siez_t num);函数memcpy(arr2,arr1,10);从数组arr1拷贝10个字符到arr2空间内。一、为什么使用文件?使数据的持久化。二、什么是文件?磁盘上的文件是文件;程序文件、数据文件。三、程序文件,…

作者头像 李华
网站建设 2026/8/26 18:17:24

企业微信主体变更公证书办理全解析:适用场景、材料规范、踩坑点与Python信息校验脚本

摘要企业微信在企业重组、主体注销、业务划转、集团架构调整场景下,经常需要做跨主体变更认证。当新旧主体统一社会信用代码不一致时,平台要求提供《企业微信主体变更认证申请函》配套公证书,否则无法完成账号权属变更。很多运维人员实操中遇…

作者头像 李华
网站建设 2026/8/26 18:13:47

【GitHub 备份操作手册】备份不再出错

GitHub 备份操作手册&#xff08;从零到进阶&#xff09; 适用于 ROS / ROS2 / 各类工程项目的代码备份。 文中 <你的GitHub用户名>、<你的仓库名>、<你的项目目录> 均为占位符&#xff0c;使用时替换成自己的信息。 文章目录GitHub 备份操作手册&#xff08…

作者头像 李华
网站建设 2026/8/26 18:09:53

MoneyPrinterTurbo 视频生成效果实测与能力边界深度解析

随着AIGC技术的飞速发展&#xff0c;自动化视频生成工具逐渐成为内容创作者的“效率外挂”。本文将对近期备受关注的 MoneyPrinterTurbo​ 项目进行全方位的实测与评测。我们将从核心架构、多风格文案转视频、AI配音、素材匹配、画质输出、典型场景案例以及系统性能等多个维度展…

作者头像 李华