首页 >> 大全

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

2023-10-18 大全 28 作者:考证青年

文中相关图片的源文件在 。

一、音频服务初始化流程

当前版本: 11

大致的创建流程如下:

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

1.1 开机启动音频服务

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

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

1.2 模块加载

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

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

1.3 模块加载

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

总结:

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

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

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

创建音频播放组件下载__创建音频组件播放失败怎么办

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

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

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<DeviceDescriptor> 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<SwAudioOutputDescriptor> 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 得到一个描述符,设置mpClientInterfacesp<AudioInputDescriptor> 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();}}
}

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

上图会返回::中根据加载的。

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

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

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

二、 的创建流程

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

2.1 音频策略加载

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

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

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> 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<VolumeCurve> 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<engineConfig::Config>(config),static_cast<size_t>(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<ProductStrategy> 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> 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中并分配一个单独的IDproduct_strategy_t strategyId = strategy->getId();mProductStrategies[strategyId] = strategy;}mProductStrategies.initialize();return result;
}

三、 应用播放音频

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

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

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

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

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

模式下需要先调用write方法,后调用play方法;模式下先调用play方法,后调用write方法。

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

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

3.2 策略查找流程

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

3.3 播放线程和HAL的交互

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

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

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

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

3.3.1 打开输出源

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

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

3.3.2 向HAL层写数据

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

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了