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

news2025/3/20 12:12:07

音频策略的构建
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为AudioPolicyService
        return 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 thread
        mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);//创建ApmAudio线程用于执行
audio命令
        // start output activity command thread
        mOutputCommandThread = 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的接口,默认被设置为true
     if (!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确定的。
        // 其中主要对双设备做了处理,一般双设备只选取了speaker
        audio_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;
           }
            // 判断流是不是Active
            if (!(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.是否已经加载过这个interface
    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);
       }
   }
 //2.加载audio interface
 int 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.这里一般都是pcm
    if (audio_is_linear_pcm(format)) {
        //根据指定的stream类型获取匹配的output.实际的路由改变需要等到startOutput被调用的时候
        //注意这个函数是getOutputsForDevice,要获取的是一些output,而我们当前讨论的函数是获取一个output.这些outputs从
mOutputs中来.那么mOutputs来自哪里?
        SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs);
        // 从匹配到的outputs(请注意,是复数)中选出一个output
        output = 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 list
    selectOutputForMusicEffects();
    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各来一遍setOutputDevice
    if (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中也会对新设备做add
            deviceList = 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的index
            ssize_t index;
            if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) {
                index = mAudioPatches.indexOfKey(*patchHandle);
           } else {
                index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle());
           }
           //每个output有自己的patchhandle。maudiopatch是整个系统所有的audiopatch。
            //处理afPatchHandle
            sp< 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.0
            address = 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);
        else
            ret = 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_open
 pcm_prepare
 pcm_start
 ...
}

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

int select_devices(struct audio_device *adev, audio_usecase_t uc_id)
{
 ...
 //获取在start_output_stream中设置的usecase
 usecase = 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)) {undefined
      ALOGI("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.找出outputDesc
    ssize_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.coloradmin.cn/o/2318362.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

法兰克仿真软件FANUC CNC Guide v25.0 安装教程及中文设置

前言 下载地址在文末。 我们公司用新到一批FANUC 系统的设备&#xff0c;为方便使用就装了一个 FANUC 数控系统仿真软件FANUC CNC Guide v25.0 &#xff0c;还是最新版不是市面上流传的老版本。模拟学习一下。 安装操作 安装环境&#xff1a;Windows 11 专业版 24H2 安装程…

Java SE 面经

1、Java 语言有哪些特点 Java 语言的特点有&#xff1a; ①、面向对象。主要是&#xff1a;封装&#xff0c;继承&#xff0c;多态。 ②、平台无关性。一次编写&#xff0c;到处运行&#xff0c;因此采用 Java 语言编写的程序具有很好的可移植性。 ③、支持多线程。C 语言没…

关于redis中的分布式锁

目录 分布式锁的基础实现 引入过期时间 引入校验id 引入lua脚本 引入看门狗 redlock算法 分布式锁的基础实现 多个线程并发执行的时候&#xff0c;执行的先后顺序是不确定的&#xff0c;需要保证程序在任意执行顺序下&#xff0c;执行逻辑都是ok的。 在分布式系统中&am…

Python实战(2)-数据库支持

使用简单的纯文本文件可实现的功能有限。诚然&#xff0c;使用它们可做很多事情&#xff0c;但有时可能还需要额外的功能。你可能希望能够自动完成序列化&#xff0c;此时可求助于shelve和pickle&#xff08;类似于shelve&#xff09;​。不过你可能需要比这更强大的功能。例如…

从 Snowflake 到 Databend Cloud:全球游戏平台借助 Databend 实现实时数据处理

导读&#xff1a;某全球游戏平台为全球数百万玩家提供实时的技能型游戏体验与无缝的实时互动。对该游戏平台而言&#xff0c;保持数据的实时更新和实时分析&#xff0c;对提升玩家互动和留存率至关重要。他们在使用 Snowflake 进行实时数据摄取和分析时遇到了重大挑战&#xff…

Docker搭建MySQL主从服务器

一、在主机上创建MySQL配置文件——my.cnf master服务器配置文件路径&#xff1a;/data/docker/containers/mysql-cluster-master/conf.d/my.cnf slave服务器配置文件路径&#xff1a; /data/docker/containers/mysql-cluster-master/conf.d/my.cnf master服务配置文件内容 …

C语言每日一练——day_12(最后一天)

引言 针对初学者&#xff0c;每日练习几个题&#xff0c;快速上手C语言。第十二天。&#xff08;最后一天&#xff0c;完结散花啦&#xff09; 采用在线OJ的形式 什么是在线OJ&#xff1f; 在线判题系统&#xff08;英语&#xff1a;Online Judge&#xff0c;缩写OJ&#xff0…

10、STL中的unordered_map使用方法

一、了解 1、unordered_map(哈希) unordered_map是借用哈希表实现的关联容器。 访问键值对O&#xff08;1&#xff09;&#xff0c;最坏情况O&#xff08;n&#xff09;&#xff0c;例如哈希冲突严重时。【n是一个哈希桶的元素数量】 unordered_map特性 键值对存储&#xff…

本地部署deepseek-r1建立向量知识库和知识库检索实践【代码】

目录 一、本地部署DS 二、建立本地知识库 1.安装python和必要的库 2.设置主目录工作区 3.编写文档解析脚本 4.构建向量数据库 三、基于DS,使用本地知识库检索 本地部署DS,其实非常简单,我写了一篇操作记录,我终于本地部署了DeepSeek-R1(图文全过程)-CSDN博客 安装…

监控视频联网平台在智慧水利中的应用

随着智慧城市建设的深入推进&#xff0c;智慧水利作为其中的重要组成部分&#xff0c;正逐步实现数字化、智能化和网络化转型。在这一过程中&#xff0c;监控视频联网平台凭借其高效的数据采集、传输与分析能力&#xff0c;成为智慧水利建设的关键技术支撑。以下是监控视频联网…

深入解析素数筛法:从埃氏筛到欧拉筛的算法思想与实现

素数筛法是一种用于高效生成素数的算法。常见的素数筛法包括埃拉托斯特尼筛法&#xff08;埃氏筛&#xff09;和欧拉筛&#xff08;线性筛&#xff09;。下面我们将详细讲解这两种筛法的思想&#xff1a; 一、 埃拉托斯特尼筛法&#xff08;埃氏筛&#xff09; 思想&#xff1…

ubuntu20.04系统没有WiFi图标解决方案_安装Intel网卡驱动

文章目录 1. wifi网卡配置1.1 安装intel官方网卡驱动backport1.1.1 第四步可能会出现问题 1.2 ubuntu官方的驱动1.3 重启 1. wifi网卡配置 我的电脑是华硕天选4&#xff08;i7&#xff0c;4060&#xff09;&#xff0c;网卡型号intel ax201 ax211 ax210通用。 参考文章&#…

网络编程---多客户端服务器

写一个服务器和两个客户端 运行服务器和2个客户端&#xff0c;实现聊天功能 客户端1 和 客户端2 进行聊天 客户端1将聊天数据发送给服务器 服务器将聊天数据转发给客户端2 要求&#xff1a; 服务器使用 select 模型实现 客户端1使用 poll 模型实现 客户端2使用 多线程实现…

LeetCode 2614.对角线上的质数:遍历(质数判断)

【LetMeFly】2614.对角线上的质数&#xff1a;遍历(质数判断) 力扣题目链接&#xff1a;https://leetcode.cn/problems/prime-in-diagonal/ 给你一个下标从 0 开始的二维整数数组 nums 。 返回位于 nums 至少一条 对角线 上的最大 质数 。如果任一对角线上均不存在质数&…

红日靶场(二)——个人笔记

靶场搭建 新增VMnet2网卡 **web&#xff1a;**需要配置两张网卡&#xff0c;分别是外网出访NAT模式和内网域环境仅主机模式下的VMnet2网卡。 **PC&#xff1a;**跟web一样&#xff0c;也是需要配置两张网卡&#xff0c;分别是外网出访NAT模式和内网域环境仅主机模式下的VMn…

实时视频分析的破局之道:蓝耘 MaaS 如何与海螺 AI 视频实现高效协同

一、蓝耘 MaaS 平台&#xff1a;AI 模型全生命周期管理的智能引擎 蓝耘 MaaS&#xff08;Model-as-a-Service&#xff09;平台是由蓝耘科技推出的 AI 模型全生命周期管理平台&#xff0c;专注于为企业和开发者提供从模型训练、推理到部署的一站式解决方案。依托云原生架构、高…

走进Java:String字符串的基本使用

❀❀❀ 大佬求个关注吧~祝您开心每一天 ❀❀❀ 目录 一、什么是String 二、如何定义一个String 1. 用双引号定义 2. 通过构造函数定义 三、String中的一些常用方法 1 字符串比较 1.1 字符串使用 1.2 字符串使用equals() 1.3 使用 equalsIgnoreCase() 1.4 cpmpareTo…

python系列之元组(Tuple)

不为失败找理由&#xff0c;只为成功找方法。所有的不甘&#xff0c;因为还心存梦想&#xff0c;所以在你放弃之前&#xff0c;好好拼一把&#xff0c;只怕心老&#xff0c;不怕路长。 python系列之元组&#xff08;Turple&#xff09; 一、元组是什么&#xff1f;——给新手的…

破解验证码新利器:基于百度OCR与captcha-killer-modified插件的免费调用教程

破解验证码新利器&#xff1a;基于百度OCR与captcha-killer-modified插件的免费调用教程 引言 免责声明&#xff1a; 本文提供的信息仅供参考&#xff0c;不承担因操作产生的任何损失。读者需自行判断内容适用性&#xff0c;并遵守法律法规。作者不鼓励非法行为&#xff0c;保…

批量删除 PPT 中的所有图片、某张指定图片或者所有二维码图片

PPT 文档中的图片如何删除呢&#xff1f;相信很多小伙伴或碰到类似的需求。比如我们需要删除 PPT 文档中的某一张图片或者某张二维码图片&#xff0c;如果每一页都有这张图片&#xff0c;或者有很多 ppt 都有同一张要删除的图片&#xff0c;我们应该怎么快速的完成删除呢&#…