news 2026/8/7 11:01:15

Linux PipeWire深度解析之pw_stream_dequeue_buffer调用流程与实战(四十九)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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的缓冲队列中取出一个当前周期可处理的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*buf=b->buffer;structspa_data*data=&buf->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*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_queue_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_dequeue_buffer只负责取出Buffer。真正的数据来源可能是播放器解码后的PCM、网络音频流、DSP输出、测试音源或者上游环形缓冲区。

工程上要特别注意三点。

第一,返回NULL时不要误判为设备异常。低延迟图调度中,当前周期没有Buffer是可能出现的正常情况。

第二,处理完必须归还Buffer。如果dequeue之后长期不queue,Stream可用Buffer会越来越少,最终导致卡顿、延迟增大或数据流停止。

第三,process回调要保持轻量。不要在里面做阻塞文件读写、复杂锁等待、长时间内存分配或耗时算法。对于实时音频链路,process回调越短,系统越稳定。

采集流的使用方式类似,只是Buffer方向相反。播放流是“应用写Buffer”,采集流是“应用读Buffer”。

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_queue_buffer(app->stream,b);return;}src=SPA_PTROFF(data->data,data->chunk->offset,uint8_t);size=data->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归还。

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

Godot动画状态机实战:从AnimationTree到角色动画控制

1. 项目概述在游戏开发中,动画系统是赋予角色灵魂的关键。一个流畅、响应迅速且逻辑清晰的动画表现,直接决定了玩家的操作手感和游戏体验。如果你还在用一堆零散的AnimationPlayer节点,通过play()和queue()手动拼接动画,那么恭喜你…

作者头像 李华
网站建设 2026/8/7 11:00:25

PSoC 6 BSP定制指南:从硬件配置到构建系统集成

1. 从零到一:为什么我们需要一个自定义的 BSP? 如果你玩过 Infineon(英飞凌)的 PSoC™ 6 系列 MCU,比如 CY8CPROTO-062-4343W 或者 CY8CKIT-062S2-43012 这些开发板,你大概率是从 ModusToolbox™ 或者 PSoC…

作者头像 李华
网站建设 2026/8/7 11:00:00

构建健壮系统:如何通过输入验证与容错机制实现稳定可控输出

1. 先搞清楚这句话到底在说什么,以及它为什么值得技术人关注 “喜怒哀乐,皆由己出”这句话,听起来像一句人生格言,和写代码、搞技术似乎没什么关系。但如果你把它放到软件系统、数据流程或者团队协作的语境里,就会发现…

作者头像 李华
网站建设 2026/8/7 10:59:51

阿里:迈向真实世界的GUI智能体

📖标题:Qwen-UI-Agent Technical Report: Toward Next-Generation Real-World Centric Foundation GUI Agents 🌐来源:arXiv, 2607.28227v1 🛎️文章简介 🔸研究问题:如何缩小GUI智能体在模拟基…

作者头像 李华
网站建设 2026/8/7 10:59:23

从零实现多层感知机:Python手写神经网络实战

1. 项目概述 "动手学深度学习笔记:多层感知机的从零开始实现"这个标题让我想起了自己刚开始接触深度学习时的困惑。很多教程要么过于理论化,要么直接调用高级框架API,缺少从底层构建的完整过程。本文将带你用Python和NumPy从零搭建…

作者头像 李华
网站建设 2026/8/7 10:57:18

STM32 FreeRTOS与LwIP整合实战:构建高效UDP通信框架

1. 项目缘起:为什么要在STM32上搞FreeRTOSLwIP的UDP? 最近在做一个工业数据采集器的项目,核心需求是把分布在产线各处的传感器数据,实时、可靠地汇总到一台本地服务器上。传感器节点用的是STM32F407,带以太网MAC&#…

作者头像 李华