分享

Android 8.1 Audio框架(一)初始化分析

 开花结果 2020-04-29

概述

本文将讲述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。

加载audio configuration

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信息结合代码总结出来的。

可以看出这个信息是属于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中。

加载HwModulempClientInterface指向的是AudioPolicyClient,AudioPolicyClient::loadHwModule又通过IAudioFlinger调用到AudioFlinger。AudioFlinger的loadHwModule又调用了loadHwModule_l,所以mpClientInterface->loadHwModule最终调用到的是AudioFlinger::loadHwModule_l。这里调用到DeviceHalInterface的openDevice函数,这里函数涉及的内容有点多,我这里先给出结论:它会根据传递进来的名字,打开对应的hal库,比如打开audio.a2dp.default.so,然后把句柄等信息保存在AudioHwDevice对象中,并把这个对象添加到mAudioHwDevs中。这样一个mAudioHwDevs元素就是一个so库。好了,结论已经给出来了,现在来分析过程。不过这个过程涉及到很多类,我这边把类图列举出来,这样结构比较清晰。

  

说明:箭头关系参考UML类图(Class Diagram)中类与类之间的关系及表示方式 不过AudioFlinger和PlaybackThread之间的箭头是错误的。PlaybackThread是AudioFlinger的内部类,而且生命周期不一样,不能用那个箭头表示,应该使用

这种箭头表示。

先从AudioFlinger初始化的时候开始看。

可以看出DevicesFactoryHalHybrid在初始化的时候会根据需求选择初始化本地类DevicesFactoryHalLocal还是hidl类DevicesFactoryHalHidl。我这边都是本地类,所以不分析DevicesFactoryHalHidl了。所以 AudioFlinger::loadHwModule_l中的mDevicesFactoryHal->openDevice调用的是DevicesFactoryHalHybrid::openDevice。来看看openDevice函数会做什么操作。原来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的能力了。到了这里,刚才那个类图的上面部分的箭头关系已经理清楚了。mpClientInterface->loadHwModule部分已经讲解完了,现在继续往下看。

打开输出设备mpClientInterface->openOutput跟loadHwModule类似,最终会调用到AudioFlinger::openOutput。到了这里,我们可以来看看上面那个类图的下半部分了。来看看AudioHwDevice::openOutputStream会做什么可以看出StreamOutHalLocal中保存有输出流的接口,以后写音频数据就调用里面的write函数去写就行了。然后StreamOutHalLocal对象会被保存到AudioStreamOut的私有成员 sp<StreamOutHalInterface> stream中。现在我们有了AudioStreamOut这个对象,我们来看看openOutput_l中还会做什么操作。

可以看出,之前创建的AudioStreamOut作为MixerThread的参数传递进去了。这个很容易理解,MixerThread是一个混音线程,混音之后肯定要输出音频数据的,这里的AudioStreamOut作为向音频设备的输出类,肯定是要作为参数传递进去的。



原文链接:https://blog.csdn.net/qq_27136111/java/article/details/90264199

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多