Linux PipeWire深度解析之pw_stream_events调用流程与实战(八十)
2026/8/24 13:48:06 网站建设 项目流程

简介: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_events调用流程与实战。

要点概括

  • 核心功能:描述PipeWireStream生命周期中的事件回调入口。

  • 工作机制:应用把一组回调函数注册到Stream,PipeWire在状态变化、参数变化、Buffer创建、Graph调度和销毁阶段主动回调应用。

  • 典型用途:监听Stream状态、处理Buffer、响应格式变化、接收process调度、清理Stream资源。

pw_stream_events本质上不是普通函数,而是PipeWireStream API中的事件回调表。它不直接执行媒体处理,也不直接读写音频数据,而是告诉PipeWire:Stream在不同阶段发生事件时,应该调用应用侧的哪个函数。

它和pw_stream_dequeue_buffer、pw_stream_queue_buffer不同。pw_stream_events解决“什么时候通知应用”的问题;pw_stream_dequeue_buffer解决“从哪里取Buffer”的问题;pw_stream_queue_buffer解决“处理完成后如何归还Buffer”的问题。

它和pw_stream_add_listener关系更近。pw_stream_events描述事件表内容,pw_stream_add_listener负责把事件表绑定到指定Stream。使用pw_stream_new_simple时,也可以直接传入pw_stream_events,由创建接口内部完成事件绑定。

🌻2.应用场景与用法

pw_stream_events

是PipeWireStream API中用于描述Stream事件回调集合的接口结构。

它位于PipeWire客户端侧Stream控制路径和数据路径之间。应用创建Stream后,需要通过pw_stream_events接收Stream生命周期事件。状态变化、格式参数变化、Buffer创建、process调度、Drain完成和Stream销毁,都会通过这组回调通知应用。

pw_stream_events用于注册Stream生命周期事件和媒体处理事件回调。

函数原型

pw_stream_events不是C函数,而是C结构体。它的真实接口原型如下:

#definePW_VERSION_STREAM_EVENTS2structpw_stream_events{uint32_tversion;void(*destroy)(void*data);void(*state_changed)(void*data,enumpw_stream_stateold,enumpw_stream_statestate,constchar*error);void(*control_info)(void*data,uint32_tid,conststructpw_stream_control*control);void(*io_changed)(void*data,uint32_tid,void*area,uint32_tsize);void(*param_changed)(void*data,uint32_tid,conststructspa_pod*param);void(*add_buffer)(void*data,structpw_buffer*buffer);void(*remove_buffer)(void*data,structpw_buffer*buffer);void(*process)(void*data);void(*drained)(void*data);void(*command)(void*data,conststructspa_command*command);void(*trigger_done)(void*data);};

实际使用时通常写成:

staticconststructpw_stream_eventsstream_events={.version=PW_VERSION_STREAM_EVENTS,.state_changed=on_state_changed,.param_changed=on_param_changed,.add_buffer=on_add_buffer,.remove_buffer=on_remove_buffer,.process=on_process,.destroy=on_destroy,};

参数说明

uint32_tversion;

表示事件结构体版本。应用应设置为PW_VERSION_STREAM_EVENTS,用于保证应用侧事件表和当前PipeWire库版本匹配。

void(*destroy)(void*data);

Stream销毁时触发。应用可以在这里清理和Stream绑定的业务资源,但不能继续把旧Stream当作有效对象使用。

void(*state_changed)(void*data,enumpw_stream_stateold,enumpw_stream_statestate,constchar*error);

Stream状态变化时触发。old表示旧状态,state表示新状态,error用于描述错误状态下的原因。

常见状态包括UNCONNECTED、CONNECTING、PAUSED、STREAMING和ERROR。工程调试中,这个回调非常关键,可以判断Stream是否成功连接、是否进入运行态、是否发生错误。

void(*control_info)(void*data,uint32_tid,conststructpw_stream_control*control);

控制信息变化时触发。它用于通知Stream控制项相关信息,例如音量、静音或其他控制数据,具体取决于Stream暴露的控制能力。

void(*io_changed)(void*data,uint32_tid,void*area,uint32_tsize);

Stream IO区域变化时触发。area表示IO区域地址,size表示区域大小。它用于通知应用某类IO数据区发生变化。

void(*param_changed)(void*data,uint32_tid,conststructspa_pod*param);

Stream参数变化时触发。id表示参数类型,param表示参数内容。音频格式、视频格式、枚举能力和运行参数变化时,应用可以在这里读取新的参数信息。

void(*add_buffer)(void*data,structpw_buffer*buffer);

Stream创建新Buffer时触发。应用可以在这里为Buffer绑定私有数据,或者记录Buffer对应的内存信息。

void(*remove_buffer)(void*data,structpw_buffer*buffer);

Stream销毁Buffer时触发。应用应在这里清理add_buffer阶段绑定到Buffer上的私有资源。

void(*process)(void*data);

媒体处理回调。PipeWire图调度到该Stream时触发。播放流通常在这里取Buffer、填充数据、queue回Stream;采集流通常在这里取Buffer、读取数据、queue回Stream。

void(*drained)(void*data);

Stream排空完成时触发。播放场景中,当应用请求Drain后,所有已提交数据处理完成,会通过该回调通知应用。

void(*command)(void*data,conststructspa_command*command);

Stream收到命令通知时触发。它用于接收底层SPA命令类事件。

void(*trigger_done)(void*data);

trigger_process完成后触发。它通常和显式触发处理流程相关,用于确认一次触发已经完成。

返回值

pw_stream_events本身不是函数,没有返回值。

每个回调函数也通常不返回结果,而是通过回调参数把事件上下文交给应用处理。应用侧如果需要改变Stream行为,应调用对应的Stream API完成,例如pw_stream_connect、pw_stream_dequeue_buffer、pw_stream_queue_buffer、pw_stream_flush、pw_stream_disconnect等。

应用场景

第一类场景是播放流。

应用注册process回调。PipeWire需要播放数据时触发process,应用在回调中调用pw_stream_dequeue_buffer取出Buffer,填入PCM数据,再调用pw_stream_queue_buffer提交。

第二类场景是录音流。

应用注册process回调。PipeWire采集到数据后触发process,应用取出Buffer,读取PCM数据,然后归还Buffer。

第三类场景是监听Stream状态。

应用注册state_changed回调。Stream连接失败、进入PAUSED、进入STREAMING、发生ERROR时,可以在这里记录日志、更新业务状态或执行恢复动作。

第四类场景是格式协商跟踪。

应用注册param_changed回调。PipeWire完成格式协商或参数变化后,应用可以在这里读取最终音频格式、采样率、通道数、视频尺寸等信息。

第五类场景是Buffer资源绑定。

应用注册add_buffer和remove_buffer。add_buffer中为Buffer创建私有上下文,remove_buffer中释放这些资源,避免在process回调中反复分配内存。

🌻3.调用流程剖析

🌻3.1核心步骤

1.应用定义static const struct pw_stream_events事件表。

2.应用设置.version为PW_VERSION_STREAM_EVENTS。

3.应用根据需要填充state_changed、param_changed、add_buffer、remove_buffer、process、drained、destroy等回调。

4.应用创建pw_stream对象。

5.如果使用pw_stream_new_simple,创建Stream时直接传入pw_stream_events。

6.如果使用pw_stream_new,应用需要再通过pw_stream_add_listener把pw_stream_events绑定到Stream。

7.应用调用pw_stream_connect连接Stream,PipeWire开始建立客户端Stream和Graph对象之间的关系。

8.Stream连接过程中,状态变化会触发state_changed。

9.格式和参数协商过程中,参数变化会触发param_changed,IO区域变化会触发io_changed。

10.Buffer创建完成后,PipeWire触发add_buffer,应用可以记录Buffer私有信息。

11.Graph进入运行调度后,PipeWire在合适周期触发process。

12.应用在process中完成Buffer处理。播放流写Buffer,采集流读Buffer。

13.Buffer被销毁前,PipeWire触发remove_buffer,应用释放Buffer相关私有资源。

14.Stream排空、显式触发或命令事件发生时,触发drained、trigger_done或command。

15.Stream销毁时触发destroy,整个事件表生命周期结束。

🌻3.2调用流程图

🌻3.3生命周期图

🌻4.实战应用案例

下面以“播放Stream事件注册和process处理”为例,说明pw_stream_events在真实开发中的用法。

这个案例只保留主链路:定义事件表、监听状态、监听参数、处理Buffer、清理Buffer资源。

#include<pipewire/pipewire.h>#include<spa/param/audio/format-utils.h>structapp_data{structpw_main_loop*loop;structpw_stream*stream;uint32_tframe_size;};staticvoidon_state_changed(void*userdata,enumpw_stream_stateold,enumpw_stream_statestate,constchar*error){structapp_data*app=userdata;(void)app;(void)old;if(state==PW_STREAM_STATE_ERROR){/* * Stream进入错误状态。 * error保存错误原因。 * 工程中通常在这里记录日志或触发重连。 */(void)error;return;}if(state==PW_STREAM_STATE_PAUSED){/* * Stream已经连接,格式和Buffer通常已经准备好。 */return;}if(state==PW_STREAM_STATE_STREAMING){/* * Stream已经进入运行态。 * 后续process会被Graph调度触发。 */return;}}staticvoidon_param_changed(void*userdata,uint32_tid,conststructspa_pod*param){structapp_data*app=userdata;(void)app;if(param==NULL)return;if(id==SPA_PARAM_Format){/* * 这里可以解析最终协商出来的音频格式。 * 例如采样率、通道数、采样格式。 */return;}}staticvoidon_add_buffer(void*userdata,structpw_buffer*buffer){structapp_data*app=userdata;(void)app;/* * Buffer创建时触发。 * 如果应用需要为每个Buffer绑定私有资源, * 可以在这里初始化。 */buffer->user_data=NULL;}staticvoidon_remove_buffer(void*userdata,structpw_buffer*buffer){structapp_data*app=userdata;(void)app;/* * Buffer销毁前触发。 * 如果add_buffer中绑定了私有资源, * 应在这里释放。 */buffer->user_data=NULL;}staticuint32_tfill_audio(void*dst,uint32_tmax_bytes){/* * 真实工程中,数据可能来自解码器、环形缓冲区、 * DSP算法、网络音频流或测试音源。 */(void)dst;(void)max_bytes;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_audio(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);}staticvoidon_drained(void*userdata){structapp_data*app=userdata;(void)app;/* * 已提交的数据处理完成。 * 播放器停止、切歌、释放资源时可能用到。 */}staticvoidon_destroy(void*userdata){structapp_data*app=userdata;(void)app;/* * Stream对象销毁时触发。 * 不要在这里继续访问已经失效的Stream资源。 */}staticconststructpw_stream_eventsstream_events={.version=PW_VERSION_STREAM_EVENTS,.destroy=on_destroy,.state_changed=on_state_changed,.param_changed=on_param_changed,.add_buffer=on_add_buffer,.remove_buffer=on_remove_buffer,.process=on_process,.drained=on_drained,};

这段代码的重点不在创建完整播放器,而在说明pw_stream_events如何组织Stream事件。

state_changed用于观察Stream状态。它适合做连接成功、运行态进入、错误恢复这类控制逻辑。

param_changed用于观察协商结果。它适合确认最终媒体格式,避免应用误以为自己提交的格式一定就是最终格式。

add_buffer和remove_buffer用于管理Buffer级资源。它们不适合做大量数据处理,主要用于初始化和清理。

process是媒体数据路径的核心回调。它应该短、快、少阻塞。实时线程场景下,process中不要做文件读写、锁等待、复杂内存分配和耗时算法。

destroy用于Stream生命周期结束时的收尾。它不是正常处理Buffer的地方,也不是重新启动Stream的地方。

如果使用pw_stream_new_simple,可以在创建Stream时直接传入事件表:

app.stream=pw_stream_new_simple(pw_main_loop_get_loop(app.loop),"simple-playback",props,&stream_events,&app);

如果使用pw_stream_new,则通常需要手动添加listener:

structspa_hookstream_listener;app.stream=pw_stream_new(core,"manual-playback",props);pw_stream_add_listener(app.stream,&stream_listener,&stream_events,&app);

两种方式的核心差异是控制粒度不同。

pw_stream_new_simple封装度更高,适合最小客户端和快速验证。

pw_stream_new配合pw_stream_add_listener控制更细,适合已经持有pw_core、需要更复杂上下文管理的工程代码。

🌻5.一句话总结

pw_stream_events是PipeWireStream的事件回调表:它不直接传输数据,而是把Stream状态变化、参数变化、Buffer创建、process调度和销毁收尾等关键节点交给应用处理。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询