本文将讲述AudioPolicyService、AudioPolicyManager的初始化过程,解析加载xml文件之后生成的模块,研究AudioPolicyManager是如何根据profile打开对应的模块并构建好输出音频数据的路径。
1.AudioPolicyService:APS是音频框架的服务,在Main_audioserver.cpp中生成,它在第一次强引用的时候会创建AudioCommandThread和AudioPolicyClient,AudioPolicyManager。它主要由AudioSystem通过binder调用,也可以由AudioPolicyClient,AudioPolicyManager直接调用。它的大部分操作都交给AudioPolicyManager来做
2.AudioPolicyClient:APC是AudioPolicyService的内部类。它用于打开关闭输入输出,设置流音量,传递参数给hal层(如audio_hw.c)等;它主要是通过binder跨进程调用AudioFlinger去完成真正的操作。可以由AudioManager通过mpClientInterface去调用它
3.AudioPolicyManager:APM是AudioPolicyService的码农,AudioPolicyService的大部分操作都由他来执行
4.AudioFlinger:AF主要承担音频混合输出,是Audio系统的核心,从AudioTrack来的数据最终都会在这里处理,并被写入到Audio的HAL。
5.DevicesFactoryHalLocal:根据名字加载对应的hal module。比如传进去a2dp相关的名字,会加载到audio.a2dp.default.so
6.DevicesFactoryHalHidl:跨进行加载hidl hal module。我这边的平台是local的,所以hidl相关就不分析了。
7.DevicesFactoryHalInterface:用于创建子类DevicesFactoryHalHybrid。
8.DevicesFactoryHalHybrid:选择创建DevicesFactoryHalLocal或者DevicesFactoryHalHidl,我这里只创建DevicesFactoryHalLocal。
9.DeviceHalLocal:通过私有成员audio_hw_device_t *mDev,直接调用hal代码,用来设置和获取底层参数,打开和关闭stream。
10.StreamOutHalLocal:通过私有成员audio_stream_out_t *mStream直接调用hal代码,用于操作流,比如start、stop、flush、puse操作;还有调用write函数写音频数据到hal层。
11.AudioStreamOutSink:它其实是StreamOutHalLocal的一个wrapper,它也有write函数,不过是通过StreamOutHalLocal来操作的。
初始化过程可以分为四部分:
1.加载audio configuration。
2.加载每一个HwModule。
3.初始化默认输出输入设备。找到默认设备对应的IOProfile,为它创建SwAudioOutputDescriptor,然后openOutput。
AudioPolicyManager在初始化的时候会去加载xml文件,解析xml文件,然后把解析出来的信息填充到对应的类中。熟悉audio profile相关信息有助于后面分析代码。下图表示的是相关的类信息。
说明:
HwModuleCollection是一个Vector类,用于保存HwModule,我这里有三个module,一个是primary,一个是a2dp,一个是usb。可以看出我这三个module都是从三个xml文件中解析出来的,每个module都包含有对应xml文件的所有信息。可以使用dumpsys media.audio_policy 把所有的policy信息dump出来,这个dump是调用HwModuleCollection::dump之类的函数,把所有的子集都dump出来。下面就是使用这个命令dump出来的部分信息,右半部分是我手动添加的,dump信息结合代码总结出来的。
W Modules dump: HwModuleCollection::dump (HwModuleCollection mHwModules)
- HW Module 1:
- name: primary HwModule::dump (itemAt(i)->dump)
- handle: 10
- version: 2.0
- outputs:
output 0:
IOProfile::dump (mOutputProfiles[i]->dump)(OutputProfileCollection mOutputProfiles)
- name: primary output AudioPort::dump [对应xml中<mixPorts>]
- Profiles: AudioProfileVector::dump (mProfiles.dump)
Profile 0:
- format: AUDIO_FORMAT_PCM_16_BIT AudioProfile::dump(itemAt(i)->dump)
- sampling rates:44100
- channel masks:0x0003
- flags: 0x0002 (AUDIO_OUTPUT_FLAG_PRIMARY) IOProfile::dump
- Supported devices: DeviceVector::dump [对应xml中<devicePorts>] (mSupportedDevices)
Device 1: DeviceDescriptor::dump (itemAt(i))
- id: 1
- tag name: Earpiece
- type: AUDIO_DEVICE_OUT_EARPIECE
Device 2:
- id: 2
- tag name: Speaker
- type: AUDIO_DEVICE_OUT_SPEAKER
Device 5:
- tag name: BT SCO
- type: AUDIO_DEVICE_OUT_BLUETOOTH_SCO
output 2:
- name: voice_tx
- Profiles:
Profile 0:
- format: AUDIO_FORMAT_PCM_16_BIT
- sampling rates:8000, 16000
- channel masks:0x0001
- flags: 0x0000 (AUDIO_OUTPUT_FLAG_NONE)
...........
- inputs: HwModule::dump (itemAt(i)->dump)
input 0:
IOProfile::dump (mInputProfiles[i]->dump)
- name: primary input AudioPort::dump [这个对应xml文件里的<mixPorts>]
- Profiles: AudioProfileVector::dump (mProfiles.dump)
Profile 0:
- format: AUDIO_FORMAT_PCM_16_BIT
- sampling rates:8000,16000,..., 44100, 48000
- channel masks:0x000c, 0x0010, 0x0030
- flags: 0x0000 (AUDIO_INPUT_FLAG_NONE) IOProfile::dump
- Supported devices: DeviceVector::dump [对应xml中<devicePorts>](mSupportedDevices)
Device 1: DeviceDescriptor::dump (itemAt(i))
- id: 4
- tag name: Built-In Mic
- type: AUDIO_DEVICE_IN_BUILTIN_MIC
* 从xml中的routes解析出来的new AudioPolicyManager--> PolicySerializer::deserialize--> HwModule::setRoutes
* --> HwModule::refreshSupportedDevices 这个函数会去检查routes,检查通过后把source device和sink device放入
* IOProfile::mSupportedDevices中,加入到这个结构体之后,估计就不需要原始的route信息了,直接从IOProfile就可以知道
* source和sink的对应关系。随后IOProfile存储的mSupportedDevices会被用于创建SwAudioOutputDescriptor
Audio Routes (9):
- Route 1:
- Type: Mix
- Sink: Earpiece
- Sources:
primary output
compressed_offload
BT SCO Headset Mic
...........
Inputs dump: dump出当前在使用的输入源
- Input 38 dump:
ID: 11
Sampling rate: 16000
Format: 1
Channels: 0000000c
Devices 80000004 mic录音的,由于车机有一直在录音,所以这个一直存在
Audio Sessions:
Audio session 1:
- session: 25
- owner uid: 10018
- input source: 1
- format: 00000001
- sample: 16000
- channel mask: 0000000c
- is soundtrigger: false
- open count: 1
- active count: 1
* 收音机相关input,软件回路的收音机会用AudioRecord去录取收音机的声音再使用audiotrack播放。
* 当收音机启动的时候才会有,会调用AudioRecord::openRecord_l--->AudioSystem::getInputForAttr--
* -->AudioPolicyManager::getInputForAttr-->AudioPolicyManager::getInputForDevice--
* -->addInput(input, inputDesc) 添加到mInputs中,这样dump就可以dump出来了
* 当切到音乐播放器播放的时候,会调用AudioPolicyManager的stopInput和
* releaseInput,然后releaseInput会调用closeInput把这个input给释放掉,被释放之后就dump不到了。
*
- Input 46 dump:
ID: 13
Sampling rate: 32000
Format: 1
Channels: 0000000c
Devices 80002000
Audio Sessions:
Audio session 1:
- session: 33
- owner uid: 1041
- input source: 1998
- format: 00000001
- sample: 32000
- channel mask: 0000000c
- is soundtrigger: false
- open count: 1
- active count: 1
可以看出这个信息是属于primary这个module的,也就是对应audio.primary.default.so这个库。module可能会有多个output,多个input,
每一个output或者input对应一个IOProfile。我们来看下上面的output 0,它只有一个profile,支持16位pcm,44100采样率,channel masks为0x0003,这个值对应AUDIO_CHANNEL_OUT_STEREO,也就是立体声的意思。这些采样率等信息存放在AudioPort里面的AudioProfile中。output 0支持好几个devices,比如AUDIO_DEVICE_OUT_SPEAKER、AUDIO_DEVICE_OUT_BLUETOOTH_SCO等等。这些device经常拿来作为选择哪一个module的指标。这些device信息都存放在DeviceDescriptor中。
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)....{
//加载xml文件,填充对应的类,填充到mHwModules中
mVolumeCurves = new VolumeCurvesCollection();
AudioPolicyConfig config(mHwModules, mAvailableOutputDevices, mAvailableInputDevices...)
if (deserializeAudioPolicyXmlConfig(config) != NO_ERROR) {
....
}
for (size_t i = 0; i < mHwModules.size(); i++) {
mHwModules[i]->mHandle = mpClientInterface->loadHwModule(mHwModules[i]->getName()); //加载每一个HwModule
...
}
}
mpClientInterface指向的是AudioPolicyClient,AudioPolicyClient::loadHwModule又通过IAudioFlinger调用到AudioFlinger。AudioFlinger的loadHwModule又调用了loadHwModule_l,所以mpClientInterface->loadHwModule最终调用到的是AudioFlinger::loadHwModule_l。
audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
{
for (size_t i = 0; i < mAudioHwDevs.size(); i++) { //查找是否存在已经打开过的模块
if (strncmp(mAudioHwDevs.valueAt(i)->moduleName(), name, strlen(name)) == 0) {
ALOGW("loadHwModule() module %s already loaded", name);
return mAudioHwDevs.keyAt(i); //如果存在,则直接返回
}
}
//到了这里说明没有打开过模块,需要打开。
sp<DeviceHalInterface> dev;
int rc = mDevicesFactoryHal->openDevice(name, &dev);
.....
}
这里调用到DeviceHalInterface的openDevice函数,这里函数涉及的内容有点多,我这里先给出结论:它会根据传递进来的名字,打开对应的hal库,比如打开audio.a2dp.default.so,然后把句柄等信息保存在AudioHwDevice对象中,并把这个对象添加到mAudioHwDevs中。这样一个mAudioHwDevs元素就是一个so库。好了,结论已经给出来了,现在来分析过程。不过这个过程涉及到很多类,我这边把类图列举出来,这样结构比较清晰。
说明:箭头关系参考UML类图(Class Diagram)中类与类之间的关系及表示方式 不过AudioFlinger和PlaybackThread之间的箭头是错误的。PlaybackThread是AudioFlinger的内部类,而且生命周期不一样,不能用那个箭头表示,应该使用这种箭头表示。
先从AudioFlinger初始化的时候开始看。
AudioFlinger::AudioFlinger(){
mDevicesFactoryHal = DevicesFactoryHalInterface::create();
}
sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
return new DevicesFactoryHalHybrid();
}
DevicesFactoryHalHybrid::DevicesFactoryHalHybrid()
: mLocalFactory(new DevicesFactoryHalLocal()),
mHidlFactory(
#ifdef USE_LEGACY_LOCAL_AUDIO_HAL
nullptr
#else
new DevicesFactoryHalHidl()
#endif
) {
}
可以看出DevicesFactoryHalHybrid在初始化的时候会根据需求选择初始化本地类DevicesFactoryHalLocal还是hidl类DevicesFactoryHalHidl。我这边都是本地类,所以不分析DevicesFactoryHalHidl了。所以 AudioFlinger::loadHwModule_l中的mDevicesFactoryHal->openDevice调用的是DevicesFactoryHalHybrid::openDevice。来看看openDevice函数会做什么操作。
status_t DevicesFactoryHalHybrid::openDevice(const char *name, sp<DeviceHalInterface> *device) {
if (mHidlFactory != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_A2DP, name) != 0) {
return mHidlFactory->openDevice(name, device); //调用DevicesFactoryHalLocal::openDevice
}
return mLocalFactory->openDevice(name, device);
}
status_t DevicesFactoryHalLocal::openDevice(const char *name, sp<DeviceHalInterface> *device) {
audio_hw_device_t *dev;
status_t rc = load_audio_interface(name, &dev); //加载hardware module
if (rc == OK) {
*device = new DeviceHalLocal(dev);
}
return rc;
}
static status_t load_audio_interface(const char *if_name, audio_hw_device_t **dev)//根据名字加载对应的hardware module
{
const hw_module_t *mod;
rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);audio_hw_device_open(mod, dev);
rc = audio_hw_device_open(mod, dev);
return rc;
}
DeviceHalLocal::DeviceHalLocal(audio_hw_device_t *dev)
: mDev(dev) {
}
原来DevicesFactoryHalHybrid::openDevice就是调用 DevicesFactoryHalLocal::openDevice去加载对应的hardware module。比如我这里传入的是a2dp相关的名字,所以我这里加载的so是audio.a2dp.default.so。然后把audio_hw_device_t作为参数传递给DeviceHalLocal,DeviceHalLocal把这个参数保存在自己的私有变量中audio_hw_device_t *mDev。
所以,loadHwModule_l传递回去的dev就是等于DeviceHalLocal,该类保存有hardware module相关信息。然后dev又作为参数传递给AudioHwDevice,这样AudioHwDevice就拥有访问hardware module的能力了。
audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name){
...
sp<DeviceHalInterface> dev;
int rc = mDevicesFactoryHal->openDevice(name, &dev); //dev 等于DeviceHalLocal
...
mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
}
到了这里,刚才那个类图的上面部分的箭头关系已经理清楚了。mpClientInterface->loadHwModule部分已经讲解完了,现在继续往下看。
AudioPolicyManager::AudioPolicyManager(...)...{
for (size_t i = 0; i < mHwModules.size(); i++) {
mHwModules[i]->mHandle = mpClientInterface->loadHwModule(mHwModules[i]->getName());
for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++){ //每一个module下面都有一个或这多个IOProfile
const sp<IOProfile> outProfile = mHwModules[i]->mOutputProfiles[j]; //取出每一个IOProfile
audio_devices_t profileType = outProfile->getSupportedDevicesType();
if ((profileType & mDefaultOutputDevice->type()) != AUDIO_DEVICE_NONE) {
profileType = mDefaultOutputDevice->type(); //如果xml文件中有设置默认设备,则遍历直到找到默认设备对应的IOProfile
}
//为默认设备的IOProfile创建SwAudioOutputDescriptor,它保存着支持的输出格式,采样率,声道类型等信息
sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile, mpClientInterface);
//从outputDesc中取出信息,保存到config中
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = outputDesc->mSamplingRate;
config.channel_mask = outputDesc->mChannelMask;
config.format = outputDesc->mFormat;
//打开输出设备
status_t status = mpClientInterface->openOutput(outProfile->getModuleHandle(),&output,&config...);
}
}
}
mpClientInterface->openOutput跟loadHwModule类似,最终会调用到AudioFlinger::openOutput。
status_t AudioFlinger::openOutput(...){
sp<ThreadBase> thread = openOutput_l(module, output, config, *devices, address, flags);
}
sp<AudioFlinger::ThreadBase> AudioFlinger::openOutput_l(...){
AudioHwDevice *outHwDev = findSuitableHwDev_l(module, devices); //加载对应的AudioHwDevice
AudioStreamOut *outputStream = NULL;
status_t status = outHwDev->openOutputStream(&outputStream, *output, devices,flags,config,address.string());
}
到了这里,我们可以来看看上面那个类图的下半部分了。来看看AudioHwDevice::openOutputStream会做什么
status_t AudioHwDevice::openOutputStream(AudioStreamOut **ppStreamOut,audio_io_handle_t handle,audio_devices_t devices...)
{
AudioStreamOut *outputStream = new AudioStreamOut(this, flags);
status_t status = outputStream->open(handle, devices, config, address);
}
AudioStreamOut::AudioStreamOut(AudioHwDevice *dev, audio_output_flags_t flags)
: audioHwDev(dev)... //保存有AudioHwDevice对象
{
}
AudioStreamOut::open(...){
sp<StreamOutHalInterface> outStream;
//hwDev()等于mHwDevice,所以hwDev()->openOutputStream相当于调用DeviceHalLocal::openOutputStream
int status = hwDev()->openOutputStream( handle,devices,customFlags,config,address,&outStream);
if (status == NO_ERROR) {
stream = outStream;
...
}
...
}
sp<DeviceHalInterface> AudioStreamOut::hwDev() const
{
return audioHwDev->hwDevice();
}
class AudioHwDevice {
sp<DeviceHalInterface> hwDevice() const { return mHwDevice; }
}
status_t DeviceHalLocal::openOutputStream(...sp<StreamOutHalInterface> *outStream) {
audio_stream_out_t *halStream;
int openResut = mDev->open_output_stream( //调用hardware的open_output_stream函数
mDev, handle, devices, flags, config, &halStream, address);
if (openResut == OK) {
*outStream = new StreamOutHalLocal(halStream, this);//把输出流保存起来,以后写音频数据就往这个接口写就行。
}
return openResut;
}
可以看出StreamOutHalLocal中保存有输出流的接口,以后写音频数据就调用里面的write函数去写就行了。然后StreamOutHalLocal对象会被保存到AudioStreamOut的私有成员 sp
sp<AudioFlinger::ThreadBase> AudioFlinger::openOutput_l(...){
AudioStreamOut *outputStream = NULL;
status_t status = outHwDev->openOutputStream( &outputStream,...)
if (status == NO_ERROR) {
if (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
} else {
sp<PlaybackThread> thread;
if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
} else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT)....)
} else { //我的primary设备只走这个分支
thread = new MixerThread(this, outputStream, *output, devices, mSystemReady);
}
mPlaybackThreads.add(*output, thread);
return thread;
}
}
}
可以看出,之前创建的AudioStreamOut作为MixerThread的参数传递进去了。这个很容易理解,MixerThread是一个混音线程,混音之后肯定要输出音频数据的,这里的AudioStreamOut作为向音频设备的输出类,肯定是要作为参数传递进去的。