一、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就是44100
 
     AudioFormat.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-diagram

autonumber
'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-diagram

autonumber

'从audioflinger调用到libaudiohal的StreamOutHalInterface.h
'StreamHalHidl继承了StreamOutHalInterface
AudioStreamOut -> StreamHalHidl: write

StreamHalHidl -> 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_write

StreamHalHidl -> 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,将音频数据与音量数据相乘。

这种情况下,开机时原生会有日志打印

09-19 10:46:08.195  2718  2718 I CAR.AUDIO: Audio dynamic routing not enabled, run in legacy mode

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

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐