赞
踩
当前版本:Android 11
大致的创建流程如下:
经过上面的流程系统音频服务已经启动处于待命状态,如果有应用需要播放则会通过服务最终选择合适的硬件将声音播出,接下来按照上面的流程进行进一步的细分。
音频服务在frameworks/av/media/audioserver/main_audioserver.cpp
中,这里会启动音频的AudioFlinger
和AudioPolicyService
两大组件,简单的流程如下:
经过上面流程之后音频系统中会启动AudioFlinger
用于处理后面所有的音频播放,AudioFlinger
具体的功能后面再详细分析,AudioPolicyService
负责后面的音频策略的处理等流程,AudioFlinger
和AudioPolicyService
之间进行交互
AudioFlinger
模块加载通过上面流程会调用到AudioFlinger
的构造函数,进行AudioFlinger
模块的处理,流程如下:
在上面的构造函数中实例化了设备接口以及音效接口,此时AudioFlinger
模块已经成功创建出来,后面即使AudioPolicyService
创建出来后和AudioFlinger
之间的交互过程
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
文件解析流程配置解析总结:
- 首先分析了从
AudioPolicyService
创建AudioPolicyManager
的流程,这里涉及到第三方自己的AudioPolicyManager
,所以中间多了一个AudioPolicyManagerSPRD
的创建- 有了
AudioPolicyManager
对象之后需要加载音频的一些配置文件,包括描述所有外部设备以及设备之间路由情况的primary_audio_policy_configuration.xml
,这个xml
文件解析之后会将内容存放到AudioPolicyManager
持有的AudioPolicyConfig
对象中,具体的作用我们后面分析- 加载完
xml
之后就是进行Engine
对象的创建,这个Engine
对象负责管理音频策略相关的内容Engine
对象创建完毕之后需要对xml
解析出来的内容进行处理,这部分涉及和AudioFlinger
的交互,放在后面一节讲述
AudioPolicyService
和AudioFlinger
交互流程这里涉及几个重要的文件位置:
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
等函数做准备,这些函数就包含在这个对象中
在前面流程图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<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 得到一个描述符,设置mpClientInterface sp<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(); } } }
上面代码中提到在AudioFlinger
中打开了输入和输出设备,下面是输出流打开流程包括线程的创建:
上图findSuitableHwDev_l
会返回AudioFlinger::loadHwModule_l
中根据Module
加载的AudioHwDevice
,
接下来继续分析输入流的打开流程以及线程的创建:
上面讲输入和输出的线程创建已经分析完毕,现在我们的Android系统中已经存在了一些播放线程了,后面就是一个应用如果通过AudioTrack
开始播放音频该怎么将这段音频对应到相应的线程中去的问题
整个第一节音频服务初始化到这里结束,音频服务启动之后加载相应的音频库,扫描了配置文件中的一些模块和连接的外设,并打开这些外设创建了相应的工作线程,比如最常用的MixerThread
线程和录音的RecordThread
,现在整个音频系统已经启动,剩下的就是其他应用等访问音频服务的过程。
Engine
的创建流程前面分析Engine的时候没有进行详细的分析,Engine作为音频后期统筹管理的模块,是如何将策略加载进去的,以及后面设备切换的时候怎么进行修改,这些内容在这一节进行分析
在前面1.2节中AudioPolicyManager
的initialize
方法中涉及到了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> 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中并分配一个单独的ID product_strategy_t strategyId = strategy->getId(); mProductStrategies[strategyId] = strategy; } mProductStrategies.initialize(); return result; }
应用播放音频是通过创建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)]
Track
流程AudioTrack::getMinBufferSize
接口传入音频参数获取最小缓冲区大小,调用到HAL层判断当前硬件是否支持该种类型的音频播放,如果不支持后续也不会创建Track
new AudioTrack
AudioTrack::write
AudioTrack::play
需要注意的是音频播放的时候有两种模式:
static
模式,一次性将音频加载进内存中供AudioFlinger
读取,这种方式适用于对时延要求高且内存较小的音频stream
模式,这种是AudioTrack
向内存中不断的写数据,AudioFlinger
去内存中读数据,动态的进行,一般情况下遇到的都是这种情况static
模式下需要先调用write
方法,后调用play
方法
stream
模式下先调用play
方法,后调用write
方法
在这里创建共享内存的时候会用到getMinBufferSize获取的值最终创建缓冲区大小,这一步最终会进入到HAL层使用应用的采样率等一些参数来判断当前硬件是否支持该种类型的音频播放。
在set函数中会和AudioFlinger以及AudioPolicyManager进行互动,主要是找到给当前Track播放的设备以及该设备使用的线程,最终实现播放音频的目的。
在AudioPolicyManager
种获取output
的候可以选择主设备和次设备输出,但是次设备都是通过动态策略进行选择的,Engine
不支持选中次设备,主要都是通过这里的APM
从前面解析xml
之后得到的一些结果中去查询需要的数据
HAL
的交互将输出设备选中之后就需要针对该输出设备创建相应的线程进行数据的写入了,前面部分已经获取了当前应用要播放的流类型应该打开的output
的所有信息,剩下的工作就是怎么找到当前output
所对应的播放线程:
Track
创建这里首先根据当前找到的outputId
查找对应的播放线程,这个播放线程是在一开始创建AudioFlinger
和AudioPolicyManager
的时候就打开的,然后在该线程中为当前要播放的音频创建一个Track
并将该Track
添加进当前线程持有的Track
容器中。
到此从应用发起创建AudioTrack
的请求到AudioFlinger
最终创建AudioTrack
已经结束,剩下的事情就是应用开始往共享内存中写入内容,AudioFlinger
获取内容并发送到HAL层中通过tinyALSA
驱动打开硬件进行最终的播放。
AudioTrack
打开输出源上面的内容是找到了当前AudioTrack
相关的output
以及相应的工作线程,接下来就需要打开这个output
,然后开始往里面写数据,也就是应用调用write
或play
之后开始写数据的内容,直接从AudioTrack.cpp
中开始看
在上面调用startSource
的时候需要进行一些判断来决定最终是否打开该输出源,如果不能打开是不会向底层写数据的,调试的时候需要重点关注这部分内容,判断是否所有情况都准备就绪再去看数据处理的部分。
AudioFlinger
向HAL
层写数据这里AudioTrack
向共享内存写入数据,AudioFlinger
从共享内存中读取数据,我们这里直接从AudioFlinger
里面的线程开始分析,假设是MixerThread
(大部分情况都是这种)过程如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。