Android audio(8)-native音频服务的启动与协作(audiopolicyservice和audioflinger)

音频策略的构建
1、概述
2、AudiopolicyService
2.1 任务
2.2 启动流程
2.2.1 加载audio_policy.conf(xml)配置文件
2.2.2 初始化各种音频流对应的音量调节点
2.2.3 加载audio policy硬件抽象库
2.2.4设置输出设备
ps:audiopatch流程简介
2.2.5打开输出设备
2.3 策略制定总结
3、AudioFlinger
3.1 任务
3.2 启动流程
3.3 打开output
4、总结

1、概述

本文讲解音频系统的“核心组成:音频策略的制定者AudiopolicyService以及音频策略的执行者AudioFlinger。其它的文章深度都不高,而且独立性较强,分别讲解audiofligner和audiopolicyservice服务能够独立完成的业务功能,本篇重点讲解audioflinger和audiopolicyservice如何协同工作以及分工,共同保障音频系统的正常工作。
其中 AudioPolicy 相当于军师的角色,专门来制定 Audio 播放时的相关的策略及设定相关的参数,而 AudioFlinger 相当于将军,会根据军师的策略来执行。
上面的话只要是学过音频相关知识的人都会说,都了解的。***那么什么是策略呢?什么是执行呢?策略和执行能放到一起吗?策略和执行的隔离程度是什么级别的?***把这个问题想清楚,你的思维就比别人跟进一步。
***策略就是组织各种数据的关系,定义整体的结构,制定事务的流程。***而执行则是处理具体的事务,负责流程中某个具体的步骤,管理某个数据。
策略和执行从代码的角度来看完全可以放在一起。但是根据我们开发的经验可以知道,策略的修改频率要远高于执行。如果我们把策略和执行放到一个库里面,那么每次修改策略都会对执行引入风险。所以我们要将策略和执行分开。这也是软件设计思想,分离变化的一种体现。
策略和执行(policy和finger)的隔离程度是库隔离,但是还是运行在一个进程。如果其中一个模块发生崩溃还是会连累另外一个模块。
隔离程度简单划分有三种类隔离,库隔离,进程隔离。

2、AudiopolicyService

在这里插入图片描述

2.1 任务

管理输入输出设备,包括设备的断连,设备的选择和切换等(加载audio_policy.default.so库得到audio_policy_module模块)
管理系统的音频策略,比如通话时播放音乐、或者播放音乐时来电的一系列处理(通过audio_policy_module模块打开audio_policy_device设备)
管理系统的音量
上层的一些音频参数也可以通过AudioPolicyService设置到底层去 (通过audio_policy_device
设备创建audio_policy)

2.2 启动流程

AudioPolicyService服务运行在audioserver进程中, 随着audioserver进程启动而启动。

//frameworks/av/media/audioserver/main_audioserver.cpp
int main(int argc __unused, char **argv)
......
sp<ProcessState> proc(ProcessState::self());sp<IServiceManager> sm = defaultServiceManager();ALOGI("ServiceManager: %p", sm.get());AudioFlinger::instantiate();//实例化AudioFlinger服务AudioPolicyService::instantiate();//实例化AudioPlicyService服务

其中,AudioPolicyService::instantiate()并不由AudioPolicyService实现,而是BinderService类的一个实现。包括AudioFlinger,AudioPolicyservice等在内的几个服务都继承自这个统一的Binder的服务类,具体实现在BinderService.h中。如果后续我们需要自己开发一个基于binder的服务那么也可以继承binderservice来实现。

/frameworks/native/include/binder
static status_t publish(bool allowIsolated = false,int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {sp<IServiceManager> sm(defaultServiceManager());//SERVICE是文件中定义的一个模板,AudioPolicyService调用了instantiate()函数,//所以当前的SERVICE为AudioPolicyServicereturn sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated,dumpFlags);

publish()函数获取到ServiceManager的代理,然后new一个调用instantiate的service对象并把它添加到ServiceManager中。所以下一步就是去分析AudioPolicyService的构造函数。

// frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp
AudioPolicyService::AudioPolicyService(): BnAudioPolicyService(),mAudioPolicyManager(NULL),mAudioPolicyClient(NULL),mPhoneState(AUDIO_MODE_INVALID),mCaptureStateNotifier(false) {
}

它的构造函数里面初始化了一些变量,那么AudioPolicyService所作的初始化的事情是在什么地方进行的呢,继续分析上面的构造函数,AudioPolicyService是继承自BnAudioPolicyService的,一步步往上推,最终发现它的祖先是RefBase,根据强指针的特性,目标对象在第一次被引用时会调用onFirstRef()的,我们就去看一下AudioPolicyService::onFirstRef()。不难发现,android代码中的构造函数一般都用于初始化一些成员变量。大部分的工作都是在其它函数中实现。比如onfirstref。

void AudioPolicyService::onFirstRef()
{{Mutex::Autolock _l(mLock);// start audio commands threadmAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);//创建ApmAudio线程用于执行
audio命令// start output activity command threadmOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);//创建ApmOutpur线程用于执行
输出命令mAudioPolicyClient = new AudioPolicyClient(this);//实例化AudioPolicyClient对象mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);//实例化AudioPolicyManager对象}
......// load audio processing modules//解析audio_effects.conf 文件,得到并加载系统支持的音效库。初始化各个音效对应的参数,将各音效和对应的输入和输出
//流绑定在一起,这样,当上层要使用音效时,就会在对应的threadloop中调用process_l音效处理函数。sp<AudioPolicyEffects> audioPolicyEffects = new AudioPolicyEffects();//初始化音效相关
#ifdef SPRD_CUSTOM_AUDIO_POLICY{Mutex::Autolock _l(mLock);mAudioPolicyEffects = audioPolicyEffects;//}pthread_t uidpolicy;

这里audiopolicyservice为什么要起两个commandthread?audioflinger和audiopoliyservice是在同一个进程。如果audiopolicymanager直接调用audioflinger的接口就会阻塞住。(也就是同步的)。而通过线程去调用audioflinger的话或者处理一些耗时的任务,audiopolicymanager就可以很快返回不需要阻塞住。我自己的看法如果没了这两个thread。audiopolicyservice都不能叫service了,直接和manager合二为一算了

AudioPolicyClient,AudioPolicyClient类定义在AudioPolicyService.h中

//frameworks/av/services/audiopolicy/service/AudioPolicyService.h
class AudioPolicyClient : public AudioPolicyClientInterface
{
public:explicit AudioPolicyClient(AudioPolicyService *service) : mAudioPolicyService(service) {}......
}

它的实现在frameworks/av/services/audiopolicy/service/AudioPolicyClientImpl.cpp中。创建完AudioPolicyClient之后通过调用createAudioPolicyManager方法创建了一个AudioPolicyManager对象,下面看一下createAudioPolicyManager方法中是怎样创建
AudioPolicyManager的。

//frameworks/av/services/audiopolicy/manager/AudioPolicyFactory.cpp
extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
{return new AudioPolicyManager(clientInterface);//将会调用类AudioPolicyManager的构造函数,接下来将重点分析该构
造函数,这是我们分析AudioPolicyService的关键。
}

可见他是直接new了一个AudioPolicyManager然后把刚才创建的AudioPolicyClient传了进去,使用AudioPolicyClientInterface对象来构造AudioPolicyManager对象,AudioPolicyManager继承于AudioPolicyInterface

AudioPolicyManager
直接看其构造函数:最新的android版本应该是在onfirstref函数

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface) :......
{//1.加载解析配置文件//配置文件路径:/vendor/etc/ 或者 /system/etc/ConfigParsingUtils::loadConfig(....);//2.初始化各种音频流对应的音量调节点checkAndSetVolume;setVolumeCurveInsdex//3.加载audio policy硬件抽象库即根据名字加载mHwmodule对应的so文件,该文件由参加提供hwModule->setHandle(mpClientInterface->loadHwModule(hwModule->getName()));//4.打开output输出设备,同时会创建playbackthread,其会得到一个output整数(该函数的参数),这个整数会对应播放线程,
也可以对应SwAudioOutputDescriptor*/status_t status = mpClientInterface->openOutput(outProfile-
>getModuleHandle(),&output,&config,&outputDesc->mDevice,address,&outputDesc->mLatency,outputDesc->mFlags);//5.,根据output添加outputDesc描述符,保存输出设备描述符对象addOutput(output, outputDesc);//6.设置输出设备setOutputDevice(....);//里面还有一些根据属性选择output,getOutputForAttr等等,根据stratery选择设备由enfine完//7.打开输入设备mpClientInterface->openInput(....);//8.更新输出设备updateDevicesAndOutputs();
}

通过上面的1到8步基本就把整个音频处理系统的架子给搭起来!!!!接下来看看其中的这几个重要的函数

2.2.1 加载配置文件

在AudioPolicyManager(的构造函数中,能找到loadConfig,这个函数负责解析xml配置文件,在这个文件中会找出所有的Interface(audio_hw_module关键字定义的模块)比如primary、usb、a2dp、r_submix(WiDi用)。在每个interface下面会包含若干个Outputs,有的还有Inputs,同时在每个Outputs/Inputs下面又包含若干个Profile,每个Profile描述了该input/output支持sample rate,channel mask, devices &flags。

status_t AudioPolicyManager::loadConfig(const char *path)
status_t AudioPolicyManager::loadOutput(cnode *root, HwModule *module)
void AudioPolicyManager::loadHwModule(cnode *root)
......AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface): AudioPolicyManager(clientInterface, false /*forTesting*/)
{loadConfig();//加载配置文件initialize();//根据配置文件做动作
}void AudioPolicyManager::loadConfig() {
#ifdef USE_XML_AUDIO_POLICY_CONF
if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {
#else
if ((ConfigParsingUtils::loadConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE, getConfig()) != NO_ERROR)
&& (ConfigParsingUtils::loadConfig(AUDIO_POLICY_CONFIG_FILE, getConfig()) != NO_ERROR)) {
#endif
ALOGE("could not load audio policy configuration file, setting defaults");getConfig().setDefault();
}
}系统中的xml被读取之后,以 mConfig(mHwModulesAll, mAvailableOutputDevices, mAvailableInputDevices,mDefaultOutputDevice, static_cast<VolumeCurvesCollection*>(mVolumeCurves.get())的形式保存,后续需要和底层interface交互时,就可以通过这些成员获取相关信息;
......//加载xml配置文件
static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH];//其中#define AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH 128//#define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml"//#define AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME \

在AudioPolicyManager创建过程中会通过加载(AudioPolicyManager的构造函数)audio_policy_configuration.xml配置文件来加载音频设备,Android为每种音频接口定义了对应的硬件抽象层。每种音频接口定义了不同的输入输出,一个接口可以具有多个输入或者输出,每个输入输出可以支持不同的设备,通过读取audio_policy_configuration.xml文件可以获取系统支持的音频接口参数,在AudioPolicyManager中会优先加载/vendor/etc/audio_policy_configuration.xml配置文件, 如果该配置文件不存在, 则加载/system/etc/audio_policy_configuration.xml配置文件,统中的audio_policy_configuration.xml被读取之后,以profile的形式保存,后续打开/关闭设备的时候,就通过调用profile获取所有的配置参数;当AudioPolicyManager构造时,它会根据用户提供的audio_policy_configuration.xml来分析系统中有哪些audio接口(primary,a2dp以及usb),然后通过AudioFlinger::loadHwModule加载各audio接口对应的库文件,并依次打开其中的output(openOutput)和input(openInput)打开音频输出时创建一个audio_stream_out通道,并创建AudioStreamOut对象以及新建PlaybackThread播放线程
打开音频输入时创建一个audio_stream_in通道,并创建AudioStreamIn对象以及创建RecordThread录音线程。

2.2.2 初始化各种音频流对应的音量调节点

在AudioPolicyManager.cpp的构造函数中会执行两个方法

AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface): AudioPolicyManager(clientInterface, false /*forTesting*/)
{loadConfig();initialize();
}

其中loadConfig方法便会去解析配置文件audio_policy_configuration.xml

void AudioPolicyManager::loadConfig() {if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {ALOGE("could not load audio policy configuration file, setting defaults");getConfig().setDefault();}
}

调用deserializeAudioPolicyXmlConfig(getConfig()) 方法去解析配置文件时,使用
getConfig()方法传入一个参数,这个参数的类型是AudioPolicyConfig
在AudioPolicyConfig.h中:

class AudioPolicyConfig
{
public:AudioPolicyConfig(......: ......mVolumeCurves(volumes),//音频曲线......

然后再来看一下deserializeAudioPolicyXmlConfig()的实现

static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH];std::vector<const char*> fileNames;status_t ret;
......fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME);for (const char* fileName : fileNames) {for (const auto& path : audio_get_configuration_paths()) {......return ret;
}

这段代码做了一个循环,解析了”/odm/etc”, “/vendor/etc”, “/system/etc”这三个路径和
AUDIO_POLICY_XML_CONFIG_FILE_NAME拼接的文件,而
AUDIO_POLICY_XML_CONFIG_FILE_NAME就是audio_policy_configuration.xml

#define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml"

通过include的方式包含了两个xml文件

//audio_policy_configuration.xml<xi:include href="audio_policy_volumes.xml"/>//规定了音频流、输出设备和音量曲线的关系<xi:include href="default_volume_tables.xml"/>//规定了具体音频曲线的值

因为不同的音频流使用不同的音频曲线,而同一音频流在输出设备不同时也采用不同的音频曲线,
所以必须规定这三者的对应关系,xml中的这种对应关系被serializer.deserialize方法解析后,在代
码中体现为VolumeCurvesCollection-VolumeCurvesForStream-VolumeCurve的对应关系

audio_policy_volumes.xml:这个配置文件描述f(stream,device )= volume-curve的映射关系。

<volume stream="音频类型" deviceCategory="输出设备"ref="音频曲线"/><volume stream="AUDIO_STREAM_RING" deviceCategory="DEVICE_CATEGORY_EXT_MEDIA"ref="DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE"/>

default_volume_tables.xml:这个文件描述不同类型的音量曲线。

......
<reference name="DEFAULT_MEDIA_VOLUME_CURVE">//DEFAULT_MEDIA_VOLUME_CURVE曲线上点的xy值<!-- Default Media reference Volume Curve --><point>1,-5800</point><point>20,-4000</point><point>60,-1700</point><point>100,0</point>
</reference>
......

所以如果想要修改音频曲线,只要修改default_volume_tables.xml文件就行了。想要对(stream,device)使用不同的音频曲线就修改audio_policy_volumes.xml。

AudioPolicymanger调节音量的接口,setStreamVolumeIndex,设置特定流特定设备的音量其中参数device是 Stream->Strategy->Device的方式获得, 这也是AudioPolicy获得设备的标准方式。在分析audiopolicymanager如何实现调节stream的音量时我们先进行一些思考。首先我们知道android的音量有两种描述方式index和DB。这里我们的入参是index。我们首先要完成的一个工作是将index转化为DB。其次,音量调节需要作用到具体的数据流中。而audioflinger是负责具体音频数据流的处理工作!那么我们就需要把音量值(DB)设置给audioflinger。audioflinger当中具体进行数据处理的类是threads。从设计的角度看,audiopolicymanager是不需要知道threads的信息的。但是audiopolicymanager又需要将具体的音量值通知给threads。这怎么办呢?从上面的内容我们可以知道audiopolicymanager在初始化的时候会打开所有的ouput。而audioflinger内部通过map保存了output和thread之间的关系。那么audiopolicymanager在设置音量的时候只需要知道所有支持入参的stream和device的output,并将音量设置给该toutput就行。事实上,setStreamVolumeIndex也真是按照这个思路实现的。

  
//AudioPolicyManager.cpp
status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,int index, audio_devices_t device)// 1. 判断传入的参数// 音量不可大于流的最大音量,小于最小音量值if ((index < mVolumeCurves->getVolumeIndexMin(stream)) ||(index > mVolumeCurves->getVolumeIndexMax(stream)))return BAD_VALUE;...// 设备需要是输出设备if (!audio_is_output_device(device))
return BAD_VALUE;...// 如果传入的流不能被Mute, 强制使用该流的最高音量// canBeMuted,现在代码中,没有设置canBeMuted的接口,默认被设置为trueif (!volumeCurves.canBeMuted()) index = volumeCurves.getVolumeIndexMax();...// 2. 更新与传入的流,设备的音量值for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {// return (stream1 == stream2)if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {continue;}// 更新特定流,特定设备的音量值VolumeCurves->addCurrentVolumeIndex( device, index);}...// 3. 遍历已经打开的所有的output,对所有的符合条件的output和流设置音量status_t status = NO_ERROR;for (size_t i = 0; i < mOutputs.size(); i++) {// 获得该output的配置信息sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);// curSrcDevice 是根据当前output使用的设备得出的。output和设备的关系是通过route确定的。// 其中主要对双设备做了处理,一般双设备只选取了speakeraudio_devices_t  curSrcDevice = Volume::getDeviceForVolume(desc->device());//更新for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {...// (1) 请求的流必须在当前output中Active(可以理解为正在播放)// 遍历所有的流,仅对跟请求的流符合的流(当前的代码下可以认为自有请求的流)if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {continue;}// 判断流是不是Activeif (!(desc->isStreamActive((audio_stream_type_t)curStream) ||(isInCall() && (curStream == AUDIO_STREAM_VOICE_CALL)))) {continue;}// (2) 判断请求的设备是否跟当前获得的设备匹配// 获得请求的流在当前场景下应该使用的设备routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);audio_devices_t curStreamDevice = Volume::getDeviceForVolume(getDeviceForStrategy(curStrategy, false /*fromCache*/));// 请求的设备跟curStreamDevice是否有相同的设备, 是否是默认设备// 如果两个条件都不符合,不会调整当前流的音量if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) &&((curStreamDevice & device) == 0)) {continue;}... bool applyVolume;// (3) OutPut的当前设备是否与请求的设备或者请求的设备的子设备相同if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {curStreamDevice |= device;applyVolume = (curDevice & curStreamDevice) != 0;} else {// (4) 如果请求的设备是默认设备,需要curStreamDevice没有音量配置applyVolume = !mVolumeCurves->hasVolumeIndexForDevice(stream, curStreamDevice);}if (applyVolume) {// 调用checkAndSetVolume应用该音量值status_t volStatus =checkAndSetVolume((audio_stream_type_t)curStream, index, desc, curDevice,(stream == AUDIO_STREAM_SYSTEM) ? TOUCH_SOUND_FIXED_DELAY_MS : 0);

我认为上面代码的逻辑是这样的:我们要找到output。但是output可能支持不同的streamtype。这点我们从配置文件可以分析得出。因为output的约束主要是flag,采样率,音频格式,位宽等,而不涉及sreamtype。所以第一步判断output上我们希望修改的streamtype是否是活跃的。其次看该output上输出的设备是否就是我们期望的设备。这里是不是为了避免:outputA上stream是活跃的,ouputB上stream也是活跃的。但是device在outputA上支持,在outputB上不支持。思考一下这种情况可不可能出现。首先stream通过做策略一定要在devcice上播出,而outputB不支持device,stream也就不应该在outputB上活跃。上面的代码是否可以优化一下!

最后调用checkAndSetVolume,( Audiopoicy真正通知Audofinger调节音量的接口是checkAndSetVolume)。

此软件音量曲线的加载就完成了,调节音量时,会根据传入的stream参数先找到getVolumeCurvesForStreamType对象,再根据传入的device参数找到具体的VolumeCurve,最后根据index参数及音量曲线计算出音量的分贝值。(上层设置到AudioPolicy的音量是用户设置的
音量值, 而底层把音量值转换成分贝值才能处理该音量。音量曲线的作用就是做这种转换)在后面就是checkAndSetVolume调用AudioFlinger的setstreamvolume去执行Playbackthread的setstreamvolume操作了。

2.2.3 加载audio policy硬件抽象库

AudioPolicyManager加载完配置文件后,就知道了系统支持的所有音频接口参数,可以为选择音频输出的依据。audio_policy_configuration.xml同时定义了多个audio接口,每一个audio接口包含若干output和input,而每个output和input又同时支持多种输入输出模式,每种输入输出模式又支持若干种设备.最终会去调用到AudioFlinger去加载生成的动态库so

//frameworks/av/services/audiopolicy/service/AudioPolicyClientImpl.cpp
audio_module_handle_t AudioPolicyService::AudioPolicyClient::loadHwModule(const char *name)
{sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();if (af == 0) {ALOGW("%s: could not get AudioFlinger", __func__);return AUDIO_MODULE_HANDLE_NONE;}return af->loadHwModule(name);//这里直接调用了AudioFlinger::loadHwModule()。
}

我们进到AudioFlinger看看做了啥

audio_module_handle_t AudioFlinger::loadHwModule(const char *name)
{if (name == NULL) {return AUDIO_MODULE_HANDLE_NONE;}if (!settingsAllowed()) {return AUDIO_MODULE_HANDLE_NONE;}Mutex::Autolock _l(mLock);return loadHwModule_l(name);
}
......audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
{//1.是否已经加载过这个interfacefor (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);}}//2.加载audio interfaceint rc = mDevicesFactoryHal->openDevice(name, &dev);//3.初始化rc = dev->initCheck();//4.添加到全局变量中audio_module_handle_t handle = (audio_module_handle_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_MODULE);mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
}

loadHwModule_l是通过调用openDevice方法来打开加载audio设备的,该方法的实现类是
DevicesFactoryHalHybrid

//frameworks/av/media/libaudiohal/DevicesFactoryHalHybrid.cpp
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); //Hidl方式加载}return mLocalFactory->openDevice(name, device);  //旧版本本地加载
}

到DevicesFactoryHalHidl::openDevice看

//DevicesFactoryHalHidl.cpp
status_t DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device) {if (mDevicesFactory == 0) return NO_INIT;IDevicesFactory::Device hidlDevice;status_t status = nameFromHal(name, &hidlDevice);if (status != OK) return status;Result retval = Result::NOT_INITIALIZED;Return<void> ret = mDevicesFactory->openDevice(

DevicesFactory->openDevice:

/DevicesFactory.cpp
Return<void> DevicesFactory::openDevice(IDevicesFactory::Device device, openDevice_cb _hidl_cb) {int halStatus = loadAudioInterface(moduleName, &halDevice);//加载audio interface......_hidl_cb(retval, result);//将加载的设备通过回调匿名方法传递回去return Void();
}
int DevicesFactory::loadAudioInterface(const char *if_name, audio_hw_device_t **dev)
{const hw_module_t *mod;int rc;//在system/lib/hw/等目录下查找对应的动态库并加载rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);//打开对应的device,并获取hw_device_t指针类型的设备对象rc = audio_hw_device_open(mod, dev);......
}

loadHwModlule_l. 加载指定的audiointerface,比如“primary”、“a2dp”或者“usb”。函数load_audio_interface用来加载设备所需的库文件,然后打开设备并创建一个audio_hw_device_t实例。音频接口设备所对应的库文件名称是有一定格式的,比如a2dp的模块名可能是audio.a2dp.so或者audio.a2dp.default.so等等。每种音频设备接口由一个对应的so库提供支持。那么AudioFlinger怎么会知道当前设备中支持上述的哪些接口,每种接口又支持哪些具体的音频设备呢?这是AudioPolicyService的责任之一,即根据用户配置来指导AudioFlinger加载设备接口。当AudioPolicyManager构造时,它会读取厂商关于音频设备的描述文件,然后据此来打开音频接口(如果存在的话)。这一过程最终会调用loadHwModule(AudioFlinger)。完成了audiointerface的模块加载只是万里长征的第一步。因为每一个interface包含的设备通常不止一个,Android系统目前支持的音频设备如下列表所示

在这里插入图片描述

大家可能会有疑问:
这么多的输出设备,那么当我们回放音频流(录音也是类似的情况)时,该选择哪一种呢?
而且当前系统中audio interface也很可能不止一个,应该如何选择?
显然这些决策工作将由AudioPolicyService来完成,我们会在下一小节做详细阐述。这里先给大家
分析下,AudioFlinger是如何打开一个Output通道的(一个audiointerface可能包含若干个
output)。

2.2.4设置输出设备

App构造audiotrack时制定了stream type,然后根据stream type来设置属性Attributes,然后
audiomanager根据属性来选择strategy,从再根据strategy类别来获得从哪个设备播放,AudioStream在
Audio Base.h中有定义,包含以下内容:((同时,在AudioSystem.java定义的流类型与audiobase.h中定义的audio_stream_type_t结构体一一对应,所以在JNI中经常可以将其类型强制转换))

// /system/media/audio/include/system/audio-base.h
typedef enum {AUDIO_STREAM_DEFAULT = -1, // (-1)AUDIO_STREAM_MIN = 0,AUDIO_STREAM_VOICE_CALL = 0,AUDIO_STREAM_SYSTEM = 1,AUDIO_STREAM_RING = 2,AUDIO_STREAM_MUSIC = 3,AUDIO_STREAM_ALARM = 4,AUDIO_STREAM_NOTIFICATION = 5,AUDIO_STREAM_BLUETOOTH_SCO = 6,AUDIO_STREAM_ENFORCED_AUDIBLE = 7,AUDIO_STREAM_DTMF = 8,AUDIO_STREAM_TTS = 9,AUDIO_STREAM_ACCESSIBILITY = 10,AUDIO_STREAM_ASSISTANT = 11,
#ifndef AUDIO_NO_SYSTEM_DECLARATIONS/** For dynamic policy output mixes. Only used by the audio policy */AUDIO_STREAM_REROUTING = 12,/** For audio flinger tracks volume. Only used by the audioflinger */AUDIO_STREAM_PATCH = 13,/** stream for corresponding to AUDIO_USAGE_CALL_ASSISTANT */AUDIO_STREAM_CALL_ASSISTANT = 14,
#endif // AUDIO_NO_SYSTEM_DECLARATIONS
} audio_stream_type_t;

在现在的大多数fw架构中,AudioStream仅用来标识音频的音量,使用音频属性
AudioAttributes(属性)和AudioStream共同决定AudioStrategy(策略),因为AudioAttributes可以
携带比音频流更多的信息,如:Content、Usage、flag等,

//services/audiopolicy/enginedefault/src/Engine.h
enum legacy_strategy {STRATEGY_NONE = -1,STRATEGY_MEDIA,STRATEGY_PHONE,STRATEGY_SONIFICATION,STRATEGY_SONIFICATION_RESPECTFUL,STRATEGY_DTMF,STRATEGY_ENFORCED_AUDIBLE,STRATEGY_TRANSMITTED_THROUGH_SPEAKER,STRATEGY_ACCESSIBILITY,STRATEGY_REROUTING,STRATEGY_CALL_ASSISTANT,
};

根据attributes获取strategy

getStrategyForAttr

然后根据strategy来选择设备

//frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp
DeviceVector Engine::getDevicesForStrategyInt

最后根据设备来获取output

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
audio_io_handle_t AudioPolicyManager::getOutputForDevices(......

比如说,下面的例子是播放音乐(AUDIO_STREAM_MUSIC)的时候选中的Strategy:

//frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp
case STRATEGY_REROUTING:
case STRATEGY_MEDIA: {if (strategy != STRATEGY_SONIFICATION)......if (isInCall() && (strategy == STRATEGY_MEDIA)) {//是否在电话中且strage属于媒体类if ((devices2.isEmpty()) &&(getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP)) {devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID);}if ((devices2.isEmpty()) &&(getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_SPEAKER)) {devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);}
......//都是一些优先级的判断

根据上面的代码,简单做个总结吧:
播放音乐选设备优先级如下
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP(蓝牙高保真设备)
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES(普通蓝牙耳机)
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER(蓝牙小音箱)
//此处属于setForceUse的强制插队,音频焦点,特殊情况如何处理,比如来电后,音乐声音要变小
(if FORCE_SPEAKER)AUDIO_DEVICE_OUT_SPEAKER(扬声器)
AUDIO_DEVICE_OUT_WIRED_HEADPHONE(普通耳机,只能听,不能操控播放)
AUDIO_DEVICE_OUT_LINE
AUDIO_DEVICE_OUT_WIRED_HEADSET(线控耳机)
AUDIO_DEVICE_OUT_USB_HEADSET(USB耳机)

AUDIO_DEVICE_OUT_SPEAKER(扬声器)
选好设备就返回device,然后getOutputForDevice

AudioPolicyManager::getOutputForDevice//处理入参flags
if ((flags & AUDIO_OUTPUT_FLAG_XXX) != 0) {flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_XXX);
}
//咱一般不是AUDIO_OUTPUT_FLAG_DIRECT,当然是
goto non_direct_output;
......
//播放音乐什么之类的,mediaplayer已经做完decodec.这里一般都是pcmif (audio_is_linear_pcm(format)) {//根据指定的stream类型获取匹配的output.实际的路由改变需要等到startOutput被调用的时候//注意这个函数是getOutputsForDevice,要获取的是一些output,而我们当前讨论的函数是获取一个output.这些outputs从
mOutputs中来.那么mOutputs来自哪里?SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs);// 从匹配到的outputs(请注意,是复数)中选出一个outputoutput = selectOutput(outputs, flags, format);}

我们分析ouput的创建和使用过程,不难发现outputs和线程池有很大的相似之处。此外每个ouput都有一个thread。这就是典型的享元设计模式。
那么mOutputs来自哪里?

void AudioPolicyManager::addOutput(audio_io_handle_t output, const sp<SwAudioOutputDescriptor>& outputDesc)
{outputDesc->setIoHandle(output);mOutputs.add(output, outputDesc);updateMono(output); // update mono status when adding to output listselectOutputForMusicEffects();nextAudioPortGeneration();
}

从mOutputs中选出和匹配的一些output之后,用selectOutput选中我们真正需要的那一个.

AudioPolicyManager::selectOutput// select one output among several that provide a path to a particular device or set of// devices (the list was previously build by getOutputsForDevice()).// The priority is as follows:// 1: the output with the highest number of requested policy flags// 2: the output with the bit depth the closest to the requested one// 3: the primary output// 4: the first output in the list
......
//在几个提供一个特定设备或一组路径的路径中选择一个输出//设备(该列表以前由getOutputsForDevice()构建)。//优先级如下:// 1:请求的policy flags数量最多的输出// 2:bit depth最接近请求的输出// 3:主输出// 4:列表中的第一个输出

然后返回选中的output即可.
整个getOutputForAttr就完成了.(attr>>strategy>>device>>output)

setoutputdevices这个函数里还创建patch,也就是建立起output和device之间的关系。

AudioPolicyManager::setOutputDevice        //Duplicated output,output1和output2各来一遍setOutputDeviceif (outputDesc->isDuplicated()) {muteWaitMs = setOutputDevice(outputDesc->subOutput1(), device, force, delayMs);muteWaitMs += setOutputDevice(outputDesc->subOutput2(), device, force, delayMs);return muteWaitMs;}...if (device == AUDIO_DEVICE_NONE) {resetOutputDevice(outputDesc, delayMs, NULL);} else {DeviceVector deviceList;if ((address == NULL) || (strlen(address) == 0)) {//mAvailableOutputDevices在APM构造的时候就已经准备好了//setDeviceConnectionStateInt中也会对新设备做adddeviceList = mAvailableOutputDevices.getDevicesFromType(device);} else {deviceList = mAvailableOutputDevices.getDevicesFromTypeAddr(device, String8(address));}if (!deviceList.isEmpty()) {struct audio_patch patch;outputDesc->toAudioPortConfig(&patch.sources[0]);patch.num_sources = 1;patch.num_sinks = 0;for (size_t i = 0; i < deviceList.size() && i < AUDIO_PATCH_PORTS_MAX; i++)             {deviceList.itemAt(i)->toAudioPortConfig(&patch.sinks[i]);patch.num_sinks++;}//从mAudioPatches中取出patch的indexssize_t index;if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) {index = mAudioPatches.indexOfKey(*patchHandle);} else {index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle());}//每个output有自己的patchhandle。maudiopatch是整个系统所有的audiopatch。//处理afPatchHandlesp< AudioPatch> patchDesc;audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;if (index >= 0) {patchDesc = mAudioPatches.valueAt(index);afPatchHandle = patchDesc->mAfPatchHandle;}

关于Audio Patch(就是一个有source和sink的一个结构体)的分析,比较复杂,我们不做赘述了,有兴趣的可以去网上查查资料,它最终会调用AudioFlinger::PlaybackThread::createAudioPatch_l

status_t AudioFlinger::PlaybackThread::createAudioPatch_l(const struct audio_patch *patch,audio_patch_handle_t *handle)
{...if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {audio_hw_device_t *hwDevice = mOutput->audioHwDev->hwDevice();status = hwDevice->create_audio_patch(hwDevice,patch->num_sources,
patch->sources,
patch->num_sinks,
patch->sinks,
handle);} else {char *address;if (strcmp(patch->sinks[0].ext.device.address, "") != 0) {//FIXME: we only support address on first sink with HAL version < 3.0address = audio_device_address_to_parameter(patch->sinks[0].ext.device.type,patch->sinks[0].ext.device.address);} else {address = (char *)calloc(1, 1);}AudioParameter param = AudioParameter(String8(address));free(address);param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type);status = mOutput->stream->common.set_parameters(&mOutput->stream->common,param.toString().string());*handle = AUDIO_PATCH_HANDLE_NONE;}if (configChanged) {mPrevOutDevice = type;sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);}...
}

如果supportsAudioPatches.那么就继续createAudioPatch.分别会经过 libaudiohal底下
DeviceHalHidl::createAudioPatch和hardware底下的Device.cpp.然后进入
底下的audio_hw.c中实现的create_audio_patch函数.
如果hal版本过低,不支持audiopatch,AudioPolicy下发的AudioPatch会在AudioFlinger中转
化为set_parameters向Hal下发
out_set_parameter或in_set_parameter完成设备下发

static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
{struct stream_out *out = (struct stream_out *)stream;struct audio_device *adev = out->dev;struct str_parms *parms;char value[32];int ret = 0, val = 0, err;ALOGE("%s: enter: usecase(%d: %s) kvpairs: %s",__func__, out->usecase, use_case_table[out->usecase], kvpairs);...
}

关于参数说明:
out->usecase:来自于audiofinger的openOutputStream方法,调用路径请查看:<音频输出设备是
如何决定的>章节,需要注意的是每条音频路径的openOutputStream只会在APS初始化的时候调用一
次,之后音频播放的时候不会再次调用,它会传递到Audio_hw.c的adev_open_output_stream。

adev_open_output_stream

} else  if (out->devices == AUDIO_DEVICE_OUT_TELEPHONY_TX) {
...
out->usecase = USECASE_AUDIO_PLAYBACK_AFE_PROXY;

上Hal会根据传入设备的类型来决定usecase的值。
关于第二个参数
kvpairs来自于上文提到的createAudioPatch_l,注意下面这个字段:

param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type);

在APS初始化的时候,会将系统可用音频路径的PlaybackThread创建好(offload的除外),在这个过程中也会调用可用音频路径的默认输出设备(一般是Speaker,Voice的是听筒)的setOutputDevice,从而为每条可用音频路径设置默认的路由。因此,APS初始化后,会打出如Log:

01-27 05:26:07.504  4588  4588 E APM_AudioPolicyManager: Jon,deviceList size = 1
01-27 05:26:07.506  4588  4595 E AudioFlinger: Jon,address =
01-27 05:26:07.506  4588  4595 E audio_hw_primary: out_set_parameters: Jon, enter: usecase(1: low-latencyplayback) kvpairs: routing=2
01-27 05:26:07.518  4588  4588 E APM_AudioPolicyManager: Jon,deviceList size = 1
01-27 05:26:07.519  4588  4597 E AudioFlinger: Jon,address =
01-27 05:26:07.520  4588  4597 E audio_hw_primary: out_set_parameters: Jon, enter: usecase(12: audio-ullplayback) kvpairs: routing=2
01-27 05:26:07.524  4588  4588 E APM_AudioPolicyManager: Jon,deviceList size = 1
01-27 05:26:07.525  4588  4598 E AudioFlinger: Jon,address =
01-27 05:26:07.525  4588  4598 E audio_hw_primary: out_set_parameters: Jon, enter: usecase(0: deep-bufferplayback) kvpairs: routing=2
01-27 05:26:07.530  4588  4588 E APM_AudioPolicyManager: Jon,deviceList size = 1
01-27 05:26:07.531  4588  4600 E AudioFlinger: Jon,address =
01-27 05:26:07.531  4588  4600 E audio_hw_primary: out_set_parameters: Jon, enter: usecase(38: afe-proxyplayback) kvpairs: routing=65536

但是由于初始化阶段,所有的stream并没有被active,因此select_devices没有机会在这个时候
被调度到
既然如此,那么问题来了,Hal是在什么时候为Stream真正设置输出的呢,答案是在track->start
后真正向Hal下发数据的时候,如下:

//audio_hw.c
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,size_t bytes)
{...if (out->standby) {out->standby = false;pthread_mutex_lock(&adev->lock);if (out->usecase == USECASE_COMPRESS_VOIP_CALL)ret = voice_extn_compress_voip_start_output_stream(out);elseret = start_output_stream(out);pthread_mutex_unlock(&adev->lock);/* ToDo: If use case is compress offload should return 0 */if (ret != 0) {out->standby = true;goto exit;}if (last_known_cal_step != -1) {ALOGD("%s: retry previous failed cal level set", __func__);audio_hw_send_gain_dep_calibration(last_known_cal_step);}}...
}

答案就在start_output_stream中,而且很明显在单次音频的播放中,start_output_stream只会被
调用一次。

int start_output_stream(struct stream_out *out)
{...//Hal选择音频的输出设备select_devices(adev, out->usecase);...pcm_openpcm_preparepcm_start...
}

在后面就是hal与alsa的交互了,我们这里不做赘述

int select_devices(struct audio_device *adev, audio_usecase_t uc_id)
{...//获取在start_output_stream中设置的usecaseusecase = get_usecase_from_list(adev, uc_id);...//禁止当前声卡设备if (usecase->out_snd_device != SND_DEVICE_NONE) {disable_audio_route(adev, usecase);disable_snd_device(adev, usecase->out_snd_device);}//使能新的声卡设备if (out_snd_device != SND_DEVICE_NONE) {//检查是否有其他的usecase需要切换路由check_usecases_codec_backend(adev, usecase, out_snd_device);enable_snd_device(adev, out_snd_device);}//这里的enable_snd_device的作用可以理解为通过tinymix进行下面的操作:
//tinymix 'RX3 MIX1 INP1' 'RX1'
//tinymix 'SPK' ‘Switch’
//也就是将BE DAIs ----> device之间的硬件通路打开...//使能新的音频路由enable_audio_route(adev, usecase);...
}//这里的enable_audio_route的作用可以理解为通过tinymix进行下面的操作:
//tinymix 'PRI_MI2S_RX Audio Mixer MultiMedia1' 1
//也就是将FE PCMs---->BE DAIs之间的硬件通路打开
//

audiopatch流程简介
getNewOutputDevice:检查AudioPatch,如果没有,就用checkStrategyRoute(参数
一:getStrategy(stream),参数二:output)
然后不管两种用哪一个都会AudioPolicyManager::setOutputDevice (从mAudioPatches中取出
patch的index, 处理afPatchHandle)
经过一堆调用流程之后,代码会走到这里
status_t AudioFlinger::PlaybackThread::createAudioPatch_l
通过Device.cpp进入hal的audio_hw.c中实现的static int adev_create_audio_patch函数

//会打fw传下来的log:LOG_I("in_device:%x in_device_name:%s",sources_device_type,devicetostring(sources_device_type));LOG_I("out_device:%x out_device_name:%s",sinks_device_type,devicetostring(sinks_device_type));LOG_I("sinks type:%x sources type:%x",sinks[0].type,sources[0].type);

select_devices会拿set_audio_patch的设备。

2.2.5打开输出设备

取得output之后,需要应用它.
首先

//AudioTrack::createTrack_l()
...
AudioSystem::getLatency(output, &mAfLatency);
...
AudioSystem::getFrameCount(output, &mAfFrameCount);
...
AudioSystem::getFrameCountHAL(output, &afFrameCountHAL);
...
AudioSystem::getSamplingRate(output, &mAfSampleRate);

注意上方调用的参数,第一个入参,第二个是出参(指针)!
以上过程获得了mAfLatency,mAfFrameCount,afFrameCountHAL,mAfSampleRate四个值.注意,全

af打头的,表示AudioFlinger端对output的设置.做一些修正调整,变成
mSampleRate,temp(mAfFrameCount和mAfFrameCount,mAfLatency三个参数综合计算).
然后

sp<IAudioTrack> track = audioFlinger->createTrack(streamType,mSampleRate,
mFormat,
mChannelMask,
&temp,
&flags,
mSharedBuffer,
output,
mClientPid,
tid,
&mSessionId,
mClientUid,
&status,
mPortId);

我们跳到AudioFlinger端去看看

//AudioFlinger::createTrack
...
PlaybackThread *thread = checkPlaybackThread_l(output);
...

首先checkPlaybackThread_l从mPlaybackThreads中检索出output对应的回放线程.
我们之前说过,在AudioPolicyManager构造的时候,会根据audio_policy.conf中的配置,挨个调用
mpClientInterface->openOutput.(最终调用AudioFlinger::openOutput_l)

//frameworks/av/services/audiopolicy/service/AudioPolicyClientImpl.cpp
status_t AudioPolicyService::AudioPolicyClient::openOutput(audio_module_handle_t module,audio_io_handle_t *output,audio_config_t *config,audio_devices_t *devices,const String8& address,uint32_t *latencyMs,audio_output_flags_t flags)
{sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();if (af == 0) {ALOGW("%s: could not get AudioFlinger", __func__);return PERMISSION_DENIED;}return af->openOutput(module, output, config, devices, address, latencyMs, flags);//这里直接调用了
AudioFlinger::openOutput()}

AudioFlinger打开output

//frameworks/av/services/audioflinger/AudioFlinger.cpp
status_t AudioFlinger::openOutput()
{......sp<ThreadBase> thread = openOutput_l(module, output, config, *devices, address, flags);......
}
......
sp<AudioFlinger::ThreadBase> AudioFlinger::openOutput_l(......)//最终调用AudioFlinger::openOutput_
{// 查找相应的audio interface,查找合适的设备并打开AudioHwDevice *outHwDev = findSuitableHwDev_l(module, devices);......AudioStreamOut *outputStream = NULL;//在硬件设备上打开一个输出流status_t status = outHwDev->openOutputStream(&outputStream,*output,devices,flags,config,address.string());
......
//创建PlaybackThread*
sp<PlaybackThread> thread;if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {//AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD音频流,创建OffloadThread实例thread = new OffloadThread(this, outputStream, *output, devices, mSystemReady);ALOGV("openOutput_l() created offload output: ID %d thread %p",*output, thread.get());} else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT)|| !isValidPcmSinkFormat(config->format)|| !isValidPcmSinkChannelMask(config->channel_mask)) {//若是AUDIO_OUTPUT_FLAG_DIRECT音频,则创建DirectOutputThread实例    thread = new DirectOutputThread(this, outputStream, *output, devices, mSystemReady);ALOGV("openOutput_l() created direct output: ID %d thread %p",*output, thread.get());} else {//其他音频流,则创建MixerThread实例thread = new MixerThread(this, outputStream, *output, devices, mSystemReady);//关联output和PlaybackThread,添加播放线程mPlaybackThreads.add(*output, thread);
......
//Primary output情况下的处理:如果当前设备是主设备,则还需要进行相应的设置,包括模式、主音量等等
if ((mPrimaryHardwareDev== NULL) &&flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {undefinedALOGI("Usingmodule %d has the primary audio interface", module);mPrimaryHardwareDev = outHwDev;AutoMutexlock(mHardwareLock);mHardwareStatus =AUDIO_HW_SET_MODE;outHwDev->set_mode(outHwDev, mMode);//模式
......mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME; //测试设备是否支持主音量获取

Android系统是怎么从AudioTrack的start开始之后,一路走到AudioFlinger的Track::start的,我们略
过不提了,毕竟今天的主题是策略.我们知道,AudioFlinger::Track::start中,调用
PlaybackThread::addTrack_l,使沉睡的PlaybackThread被唤醒,然后就调用到了我们重要的

status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
{status = AudioSystem::startOutput(mId, track->streamType(),track->sessionId());...
}
status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
{status = AudioSystem::startOutput(mId, track->streamType(),track->sessionId());...
}//mId就是PlaybackThread的序号,标识,output!

接下来我们转入startOutput函数

status_t AudioPolicyManager::startOutput(audio_io_handle_t output,audio_stream_type_t stream,
audio_session_t session)
{//根据output这个id.找出outputDescssize_t index = mOutputs.indexOfKey(output);sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);//if (outputDesc->mPolicyMix != NULL) {...} else if (mOutputRoutes.hasRouteChanged(session)) {//选用新的设备newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);checkStrategyRoute(getStrategy(stream), output);}...status_t status = startSource(outputDesc, stream, newDevice, address, &delayMs);...
}

2.3 策略制定总结
AudioPolicyService加载解析/vendor/etc/audio_policy.conf或/system/etc/audio_policy.conf
对于配置文件里的每一个module项, new HwModule(name), 放入mHwModules数组
对于module里的每一个output, new IOProfile, 放入module的mOutputProfiles
对于module里的每一个input, new IOProfile, 放入module的mInputProfiles
根据module的name加载厂家提供的so文件 (通过AudioFlinger来加载)
打开对应的output (通过AudioFlinger来open output)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/35949.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Springdoc 全部注解一文解释清楚

文章目录 **1. 核心注解****Tag-Class类上** **2. 方法级别注解****Operation-方法描述****ApiResponse 和 ApiResponses-方法的返回结果** **3. 参数相关注解****Parameter-方法参数****Parameters方法参数&#xff08;单个&#xff09;** **4. 实体模型相关注解****Schema-描…

Git的基本指令

一、回滚 1.git init 在项目文件夹中打开bash生成一个.git的子目录&#xff0c;产生一个仓库 2.git status 查看当前目录下的所有文件的状态 3.git add . 将该目录下的所有文件提交到暂存区 4.git add 文件名 将该目录下的指定文件提交到暂存区 5.git commit -m 备注信…

通过qemu仿真树莓派系统调试IoT固件和程序

通过qemu仿真树莓派系统调试IoT固件和程序 本文将介绍如何使用 QEMU 模拟器在 x86 架构的主机上运行 Raspberry Pi OS&#xff08;树莓派操作系统&#xff09;。我们将从下载镜像、提取内核和设备树文件&#xff0c;到启动模拟环境&#xff0c;并进行一些常见的操作&#xff0…

DeepSeek大模型在政务服务领域的应用

DeepSeek大模型作为国产人工智能技术的代表&#xff0c;近年来在政务服务领域的应用呈现多点开花的态势。通过多地实践&#xff0c;该技术不仅显著提升了政务服务的效率与智能化水平&#xff0c;还推动了政府治理模式的创新。以下从技术应用场景、典型案例及发展趋势三个维度进…

【HarmonyOS Next之旅】DevEco Studio使用指南(三)

目录 1 -> 一体化工程迁移 1.1 -> 自动迁移 1.2 -> 手动迁移 1.2.1 -> API 10及以上历史工程迁移 1.2.2 -> API 9历史工程迁移 1 -> 一体化工程迁移 DevEco Studio从 NEXT Developer Beta1版本开始&#xff0c;提供开箱即用的开发体验&#xff0c;将SD…

FPGA中级项目4——DDS实现

FPGA中级项目4——DDS实现 DDS简介 DDS&#xff08;直接数字频率合成器&#xff0c;Direct Digital Frequency Synthesis&#xff09;是一种基于数字信号处理技术的频率合成方法&#xff0c;广泛应用于通信、雷达、仪器仪表等领域。在 FPGA中实现 DDS 具有灵活性高、集成度强、…

SAP的WPS导出找不到路径怎么办;上载报错怎么办

一.打开注册编辑器 二.输入以下地址 计算机\HKEY_CLASSES_ROOT\ExcelWorksheet\Protocol\StdFileEditing\Server 去除掉EXE后面的命令即可 二&#xff1a;WPS上载文件没反应怎么办 如何切换整合模式或多组件模式-WPS学堂 根据官方操作把整合模式改成多组件模式

STM32 DAC详解:从原理到实战输出正弦波

目录 一、DAC基础原理1.1 DAC的作用与特性1.2 DAC功能框图解析 二、DAC配置步骤2.1 硬件配置2.2 初始化结构体详解 三、DAC数据输出与波形生成3.1 数据格式与电压计算3.2 正弦波生成实战3.2.1 生成正弦波数组3.2.2 配置DMA传输3.2.3 定时器触发配置 四、常见问题与优化建议4.1 …

MVC_Publish-Subscriber 模式中的事件处理程序

MVC_Publish-Subscriber 模式中的事件处理程序 MVC 中的事件处理&#xff1a;发布者-订阅者模式 程序启动时&#xff0c;controlRecipes将被传入addHandlerRender; addHandlerRender会侦听事件&#xff08;addEventListener&#xff09;,并使用controlRecipes作为回调&#xf…

网络华为HCIA+HCIP 以太网链路聚合与交换机堆叠、集群

网络可靠性 网络的可靠性指当设备或者链路出现单点或者多点故障时保证网络服务不间断的能力。网络的可靠性可以从单板、设备、链路多个层面实现。 单板可靠性 以S12700E-8为例&#xff0c;设备提供8个线路板槽位、4个交换网板槽位、2个主控板槽位、6个电源模块槽位、4个风扇…

施耐德PLC仿真软件Modbus tcp通讯测试

安装仿真软件&#xff1a;EcoStruxure™ Control Expert - PLC 仿真器 下载地址&#xff1a;https://www.schneider-electric.cn/zh/download/document/EIO0000001719/ 配置CPU&#xff1a; 切换至仿真模式&#xff0c;系统托盘中出现仿真器图标 新建变量test&#xff0c;地址…

LabVIEW棉花穴播器排种自动监测系统

一、项目背景与行业痛点 1. 农业需求驱动 我国棉花主产区&#xff0c;种植面积常年超250万公顷&#xff0c;传统人工播种存在两大核心问题&#xff1a; 效率瓶颈&#xff1a;人均日播种面积不足0.5公顷&#xff0c;难以匹配规模化种植需求&#xff1b; 精度缺陷&#xff1a;人…

2024浙江大学计算机考研上机真题

2024浙江大学计算机考研上机真题 2024浙江大学计算机考研复试上机真题 2024浙江大学计算机考研机试真题 2024浙江大学计算机考研复试机试真题 历年浙江大学计算机复试上机真题 历年浙江大学计算机复试机试真题 2024浙江大学计算机复试上机真题 2024浙江大学计算机复试机试真题 …

GPU视频编解码:Jetson VPI+multimedia_api视频编解码入门(一)

目录 一.Pipline与工具栈 二.硬件设备概况 三.GPU视频编解码框架 四.VPI编译使用实例 五. jetson_multimedia_api编译使用实例 一.Pipline与工具栈 二.硬件设备概况 三.GPU视频编解码框架 jetson设备目前不支持VPF框架&#xff0c;关于VPF的使用我在下节PC段使用X86进行安…

设计模式-对象创建

对象创建 前言1. Factory Method1.1 模式介绍1.2 模式代码1.2.1 问题代码1.2.2 重构代码 1.3 模式类图1.4 要点总结 2. Abstract Factory2.1 模式介绍2.2 模式代码2.2.1 问题代码2.2.2 重构代码 2.3 模式类图2.4 要点总结 3. Prototype3.1 模式介绍3.2 模式代码3.3 模式类图3.4…

【大模型基础_毛玉仁】2.6 非 Transformer 架构

更多内容&#xff1a;XiaoJ的知识星球 目录 2.6 非 Transformer 架构2.6.1 状态空间模型 SSM1&#xff09;SSM&#xff08;State Space Model&#xff09;2&#xff09;RWKV&#xff08;Receptance Weighted Key Value&#xff09;3&#xff09;Mamba 2.6.2 训练时更新TTT(Test…

压测实战 | 微信小程序商城 “双 11” 的压测实践

背景 某全球知名珠宝品牌&#xff0c;始终以创新驱动零售变革。随着全渠道战略的深化&#xff0c;其小程序官方商城逐渐成为品牌私域流量的核心阵地&#xff0c;不仅承载了线上销售、会员运营等功能&#xff0c;同时还与其内部系统打通&#xff0c;如会员管理系统、人力资源系…

Webpack vs Rollup vs Parcel:构建工具深度对比

文章目录 1. 核心特性对比1.1 功能定位1.2 技术架构对比 2. 配置与使用2.1 Webpack 配置示例2.2 Rollup 配置示例2.3 Parcel 使用示例 3. 性能对比3.1 构建速度3.2 输出质量 4. 生态系统4.1 插件生态4.2 学习曲线 5. 适用场景分析5.1 Webpack 适用场景5.2 Rollup 适用场景5.3 P…

JUC大揭秘:从ConcurrentHashMap到线程池,玩转Java并发编程!

目录 JUC实现类 ConcurrentHashMap 回顾HashMap ConcurrentHashMap CopyOnWriteArrayList 回顾ArrayList CopyOnWriteArrayList: CopyOnWriteArraySet 辅助类 CountDownLatch 线程池 线程池 线程池优点 ThreadPoolExecutor 构造器各个参数含义&#xff1a; 线程…

【unity实战】用unity封装一个复杂全面且带不同射击模式的飞机大战射击系统

考虑到每个人基础可能不一样,且并不是所有人都有同时做2D、3D开发的需求,所以我把 【零基础入门unity游戏开发】 分为成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】:主要讲解C#的基础语法,包括变量、数据类型、运算符、流程控制、面向对象等,适合没有编程基础的…