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

相关推荐
ZenasLDR2 小时前
蓝牙应用走向万物智联
音频·芯片
Raymond16881 天前
零样本TTS长视频配音的工程取舍:自回归、非自回归与韵律边界
人工智能·数据挖掘·回归·音视频·音频
哦***72 天前
一开降噪耳朵就闷?耳机有耳压感是怎么回事?
华为·音频·harmonyos
一个简单的TTS开发者3 天前
搭建自媒体AI配音工作流选型指南:聚合工具与统一接口怎么选
人工智能·音频·媒体·tts
DogDaoDao7 天前
声音、图像、视频是如何形成的:从物理量到比特流的完整链条
图像处理·数学·音视频·音频·数字信号处理·视频处理·音视频编解码
jiangyoushushu7 天前
FLAC手机播不了?转成MP3会更方便
音视频·音频
AI技术新视界12 天前
whisper.cpp 与 audio.cpp
人工智能·音频·推理引擎·本地ai
ZenasLDR13 天前
LDR6021Q音频转接器应用介绍
音视频·音频·接口
开发笔记-阿牛18 天前
AM收音机蓝牙二合一设计复盘
人工智能·单片机·嵌入式硬件·音视频·音频
zwd200522 天前
Manim add_sound 用法详解:time_offset、gain、多条音频与旁白对齐(2026 最新教程,0.21.0)
python·音频·动画·manim·数学动画·add_sound