当前位置: 首页 > news >正文

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/mediaCarAudioManager
packages/services/Car/service/src/com/android/car/audioCarAudioService
frameworks/base/media/java/android/mediaAudioManager,AudioSystem
frameworks/base/services/core/java/com/android/server/audioAudioService
frameworks/av/media/libaudioclientAudioSystem.cpp
frameworks/av/services/audioflingerAudioFlinger
frameworks/av/services/audiopolicyAudioPolicyService
hardware/interfaces/audioIDevice.hal
hardware/libhardware/include/hardwareaudio.h
frameworks/av/media/libaudiohalStreamHalHidl
hardware/interfaces/automotive/audiocontrolAudioControl.cpp
hardware/qcom/audio/halaudio_hw.c(原生)
external/tinyalsaasoundlib.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 137.1
Android 127.0
Android 116.0
Android 105.0
Android 94.0
Android 82.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

http://www.dtcms.com/a/350985.html

相关文章:

  • Spark 节点 IDO 正式开启 —引领 PayFi 新时代
  • 解析蛋白质三维结构-Bio3D R包
  • Elasticsearch精准匹配与全文检索对比
  • 矩阵微积分的链式法则(chain rule)
  • 一步一步在Kubernetes集群部署NVIDIA KAI Scheduler
  • 数据挖掘 7.1~7.4 Clustering聚类
  • Spark云原生流处理实战与风控应用
  • 【贪心】11 盛最多水的容器(双指针解法)
  • 解决Windows更新后WPF程序报TypeLoadException异常的问题
  • 论文Review 激光3DGS GS-SDF | IROS2025 港大-MARS!| 激光+3DGS+NeRF会得到更好的几何一致性和渲染结果!?
  • OceanStor Pacific 9926全闪分布式存储,海量数据大容量场景的救星来咯[特殊字符]!
  • 列表里的对象,按对象的某个属性值排序
  • 如何利用ArcGIS探究环境与生态因子对水体、土壤、大气污染物等影响实践技术
  • 深入了解linux系统—— 线程互斥
  • 软件开发|Shiro框架以及集成Spring Boot
  • AI算力提升7.5倍!英伟达发布新一代机器人超级计算机Jetson Thor,驱动物理AI革命
  • SMOTE过采样实现过程详解
  • 论文阅读 2025-8-26 一些半监督学习的工作
  • JVM-(11)JVM-定位OOM问题
  • 论文学习日志——忆阻器与神经网络——part1
  • Python大型数组计算完全指南:从基础到分布式系统实践
  • Zookeeper(分布式RPC调用和分布式文件储存)
  • [小练习]100行不到使用Java Socket网络编程实现定向聊天
  • 从双重检查锁定的设计意图、锁的作用、第一次检查提升性能的原理三个角度,详细拆解单例模式的逻辑
  • 基于SpringBoot的网上点餐系统
  • OpenTenBase核心技术解密:突破OLTP与OLAP边界的分布式数据库革新
  • Flask模块如何使用
  • 浅谈技术顾问的转型困境
  • Web前端开发基础
  • windows 下 使用C++ 集成 zenoh