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

相关推荐
爱上珍珠的贝壳1 天前
ESP32-S3-CAM:豆包语音识别文字后控制小车(规划)
人工智能·音频·语音识别·esp32-s3·小车
爱上珍珠的贝壳1 天前
ESP32-S3-CAM:豆包语音识别文字后控制小车(四)——增加mic拾音后通过豆包语音识别后转文字输出
音频·语音识别·智能硬件·esp32-s3·豆包
爱上珍珠的贝壳1 天前
ESP32-S3-CAM:豆包语音识别文字后控制小车(三)——SD卡本地音频识别转文字
人工智能·音频·语音识别·智能硬件·esp32-s3
憨波个2 天前
【说话人日志】从 LSTM attractor 到 Transformer attractor:EEND-TA
人工智能·深度学习·lstm·transformer·音频·语音识别
憨波个2 天前
【说话人日志】从固定输出到可变说话人数:EEND-EDA
人工智能·深度学习·音频·语音识别
超开心~3 天前
Audio 中的 drain 和 flash
音频
筱谙3 天前
BES 芯片跨核通讯与共享内存设计原理
嵌入式硬件·音频·蓝牙
iceslime8 天前
Windows10系统静音修复相关
windows·音频·修复
憨波个12 天前
【说话人日志】从 BLSTM 到 Self-Attention:SA-EEND
人工智能·transformer·音频·语音识别