Android 中打开音频流所用的配置

news/2024/11/28 21:52:43/

我们在 AudioPolicyManager::onNewAudioModulesAvailableInt(DeviceVector *newDevices) 函数中看到它创建了 SwAudioOutputDescriptor 对象,后者的构造函数的定义 (位于 frameworks/av/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp) 如下:

AudioOutputDescriptor::AudioOutputDescriptor(const sp<PolicyAudioPort>& policyAudioPort,AudioPolicyClientInterface *clientInterface): mPolicyAudioPort(policyAudioPort), mClientInterface(clientInterface)
{if (mPolicyAudioPort.get() != nullptr) {mPolicyAudioPort->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);if (mPolicyAudioPort->asAudioPort()->getGains().size() > 0) {mPolicyAudioPort->asAudioPort()->getGains()[0]->getDefaultConfig(&mGain);}}
}. . . . . .
SwAudioOutputDescriptor::SwAudioOutputDescriptor(const sp<IOProfile>& profile,AudioPolicyClientInterface *clientInterface): AudioOutputDescriptor(profile, clientInterface),mProfile(profile), mIoHandle(AUDIO_IO_HANDLE_NONE), mLatency(0),mFlags((audio_output_flags_t)0),mOutput1(0), mOutput2(0), mDirectOpenCount(0),mDirectClientSession(AUDIO_SESSION_NONE)
{if (profile != NULL) {mFlags = (audio_output_flags_t)profile->getFlags();}
}

Android 中打开音频输出流,所需的主要参数采样率、通道掩码(通道数)、采样格式、增益和标记来自于传入的 IOProfile。如 Android 音频设备信息加载 一文中的说明,IOProfile 的信息主要来自于解析 audio_policy_configuration.xml 音频策略配置文件,更具地说,来自于音频策略配置文件中的 mixPort 元素,如下面 (位于 device/generic/car/emulator/audio/audio_policy_configuration.xml) 这样:

            <defaultOutputDevice>bus0_media_out</defaultOutputDevice><mixPorts><mixPort name="mixport_bus0_media_out" role="source"flags="AUDIO_OUTPUT_FLAG_PRIMARY"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="48000"channelMasks="AUDIO_CHANNEL_OUT_STEREO"/></mixPort>. . . . . .<mixPort name="mixport_bus200_audio_zone_2" role="source"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="48000"channelMasks="AUDIO_CHANNEL_OUT_STEREO"/></mixPort><mixPort name="primary input" role="sink"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/></mixPort>

IOProfile 通过 AudioRouteDeviceDescriptor 建立连接。在音频策略配置文件中,AudioRouteDeviceDescriptor 对应的 XML 元素定义如下面这样:

            <devicePorts><devicePort tagName="bus0_media_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"address="bus0_media_out"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/><gains><gain name="" mode="AUDIO_GAIN_MODE_JOINT"minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/></gains></devicePort>. . . . . .<routes><route type="mix" sink="bus0_media_out" sources="mixport_bus0_media_out"/>

SwAudioOutputDescriptor 的标记和增益直接来自于音频策略配置文件的 mixPort 元素。音频策略配置文件支持为 mixPort 定义增益,也支持为 devicePort 定义增益,上面的这个配置文件没有为 mixPort 定义增益。采样率、通道掩码(通道数)和采样格式则会从多个 AudioProfile 中选择最佳的一个,具体的策略如 PolicyAudioPort::pickAudioProfile() 函数的定义(位于 frameworks/av/services/audiopolicy/common/managerdefinitions/src/PolicyAudioPort.cpp):

void PolicyAudioPort::pickSamplingRate(uint32_t &pickedRate,const SampleRateSet &samplingRates) const
{pickedRate = 0;// For direct outputs, pick minimum sampling rate: this helps ensuring that the// channel count / sampling rate combination chosen will be supported by the connected// sinkif (isDirectOutput()) {uint32_t samplingRate = UINT_MAX;for (const auto rate : samplingRates) {if ((rate < samplingRate) && (rate > 0)) {samplingRate = rate;}}pickedRate = (samplingRate == UINT_MAX) ? 0 : samplingRate;} else {uint32_t maxRate = SAMPLE_RATE_HZ_MAX;// For mixed output and inputs, use max mixer sampling rates. Do not// limit sampling rate otherwise// For inputs, also see checkCompatibleSamplingRate().if (asAudioPort()->getType() == AUDIO_PORT_TYPE_MIX) {maxRate = UINT_MAX;}// TODO: should mSamplingRates[] be ordered in terms of our preference// and we return the first (and hence most preferred) match?  This is of concern if// we want to choose 96kHz over 192kHz for USB driver stability or resource constraints.for (const auto rate : samplingRates) {if ((rate > pickedRate) && (rate <= maxRate)) {pickedRate = rate;}}}
}void PolicyAudioPort::pickChannelMask(audio_channel_mask_t &pickedChannelMask,const ChannelMaskSet &channelMasks) const
{pickedChannelMask = AUDIO_CHANNEL_NONE;// For direct outputs, pick minimum channel count: this helps ensuring that the// channel count / sampling rate combination chosen will be supported by the connected// sinkif (isDirectOutput()) {uint32_t channelCount = UINT_MAX;for (const auto channelMask : channelMasks) {uint32_t cnlCount;if (asAudioPort()->useInputChannelMask()) {cnlCount = audio_channel_count_from_in_mask(channelMask);} else {cnlCount = audio_channel_count_from_out_mask(channelMask);}if ((cnlCount < channelCount) && (cnlCount > 0)) {pickedChannelMask = channelMask;channelCount = cnlCount;}}} else {uint32_t channelCount = 0;uint32_t maxCount = MAX_MIXER_CHANNEL_COUNT;// For mixed output and inputs, use max mixer channel count. Do not// limit channel count otherwiseif (asAudioPort()->getType() != AUDIO_PORT_TYPE_MIX) {maxCount = UINT_MAX;}for (const auto channelMask : channelMasks) {uint32_t cnlCount;if (asAudioPort()->useInputChannelMask()) {cnlCount = audio_channel_count_from_in_mask(channelMask);} else {cnlCount = audio_channel_count_from_out_mask(channelMask);}if ((cnlCount > channelCount) && (cnlCount <= maxCount)) {pickedChannelMask = channelMask;channelCount = cnlCount;}}}
}/* format in order of increasing preference */
const audio_format_t PolicyAudioPort::sPcmFormatCompareTable[] = {AUDIO_FORMAT_DEFAULT,AUDIO_FORMAT_PCM_16_BIT,AUDIO_FORMAT_PCM_8_24_BIT,AUDIO_FORMAT_PCM_24_BIT_PACKED,AUDIO_FORMAT_PCM_32_BIT,AUDIO_FORMAT_PCM_FLOAT,
};int PolicyAudioPort::compareFormats(audio_format_t format1, audio_format_t format2)
{// NOTE: AUDIO_FORMAT_INVALID is also considered not PCM and will be compared equal to any// compressed format and better than any PCM format. This is by design of pickFormat()if (!audio_is_linear_pcm(format1)) {if (!audio_is_linear_pcm(format2)) {return 0;}return 1;}if (!audio_is_linear_pcm(format2)) {return -1;}int index1 = -1, index2 = -1;for (size_t i = 0;(i < ARRAY_SIZE(sPcmFormatCompareTable)) && ((index1 == -1) || (index2 == -1));i ++) {if (sPcmFormatCompareTable[i] == format1) {index1 = i;}if (sPcmFormatCompareTable[i] == format2) {index2 = i;}}// format1 not found => index1 < 0 => format2 > format1// format2 not found => index2 < 0 => format2 < format1return index1 - index2;
}uint32_t PolicyAudioPort::formatDistance(audio_format_t format1, audio_format_t format2)
{if (format1 == format2) {return 0;}if (format1 == AUDIO_FORMAT_INVALID || format2 == AUDIO_FORMAT_INVALID) {return kFormatDistanceMax;}int diffBytes = (int)audio_bytes_per_sample(format1) -audio_bytes_per_sample(format2);return abs(diffBytes);
}bool PolicyAudioPort::isBetterFormatMatch(audio_format_t newFormat,audio_format_t currentFormat,audio_format_t targetFormat)
{return formatDistance(newFormat, targetFormat) < formatDistance(currentFormat, targetFormat);
}void PolicyAudioPort::pickAudioProfile(uint32_t &samplingRate,audio_channel_mask_t &channelMask,audio_format_t &format) const
{format = AUDIO_FORMAT_DEFAULT;samplingRate = 0;channelMask = AUDIO_CHANNEL_NONE;// special case for uninitialized dynamic profileif (!asAudioPort()->hasValidAudioProfile()) {return;}audio_format_t bestFormat = sPcmFormatCompareTable[ARRAY_SIZE(sPcmFormatCompareTable) - 1];// For mixed output and inputs, use best mixer output format.// Do not limit format otherwiseif ((asAudioPort()->getType() != AUDIO_PORT_TYPE_MIX) || isDirectOutput()) {bestFormat = AUDIO_FORMAT_INVALID;}const AudioProfileVector& audioProfiles = asAudioPort()->getAudioProfiles();for (size_t i = 0; i < audioProfiles.size(); i ++) {if (!audioProfiles[i]->isValid()) {continue;}audio_format_t formatToCompare = audioProfiles[i]->getFormat();if ((compareFormats(formatToCompare, format) > 0) &&(compareFormats(formatToCompare, bestFormat) <= 0)) {uint32_t pickedSamplingRate = 0;audio_channel_mask_t pickedChannelMask = AUDIO_CHANNEL_NONE;pickChannelMask(pickedChannelMask, audioProfiles[i]->getChannels());pickSamplingRate(pickedSamplingRate, audioProfiles[i]->getSampleRates());if (formatToCompare != AUDIO_FORMAT_DEFAULT && pickedChannelMask != AUDIO_CHANNEL_NONE&& pickedSamplingRate != 0) {format = formatToCompare;channelMask = pickedChannelMask;samplingRate = pickedSamplingRate;// TODO: shall we return on the first one or still trying to pick a better Profile?}}}ALOGV("%s Port[nm:%s] profile rate=%d, format=%d, channels=%d", __FUNCTION__,asAudioPort()->getName().c_str(), samplingRate, channelMask, format);
}

对于采样格式,精度越高优先级越高;对于通道掩码(通道数),如果是直接输出 (Direct Output),则通道数越小优先级越高,否则,通道数越大优先级越高;对于采样率,如果是直接输出 (Direct Output),则采样率越小优先级越高,否则,采样率越大优先级越高。


http://www.ppmy.cn/news/41747.html

相关文章

今年最火的拼团玩法,全民拼购模式,这个点子你可以看看

什么是全民拼购&#xff1f; “全民拼购”是将“社交电商”与“拼购玩法”结合起来的一种全新的营销模式&#xff0c;它通过更人性化的方式&#xff0c;将商品的销售利益最大化&#xff0c;既能激励消费者参与&#xff0c;又能促进商品的销售&#xff0c;实现“多方共赢”。 …

十分钟在 macOS 快速搭建 Linux C/C++ 开发环境

有一个使用了 Epoll 的 C 项目&#xff0c;笔者平时用的 Linux 主力开发机不在身边&#xff0c;想在 macOS 上开发调试&#xff0c;但是没有 Linux 虚拟机。恰好&#xff0c;JetBrains CLion 的 Toolchains 配置除了使用本地环境&#xff0c;还支持 SSH、Docker。 笔者使用 CL…

学习系统编程No.19【进程间通信之控制进程】

引言&#xff1a; 北京时间&#xff1a;2023/4/13/8:00&#xff0c;早八人&#xff0c;早八魂&#xff0c;时间不怎么充足&#xff0c;磨磨引言刚好&#xff0c;学习Linux和Linux有关的系统级知识已经许久了&#xff0c;在不知不觉之中&#xff0c;发现自己已经更到了第19篇&a…

【Android笔记92】Android小案例(二)之模仿小米商城(底部菜单栏、Fragment页面切换)

这篇文章,主要介绍Android小案例(二)之模仿小米商城(底部菜单栏、Fragment页面切换)。 目录 一、模仿小米商城 1.1、运行效果 1.2、底部菜单栏 (1)布局文件

Map排序

(一&#xff09;treeHap 特点&#xff1a;treeMap中的元素根据键的大小自然排序&#xff08;默认是升序&#xff09; 1、treeHap遍历测试 import java.io.IOException; import java.util.*; public class Main {public static void main(String[] args)throws IOException {…

Excel VBA 之For Next循环

一.简单for循环 当你知道你需要重复运行多少次某段语句时&#xff0c;可以使用For…Next语句。它的语法如下&#xff1a; For 计数器 开始 To 结束 [step 步长]语句1语句2[Exit For]语句N Next [计数器] 从开始到结束&#xff0c;反复执行For和Next之间的指令块&#xff0c;除…

【Docker】Dockerfile简介

介绍 Dockerfile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本。 我们使用docker commit可以构造镜像&#xff0c;但是docker中的镜像随时变化&#xff0c;不能一次次的使用commit&#xff0c;因此使用Dockerfile来一次性构建。 …

0302TodoList案例-react应用

文章目录1 效果2 功能拆分和静态组件3 动态初始化4 功能实现4.1 添加todo4.2 鼠标移入效果和删除todo4.3 todo选中和取消选中4.4 底部统计和删除已完成5 TodoList案例总结结语1 效果 通过前面学习React基础和create-react-app脚手架&#xff0c;下面我们做一个经典的入门案例T…