news 2026/4/14 15:32:13

Android audio中的AUDIO_OUTPUT_FLAG_NON_BLOCKING

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android audio中的AUDIO_OUTPUT_FLAG_NON_BLOCKING

什么是AUDIO_OUTPUT_FLAG_NON_BLOCKING

如字面意思,非阻塞式写入,在以下文件中也有相关定义。

AudioOutputFlags.aidl
/**
* Write operations must return as fast as possible instead of
* being blocked until all provided data has been consumed.
*/
NON_BLOCKING = 5,

平时通过alsa调用pcm_write一般都采用的是阻塞式。但在Offload模式下,通常需要使用非阻塞式。

为什么需要非阻塞式:

1. 延长 CPU 睡眠时间

这是大缓冲区最主要的原因。
普通模式(PCM):缓冲区通常很小(比如 20ms~40ms)。这意味着 CPU 每隔几十毫秒就必须醒来一次,往缓冲区里填数据。CPU 频繁地在“工作”和“睡眠”之间切换,无法进入深度的省电状态(Deep Sleep States)。
Offload 模式:通过使用非常大的缓冲区(通常可以容纳 1秒到数秒 的音频数据),CPU 可以一次性把大量的压缩数据(如 MP3, AAC)“推”给 DSP。
结果:数据推完后,CPU 就可以彻底“躺平”睡大觉。在接下来的 2 秒钟里,CPU 都不需要处理音频,只有低功耗的 DSP 在工作。这种长周期的睡眠能显著降低功耗。

2. 压缩数据的高效率

压缩音频数据的“密度”比原始 PCM 数据大得多。
同样的 128KB 空间:
* 如果存 PCM(44.1kHz, 16bit, 双声道),只能支撑约 0.7 秒。
* 如果存 MP3(128kbps 比特率),可以支撑约 8 秒。
因为 Offload 传输的是压缩数据,大缓冲区配合高压缩比,使得 CPU 每次“醒来”填满缓冲区后,获得的“收益”(播放时长)极大,数据需要消耗的时间也相当长,如果一直阻塞等待消耗完,十分浪费CPU资源。

如何实现非阻塞式:

1.配置文件

在AudioPolicyConfiguration.xml中Offload模式下(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)同时配置AUDIO_OUTPUT_FLAG_NON_BLOCKING.

2.flag检测

在audio HAL层检测flag是否包含AUDIO_OUTPUT_FLAG_NON_BLOCKING,如若包含,创建对应的StreamOut,并且保存回调函数接口(callback)

3.修改write()

在write函数下实现非阻塞写入:如果有空间,写入数据,返回写入字节数。如果没空间,立即返回-EAGAIN 或 0,不要调用 `usleep` 或等待信号量。

4. 异步回调:

HAL 内部维护一个线程处理实际的硬件写入。当硬件消费了数据,空出缓冲区时,调用 `IStreamOutCallback->onWriteReady()`。当 AudioFlinger 层收到 `onWriteReady` 回调时,`AudioFlinger::PlaybackThread` 或`AudioFlinger::OffloadThread` 会被唤醒。它会重新调用 `mStream->write()`。

非阻塞模式通常还伴随着 `drain()` 操作。当数据写完时,Framework 会调用 `drain(AUDIO_DRAIN_ALL)`。HAL 在硬件播放完成后,需要回调 `IStreamOutCallback->onDrainReady()`。

其它要点:

pause与resume
状态同步:当调用 `pause` 时,HAL 应该立即停止向硬件推送数据,但不能丢弃已在硬件缓冲区中的数据。
Resume 的瞬时性:调用 `resume` 后,硬件应立即从暂停的位置恢复播放。
在非阻塞模式下,`pause` 之后不应触发数据请求回调。如果正在进行 `drain` 操作时执行 `pause`,`drain` 的完成回调通常会被挂起,直到 `resume`。

drain

这是非阻塞模式下最复杂的操作。它的目的是确保所有已写入缓冲区的音频数据都被完整播放。
异步特性:在非阻塞模式下,`drain()` 操作必须是非阻塞的。HAL 接收到指令后开始播放剩余数据,并在播放完成后发送 `AUDIO_DRAIN_READY` 回调。
类型:
`DRAIN_ALL`: 等待所有数据播放完。
`DRAIN_EARLY_NOTIFY`: 在数据接近播放完时提前通知(常用于下一曲等无缝切换)。
在 `drain` 过程中,如果收到 `flush` 或 `stop` 指令,必须取消 `drain` 等待。
在 `drain` 完成前,不要关闭 Stream。

测试验证

在应用层快速写入大量数据,检验是否立即返回,返回写入数据长度或0

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

【图像融合】基于小波变换实现红外和可见光图像融合算法设计(含融合图像评估:信息熵EN 空间频率SF 平均梯度AG 标准差SD 互信息 MI)附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 🍎 往期回顾关注个人主页:Matlab科研工作室 👇 关注我领取海量matlab电子书…

作者头像 李华
网站建设 2026/4/14 15:30:14

SpringBoot与MybatisPlus结合:深入解析IPage分页机制与实战应用

1. 为什么需要分页功能? 想象一下你去图书馆借书,管理员把全馆100万本书一次性堆在你面前让你挑,这场景是不是很崩溃?数据库查询也是同样的道理。当数据量达到百万级时,一次性加载所有数据会导致内存溢出、网络阻塞、页…

作者头像 李华
网站建设 2026/4/14 15:27:55

STM32F407烧录全攻略:从CubeMX配置到ST-Link/V2连接(含SWD接线图)

STM32F407烧录全攻略:从CubeMX配置到ST-Link/V2连接 第一次拿到STM32F407开发板时,最令人兴奋的莫过于点亮第一个LED。但在此之前,我们需要跨越从代码生成到成功烧录的整个流程。本文将手把手带你完成这个关键过程,避开那些新手常…

作者头像 李华
网站建设 2026/4/14 15:27:31

轻松掌握虚拟游戏控制器:3步搞定Windows设备兼容性难题

轻松掌握虚拟游戏控制器:3步搞定Windows设备兼容性难题 【免费下载链接】ViGEmBus Windows kernel-mode driver emulating well-known USB game controllers. 项目地址: https://gitcode.com/gh_mirrors/vi/ViGEmBus 你是否遇到过心爱的游戏手柄无法在Window…

作者头像 李华
网站建设 2026/4/14 15:26:38

3步实现飞书文档本地转换:Cloud Document Converter全场景解决方案

3步实现飞书文档本地转换:Cloud Document Converter全场景解决方案 【免费下载链接】cloud-document-converter Convert Lark Doc to Markdown 项目地址: https://gitcode.com/gh_mirrors/cl/cloud-document-converter 想象一下,当你需要将飞书文…

作者头像 李华
网站建设 2026/4/14 15:25:35

TVA时代企业视觉检测核心痛点突破系列(8)

技术背景介绍:AI智能体视觉检测系统(TVA,全称为“Transformer-based Vision Agent”),即基于Transformer架构以及“因式智能体”创新理论的高精度视觉智能体,并非传统机器视觉软件或者早期AI视觉技术&#…

作者头像 李华