简介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_dequeue_buffer调用流程与实战。要点概括核心功能从PipeWireStream的缓冲队列中取出一个当前周期可处理的pw_buffer。工作机制在process回调中根据Stream方向取得可读或可写Buffer应用处理完成后再通过pw_stream_queue_buffer归还。典型用途音频播放填充PCM数据、音频采集读取PCM数据、视频帧生产、视频帧消费、低延迟媒体处理链路。pw_stream_dequeue_buffer的本质不是“分配Buffer”而是“从Stream已经协商好的Buffer池中取出一个可用Buffer”。Buffer的创建、格式协商、内存映射和生命周期管理不由它完成而是在Stream连接和Buffer协商阶段完成。它通常工作在process回调中。对于播放流dequeue得到的是一个可填充的空Buffer对于采集流dequeue得到的是一个已经带有媒体数据的Buffer。应用只在dequeue到queue这段时间内短暂持有Buffer。它和pw_stream_queue_buffer是一对接口。dequeue负责取出queue负责归还或提交。它和pw_stream_return_buffer也不同return_buffer更偏向于把取出的Buffer直接退回不表示已经完成正常的媒体生产或消费。它和pw_stream_get_time_n也不同后者用于查询Stream时间信息不参与Buffer流转。2.应用场景与用法pw_stream_dequeue_buffer是PipeWire的Stream API中用于从Stream缓冲队列取出可处理Buffer的接口。它位于PipeWire客户端媒体数据路径的核心位置。应用创建Stream、连接目标Node、完成格式和Buffer协商之后PipeWire会在合适的图调度周期触发process回调。应用在process回调中调用pw_stream_dequeue_buffer取得Buffer然后读取或写入媒体数据最后调用pw_stream_queue_buffer把Buffer交还给PipeWire继续调度。pw_stream_dequeue_buffer用于在process回调中取得当前周期可读或可写的Buffer。函数原型structpw_buffer*pw_stream_dequeue_buffer(structpw_stream*stream);参数说明structpw_stream*stream;stream表示已经创建并连接的PipeWireStream对象。它必须是当前媒体处理链路中的有效Stream。正常情况下该函数应在该Stream对应的process回调中调用避免跨Stream错误使用Buffer。Buffer属于触发该回调的Stream不能把一个Stream取出的Buffer交给另一个Stream使用。返回值成功时返回structpw_buffer*表示当前周期可处理的Buffer。返回NULL表示当前没有可用Buffer。它不等价于设备断开也不一定表示Stream错误。工程上遇到NULL时通常直接退出本次process回调等待下一次调度。返回的pw_buffer内部会关联底层spa_buffer。应用真正读写媒体数据时通常会访问structspa_buffer*bufb-buffer;structspa_data*databuf-datas[0];其中datas保存实际媒体数据区chunk描述本次有效数据的offset、size、stride等信息。应用场景第一类场景是音频播放。应用创建PW_DIRECTION_OUTPUT方向的Stream用于向PipeWire图中生产音频数据。process回调触发后应用通过pw_stream_dequeue_buffer取出空闲Buffer把PCM数据写入datas然后设置chunk信息最后调用pw_stream_queue_buffer提交给PipeWire播放链路。第二类场景是音频采集。应用创建PW_DIRECTION_INPUT方向的Stream用于从PipeWire图中消费音频数据。process回调触发后应用通过pw_stream_dequeue_buffer取出已经带有采集数据的Buffer从datas中读取PCM数据处理完成后调用pw_stream_queue_buffer归还Buffer。第三类场景是视频处理。PipeWire不仅处理音频也处理视频帧。视频源、摄像头采集、屏幕共享、视频播放等场景也可以通过类似的Buffer模型在process回调中交换帧数据。第四类场景是低延迟实时处理。在实时音频、虚拟声卡、回声消除、DSP处理、音视频桥接等场景中Buffer不能长时间停留在应用侧。dequeue、处理、queue应尽量在一个短路径内完成减少额外缓存和调度延迟。3.调用流程剖析3.1核心步骤1.应用创建pw_stream对象并注册process、add_buffer、remove_buffer等事件回调。2.应用调用pw_stream_connect连接到PipeWire图中的目标对象并完成媒体格式、参数和Buffer协商。3.PipeWire完成Buffer准备后通过add_buffer事件把Buffer登记到Stream内部缓冲池。4.Stream进入PAUSED或STREAMING状态后PipeWire图调度开始驱动process回调。5.应用在process回调中调用pw_stream_dequeue_buffer从Stream可用队列中取出一个pw_buffer。6.如果返回NULL说明当前周期没有可用Buffer本次process应快速返回。7.如果是播放/输出流应用把音频或视频数据写入Buffer并设置chunk有效数据范围。8.如果是采集/输入流应用从Buffer中读取已经到达的音频或视频数据。9.处理完成后应用调用pw_stream_queue_buffer把Buffer归还给Stream。10.PipeWire在后续图调度周期中继续复用该Buffer直到remove_buffer事件或Stream销毁。3.2调用流程图3.3生命周期图4.实战应用案例下面以“播放流填充PCM数据”为例说明pw_stream_dequeue_buffer在真实开发中的用法。播放流的核心目标是PipeWire通知应用需要数据时应用取出一个空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*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);}这个案例中pw_stream_dequeue_buffer只负责取出Buffer。真正的数据来源可能是播放器解码后的PCM、网络音频流、DSP输出、测试音源或者上游环形缓冲区。工程上要特别注意三点。第一返回NULL时不要误判为设备异常。低延迟图调度中当前周期没有Buffer是可能出现的正常情况。第二处理完必须归还Buffer。如果dequeue之后长期不queueStream可用Buffer会越来越少最终导致卡顿、延迟增大或数据流停止。第三process回调要保持轻量。不要在里面做阻塞文件读写、复杂锁等待、长时间内存分配或耗时算法。对于实时音频链路process回调越短系统越稳定。采集流的使用方式类似只是Buffer方向相反。播放流是“应用写Buffer”采集流是“应用读Buffer”。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;/* * 这里通常把采集到的数据送入录音文件、编码器、 * 回声消除算法、语音识别前处理或网络发送模块。 */(void)src;(void)size;pw_stream_queue_buffer(app-stream,b);}播放和采集的代码结构很像但语义不同播放流中Buffer是待填充的数据载体。采集流中Buffer是已经包含有效数据的数据载体。这也是pw_stream_dequeue_buffer最容易被误解的地方。它不是单纯的“读取函数”也不是单纯的“写入函数”而是根据Stream方向交出当前周期可处理的Buffer。5.一句话总结pw_stream_dequeue_buffer是PipeWireStream数据路径中的取Buffer接口在process回调中取出当前周期可用的pw_buffer播放流用它填充数据采集流用它消费数据处理完成后必须用pw_stream_queue_buffer归还。