HIDL的Car Audio架构简单梳理
一、Car Audio的结构说明
从 Android 14 开始,使用AIDL 定义音频 HAL 接口。对于以前的版本,音频 HAL 接口使用 HIDL 定义。
上面的架构图来自官网https://source.android.com/docs/automotive/audio
涉及代码路径以及对应的内容
代码路径 | 内容 |
packages/services/Car/car-lib/src/android/car/media | CarAudioManager |
packages/services/Car/service/src/com/android/car/audio | CarAudioService |
frameworks/base/media/java/android/media | AudioManager,AudioSystem |
frameworks/base/services/core/java/com/android/server/audio | AudioService |
frameworks/av/media/libaudioclient | AudioSystem.cpp |
frameworks/av/services/audioflinger | AudioFlinger |
frameworks/av/services/audiopolicy | AudioPolicyService |
hardware/interfaces/audio | IDevice.hal |
hardware/libhardware/include/hardware | audio.h |
frameworks/av/media/libaudiohal | StreamHalHidl |
hardware/interfaces/automotive/audiocontrol | AudioControl.cpp |
hardware/qcom/audio/hal | audio_hw.c(原生) |
external/tinyalsa | asoundlib.h |
hardware/qcom/audio/hal是原生的,可能会被其它的audio hal替换掉,例如展锐的代码中,实际生效的的audio hal路径为vendor/sprd/modules/audio。hardware/qcom/audio/hal的代码虽然在工程里,实际不生效。
二、Java API(MediaPlayer和AudioTrack)
播放声音可以用MediaPlayer和AudioTrack,两者都提供了java API供应用开发者使用。虽然都可以播放声音,但两者还是有很大的区别的。其中最大的区别是MediaPlayer可以播放多种格式的声音文件,例如MP3,AAC,WAV,OGG,MIDI等。MediaPlayer会在framework层创建对应的音频解码器。而AudioTrack只能播放已经解码的PCM流,如果是文件的话只支持wav格式的音频文件,因为wav格式的音频文件大部分都是PCM流。AudioTrack不创建解码器,所以只能播放不需要解码的wav文件。当然两者之间还是有紧密的联系,MediaPlayer在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,AudioTrack再传递给AudioFlinger进行混音,然后才传递给硬件播放,所以是MediaPlayer包含了AudioTrack。使用AudioTrack播放音乐示例:
AudioTrack audio = new AudioTrack(AudioManager.STREAM_MUSIC, // 指定流的类型32000, // 设置音频数据的采样率 32k,如果是44.1k就是44100AudioFormat.CHANNEL_OUT_STEREO, // 设置输出声道为双声道立体声,而CHANNEL_OUT_MONO类型是单声道AudioFormat.ENCODING_PCM_16BIT, // 设置音频数据块是8位还是16位,这里设置为16位。好像现在绝大多数的音频都是16位的了AudioTrack.MODE_STREAM // 设置模式类型,在这里设置为流类型,另外一种MODE_STATIC貌似没有什么效果);audio.play(); // 启动音频设备,下面就可以真正开始音频数据的播放了// 打开mp3文件,读取数据,解码等操作省略 ...byte[] buffer = new buffer[4096];int count;while(true){// 最关键的是将解码后的数据,从缓冲区写入到AudioTrack对象中audio.write(buffer, 0, 4096);if(文件结束) break;}//关闭并释放资源audio.stop();audio.release();
三、Car Audio调用链梳理
为了了解car audio的整体结构,接下来从3个功能追踪car audio的调用链:安卓初始化打开output、AudioTrack播放音频、音量设置。
1、系统启动时打开output流程
附上此图的plantuml代码和对应的注释:
@startuml
'https://plantuml.com/sequence-diagramautonumber
'android 13'system/core/init/main.cpp
-> main.cpp: main()
'system/core/init/init.cpp
main.cpp -> init.cpp: SecondStageMain()
init.cpp -> init.cpp: LoadBootScripts()
'LoadBootScripts中加载.rc文件
'audioserver.rc位于/system/etc/init目录下
init.cpp -> main_audioserver.cpp: main()
'先初始化AudioFlinger,然后初始化AudioPolicyService
'main_audioserver.cpp -> AudioFlinger: instantiate()
main_audioserver.cpp -> "AudioPolicyService(BinderService)": instantiate()
"AudioPolicyService(BinderService)" -> BinderService: publish()
BinderService -> IServiceManager: addService(,, new SERVICE(), )
'new SERVICE()调用AudioPolicyService::AudioPolicyService()构造函数,初始化
IServiceManager -> AudioPolicyService: onFirstRef()
AudioPolicyService -> AudioPolicyService: createAudioPolicyManager()
AudioPolicyService -> AudioPolicyManager: new AudioPolicyManager()
AudioPolicyManager -> AudioPolicyManager: loadConfig()
'解析配置文件/vendor/etc/audio/audio_policy_configuration.xml
'解析可参考
'https://blog.csdn.net/jackzhouyu/article/details/121983162
'https://blog.csdn.net/u012188065/article/details/84104275
AudioPolicyManager -> AudioPolicyManager: deserializeAudioPolicyXmlConfig()
'解析结果会存在成员变量mConfig(mHwModulesAll, mOutputDevicesAll, mInputDevicesAll, mDefaultOutputDevice),
'initialize()中使用到xml解析出的结果AudioPolicyService -> AudioPolicyManager: initialize()
AudioPolicyManager -> AudioPolicyManager: onNewAudioModulesAvailableInt()
AudioPolicyManager -> SwAudioOutputDescriptor: open()
SwAudioOutputDescriptor -> AudioPolicyClient: openOutput()
AudioPolicyClient -> AudioPolicyClientImpl: openOutput()
AudioPolicyClientImpl -> AudioFlinger: openOutput()
AudioFlinger -> AudioFlinger: openOutput_l()
AudioFlinger -> AudioStreamOut: openOutputStream()
'openOutputStream成功后,根据flag创建不同的PlaybackThread
'从audioflinger调用到libaudiohal的DeviceHalInterface.h
'DeviceHalHidl继承了DeviceHalInterface
AudioStreamOut -> DeviceHalHidl: openOutputStream'DeviceHalHidl调用IDevice.hal生成的接口
'DeviceHalHidl中根据版本判断调用对应的openOutputStream,安卓13调用openOutputStream_7_1
'版本对应hidl接口表格https://source.android.com/docs/core/audio/hidl-implement?hl=zh-cn
DeviceHalHidl -> Device.cpp: openOutputStream_7_1'Device.cpp继承了IDevice
'Device.cpp所在目录hardware/interfaces/audio/core/all-versions/default/Device.cpp
Device.cpp -> Device.cpp: openOutputStreamImpl
Device.cpp -> Device.cpp: openOutputStreamCore
Device.cpp -> audio_hw_device_t:open_output_stream'audio_hw_device_t是一个结构体,定义在/hardware/libhardware/include/hardware/audio.h
'每一个audio_hw_device对应一个音频设备
'具体实现在audiohal中,audio_hw.c里
' adev->hw_device.open_output_stream = adev_open_output_stream;
audio_hw_device_t -> audio_hw.c:adev_open_output_stream'adev_open_output_stream中对audio_stream_out进行配置和初始化
'例如,将audio_hw.c的out_write函数注册到上层的write
'out->stream.write = out_write;
@enduml
audio hal的接口见下图
Android版本 | HIDL HAL版本 |
Android 13 | 7.1 |
Android 12 | 7.0 |
Android 11 | 6.0 |
Android 10 | 5.0 |
Android 9 | 4.0 |
Android 8 | 2.0 |
2、AudioTrack与播放线程的数据传递流程
上面是音频播放的大体流程和结构。
接下来是具体的流程图。
1)音频写入流程
android_media_AudioTrack.cpp中取出之前创建的AudioTrack.
AudioTrack中获取到共享内存后,通过memcpy()向其中写入。
2)音频播放流程
应用层调用AudioTrack.play(),最终会在addTrack_l里将track加入mActiveTracks
@startuml
'https://plantuml.com/sequence-diagramautonumber'从audioflinger调用到libaudiohal的StreamOutHalInterface.h
'StreamHalHidl继承了StreamOutHalInterface
AudioStreamOut -> StreamHalHidl: writeStreamHalHidl -> StreamHalHidl: prepareForWriting
'StreamHalHidl调用IStreamout.hal生成的接口prepareForWriting
'版本对应hidl接口表格https://source.android.com/docs/core/audio/hidl-implement?hl=zh-cn
StreamHalHidl -> Streamout.cpp: prepareForWriting
'prepareForWriting中生成消息队列CommandMQ、DataMQ等,并通过回调返回给libaudiohal里的StreamHalHidl
Streamout.cpp -> WriteThread: threadLoop
WriteThread -> WriteThread: doWrite
WriteThread -> DataMQ: read
WriteThread -> audio_stream_out_t: write
'audio_hw.c的adev_open_output_stream中,
'out->stream.write = out_write;
audio_stream_out_t -> audio_hw.c: out_write
audio_hw.c -> tinylasa: pcm_writeStreamHalHidl -> StreamHalHidl: callWriterThread
StreamHalHidl -> CommandMQ: write
StreamHalHidl -> DataMQ: write@enduml
此处专注于播放流程,播放线程PlaybackThread的初始化在此省略。
初始化后会循环调用threadLoop()。
其中有3个主要的函数:
- prepareTracks_l:prepareTracks_l比较复杂,其中遍历了整个mActiveTracks
这里使用的DirectOutputThread,这种线程里面只能有一个Track,所以这里是mActiveTrack,并不是一个复数;如果是MixThread,内部用mActiveTracks保存多个Track,而且还涉及到混音等
- threadLoop_mix:执行mHook指向的函数,从共享内存中读取音频数据到buffer。
getNextBuffer()会获取buffer;后续会把数据拷贝音频数据到curBuf,也就是mSinkBuffer中去:memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);
- threadLoop_write:callWriterThread中向共享内存mDataMQ写入数据,这个共享内存是在hal层的StreamOut::prepareForWriting()中创建。HAL层开启了WriteThread线程,读取音频数据,然后写入Kernel驱动。
3、音量设置
假如没有配置动态路由
packages/services/Car/service/res/values/config.xml中<bool name="audioUseDynamicRouting">false</bool>
则音量设置最终没有到hal层,而是保存在framework。回播线程按照streamType类型集合保存了音量值,准确说应该是音量值对应的功率值,在播放音频或混音时,回播线程会根据streamType取出对应value,将音频数据与音量数据相乘。
CarAudioManager -> CarAudioService: setGroupVolume()
'非动态路由
CarAudioService -> AudioManager: setStreamVolume()
AudioManager -> AudioService: setStreamVolumeWithAttribution
AudioService -> AudioService: setStreamVolume
AudioService -> AudioService: onSetStreamVolume
AudioService -> AudioService: setStreamVolumeInt
AudioService -> AudioService: sendMsg(MSG_SET_DEVICE_VOLUME)
AudioService -> AudioService: setDeviceVolume
AudioService -> VolumeStreamState: applyDeviceVolume_syncVSS
VolumeStreamState -> VolumeStreamState: setStreamVolumeIndex
VolumeStreamState -> AudioSystem: setStreamVolumeIndexAS
AudioSystem -> AudioSystem: setStreamVolumeIndex
AudioSystem -> IAudioPolicyService: setStreamVolumeIndex
IAudioPolicyService -> AudioPolicyManager: setStreamVolumeIndex
AudioPolicyManager -> AudioPolicyManager: setVolumeIndexForAttributes
AudioPolicyManager -> AudioPolicyManager: checkAndSetVolume
AudioPolicyManager -> SwAudioOutputDescriptor: setVolume
SwAudioOutputDescriptor -> AudioPolicyClientInterface: setStreamVolume
AudioPolicyClientInterface -> AudioPolicyServie.cpp: setStreamVolume
AudioPolicyServie.cpp -> AudioCommandThread: volumeCommand
AudioCommandThread -> AudioCommandThread: SET_VOLUME
AudioCommandThread -> AudioSystem: setStreamVolume
AudioSystem -> AudioFlinger: setStreamVolume
AudioFlinger -> VolumeInterface: setStreamVolume
'回播线程按照streamType类型集合保存了音量值,准确说应该是音量值对应的功率值,在播放音频或混音时,回播线程会将音频数据与音量数据相乘
'frameworks/av/services/audioflinger/Threads.cpp
VolumeInterface -> PlaybackThread: setStreamVolume
void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{Mutex::Autolock _l(mLock);mStreamTypes[stream].volume = value;broadcast_l();
}
如果是voice的音量,在AudioPolicyManager: checkAndSetVolume会有差异,最终会通过hal层调用到tinyalsa
CarAudioManager -> CarAudioService: setGroupVolume()
'非动态路由
CarAudioService -> AudioManager: setStreamVolume()
AudioManager -> AudioService: setStreamVolumeWithAttribution
AudioService -> AudioService: setStreamVolume
AudioService -> AudioService: onSetStreamVolume
AudioService -> AudioService: setStreamVolumeInt
AudioService -> AudioService: sendMsg(MSG_SET_DEVICE_VOLUME)
AudioService -> AudioService: setDeviceVolume
AudioService -> VolumeStreamState: applyDeviceVolume_syncVSS
VolumeStreamState -> VolumeStreamState: setStreamVolumeIndex
VolumeStreamState -> AudioSystem: setStreamVolumeIndexAS
AudioSystem -> AudioSystem: setStreamVolumeIndex
AudioSystem -> IAudioPolicyService: setStreamVolumeIndex
IAudioPolicyService -> AudioPolicyManager: setStreamVolumeIndex
AudioPolicyManager -> AudioPolicyManager: setVolumeIndexForAttributes
AudioPolicyManager -> AudioPolicyManager: checkAndSetVolume
'通话音量调节,从checkAndSetVolume()中进入mpClientInterface->setVoiceVolume
AudioPolicyManager -> AudioPolicyClientInterface: setVoiceVolume
AudioPolicyClientInterface -> AudioPolicyServie.cpp: setVoiceVolume
AudioPolicyServie.cpp -> AudioCommandThread: voiceVolumeCommand
AudioCommandThread -> AudioCommandThread: SET_VOICE_VOLUME
AudioCommandThread -> AudioSystem: setVoiceVolume
AudioSystem -> AudioFlinger: setVoiceVolume
AudioFlinger -> DeviceHalHidl: setVoiceVolume
DeviceHalHidl -> PrimaryDevice::setVoiceVolume
PrimaryDevice -> audio_hw_device_t::set_voice_volume
audio_hw_device_t -> audio_hw.c:adev_set_voice_volume
audio_hw.c -> tinyalsa: mixer_ctl_set_value
与之对应,假如配置了动态路由,是可以从framework调用到hal层的
CarAudioManager -> CarAudioService: setGroupVolume()
'动态路由
CarAudioService -> CarVolumeGroup: setCurrentGainIndex()
CarVolumeGroup -> CarVolumeGroup: setCurrentGainIndexLocked()
CarVolumeGroup -> CarAudioDeviceInfo: setCurrentGain()
CarAudioDeviceInfo -> AudioManagerHelper: setAudioDeviceGain()
AudioManagerHelper -> AudioManager: setAudioPortGain()
AudioManager -> AudioSystem: setAudioPortConfig()
AudioSystem -> IAudioPolicyService: setAudioPortConfig()
IAudioPolicyService -> AudioPolicyManager: setAudioPortConfig()
AudioPolicyManager -> AudioPolicyClientInterface: setAudioPortConfig()
AudioPolicyClientInterface -> AudioPolicyServie.cpp: setAudioPortConfigCommand()
AudioPolicyServie.cpp -> AudioCommandThread: setAudioPortConfigCommand()
AudioCommandThread -> AudioCommandThread: SET_AUDIOPORT_CONFIG
AudioCommandThread -> AudioFlinger: setAudioPortConfig()
AudioFlinger -> AudioHwDevice: setAudioPortConfig()
AudioHwDevice -> DeviceHalHidl: setAudioPortConfig()
DeviceHalHidl -> Device.cpp: setAudioPortConfig()
Device.cpp -> audio_hw_device_t:set_audio_port_config
'adev->hw_device.set_audio_port_config = adev_set_audio_port_config;
audio_hw_device_t -> audio_hw.c:adev_set_audio_port_config