简介: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_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复用。