目录
音量调节从CarAudioManager到tinyalsa
本着从调用者视角入手,带你从Java层到C++/C层的音量调节调用流程。
开头
写这篇博客花了不少时间,断断续续大概花了两周时间,期间经历了美股的4次熔断,今年真是多灾多难的一年,新冠病毒、石油价格战、金融危机。。。。。额,扯远了,,,,
为什么要写博客,因为我相信只有不断输出才能不断学习不断进步,也就是有输出才能带动输入。另外通过写博客或许还能发现自己的理解上的缺陷,我觉得吧,理越辩越明,所以欢迎各位看官多多指教。
正文
本文将从调用者(CarAudioManager)开始,到tinyalsa结束,从调用角度来看一看audio系统大概流程。
音量调节分为两种实现,一种是Android原生(手机)通过设置PCM数据直接调节输出的振幅来调节音量,也是就是软音量。另外一种则是通过硬件来实现音量大小控制,也就是所谓的硬音量,通过DSP设置硬件功放等来进行音量大小控制。本文介绍的是后一种方式。我将尽量采用最简单有效的方式进行呈现:画图。能画画就绝不写写。调用流程我就不再赘述,看图即可。
整体的调用流程:
分三步走:
1、从CarAudioManager到AudioSystem
2、从audiopolicy到audioflinger
3、从audioflinger到audiohal
1、从CarAudioManager到AudioSystem
1.1 CarAudioManager:setGroupVolume(int groupId, int index, int flags)
CarAudioManager.java文件位置:
packages/services/Car/car-lib/src/android/car/media/CarAudioManager.java
packages/services/Car/service/src/com/android/car/audio/CarAudioService.java
packages/services/Car/service/src/com/android/car/audio/CarAudioDeviceInfo.java
一睹为快,第一部分比较简单:
首先应用直接使用setGroupVolume进行音量设置,setGroupVolume(int groupId, int index, int flags)
已经从CarAudioManager到AudioSystem.java了,从这开始准备进入了C/C++的世界了,C++里也有一个AudioSystem.cpp和上层的Java层对应,也就是AudioSystem.java了。
CarAudioService在汽车音频里是一个很重要的服务,里面包含了音量,音效等保存和控制,一般来说定制ROM的时候发布一些新的接口都需在CarAudioService添加相应的接口并由此往下调用,其中AudioControl就是一个经典的案例(AudioControl是用来控制音效音场之类的Hal层实现)。
上面需要注意三点:
1.1 一点是,上面提到的音量调节两种方式,就是通过这个mUseDynamicRouting这个变量走上了截然不同的方向:硬音量调节方式;可以通过mUseDynamicRouting变量进行选择,该变量可通过配置文件进行配置。
// For legacy stream type based volume control
if (!mUseDynamicRouting) {
mAudioManager.setStreamVolume(STREAM_TYPES[groupId], index, flags);
return;
}
1.2 另外一点是音量组概念,一般情况下音乐多媒体,蓝牙音乐,收音机等属于多媒体音量组。
CarAudioService有一个变量mCarVolumeGroups,该变量是用来保存各个类型的音量,它的初始化可通过配置文件进行初始化。各音量组的定义在如下car_volume_groups.xml文件中有定义:
packages/services/Car/service/res/xml/car_volume_groups.xml
<volumeGroups xmlns:car="http://schemas.android.com/apk/res-auto"
car:isDeprecated="true">
<group>
<context car:context="music"/>
<context car:context="call_ring"/>
<context car:context="notification"/>
<context car:context="system_sound"/>
</group>
<group>
<context car:context="navigation"/>
<context car:context="voice_command"/>
</group>
<group>
<context car:context="call"/>
</group>
<group>
<context car:context="alarm"/>
</group>
</volumeGroups>
1.3、 从CarAudioDeviceInfo开始,我们看到往下传入的参数变为了AudioGainConfig类型,这个很重要,这个是用来区分设置哪个声卡的音量,对底层很重要,这点后续在进行详尽分析,这里只需要知道,不同类型的音量类型对应着各自的 AudioGainConfig配置。
好了,到了这里,音量设置调用来到了AudioSystem了,在native层也有一个对应的AudioSystem.cpp文件,其中方法一一对应。这里涉及到JNI,如有不懂,可自行google。
2、从audiopolicy到audioflinger
frameworks/av/media/libaudioclient/AudioSystem.cpp
frameworks/av/media/libaudioclient/IAudioPolicyService.cpp
2.1、 其实这部分,可以分为两小部分,但是为了整体性,还是将其整合成一个整体。先看下组成架构。
2.2、调用流程:
其实到了native层之后调用就变得比较单纯了,因为大部分的业务逻辑都是在上层实现的。其中最主要关注两个,一个是AudioPolicyService音频策略定制者,AudioFlinger音频策略的执行者,两者相辅相成。
从上图可以看到,AudioPolicyService主要通过AudioPolicyManager对外提供服务,而AudioFlinger并没有类似AudioFlingerManager这样的类,因为AudioFlinger主要向AudioPolicyService提供服务并不需要再向其他提供服务,所以两者进行了深度绑定。
其中mAudioHwDevs这边变量很重要,这个变量是通过AudioFlinger初始化的时候进行初始化的,AudioFlinger通过它与audiohal进行通信。AudioFlinger初始化比较复杂,不在本文讨论范围内,后续会继续再写一篇关于AudioFlinger的初始化。
3、从audioflinger到audiohal
3.1先一睹为快,AudioFlinger才是真正与AudioHal打交道的,以下是调用流程简图(HIDL方式):
3.2 audioflinger到audiohal架构图:
可以看到,调用流程和架构区别还是比较大的,主要因为Android系统需要支持HIDL方式和传统的so方式,增加兼容导致架构更复杂了。
到了audiohal层之后就是根据各个ROM厂商根据自身硬件进行适配,audiohal会根据每个厂商自行实现,但对上层接口保持一致,这样就实针对底层的适配了。
到这里,整个流程已经调用完毕,再往下就是经tinyalsa调用到驱动了。
总结
Auto Android的音频系统是一个非常复杂的一个音频系统,其中包含了以CarAudioService为核心的音频焦点管理模块,以AudioFlinger&AudioPolicyService为核心的音频流管理模块(音频路由),而音量管理模块算是音频系统中较为简单的模块。其中音量的其他模块也并未进行详尽,比如音量组初始化,音量显示,音量回调。篇幅所限,不写太多,另外主要是偏向底层的,所以上层就不过多分析。另外,由于知识能力有限,并未能完善甚至可能还有理解错误的,如有不同意看法的请指出。
恭喜你,又看完了一篇博客。
参考:
https://source.android.google.cn/devices/automotive/audio
https://cs.android.com/
https://blog.csdn.net/bberdong/article/details/79472208
https://blog.csdn.net/qq_19923217/article/details/88398660