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创建的共享缓冲区
相关推荐
取个名字太难了~1 小时前
最新手机外接相机实时传输 + 动态美颜完整项目源码
android·数码相机·智能手机·美颜·相机连接·demu
say_fall1 小时前
【Linux系统编程】文件操作基础:C标准库、系统调用、fd是什么和fd与FILE*的关系
android·linux·c语言
ii_best10 小时前
更新!移动端开发软件按键安卓版&手机助手v5.1.0上线!本地AI识别全面解锁,脚本开发再升级
android·人工智能·ios·按键精灵
方乐寺村11 小时前
s,使用libpng提升png图片的保存速度。接下来本文将阐述在Android中如何集成libpng,以及在使用过程中遇到的问题和最 ...
android
name好难取诶11 小时前
PHP 静态分析工具实战 PHPStan 和 Psalm 完全指南
android·开发语言·php
Lydia ,12 小时前
安卓基础-线性布局
android
小小勇气大爆发15 小时前
Android四大组件总结
android
码农coding16 小时前
android16 SystemUI之StatusBar启动
android
朱涛的自习室17 小时前
一个 7 x 24 小时为你打工的 AI,Munk AI 开始内测
android·前端·人工智能