Android AAudio介绍及流程分析

AAudio 是 Android 系统提供的一种高性能、低延迟的原生 C API 音频库。

源码下的使用样例及测试程序:

frameworks/av/media/libaaudio/examples/write_sine/

为什么可以做到低延时?

1.使用mmap机制减少拷贝。

在支持的硬件上,AAudio 可以进入 MMAP 模式。

在普通模式下,音频数据需要在应用层、系统音频引擎和硬件驱动之间多次拷贝。而在 MMAP 模式下,数据直接写入一个应用和硬件共享的内存区域。这种方式极大地降低了 CPU 占用和延迟。

AAudio Sharing Mode分为两种:

  • AAUDIO_SHARING_MODE_SHARED
  • AAUDIO_SHARING_MODE_EXCLUSIVE

SHARED模式为默认模式,可以支持多应用程序同时使用,多个客户端播放时会在AAudio Server中进行混音,所以会多出一个缓冲区,这种模式下拥有双重缓冲区,会增加时延。

AAudioStream_setBufferSizeInFrames和AAudioStreamBuilder_setBufferCapacityInFrames ** 主要也是针对AAudio Server中的缓冲区进行设定,capacity最大可设定为32x1024。另一个缓冲区是HAL层创建的"共享内存"。

EXCLUSIVE模式只有一个缓冲区,即HAL层创建的"共享内存",capacity size和burst size是由HAL来决定,但buffer size依然是由上层设定。

2.减小buffer size,降低延时。在确保不发生underrun的情况下,尽量使用最小buffer size。

AAudioStream_getFramesPerBurst 每次消耗的数据大小。

AAudioStream_setBufferSizeInFrames ringbuffer size,由应用层设置,一般要为burst frames的整数倍,默认是2倍。可以在播放过程中动态调整。

AAudioStreamBuilder_setBufferCapacityInFrames ringbuffer size最大限制,由应用层设置,动态调整buffer size时不能超过这个值。

buffer size较小的时候,cpu稍微抖动就会发生underrun出现断音,闲置情况单独使用write_sine播放,使用512的buffer size,不会发生断音,此时延时也最低。

3.使用实时高优先级回调线程取数据

AAudio数据传输有两种模式:

  • Read/Write同步模式
  • callback回调异步模式

write_sine对应的是同步模式,线程优先级低,所以很容易被cpu抖动产生影响,其它进程有操作时,断音明显。

write_sine_callback 对于的是回调模式,AAudio 会在一个专门的高优先级线程中自动调用它,优先级极高且由系统调度,不容易受应用主线程或其他繁忙任务的影响。

分别对比同步模式和异步模式两种情况下线程的状态:

复制代码
1|RTD1325A16:/ # ps -T -p 5973 -o USER,PID,TID,PRI,RTPRIO,SCHED,PCY,CMD
USER           PID   TID PRI RTPRIO SCH PCY CMD
root          5973  5973  19      -   0  fg write_sine_call
root          5973  5974  19      -   0  fg binder:5973_1
root          5973  5975  19      -   0  fg binder:5973_2
root          5973  5982  42      2   1  fg AAudio_1 

RTD1325A16:/ # ps -T -p 3467 -o USER,PID,TID,PRI,RTPRIO,SCHED,PCY,CMD
USER           PID   TID PRI RTPRIO SCH PCY CMD
root          3467  3467  19      -   0  fg write_sine
root          3467  3468  19      -   0  fg binder:3467_1
root          3467  3469  19      -   0  fg binder:3467_2

可以看到回调模式下,会多出一个AAudio创建的线程,其优先级(PRI)更高,并且属于实时线程(RTPRIO),调度模式(SCH)为FIFO,普通线程调度模式为NORMAL。

如何配置audio_policy_configuration.xml

<mixPort name="mmap_no_irq_out" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_MMAP_NOIRQ" >

<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"

samplingRates="44100 48000"

channelMasks="AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_5POINT1"/>

</mixPort>

A good blog for reference.

AA​​​​​​​【Android Audio】AAudio 架构讲解以及实现范例 (附带visio流程图)_aaudio demo-CSDN博客

open流程

shared模式流程会复用exclusive mode,如果只是exclusive mode,会跳过2-10步。

复制代码
1 Aaudiostreambuilder_openstream
2  AudioStreamInternalPlay::open		这部分 AudioStreamInternalPlay 运行在client进程下。share mode 为AAUDIO_SHARING_MODE_SHARED
3 	AudioStreamInternal::open
4  	 AAudioService::openStream
5 	  AAudioServiceStreamShared::open
6 	   AAudioServiceStreamBase::open
7 		AAudioEndpointManager::openEndpoint
8 		 AAudioEndpointManager::openSharedEndpoint
9 		  AAudioServiceEndpointPlay::open	&& AAudioMixer::allocate,这里创建第一个缓冲区,同时在构造方法中,创建 AAudioServiceEndpointShared,将 AudioStreamInternalPlay 作为参数传入。
10 		   AAudioServiceEndpointShared::open  将share mode设置为 AAUDIO_SHARING_MODE_EXCLUSIVE ,再次打开 AudioStreamInternalPlay。
11 			AudioStreamInternalPlay::open	此时 AudioStreamInternalPlay 运行在service进程下,share mode 为 AAUDIO_SHARING_MODE_EXCLUSIVE
12 			 AudioStreamInternal::open
13 			  AAudioService::openStream
14 			   AAudioServiceStreamMMAP::open
15 				AAudioServiceStreamBase::open
16 				 AAudioEndpointManager::openEndpoint
17 				  AAudioEndpointManager::openExclusiveEndpoint
18 				   AAudioServiceEndpointMMAP::open
19 					AAudioServiceEndpointMMAP::openWithConfig
20 					 MmapStreamInterface::openMmapStream
21					  AudioFlinger::openMmapStream && MmapStreamInterface::createMmapBuffer	这里最终到HAL层创建第二个缓冲区

start 流程

复制代码
与open流程类似,需要注意的是它会在aaudio service中会创建一个混音线程。
AAudioServiceEndpointShared::startStream        第一个请求 sartStream 的时候,创建混音线程
    AAudioServiceEndpointShared::startSharingThread_l

write 流程

复制代码
AAudioStream_write()
    AudioStreamInternalPlay::write
        AudioStreamInternalPlay::processDataNow
            AAudioFlowGraph::process()            写入第一个缓冲区 FIFO RingBuffer
AAudioServiceEndpointPlay::callbackLoop  唤起混音线程,处理所有客户端传过来的buffer
    AAudioMixer::mix
        AAudioServiceEndpointShared::write
            AudioStreamInternalPlay::processDataNow
                AAudioFlowGraph::process()        写入第二个缓冲区,HAL创建的共享缓冲区
相关推荐
嘟哩DuliDuli2 分钟前
AI 短剧生成为什么要有角色库、场景库和镜头卡
android·人工智能·安全·ai·软件工程
NINO2 小时前
深度解析 Android ANR:从标准分析流程到 NativePollOnce 疑难破局
android
阿巴斯甜2 小时前
AppFunctions 完整介绍
android
蜡台2 小时前
Jetpack Compose 从入门到精通
android·前端·kotlin·compose·jepack
toooooop83 小时前
踩坑:PHP7.2导出Excel正常,升级7.3文件损坏需修复
android·excel
mmsx3 小时前
我明明调用了 zoomToBounds,地图却总是停在别处?延迟加到 5 秒也没用,真相只有一个
android·人工智能·bug·地图
蜡台5 小时前
Jetpack Compose 核心:状态管理 + Lazy 列表
android·kotlin·state·jetpack compose
Escalating_xu6 小时前
【Linux线程同步】从数据竞争到 mutex、条件变量与生产者消费者(上篇)
android·java·linux
00后程序员张6 小时前
Windows / Linux / Mac 上不用 Xcode 把 IPA 上传到 App Store,upload 命令详解
android·ios·小程序·https·uni-app·iphone·webview
秦少游在淮海7 小时前
id - Android、iOS
android·ios