Android 11音频服务创建以及播放的流程

Android 11音频服务创建以及播放的流程,第1张

1、音频服务初始化流程

当前版本:Android 11

大致的创建流程如下:

经过上面的流程系统音频服务已经启动处于待命状态,如果有应用需要播放则会通过服务最终选择合适的硬件将声音播出,接下来按照上面的流程进行进一步的细分。

1.1 开机启动音频服务

音频服务在frameworks/av/media/audioserver/main_audioserver.cpp中,这里会启动音频的AudioFlingerAudioPolicyService两大组件,简单的流程如下:

经过上面流程之后音频系统中会启动AudioFlinger用于处理后面所有的音频播放,AudioFlinger具体的功能后面再详细分析,AudioPolicyService负责后面的音频策略的处理等流程,AudioFlingerAudioPolicyService之间进行交互

1.2 AudioFlinger模块加载

通过上面流程会调用到AudioFlinger的构造函数,进行AudioFlinger模块的处理,流程如下:

在上面的构造函数中实例化了设备接口以及音效接口,此时AudioFlinger模块已经成功创建出来,后面即使AudioPolicyService创建出来后和AudioFlinger之间的交互过程

1.3 AudioPolicyService模块加载

AudioPolicyService负责音频的一些策略管理以及命令处理,具体的启动流程如下:

这里调用的createAudioPolicyManager定义在vendor/sprd/modules/audio/newapm/R.x/AudioPolicyManagerSPRD.cpp中解析的配置文件包含audio_policy_configuration.xml,用于解析包含音频策略在内的一些配置文件deserializeAudioPolicyFile方法定义在frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp文件中primary_audio_policy_configuration.xml文件解析流程配置解析Engine配置分析

总结:

首先分析了从AudioPolicyService创建AudioPolicyManager的流程,这里涉及到第三方自己的AudioPolicyManager,所以中间多了一个AudioPolicyManagerSPRD的创建有了AudioPolicyManager对象之后需要加载音频的一些配置文件,包括描述所有外部设备以及设备之间路由情况的primary_audio_policy_configuration.xml,这个xml文件解析之后会将内容存放到AudioPolicyManager持有的AudioPolicyConfig对象中,具体的作用我们后面分析加载完xml之后就是进行Engine对象的创建,这个Engine对象负责管理音频策略相关的内容Engine对象创建完毕之后需要对xml解析出来的内容进行处理,这部分涉及和AudioFlinger的交互,放在后面一节讲述
1.3.1 AudioPolicyServiceAudioFlinger交互流程

这里涉及几个重要的文件位置:

frameworks/av/services/audiopolicy/common/managerdefinitions/include/HwModule.h

hardware/libhardware/include/hardware/audio.h

hardware/libhardware/hardware.c

frameworks/av/media/libaudiohal/impl/DeviceHalLocal.cpp

分析:在hw_get_module_by_class的时候会加载音频库,这里会加载第三方自己实现的库,一般第三方的库中都有对open函数指针的赋值,这里赋的是adev_open;然后调用audio_hw_device_open最终调用到adev_open之后会打开相应的模块,并将所有的 *** 作指针进行赋值,如果成功返回再创建DeviceHalLocal对象,在这里也是为以后调用open_output_stream/open_input_stream等函数做准备,这些函数就包含在这个对象中

1.3.1.1 输入输出设备的打开和线程创建

在前面流程图AudioPolicyManager的第三步,具体代码如下:

void AudioPolicyManager::onNewAudioModulesAvailableInt(DeviceVector *newDevices)
{
    for (const auto& hwModule : mHwModulesAll) {
        if (std::find(mHwModules.begin(), mHwModules.end(), hwModule) != mHwModules.end()) {
            continue;
        }
        hwModule->setHandle(mpClientInterface->loadHwModule(hwModule->getName()));
        if (hwModule->getHandle() == AUDIO_MODULE_HANDLE_NONE) {
            ALOGW("could not open HW module %s", hwModule->getName());
            continue;
        }
        mHwModules.push_back(hwModule);
       // 打开访问附加设备所需的所有输出流,除了仅在应用程序实际需
       // 要时才打开的直接输出流。这也验证了 mAvailableOutputDevices 列表 
        for (const auto& outProfile : hwModule->getOutputProfiles()) {
            if (!outProfile->canOpenNewIo()) {
                ALOGE("Invalid Output profile max open count %u for profile %s",
                      outProfile->maxOpenCount, outProfile->getTagName().c_str());
                continue;
            }
            if (!outProfile->hasSupportedDevices()) {
                ALOGW("Output profile contains no device on module %s", hwModule->getName());
                continue;
            }
            // 这里和primary_audio_policy_coinfiguration.xml中给hifi音源加的flag进行对应
            if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_TTS) != 0) {
                mTtsOutputAvailable = true;
            }

            const DeviceVector &supportedDevices = outProfile->getSupportedDevices();
            DeviceVector availProfileDevices = supportedDevices.filter(mOutputDevicesAll);
            sp supportedDevice = 0;
            if (supportedDevices.contains(mDefaultOutputDevice)) {
                supportedDevice = mDefaultOutputDevice;
            } else {
                // 选择配置文件的 SupportedDevices 中存在的第一个设备,
                // 也是 mAvailableOutputDevices 的一部分。 
                if (availProfileDevices.isEmpty()) {
                    continue;
                }
                supportedDevice = availProfileDevices.itemAt(0);
            }
            if (!mOutputDevicesAll.contains(supportedDevice)) {
                continue;
            }
            // 根据outProfile 得到一个描述符,设置mpClientInterface
            // 描述了source和sink之间的关系
            sp outputDesc = new SwAudioOutputDescriptor(outProfile,
                                                                                 mpClientInterface);
            audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
            // 在这里最终调用AudioFlinger的openOutput函数打开这个输出流,并返回表示这个设备的输出流
            // 的句柄audio_io_handle_t用于后面AudioTrack创建的时候查找具体的线程使用
            status_t status = outputDesc->open(nullptr, DeviceVector(supportedDevice),
                                               AUDIO_STREAM_DEFAULT,
                                               AUDIO_OUTPUT_FLAG_NONE, &output);
            if (status != NO_ERROR) {
                ALOGW("Cannot open output stream for devices %s on hw module %s",
                      // 表示失败,不能打开profile对应的device的输出流
                      supportedDevice->toString().c_str(), hwModule->getName());
                continue;
            }
            for (const auto &device : availProfileDevices) {
                // 一旦确认连接的设备可以访问,就为它提供一个有效的 ID 
                if (!device->isAttached()) {
                    device->attach(hwModule);
                    mAvailableOutputDevices.add(device); // 添加到可用的输出设备中
                    device->setEncapsulationInfoFromHal(mpClientInterface);
                    if (newDevices) newDevices->add(device);
                    setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
                }
            }
            if (mPrimaryOutput == 0 &&
                    outProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
                mPrimaryOutput = outputDesc; // 设置主音频输出
            }
            if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
                outputDesc->close(); // 如果是直接输出流则先关闭
            } else {
                // 将这个添加到打开的输出描述符列表mOutputs中(SwAudioOutputCollection类型)
                addOutput(output, outputDesc);
                // 设置适合当前Source的输出设备,这一部分内容和修改的pfw文件有关
                setOutputDevices(outputDesc,
                                 DeviceVector(supportedDevice),
                                 true,
                                 0,
                                 NULL);
            }
        }
        // 打开访问附加设备所需的输入流以验证 mAvailableInputDevices 列表 
        for (const auto& inProfile : hwModule->getInputProfiles()) {
            if (!inProfile->canOpenNewIo()) {
                ALOGE("Invalid Input profile max open count %u for profile %s",
                      inProfile->maxOpenCount, inProfile->getTagName().c_str());
                continue;
            }
            // 如果当前sink没有相应的设备支持则结束
            if (!inProfile->hasSupportedDevices()) {
                ALOGW("Input profile contains no device on module %s", hwModule->getName());
                continue;
            }
            // 选择配置文件中存在的第一个设备支持的设备也是可用输入设备的一部分 
            const DeviceVector &supportedDevices = inProfile->getSupportedDevices();
            DeviceVector availProfileDevices = supportedDevices.filter(mInputDevicesAll);
            if (availProfileDevices.isEmpty()) {
                ALOGE("%s: Input device list is empty!", __FUNCTION__);
                continue;
            }
            // 根据inProfile 得到一个描述符,设置mpClientInterface
            sp inputDesc =
                    new AudioInputDescriptor(inProfile, mpClientInterface);

            audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
            // 这里最终调用到AudioFlinger的openIntput打开输入流
            status_t status = inputDesc->open(nullptr,
                                              availProfileDevices.itemAt(0),
                                              AUDIO_SOURCE_MIC,
                                              AUDIO_INPUT_FLAG_NONE,
                                              &input);
            if (status != NO_ERROR) {
                ALOGW("Cannot open input stream for device %s on hw module %s",
                      availProfileDevices.toString().c_str(),
                      hwModule->getName());
                continue;
            }
            for (const auto &device : availProfileDevices) {
                // 一旦确认连接的设备可以访问,就为它提供一个有效的 ID 
                if (!device->isAttached()) {
                    device->attach(hwModule);
                    device->importAudioPortAndPickAudioProfile(inProfile, true);
                    mAvailableInputDevices.add(device);
                    if (newDevices) newDevices->add(device);
                    setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
                }
            }
            inputDesc->close();
        }
    }
}

上面代码中提到在AudioFlinger中打开了输入和输出设备,下面是输出流打开流程包括线程的创建:

上图findSuitableHwDev_l会返回AudioFlinger::loadHwModule_l中根据Module加载的AudioHwDevice

接下来继续分析输入流的打开流程以及线程的创建:

上面讲输入和输出的线程创建已经分析完毕,现在我们的Android系统中已经存在了一些播放线程了,后面就是一个应用如果通过AudioTrack开始播放音频该怎么将这段音频对应到相应的线程中去的问题

整个第一节音频服务初始化到这里结束,音频服务启动之后加载相应的音频库,扫描了配置文件中的一些模块和连接的外设,并打开这些外设创建了相应的工作线程,比如最常用的MixerThread线程和录音的RecordThread,现在整个音频系统已经启动,剩下的就是其他应用等访问音频服务的过程。

2、 Engine的创建流程

前面分析Engine的时候没有进行详细的分析,Engine作为音频后期统筹管理的模块,是如何将策略加载进去的,以及后面设备切换的时候怎么进行修改,这些内容在这一节进行分析

2.1 音频策略加载

在前面1.2节中AudioPolicyManagerinitialize方法中涉及到了Engine的创建,接着这个内容进一步详细分析:

音频策略加载的时候有一个比较重要的函数,这个函数EngineBase里面的loadAudioPolicyEngineConfig,代码如下:

engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig()
{
    auto loadVolumeConfig = [](auto &volumeGroups, auto &volumeConfig) {
        // 确保名称唯一性以防止重复 
        LOG_ALWAYS_FATAL_IF(std::any_of(std::begin(volumeGroups), std::end(volumeGroups),
                                     [&volumeConfig](const auto &volumeGroup) {
                return volumeConfig.name == volumeGroup.second->getName(); }),
                            "group name %s defined twice, review the configuration",
                            volumeConfig.name.c_str());

        // 表示当前VolumeGroup还没有被加载过,开始创建加载加载
        sp volumeGroup = new VolumeGroup(volumeConfig.name, volumeConfig.indexMin,
                                                      volumeConfig.indexMax);
        volumeGroups[volumeGroup->getId()] = volumeGroup;

        for (auto &configCurve : volumeConfig.volumeCurves) {
            device_category deviceCat = DEVICE_CATEGORY_SPEAKER;
            if (!DeviceCategoryConverter::fromString(configCurve.deviceCategory, deviceCat)) {
                ALOGE("%s: Invalid %s", __FUNCTION__, configCurve.deviceCategory.c_str());
                continue;
            }
            sp curve = new VolumeCurve(deviceCat);
            for (auto &point : configCurve.curvePoints) {
                curve->add({point.index, point.attenuationInMb});
            }
            volumeGroup->add(curve);
        }
        return volumeGroup;
    };
    auto addSupportedAttributesToGroup = [](auto &group, auto &volumeGroup, auto &strategy) {
        for (const auto &attr : group.attributesVect) {
            strategy->addAttributes({group.stream, volumeGroup->getId(), attr});
            volumeGroup->addSupportedAttributes(attr);
        }
    };
    auto checkStreamForGroups = [](auto streamType, const auto &volumeGroups) {
        const auto &iter = std::find_if(std::begin(volumeGroups), std::end(volumeGroups),
                                     [&streamType](const auto &volumeGroup) {
            const auto& streams = volumeGroup.second->getStreamTypes();
            return std::find(std::begin(streams), std::end(streams), streamType) !=
                    std::end(streams);
        });
        return iter != end(volumeGroups);
    };

    // 前面三个lambda表达式后面会用到,实际是从这里开始的
    
    // 这里开始进行解析,最终会解析出来策略、标准、标准类型以及音量组四个内容
    // struct Config {
    // 	float version;
    // 	ProductStrategies productStrategies;
    // 	Criteria criteria;
    // 	CriterionTypes criterionTypes;
   	// 	VolumeGroups volumeGroups;
	// };
    auto result = engineConfig::parse();
    if (result.parsedConfig == nullptr) { // 如果上面没有解析成功表示没有找到配置,所以使用默认的配置
        ALOGW("%s: No configuration found, using default matching phone experience.", __FUNCTION__);
        engineConfig::Config config = gDefaultEngineConfig;
        android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups);
        result = {std::make_unique(config),
                  static_cast(ret == NO_ERROR ? 0 : 1)};
    } 
    
	...
        
    ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement); // 这里是解析的时候跳过的无效策略数

    engineConfig::VolumeGroup defaultVolumeConfig;
    engineConfig::VolumeGroup defaultSystemVolumeConfig;
    // 循环解析所有的音量组
    for (auto &volumeConfig : result.parsedConfig->volumeGroups) {
        // 保存未在配置中定义的流的默认音量配置 讲music和patch作为未定义流类型的默认配置
        if (volumeConfig.name.compare("AUDIO_STREAM_MUSIC") == 0) {
            defaultVolumeConfig = volumeConfig;
        }
        if (volumeConfig.name.compare("AUDIO_STREAM_PATCH") == 0) {
            defaultSystemVolumeConfig = volumeConfig;
        }
        // 这里调用上面第一个lamabda表达式
        // 这里定义的mVolumeGroups是一个map容器,其中second是VolumeGroup指针,定义在VolumeGroup.h中
        // 这里调用这个lambda表达式是为了讲volumeConfig中包含的volumeGroups解析到mVolumeGroups中
        loadVolumeConfig(mVolumeGroups, volumeConfig);
    }
    // 循环遍历所有的音频策略
    for (auto& strategyConfig : result.parsedConfig->productStrategies) {
        sp strategy = new ProductStrategy(strategyConfig.name);
        for (const auto &group : strategyConfig.attributesGroups) {
            // 查找该策略是否有相应的音量组
            const auto &iter = std::find_if(begin(mVolumeGroups), end(mVolumeGroups),
                                         [&group](const auto &volumeGroup) {
                    return group.volumeGroup == volumeGroup.second->getName(); });
            sp volumeGroup = nullptr;
            // 如果没有为此策略提供音量组,则使用音乐音量组配置创建一个新的音量组(视为默认设置) 
            if (iter == end(mVolumeGroups)) {
                engineConfig::VolumeGroup volumeConfig;
                if (group.stream >= AUDIO_STREAM_PUBLIC_CNT) {
                    volumeConfig = defaultSystemVolumeConfig;
                } else {
                    volumeConfig = defaultVolumeConfig;
                }
                ALOGW("%s: No configuration of %s found, using default volume configuration"
                        , __FUNCTION__, group.volumeGroup.c_str());
                volumeConfig.name = group.volumeGroup;
                volumeGroup = loadVolumeConfig(mVolumeGroups, volumeConfig);
            } else {
                volumeGroup = iter->second;
            }
            if (group.stream != AUDIO_STREAM_DEFAULT) {
                // 可以将旧流一次分配给卷组 
                LOG_ALWAYS_FATAL_IF(checkStreamForGroups(group.stream, mVolumeGroups),
                                    "stream %s already assigned to a volume group, "
                                    "review the configuration", toString(group.stream).c_str());
                volumeGroup->addSupportedStream(group.stream);
            }
            // 为策略添加相应的属性
            addSupportedAttributesToGroup(group, volumeGroup, strategy);
        }
        // 将新创建的strategy保存到mProductStrategies中并分配一个单独的ID
        product_strategy_t strategyId = strategy->getId();
        mProductStrategies[strategyId] = strategy;
    }
    mProductStrategies.initialize();
    return result;
}
3、 应用播放音频

应用播放音频是通过创建AudioTrack的方式进行播放的,每一个音频流对应着一个AudioTrack的实例,每个AudioTrack会在创建的时候注册到AudioFlinger中,由AudioFlinger将所有的AudioTrack进行混合再传入到HAL中进行播放。

通过前面的分析流程知道了AudioPolicyService启动时加载了系统支持的所有音频接口,并且打开了默认的音频输出,并且为该输出创建了一个播放线程,同时为该线程分配了一个全局唯一的audio_io_handle_t值,任何Track想要发声都需要去查找相应的audio_io_handle_t值找到对应的播放线程才能最终将音频数据传输到HAL层进行播放,大致的播放框图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lHvPE4uC-1642646395284)(./TN1.11.7870音频文档/应用播放流程.png)]

3.1 应用创建Track流程 调用AudioTrack::getMinBufferSize接口传入音频参数获取最小缓冲区大小,调用到HAL层判断当前硬件是否支持该种类型的音频播放,如果不支持后续也不会创建Tracknew AudioTrackAudioTrack::writeAudioTrack::play

需要注意的是音频播放的时候有两种模式:

static模式,一次性将音频加载进内存中供AudioFlinger读取,这种方式适用于对时延要求高且内存较小的音频stream模式,这种是AudioTrack向内存中不断的写数据,AudioFlinger去内存中读数据,动态的进行,一般情况下遇到的都是这种情况

static模式下需要先调用write方法,后调用play方法
stream模式下先调用play方法,后调用write方法

在这里创建共享内存的时候会用到getMinBufferSize获取的值最终创建缓冲区大小,这一步最终会进入到HAL层使用应用的采样率等一些参数来判断当前硬件是否支持该种类型的音频播放。

在set函数中会和AudioFlinger以及AudioPolicyManager进行互动,主要是找到给当前Track播放的设备以及该设备使用的线程,最终实现播放音频的目的。

3.2 策略查找流程

AudioPolicyManager种获取output的候可以选择主设备和次设备输出,但是次设备都是通过动态策略进行选择的,Engine不支持选中次设备,主要都是通过这里的APM从前面解析xml之后得到的一些结果中去查询需要的数据

3.3 播放线程和HAL的交互

将输出设备选中之后就需要针对该输出设备创建相应的线程进行数据的写入了,前面部分已经获取了当前应用要播放的流类型应该打开的output的所有信息,剩下的工作就是怎么找到当前output所对应的播放线程:

3.3.1 播放线程的选择以及Track创建

这里首先根据当前找到的outputId查找对应的播放线程,这个播放线程是在一开始创建AudioFlingerAudioPolicyManager的时候就打开的,然后在该线程中为当前要播放的音频创建一个Track并将该Track添加进当前线程持有的Track容器中。

到此从应用发起创建AudioTrack的请求到AudioFlinger最终创建AudioTrack已经结束,剩下的事情就是应用开始往共享内存中写入内容,AudioFlinger获取内容并发送到HAL层中通过tinyALSA驱动打开硬件进行最终的播放。

3.3.1 AudioTrack打开输出源

上面的内容是找到了当前AudioTrack相关的output以及相应的工作线程,接下来就需要打开这个output,然后开始往里面写数据,也就是应用调用writeplay之后开始写数据的内容,直接从AudioTrack.cpp中开始看

在上面调用startSource的时候需要进行一些判断来决定最终是否打开该输出源,如果不能打开是不会向底层写数据的,调试的时候需要重点关注这部分内容,判断是否所有情况都准备就绪再去看数据处理的部分。

3.3.2 AudioFlingerHAL层写数据

这里AudioTrack向共享内存写入数据,AudioFlinger从共享内存中读取数据,我们这里直接从AudioFlinger里面的线程开始分析,假设是MixerThread(大部分情况都是这种)过程如下:

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/992319.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存