Linux PipeWire深度解析之pw_stream_dequeue_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_dequeue_buffer调用流程与实战。要点概括核心功能从PipeWireStream的Buffer队列中取出一个当前周期可处理的pw_buffer。工作机制Stream完成格式协商和Buffer协商后PipeWire在process回调中通知应用处理数据应用通过该函数取出Buffer读写完成后再归还。典型用途播放流填充PCM数据、录音流读取PCM数据、视频帧处理、低延迟媒体链路中的Buffer周转。pw_stream_dequeue_buffer的本质是“取Buffer”不是“创建Buffer”。Buffer的分配、映射、数量和格式在Stream连接与协商阶段完成该函数只是在运行阶段把可用Buffer交给应用侧处理。它也不是“播放函数”或“录音函数”。同一个接口在播放流和录音流中语义不同播放流中取到的是待填充Buffer录音流中取到的是已包含采集数据的Buffer。它通常和pw_stream_queue_buffer配套使用。pw_stream_dequeue_buffer负责取出Bufferpw_stream_queue_buffer负责把Buffer交还给PipeWire。只dequeue不queue会导致可用Buffer耗尽最终引起卡顿或数据流停滞。2.应用场景与用法pw_stream_dequeue_buffer是PipeWireStream API中用于从Stream队列取出可用Buffer的接口。它位于PipeWire客户端数据处理路径中。应用创建pw_stream并连接到PipeWireGraph后PipeWire会为该Stream协商媒体格式和Buffer。运行时Graph调度触发process回调应用在回调中调用pw_stream_dequeue_buffer取得Buffer然后进行读写处理。pw_stream_dequeue_buffer用于在process回调中取得当前周期可处理的pw_buffer。函数原型structpw_buffer*pw_stream_dequeue_buffer(structpw_stream*stream);参数说明structpw_stream*stream;stream表示已经创建并连接的PipeWireStream对象。该参数必须指向有效的pw_stream。正常情况下应用应在该Stream对应的process回调中调用pw_stream_dequeue_buffer。不要在无关线程中随意调用也不要把一个Stream中取出的Buffer交给另一个Stream使用。返回值成功时返回structpw_buffer*表示当前可处理的Buffer。返回NULL表示当前没有可用Buffer。它不一定表示错误也不一定表示设备断开。工程上通常直接退出本次process回调等待下一次Graph调度。pw_buffer内部关联spa_buffer。应用真正访问媒体数据时通常会继续访问structspa_buffer*bufb-buffer;structspa_data*databuf-datas[0];其中data-data表示实际数据内存地址。data-maxsize表示当前Buffer可写入或可读取的最大空间。data-chunk描述本次有效数据区域包括offset、size、stride等字段。应用场景第一类场景是播放流。应用创建PW_DIRECTION_OUTPUT方向的Stream。process回调触发后应用调用pw_stream_dequeue_buffer取得一个可写Buffer把PCM数据写入Buffer并设置chunk有效数据范围最后调用pw_stream_queue_buffer提交给PipeWire。第二类场景是录音流。应用创建PW_DIRECTION_INPUT方向的Stream。process回调触发后应用调用pw_stream_dequeue_buffer取得一个已经包含采集数据的Buffer从Buffer中读取PCM数据处理完成后调用pw_stream_queue_buffer归还。第三类场景是视频帧处理。PipeWire不仅处理音频也处理视频。摄像头采集、屏幕共享、视频源输出等场景中也可以通过pw_stream_dequeue_buffer取得视频帧Buffer。第四类场景是实时低延迟处理。在虚拟声卡、回声消除、音频DSP、语音前处理、音视频桥接等场景中应用需要在process回调中快速完成dequeue、处理、queue避免阻塞Graph实时调度。3.调用流程剖析3.1核心步骤1.应用创建pw_stream对象并注册process事件回调。2.应用调用pw_stream_connect连接Stream声明方向、目标、格式参数和Buffer需求。3.PipeWire完成格式协商和Buffer协商为Stream准备可复用Buffer。4.Stream进入运行状态后PipeWireGraph按照调度周期触发process回调。5.应用在process回调中调用pw_stream_dequeue_buffer。6.函数从Stream内部可用队列中取出一个pw_buffer。7.如果返回NULL说明当前没有可用Buffer本次process回调应直接返回。8.如果是播放流应用向Buffer写入待播放数据并设置chunk-offset、chunk-size和chunk-stride。9.如果是录音流应用从Buffer读取已采集数据并根据chunk描述确定有效数据范围。10.应用调用pw_stream_queue_buffer把Buffer归还给PipeWire。11.PipeWire在后续Graph调度周期中继续复用该Buffer。12.Stream销毁或断开时相关Buffer随Stream生命周期释放。3.2调用流程图3.3生命周期图4.实战应用案例下面以播放流为例说明pw_stream_dequeue_buffer的典型用法。播放流的目标是PipeWire需要数据时应用从Stream中取出一个Buffer把PCM数据写进去然后归还给PipeWire继续播放。#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);}这段代码体现了播放流的主链路应用先通过pw_stream_dequeue_buffer取出Buffer。然后通过b-buffer取得spa_buffer。再通过buf-datas[0]取得实际数据区。写入PCM数据后应用需要设置chunk信息。chunk-offset表示有效数据从Buffer中的哪个偏移开始chunk-size表示本次有效数据大小chunk-stride通常表示一帧数据的字节跨度。最后必须调用pw_stream_queue_buffer归还Buffer。这个动作表示应用已经完成本轮Buffer处理PipeWire可以继续把该Buffer放回Graph数据流中。录音流的代码结构类似但数据方向相反。录音流中应用不是向Buffer写数据而是从Buffer读取已经采集到的数据。#includepipewire/pipewire.h#includespa/utils/defs.hstructcapture_data{structpw_stream*stream;};staticvoidhandle_capture_data(constvoid*src,uint32_tsize){/* * 实际项目中这里通常接入 * 1.录音文件写入模块 * 2.音频编码器 * 3.语音识别前处理 * 4.回声消除或降噪算法 */(void)src;(void)size;}staticvoidon_capture_process(void*userdata){structcapture_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);}播放流和录音流的关键区别在于Buffer语义播放流中dequeue得到的是应用要填充的数据容器。录音流中dequeue得到的是PipeWire已经写入采集数据的数据容器。所以pw_stream_dequeue_buffer不能简单理解成“读函数”或“写函数”。它只是把当前周期可处理的Buffer交给应用真正读还是写由Stream方向决定。工程使用时要注意四个问题。第一process回调中不要执行阻塞操作。不要在里面做磁盘阻塞读写、网络等待、复杂锁等待或长时间内存分配。第二dequeue失败时直接返回。返回NULL通常表示当前没有可用Buffer不要把它直接当成致命错误。第三dequeue成功后必须归还。正常路径归还使用pw_stream_queue_buffer。异常路径也要避免丢失Buffer否则会破坏Stream的Buffer循环。第四chunk信息必须正确。播放流中如果chunk-size设置错误PipeWire可能播放无效数据、静音数据或产生异常音频。录音流中如果忽略chunk-offset和chunk-size应用可能读取到无效区域。5.一句话总结pw_stream_dequeue_buffer是PipeWireStream运行阶段的取Buffer接口它在process回调中把当前周期可用的pw_buffer交给应用播放流用来填充数据录音流用来读取数据处理完成后必须通过pw_stream_queue_buffer归还。