简介:CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中……】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课🚀
专题四:Android15快速自定义与集成音效实战课🚀
专题五:Android15音频策略实战课🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例)🚀
人生格言:人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
🍉🍉🍉文章目录🍉🍉🍉
- 🌻1. 前言
- 要点概括
- 🌻2. 应用场景与用法
- 函数原型
- 参数说明
- 返回值
- 应用场景
- 🌻3. 调用流程剖析
- 🌻3.1 核心步骤
- 1. 应用层进入 mainloop
- 2. dispatch 接收已经就绪的事件
- 3. 检查 mainloop 状态
- 4. 选择待分发事件
- 5. 执行对应 callback
- 6. 更新事件状态
- 7. 返回分发结果
- 8. 驱动异步链路继续向前
- 🌻3.2 调用流程图
- 🌻3.3 mainloop dispatch 生命周期图
- 🌻4. 实战应用案例
- 🌻5. 一句话总结
🌻1. 前言
本篇目的:
Linux PulseAudio 深度解析之pa_mainloop_dispatch调用流程与实战。
要点概括
- 核心功能:分发 mainloop 中已经就绪的事件,并执行对应回调函数。
- 工作机制:
pa_mainloop_prepare()负责准备监听事件,pa_mainloop_poll()负责等待事件发生,pa_mainloop_dispatch()负责把已经发生的事件分发给对应 callback。 - 典型用途:理解 PulseAudio 异步回调机制、分析 socket 事件触发、分析 defer/time/io 事件调度、手动拆解 mainloop 运行过程。
🌻2. 应用场景与用法
pa_mainloop_dispatch()是 PulseAudio mainloop 事件循环中的核心分发接口。
在 PulseAudio 中,Context 连接完成、Stream 状态变化、Socket 可读可写、定时器触发、defer 事件触发,最终都需要通过 mainloop 的事件分发机制执行对应回调。
而该接口用于:
将已经就绪的 mainloop 事件分发出去,并调用对应的回调函数。
函数原型
intpa_mainloop_dispatch(pa_mainloop*m);参数说明
m:目标 pa_mainloop 对象返回值
返回int类型结果用于表示本轮事件分发是否成功,以及 mainloop 当前是否还能继续运行。
应用场景
pa_mainloop_dispatch()常见应用场景主要有三类。
第一类是理解 PulseAudio 异步回调机制。应用调用pa_context_connect()后,并不会立刻进入PA_CONTEXT_READY,而是要等 mainloop 后续运行。当 socket 连接完成、服务端返回协议包、状态变化事件就绪后,pa_mainloop_dispatch()才会把这些事件分发出去,最终触发context_cb()、stream_cb()、write_cb()等回调。
第二类是手动拆解 mainloop 执行流程。平时应用通常直接调用pa_mainloop_run(),它内部会循环执行 prepare、poll、dispatch。为了调试和学习,可以手动调用pa_mainloop_prepare()、pa_mainloop_poll()、pa_mainloop_dispatch(),这样可以清楚看到事件循环每一步分别负责什么。
第三类是调试 IO、Time、Defer 事件。在分析 PulseAudio 连接流程、socket 事件、延迟回调、定时器回调时,pa_mainloop_dispatch()是关键入口。它不是负责等待事件,而是负责“事件已经发生之后,应该调用哪个 callback”。
🌻3. 调用流程剖析
🌻3.1 核心步骤
1. 应用层进入 mainloop
通常应用不会直接频繁调用pa_mainloop_dispatch(),而是调用:
pa_mainloop_run(mainloop,NULL);如果手动拆解,可以写成:
pa_mainloop_prepare(mainloop,-1);pa_mainloop_poll(mainloop);pa_mainloop_dispatch(mainloop);2. dispatch 接收已经就绪的事件
pa_mainloop_dispatch()不负责阻塞等待事件。
它处理的是:
pa_mainloop_poll() 已经发现的事件也就是说:
poll 阶段负责等事件 dispatch 阶段负责执行事件3. 检查 mainloop 状态
进入 dispatch 后,会检查 mainloop 当前状态是否允许继续分发。
如果 mainloop 已经退出、终止或状态异常,本轮 dispatch 就不会继续执行正常事件分发。
4. 选择待分发事件
mainloop 内部维护多类事件:
defer_events io_events time_events pollfdsdispatch 阶段会根据当前事件状态,选择可以执行的事件。
5. 执行对应 callback
如果是 defer 事件,就执行 defer callback;如果是 IO 事件,就根据 fd 的读写状态执行 IO callback;如果是 time 事件,就执行 timer callback。
这些 callback 可能是:
连接完成回调 socket 可读回调 stream 写回调 context 状态回调 timer 回调 defer 回调6. 更新事件状态
callback 执行完成后,mainloop 会根据事件类型更新内部状态。
例如:
一次性 defer 事件可能被释放 IO 事件继续保留等待下一次 fd 就绪 time 事件可能重新设置下一次触发时间7. 返回分发结果
本轮事件处理完成后,pa_mainloop_dispatch()返回。
如果外层是pa_mainloop_run(),则继续进入下一轮:
prepare → poll → dispatch8. 驱动异步链路继续向前
对于 PulseAudio 客户端来说,pa_mainloop_dispatch()的核心价值是:
把底层事件变成上层 callback例如:
socket 可读 ↓ dispatch 执行协议处理回调 ↓ context 状态变化 ↓ 触发 context_cb()🌻3.2 调用流程图
🌻3.3 mainloop dispatch 生命周期图
🌻4. 实战应用案例
#include<pulse/pulseaudio.h>#include<stdio.h>staticvoidcontext_state_cb(pa_context*c,void*userdata){pa_context_state_tstate;state=pa_context_get_state(c);if(state==PA_CONTEXT_READY){printf("context ready\n");}elseif(state==PA_CONTEXT_FAILED){printf("context failed\n");}elseif(state==PA_CONTEXT_TERMINATED){printf("context terminated\n");}}voidmanual_mainloop_once(pa_mainloop*mainloop){intret;/* * 1. 准备本轮需要监听的事件 */ret=pa_mainloop_prepare(mainloop,-1);if(ret<0){printf("mainloop prepare failed\n");return;}/* * 2. 等待 fd、timer、defer 等事件发生 */ret=pa_mainloop_poll(mainloop);if(ret<0){printf("mainloop poll failed\n");return;}/* * 3. 分发已经发生的事件,并执行对应 callback */ret=pa_mainloop_dispatch(mainloop);if(ret<0){printf("mainloop dispatch failed\n");return;}}intmain(){pa_mainloop*mainloop;pa_context*context;mainloop=pa_mainloop_new();context=pa_context_new(pa_mainloop_get_api(mainloop),"mainloop_dispatch_demo");pa_context_set_state_callback(context,context_state_cb,NULL);pa_context_connect(context,NULL,0,NULL);/* * 为了演示 dispatch,这里手动跑几轮 mainloop。 * 实际项目中通常直接使用 pa_mainloop_run()。 */for(;;){manual_mainloop_once(mainloop);if(pa_context_get_state(context)==PA_CONTEXT_READY)break;if(pa_context_get_state(context)==PA_CONTEXT_FAILED)break;if(pa_context_get_state(context)==PA_CONTEXT_TERMINATED)break;}pa_context_disconnect(context);pa_context_unref(context);pa_mainloop_free(mainloop);return0;}🌻5. 一句话总结
pa_mainloop_dispatch()本质上是:
“把 mainloop 中已经就绪的事件分发给对应 callback”。
它不负责创建事件,也不负责等待事件,而是负责在poll返回之后,把 IO、Time、Defer 等事件转换成真正的回调执行,是 PulseAudio 异步连接、状态变化、读写通知和 Stream 回调能够运转起来的关键分发入口。