简介:CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中……】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课🚀
专题四:Android15快速自定义与集成音效实战课🚀
专题五:Android15音频策略实战课🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例)🚀
人生格言:人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
🍉🍉🍉文章目录🍉🍉🍉
- 🌻1.前言
- 要点概括
- 🌻2.应用场景与用法
- 函数原型
- 参数说明
- 返回值
- 应用场景
- 🌻3.调用流程剖析
- 🌻3.1核心步骤
- 🌻3.2调用流程图
- 🌻3.3生命周期图
- 🌻4.实战应用案例
- 🌻5.一句话总结
🌻1.前言
本篇目的:
Linux PipeWire深度解析之pw_main_loop_run调用流程与实战。
要点概括
核心功能:运行PipeWire主循环,让当前线程进入事件等待和事件分发流程。
工作机制:内部持续迭代pw_loop,等待IO、Timer、Signal、Defer等事件源就绪,并把事件派发到对应回调。
典型用途:PipeWire客户端程序、测试Demo、命令行工具、音频播放/录音示例程序中的主事件循环。
pw_main_loop_run的本质是“运行主循环”,不是创建主循环,也不是创建Context、Core或Stream。主循环对象由pw_main_loop_new创建,底层pw_loop可以通过pw_main_loop_get_loop取得,事件循环真正开始运行时才调用pw_main_loop_run。
它和pw_main_loop_get_loop不同。pw_main_loop_get_loop只返回底层pw_loop指针,用于注册Signal、IO、Timer等事件源;pw_main_loop_run会阻塞当前线程,并持续驱动事件分发。
它和pw_main_loop_quit也不同。pw_main_loop_quit只负责请求主循环退出,不负责销毁资源。pw_main_loop_run返回后,应用仍然需要按顺序销毁Stream、Core、Context和MainLoop对象。
🌻2.应用场景与用法
pw_main_loop_run
是PipeWireMainLoop API中用于运行主事件循环的接口。
它位于PipeWire客户端程序的最外层调度位置。应用完成pw_init、pw_main_loop_new、pw_context_new、pw_context_connect、Stream创建或Registry监听后,通常调用pw_main_loop_run让当前线程进入事件循环。后续PipeWire服务端消息、Stream状态变化、Registry对象通知、Signal退出事件都会在该循环中被分发。
pw_main_loop_run用于启动并运行PipeWire主事件循环。
函数原型
intpw_main_loop_run(structpw_main_loop*loop);参数说明
structpw_main_loop*loop;loop表示由pw_main_loop_new创建的PipeWire主循环对象。
该对象内部维护底层pw_loop和退出事件源。应用可以通过pw_main_loop_get_loop取得底层pw_loop,然后注册Signal、Timer、IO等事件源。pw_main_loop_run运行时,会基于这些事件源持续等待和分发事件。
返回值
成功退出时返回:
0表示主循环被正常退出请求打断,例如调用pw_main_loop_quit。
失败时返回负错误码,通常表示底层事件迭代过程出现错误。实际开发中,常见用法是把pw_main_loop_run作为客户端程序的主阻塞点,退出后再进入资源清理流程。
应用场景
第一类场景是PipeWire客户端常驻程序。
客户端连接PipeWire服务端后,需要持续接收Core、Registry、Node、Stream等事件。pw_main_loop_run负责让客户端线程一直等待事件,并在事件到达时分发回调。
第二类场景是音频播放和录音Demo。
应用创建pw_stream后,播放或录音过程依赖process、state_changed、param_changed等回调。pw_main_loop_run运行后,这些回调才会被持续触发。
第三类场景是命令行测试工具。
很多PipeWire测试程序需要等待异步结果,例如等待Registry枚举完成、等待Stream进入STREAMING状态、等待用户按Ctrl+C退出。pw_main_loop_run负责把这些异步事件串到同一个主循环里。
第四类场景是信号退出控制。
应用可以通过pw_main_loop_get_loop取得底层pw_loop,再注册SIGINT和SIGTERM信号处理。收到退出信号时调用pw_main_loop_quit,pw_main_loop_run随后返回,程序进入清理流程。
🌻3.调用流程剖析
🌻3.1核心步骤
1.应用调用pw_init初始化PipeWire库运行环境。
2.应用调用pw_main_loop_new创建主循环对象。
3.应用调用pw_main_loop_get_loop取得底层pw_loop,用于后续创建Context或注册事件源。
4.应用调用pw_context_new创建PipeWireContext。
5.应用调用pw_context_connect连接PipeWire服务端,得到Core连接对象。
6.应用根据业务需要创建Registry、Stream、Signal、Timer或IO事件源。
7.应用调用pw_main_loop_run,当前线程进入主循环运行态。
8.主循环内部持续等待事件源就绪。
9.事件就绪后,底层pw_loop把事件派发给对应回调。
10.回调中可以处理PipeWire消息、Stream数据、Registry对象变化或退出信号。
11.如果没有退出请求,pw_main_loop_run继续进入下一轮事件等待。
12.如果调用pw_main_loop_quit,主循环退出,pw_main_loop_run返回。
13.应用在run返回后销毁Core、Context、MainLoop等资源。
🌻3.2调用流程图
🌻3.3生命周期图
🌻4.实战应用案例
下面以“创建PipeWire客户端主循环并支持Ctrl+C退出”为例,说明pw_main_loop_run在真实程序中的位置。
这个案例的目标是:应用完成PipeWire初始化、创建MainLoop、创建Context、连接PipeWire服务端,然后调用pw_main_loop_run进入事件循环。用户按Ctrl+C后,Signal回调调用pw_main_loop_quit,主循环返回,程序进入清理阶段。
#include<signal.h>#include<pipewire/pipewire.h>structapp_data{structpw_main_loop*main_loop;structpw_context*context;structpw_core*core;};staticvoidon_signal(void*userdata,intsignal_number){structapp_data*data=userdata;(void)signal_number;pw_main_loop_quit(data->main_loop);}on_signal是Signal事件回调。它不直接销毁PipeWire对象,只调用pw_main_loop_quit请求主循环退出。真正的资源释放放在pw_main_loop_run返回之后执行,这样可以避免在事件回调中提前释放仍在使用的对象。
intmain(intargc,char*argv[]){structapp_datadata={0};structpw_loop*loop;pw_init(&argc,&argv);data.main_loop=pw_main_loop_new(NULL);if(data.main_loop==NULL)gotoout_deinit;loop=pw_main_loop_get_loop(data.main_loop);pw_loop_add_signal(loop,SIGINT,on_signal,&data);pw_loop_add_signal(loop,SIGTERM,on_signal,&data);data.context=pw_context_new(loop,NULL,0);if(data.context==NULL)gotoout_main_loop;data.core=pw_context_connect(data.context,NULL,0);if(data.core==NULL)gotoout_context;pw_main_loop_run(data.main_loop);pw_core_disconnect(data.core);out_context:pw_context_destroy(data.context);out_main_loop:pw_main_loop_destroy(data.main_loop);out_deinit:pw_deinit();return0;}这段代码中,pw_main_loop_run是程序的主阻塞点。它开始运行后,当前线程不会继续向下执行,直到主循环收到退出请求或底层事件循环出现错误。
主循环运行期间,PipeWire服务端消息、Signal事件和业务事件都会通过同一个pw_loop分发。客户端不需要自己写poll、epoll或事件等待逻辑,只需要把事件源注册到pw_loop,然后交给pw_main_loop_run统一驱动。
在播放或录音程序中,pw_main_loop_run的位置也类似。应用先创建Stream并注册Stream事件回调,再调用pw_main_loop_run。运行期间,Stream的state_changed、param_changed和process回调会被PipeWire事件循环持续触发。
staticvoidon_stream_process(void*userdata){/* * 播放流:通常在这里dequeue buffer并填充PCM数据。 * 录音流:通常在这里dequeue buffer并读取PCM数据。 */(void)userdata;}对于音频程序来说,pw_main_loop_run不直接处理PCM数据。它只负责运行事件循环。真正的数据处理发生在Stream回调中,例如process回调。主循环负责调度回调,Stream回调负责处理媒体数据。
工程上需要注意三点。
第一,pw_main_loop_run会阻塞当前线程。它适合放在主线程末尾,或者放在专门的PipeWire线程中运行。
第二,不要把资源销毁逻辑直接塞进Signal回调。Signal回调中只请求退出,等pw_main_loop_run返回后再统一清理资源。
第三,pw_main_loop_run不等于Graph调度本身。它运行的是客户端事件循环,负责接收服务端消息、分发回调和驱动客户端侧状态变化。PipeWire服务端Graph的实时处理由服务端和相关Driver Node调度完成。
🌻5.一句话总结
pw_main_loop_run是PipeWire客户端程序的主事件循环入口:它阻塞当前线程并持续分发pw_loop事件,让Core、Registry、Stream和Signal回调能够被触发,直到pw_main_loop_quit请求主循环退出。