)
简介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结束应用侧持有期让Buffer重新进入PipeWire图调度。典型用途音频播放提交PCM数据、音频采集归还Buffer、视频帧提交、屏幕共享帧流转、低延迟媒体处理。pw_stream_queue_buffer的本质不是“写数据函数”也不是“播放函数”而是StreamBuffer生命周期中的“归还/提交接口”。它负责把Buffer从应用持有状态交还给Stream和图调度系统。它通常和pw_stream_dequeue_buffer成对出现。dequeue_buffer负责取出Bufferqueue_buffer负责把处理完成的Buffer交回去。对于播放流queue_buffer表示应用已经把数据写入Buffer可以提交给PipeWire继续处理对于采集流queue_buffer表示应用已经读取完Buffer可以归还给PipeWire复用。它和pw_stream_return_buffer也不同。queue_buffer表达的是正常数据路径中的提交或归还return_buffer更适合直接退回不继续提交的数据场景。工程上最常见、最核心的主链路仍然是dequeue_buffer→处理数据→queue_buffer。2.应用场景与用法pw_stream_queue_buffer是PipeWireStream API中用于提交或归还StreamBuffer的接口。它位于PipeWire客户端数据处理路径的末端。应用在process回调中取得Buffer后只能在本次持有期间读写Buffer内容。一旦调用pw_stream_queue_bufferBuffer所有权就回到PipeWireStream应用不应继续修改该Buffer。pw_stream_queue_buffer用于把处理完成的pw_buffer提交或归还给PipeWireStream。函数原型voidpw_stream_queue_buffer(structpw_stream*stream,structpw_buffer*buffer);参数说明structpw_stream*stream;stream表示当前Buffer所属的PipeWireStream对象。它必须和Buffer来自同一个Stream。不能从一个Stream中dequeue出Buffer再queue到另一个Stream中。Buffer归属错误会破坏Stream内部队列关系严重时会导致数据错乱、卡顿或异常。structpw_buffer*buffer;buffer表示需要提交或归还的Buffer对象。正常情况下这个Buffer来自pw_stream_dequeue_buffer的返回值。应用在持有Buffer期间可以根据Stream方向写入或读取数据。处理完成后必须调用pw_stream_queue_buffer把它交还给PipeWire。对于播放流buffer通常已经被应用写入音频或视频数据。对于采集流buffer通常已经被应用读取完成需要重新归还给Stream复用。返回值该函数没有返回值。void它不通过返回值表示提交是否成功。工程上判断Buffer流转是否正常主要依赖Stream状态、process回调是否持续触发、Buffer是否持续dequeue、音视频链路是否稳定。应用场景第一类场景是音频播放。播放器、解码器或音频算法模块在process回调中取出Buffer把PCM数据写入Buffer的spa_data区域然后调用pw_stream_queue_buffer提交给PipeWire。后续由PipeWire图调度把数据送入目标节点例如声卡播放节点、混音节点或虚拟设备节点。第二类场景是音频采集。录音应用从PipeWire取得已经带有采集数据的Buffer读取PCM数据后需要调用pw_stream_queue_buffer归还Buffer。否则Buffer会一直停留在应用侧Stream内部可用Buffer数量会减少。第三类场景是视频帧提交。摄像头、屏幕共享、视频源节点等场景中应用可能生产一帧视频数据。写入Buffer后通过pw_stream_queue_buffer提交给PipeWire图后续由消费者节点继续处理。第四类场景是低延迟实时处理。在实时音频、车载语音链路、回声消除、虚拟声卡、音视频桥接等场景中queue_buffer必须及时调用。应用侧持有Buffer时间越短媒体图越容易保持稳定、低延迟和可预测。3.调用流程剖析3.1核心步骤1.应用创建pw_stream对象并注册process回调。2.应用调用pw_stream_connect连接目标节点完成格式协商和Buffer协商。3.PipeWire通过add_buffer事件把Buffer加入Stream内部管理。4.图调度触发process回调表示当前Stream需要处理数据。5.应用调用pw_stream_dequeue_buffer取得当前周期可处理的pw_buffer。6.如果是播放流应用向Buffer写入PCM数据或视频帧数据。7.如果是采集流应用从Buffer读取已经到达的音频或视频数据。8.应用设置或检查spa_chunk中的offset、size、stride等有效数据描述。9.应用调用pw_stream_queue_buffer把Buffer提交或归还给Stream。10.Buffer离开应用持有状态重新进入PipeWire图调度或可复用队列。11.下一次process回调触发后应用继续执行dequeue→处理→queue循环。3.2调用流程图3.3生命周期图4.实战应用案例下面以“音频播放流提交PCM数据”为例说明pw_stream_queue_buffer的典型使用方式。在播放链路中应用负责生产数据。process回调触发后应用取出一个Buffer填入PCM数据然后调用pw_stream_queue_buffer提交。#includepipewire/pipewire.h#includespa/utils/defs.hstructapp_data{structpw_stream*stream;uint32_tframe_size;};staticuint32_tfill_pcm_data(void*dst,uint32_tmax_bytes){/* * 实际工程中这里通常来自 * 1.解码器输出PCM * 2.环形缓冲区 * 3.音频算法输出 * 4.测试音源 */(void)dst;(void)max_bytes;return0;}staticvoidon_process(void*userdata){structapp_data*appuserdata;structpw_buffer*b;structspa_buffer*buf;structspa_data*data;uint32_tn_bytes;bpw_stream_dequeue_buffer(app-stream);if(bNULL)return;bufb-buffer;databuf-datas[0];if(data-dataNULL||data-chunkNULL){pw_stream_queue_buffer(app-stream,b);return;}n_bytesfill_pcm_data(data-data,data-maxsize);data-chunk-offset0;data-chunk-sizen_bytes;data-chunk-strideapp-frame_size;pw_stream_queue_buffer(app-stream,b);}这段代码的关键点不在fill_pcm_data而在Buffer所有权边界。pw_stream_dequeue_buffer返回之后Buffer暂时由应用持有。应用可以写入data-data也可以设置data-chunk中的有效数据范围。pw_stream_queue_buffer调用之后Buffer重新交给PipeWire应用不应再继续访问或修改该Buffer。对于采集流代码结构类似但语义相反。采集流中Buffer已经包含PipeWire送来的数据。应用读取完成后通过pw_stream_queue_buffer归还。#includepipewire/pipewire.h#includespa/utils/defs.hstaticvoidhandle_capture_data(constvoid*src,uint32_tsize){/* * 实际工程中这里通常接入 * 1.录音文件写入 * 2.语音识别前处理 * 3.回声消除算法 * 4.网络发送模块 */(void)src;(void)size;}staticvoidon_capture_process(void*userdata){structapp_data*appuserdata;structpw_buffer*b;structspa_buffer*buf;structspa_data*data;constuint8_t*src;uint32_tsize;bpw_stream_dequeue_buffer(app-stream);if(bNULL)return;bufb-buffer;databuf-datas[0];if(data-dataNULL||data-chunkNULL){pw_stream_queue_buffer(app-stream,b);return;}srcSPA_PTROFF(data-data,data-chunk-offset,uint8_t);sizedata-chunk-size;handle_capture_data(src,size);pw_stream_queue_buffer(app-stream,b);}播放流和采集流都调用pw_stream_queue_buffer但含义不同。播放流中它表示“我已经把数据写好了可以提交给PipeWire”。采集流中它表示“我已经把数据读完了这个Buffer可以复用”。工程开发中最容易出现的问题是dequeue之后忘记queue。短时间看可能只是偶发卡顿长时间运行后会表现为Buffer耗尽、process回调异常、延迟升高、音频停顿或视频掉帧。另一个常见问题是queue之后继续修改Buffer。queue_buffer调用完成后Buffer已经不再属于应用侧继续写入可能导致下游节点读到不一致的数据。正确做法是所有数据写入、chunk设置、状态标记都必须在queue_buffer之前完成。实时音频场景还要避免在dequeue和queue之间做耗时操作。不要在这个区间做阻塞文件IO、网络等待、复杂锁竞争、大内存分配或长时间算法处理。更稳妥的做法是把耗时任务放到非实时线程process回调只做短路径数据搬运和必要的格式处理。5.一句话总结pw_stream_queue_buffer是PipeWireStreamBuffer生命周期中的提交/归还接口应用通过dequeue_buffer取得Buffer完成读写后必须用queue_buffer交还给PipeWire让Buffer重新进入图调度和复用循环。