Android audio中的AUDIO_OUTPUT_FLAG_NON_BLOCKING

张开发
2026/4/14 15:32:18 15 分钟阅读

分享文章

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 空间* 如果存 PCM44.1kHz, 16bit, 双声道只能支撑约 0.7 秒。* 如果存 MP3128kbps 比特率可以支撑约 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 操作时执行 pausedrain 的完成回调通常会被挂起直到 resume。drain这是非阻塞模式下最复杂的操作。它的目的是确保所有已写入缓冲区的音频数据都被完整播放。异步特性在非阻塞模式下drain() 操作必须是非阻塞的。HAL 接收到指令后开始播放剩余数据并在播放完成后发送 AUDIO_DRAIN_READY 回调。类型DRAIN_ALL: 等待所有数据播放完。DRAIN_EARLY_NOTIFY: 在数据接近播放完时提前通知常用于下一曲等无缝切换。在 drain 过程中如果收到 flush 或 stop 指令必须取消 drain 等待。在 drain 完成前不要关闭 Stream。测试验证在应用层快速写入大量数据检验是否立即返回返回写入数据长度或0

更多文章