Make AudioProfile as a common class and create AudioProfileVectorBase.
AudioProfile is a class that contains information for an audio profile.
AudioProfileVectorBase is a base class that contains operations for a
vector of audio profiles. AudioProfileVector derives from
AudioProfileVectorBase and contains policy related stuff.
Bug: 135621476
Test: CTS for AudioRecord, AudioTrack, AudioManager
Test: audio smoke test, audiopolicy_tests
Test: dumpsys media.audio_policy
Change-Id: Ic2e08efcc5efa99e499a931811b7042fbd5ddf04
Merged-In: Ic2e08efcc5efa99e499a931811b7042fbd5ddf04
diff --git a/media/libaudiofoundation/AudioProfile.cpp b/media/libaudiofoundation/AudioProfile.cpp
index d1082e8..aaaa7d1 100644
--- a/media/libaudiofoundation/AudioProfile.cpp
+++ b/media/libaudiofoundation/AudioProfile.cpp
@@ -14,34 +14,30 @@
* limitations under the License.
*/
-#include <algorithm>
#include <set>
-#include <string>
-#define LOG_TAG "APM::AudioProfile"
+#define LOG_TAG "AudioProfile"
//#define LOG_NDEBUG 0
+#include <android-base/stringprintf.h>
#include <media/AudioContainers.h>
-#include <media/AudioResamplerPublic.h>
+#include <media/AudioProfile.h>
+#include <media/TypeConverter.h>
#include <utils/Errors.h>
-#include "AudioPort.h"
-#include "AudioProfile.h"
-#include "HwModule.h"
-#include "TypeConverter.h"
-
namespace android {
-bool operator == (const AudioProfile &left, const AudioProfile &compareTo)
+bool operator == (const AudioProfile &left, const AudioProfile &right)
{
- return (left.getFormat() == compareTo.getFormat()) &&
- (left.getChannels() == compareTo.getChannels()) &&
- (left.getSampleRates() == compareTo.getSampleRates());
+ return (left.getFormat() == right.getFormat()) &&
+ (left.getChannels() == right.getChannels()) &&
+ (left.getSampleRates() == right.getSampleRates());
}
-static AudioProfile* createFullDynamicImpl()
+// static
+sp<AudioProfile> AudioProfile::createFullDynamic(audio_format_t dynamicFormat)
{
- AudioProfile* dynamicProfile = new AudioProfile(gDynamicFormat,
+ AudioProfile* dynamicProfile = new AudioProfile(dynamicFormat,
ChannelMaskSet(), SampleRateSet());
dynamicProfile->setDynamicFormat(true);
dynamicProfile->setDynamicChannels(true);
@@ -49,17 +45,10 @@
return dynamicProfile;
}
-// static
-sp<AudioProfile> AudioProfile::createFullDynamic()
-{
- static sp<AudioProfile> dynamicProfile = createFullDynamicImpl();
- return dynamicProfile;
-}
-
AudioProfile::AudioProfile(audio_format_t format,
audio_channel_mask_t channelMasks,
uint32_t samplingRate) :
- mName(String8("")),
+ mName(""),
mFormat(format)
{
mChannelMasks.insert(channelMasks);
@@ -69,7 +58,7 @@
AudioProfile::AudioProfile(audio_format_t format,
const ChannelMaskSet &channelMasks,
const SampleRateSet &samplingRateCollection) :
- mName(String8("")),
+ mName(""),
mFormat(format),
mChannelMasks(channelMasks),
mSamplingRates(samplingRateCollection) {}
@@ -98,276 +87,45 @@
}
}
-status_t AudioProfile::checkExact(uint32_t samplingRate, audio_channel_mask_t channelMask,
- audio_format_t format) const
+void AudioProfile::dump(std::string *dst, int spaces) const
{
- if (audio_formats_match(format, mFormat) &&
- supportsChannels(channelMask) &&
- supportsRate(samplingRate)) {
- return NO_ERROR;
- }
- return BAD_VALUE;
-}
-
-status_t AudioProfile::checkCompatibleSamplingRate(uint32_t samplingRate,
- uint32_t &updatedSamplingRate) const
-{
- ALOG_ASSERT(samplingRate > 0);
-
- if (mSamplingRates.empty()) {
- updatedSamplingRate = samplingRate;
- return NO_ERROR;
- }
-
- // Search for the closest supported sampling rate that is above (preferred)
- // or below (acceptable) the desired sampling rate, within a permitted ratio.
- // The sampling rates are sorted in ascending order.
- auto desiredRate = mSamplingRates.lower_bound(samplingRate);
-
- // Prefer to down-sample from a higher sampling rate, as we get the desired frequency spectrum.
- if (desiredRate != mSamplingRates.end()) {
- if (*desiredRate / AUDIO_RESAMPLER_DOWN_RATIO_MAX <= samplingRate) {
- updatedSamplingRate = *desiredRate;
- return NO_ERROR;
- }
- }
- // But if we have to up-sample from a lower sampling rate, that's OK.
- if (desiredRate != mSamplingRates.begin()) {
- uint32_t candidate = *(--desiredRate);
- if (candidate * AUDIO_RESAMPLER_UP_RATIO_MAX >= samplingRate) {
- updatedSamplingRate = candidate;
- return NO_ERROR;
- }
- }
- // leave updatedSamplingRate unmodified
- return BAD_VALUE;
-}
-
-status_t AudioProfile::checkCompatibleChannelMask(audio_channel_mask_t channelMask,
- audio_channel_mask_t &updatedChannelMask,
- audio_port_type_t portType,
- audio_port_role_t portRole) const
-{
- if (mChannelMasks.empty()) {
- updatedChannelMask = channelMask;
- return NO_ERROR;
- }
- const bool isRecordThread = portType == AUDIO_PORT_TYPE_MIX && portRole == AUDIO_PORT_ROLE_SINK;
- const bool isIndex = audio_channel_mask_get_representation(channelMask)
- == AUDIO_CHANNEL_REPRESENTATION_INDEX;
- const uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
- int bestMatch = 0;
- for (const auto &supported : mChannelMasks) {
- if (supported == channelMask) {
- // Exact matches always taken.
- updatedChannelMask = channelMask;
- return NO_ERROR;
- }
-
- // AUDIO_CHANNEL_NONE (value: 0) is used for dynamic channel support
- if (isRecordThread && supported != AUDIO_CHANNEL_NONE) {
- // Approximate (best) match:
- // The match score measures how well the supported channel mask matches the
- // desired mask, where increasing-is-better.
- //
- // TODO: Some tweaks may be needed.
- // Should be a static function of the data processing library.
- //
- // In priority:
- // match score = 1000 if legacy channel conversion equivalent (always prefer this)
- // OR
- // match score += 100 if the channel mask representations match
- // match score += number of channels matched.
- // match score += 100 if the channel mask representations DO NOT match
- // but the profile has positional channel mask and less than 2 channels.
- // This is for audio HAL convention to not list index masks for less than 2 channels
- //
- // If there are no matched channels, the mask may still be accepted
- // but the playback or record will be silent.
- const bool isSupportedIndex = (audio_channel_mask_get_representation(supported)
- == AUDIO_CHANNEL_REPRESENTATION_INDEX);
- const uint32_t supportedChannelCount = audio_channel_count_from_in_mask(supported);
- int match;
- if (isIndex && isSupportedIndex) {
- // index equivalence
- match = 100 + __builtin_popcount(
- audio_channel_mask_get_bits(channelMask)
- & audio_channel_mask_get_bits(supported));
- } else if (isIndex && !isSupportedIndex) {
- const uint32_t equivalentBits = (1 << supportedChannelCount) - 1 ;
- match = __builtin_popcount(
- audio_channel_mask_get_bits(channelMask) & equivalentBits);
- if (supportedChannelCount <= FCC_2) {
- match += 100;
- }
- } else if (!isIndex && isSupportedIndex) {
- const uint32_t equivalentBits = (1 << channelCount) - 1;
- match = __builtin_popcount(
- equivalentBits & audio_channel_mask_get_bits(supported));
- } else {
- // positional equivalence
- match = 100 + __builtin_popcount(
- audio_channel_mask_get_bits(channelMask)
- & audio_channel_mask_get_bits(supported));
- switch (supported) {
- case AUDIO_CHANNEL_IN_FRONT_BACK:
- case AUDIO_CHANNEL_IN_STEREO:
- if (channelMask == AUDIO_CHANNEL_IN_MONO) {
- match = 1000;
- }
- break;
- case AUDIO_CHANNEL_IN_MONO:
- if (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK
- || channelMask == AUDIO_CHANNEL_IN_STEREO) {
- match = 1000;
- }
- break;
- default:
- break;
- }
- }
- if (match > bestMatch) {
- bestMatch = match;
- updatedChannelMask = supported;
- }
- }
- }
- return bestMatch > 0 ? NO_ERROR : BAD_VALUE;
-}
-
-void AudioProfile::dump(String8 *dst, int spaces) const
-{
- dst->appendFormat("%s%s%s\n", mIsDynamicFormat ? "[dynamic format]" : "",
+ dst->append(base::StringPrintf("%s%s%s\n", mIsDynamicFormat ? "[dynamic format]" : "",
mIsDynamicChannels ? "[dynamic channels]" : "",
- mIsDynamicRate ? "[dynamic rates]" : "");
+ mIsDynamicRate ? "[dynamic rates]" : ""));
if (mName.length() != 0) {
- dst->appendFormat("%*s- name: %s\n", spaces, "", mName.string());
+ dst->append(base::StringPrintf("%*s- name: %s\n", spaces, "", mName.c_str()));
}
std::string formatLiteral;
if (FormatConverter::toString(mFormat, formatLiteral)) {
- dst->appendFormat("%*s- format: %s\n", spaces, "", formatLiteral.c_str());
+ dst->append(base::StringPrintf("%*s- format: %s\n", spaces, "", formatLiteral.c_str()));
}
if (!mSamplingRates.empty()) {
- dst->appendFormat("%*s- sampling rates:", spaces, "");
+ dst->append(base::StringPrintf("%*s- sampling rates:", spaces, ""));
for (auto it = mSamplingRates.begin(); it != mSamplingRates.end();) {
- dst->appendFormat("%d", *it);
+ dst->append(base::StringPrintf("%d", *it));
dst->append(++it == mSamplingRates.end() ? "" : ", ");
}
dst->append("\n");
}
if (!mChannelMasks.empty()) {
- dst->appendFormat("%*s- channel masks:", spaces, "");
+ dst->append(base::StringPrintf("%*s- channel masks:", spaces, ""));
for (auto it = mChannelMasks.begin(); it != mChannelMasks.end();) {
- dst->appendFormat("0x%04x", *it);
+ dst->append(base::StringPrintf("0x%04x", *it));
dst->append(++it == mChannelMasks.end() ? "" : ", ");
}
dst->append("\n");
}
}
-ssize_t AudioProfileVector::add(const sp<AudioProfile> &profile)
+ssize_t AudioProfileVectorBase::add(const sp<AudioProfile> &profile)
{
ssize_t index = size();
push_back(profile);
- // we sort from worst to best, so that AUDIO_FORMAT_DEFAULT is always the first entry.
- std::sort(begin(), end(),
- [](const sp<AudioProfile> & a, const sp<AudioProfile> & b)
- {
- return AudioPort::compareFormats(a->getFormat(), b->getFormat()) < 0;
- });
return index;
}
-ssize_t AudioProfileVector::addProfileFromHal(const sp<AudioProfile> &profileToAdd)
-{
- // Check valid profile to add:
- if (!profileToAdd->hasValidFormat()) {
- return -1;
- }
- if (!profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) {
- FormatVector formats;
- formats.push_back(profileToAdd->getFormat());
- setFormats(FormatVector(formats));
- return 0;
- }
- if (!profileToAdd->hasValidChannels() && profileToAdd->hasValidRates()) {
- setSampleRatesFor(profileToAdd->getSampleRates(), profileToAdd->getFormat());
- return 0;
- }
- if (profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) {
- setChannelsFor(profileToAdd->getChannels(), profileToAdd->getFormat());
- return 0;
- }
- // Go through the list of profile to avoid duplicates
- for (size_t profileIndex = 0; profileIndex < size(); profileIndex++) {
- const sp<AudioProfile> &profile = at(profileIndex);
- if (profile->isValid() && profile == profileToAdd) {
- // Nothing to do
- return profileIndex;
- }
- }
- profileToAdd->setDynamicFormat(true); // set the format as dynamic to allow removal
- return add(profileToAdd);
-}
-
-status_t AudioProfileVector::checkExactProfile(uint32_t samplingRate,
- audio_channel_mask_t channelMask,
- audio_format_t format) const
-{
- if (empty()) {
- return NO_ERROR;
- }
-
- for (const auto& profile : *this) {
- if (profile->checkExact(samplingRate, channelMask, format) == NO_ERROR) {
- return NO_ERROR;
- }
- }
- return BAD_VALUE;
-}
-
-status_t AudioProfileVector::checkCompatibleProfile(uint32_t &samplingRate,
- audio_channel_mask_t &channelMask,
- audio_format_t &format,
- audio_port_type_t portType,
- audio_port_role_t portRole) const
-{
- if (empty()) {
- return NO_ERROR;
- }
-
- const bool checkInexact = // when port is input and format is linear pcm
- portType == AUDIO_PORT_TYPE_MIX && portRole == AUDIO_PORT_ROLE_SINK
- && audio_is_linear_pcm(format);
-
- // iterate from best format to worst format (reverse order)
- for (ssize_t i = size() - 1; i >= 0 ; --i) {
- const sp<AudioProfile> profile = at(i);
- audio_format_t formatToCompare = profile->getFormat();
- if (formatToCompare == format ||
- (checkInexact
- && formatToCompare != AUDIO_FORMAT_DEFAULT
- && audio_is_linear_pcm(formatToCompare))) {
- // Compatible profile has been found, checks if this profile has compatible
- // rate and channels as well
- audio_channel_mask_t updatedChannels;
- uint32_t updatedRate;
- if (profile->checkCompatibleChannelMask(channelMask, updatedChannels,
- portType, portRole) == NO_ERROR &&
- profile->checkCompatibleSamplingRate(samplingRate, updatedRate) == NO_ERROR) {
- // for inexact checks we take the first linear pcm format due to sorting.
- format = formatToCompare;
- channelMask = updatedChannels;
- samplingRate = updatedRate;
- return NO_ERROR;
- }
- }
- }
- return BAD_VALUE;
-}
-
-void AudioProfileVector::clearProfiles()
+void AudioProfileVectorBase::clearProfiles()
{
for (auto it = begin(); it != end();) {
if ((*it)->isDynamicFormat() && (*it)->hasValidFormat()) {
@@ -379,77 +137,7 @@
}
}
-// Returns an intersection between two possibly unsorted vectors and the contents of 'order'.
-// The result is ordered according to 'order'.
-template<typename T, typename Order>
-std::vector<typename T::value_type> intersectFilterAndOrder(
- const T& input1, const T& input2, const Order& order)
-{
- std::set<typename T::value_type> set1{input1.begin(), input1.end()};
- std::set<typename T::value_type> set2{input2.begin(), input2.end()};
- std::set<typename T::value_type> common;
- std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(),
- std::inserter(common, common.begin()));
- std::vector<typename T::value_type> result;
- for (const auto& e : order) {
- if (common.find(e) != common.end()) result.push_back(e);
- }
- return result;
-}
-
-// Intersect two possibly unsorted vectors, return common elements according to 'comp' ordering.
-// 'comp' is a comparator function.
-template<typename T, typename Compare>
-std::vector<typename T::value_type> intersectAndOrder(
- const T& input1, const T& input2, Compare comp)
-{
- std::set<typename T::value_type, Compare> set1{input1.begin(), input1.end(), comp};
- std::set<typename T::value_type, Compare> set2{input2.begin(), input2.end(), comp};
- std::vector<typename T::value_type> result;
- std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(),
- std::back_inserter(result), comp);
- return result;
-}
-
-status_t AudioProfileVector::findBestMatchingOutputConfig(const AudioProfileVector& outputProfiles,
- const std::vector<audio_format_t>& preferredFormats,
- const std::vector<audio_channel_mask_t>& preferredOutputChannels,
- bool preferHigherSamplingRates,
- audio_config_base *bestOutputConfig) const
-{
- auto formats = intersectFilterAndOrder(getSupportedFormats(),
- outputProfiles.getSupportedFormats(), preferredFormats);
- // Pick the best compatible profile.
- for (const auto& f : formats) {
- sp<AudioProfile> inputProfile = getFirstValidProfileFor(f);
- sp<AudioProfile> outputProfile = outputProfiles.getFirstValidProfileFor(f);
- if (inputProfile == nullptr || outputProfile == nullptr) {
- continue;
- }
- auto channels = intersectFilterAndOrder(asOutMask(inputProfile->getChannels()),
- outputProfile->getChannels(), preferredOutputChannels);
- if (channels.empty()) {
- continue;
- }
- auto sampleRates = preferHigherSamplingRates ?
- intersectAndOrder(inputProfile->getSampleRates(), outputProfile->getSampleRates(),
- std::greater<typename SampleRateSet::value_type>()) :
- intersectAndOrder(inputProfile->getSampleRates(), outputProfile->getSampleRates(),
- std::less<typename SampleRateSet::value_type>());
- if (sampleRates.empty()) {
- continue;
- }
- ALOGD("%s() found channel mask %#x and sample rate %d for format %#x.",
- __func__, *channels.begin(), *sampleRates.begin(), f);
- bestOutputConfig->format = f;
- bestOutputConfig->sample_rate = *sampleRates.begin();
- bestOutputConfig->channel_mask = *channels.begin();
- return NO_ERROR;
- }
- return BAD_VALUE;
-}
-
-sp<AudioProfile> AudioProfileVector::getFirstValidProfile() const
+sp<AudioProfile> AudioProfileVectorBase::getFirstValidProfile() const
{
for (const auto &profile : *this) {
if (profile->isValid()) {
@@ -459,7 +147,7 @@
return nullptr;
}
-sp<AudioProfile> AudioProfileVector::getFirstValidProfileFor(audio_format_t format) const
+sp<AudioProfile> AudioProfileVectorBase::getFirstValidProfileFor(audio_format_t format) const
{
for (const auto &profile : *this) {
if (profile->isValid() && profile->getFormat() == format) {
@@ -469,7 +157,7 @@
return nullptr;
}
-FormatVector AudioProfileVector::getSupportedFormats() const
+FormatVector AudioProfileVectorBase::getSupportedFormats() const
{
FormatVector supportedFormats;
for (const auto &profile : *this) {
@@ -480,7 +168,7 @@
return supportedFormats;
}
-bool AudioProfileVector::hasDynamicChannelsFor(audio_format_t format) const
+bool AudioProfileVectorBase::hasDynamicChannelsFor(audio_format_t format) const
{
for (const auto &profile : *this) {
if (profile->getFormat() == format && profile->isDynamicChannels()) {
@@ -490,7 +178,17 @@
return false;
}
-bool AudioProfileVector::hasDynamicProfile() const
+bool AudioProfileVectorBase::hasDynamicFormat() const
+{
+ for (const auto &profile : *this) {
+ if (profile->isDynamicFormat()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AudioProfileVectorBase::hasDynamicProfile() const
{
for (const auto &profile : *this) {
if (profile->isDynamic()) {
@@ -500,7 +198,7 @@
return false;
}
-bool AudioProfileVector::hasDynamicRateFor(audio_format_t format) const
+bool AudioProfileVectorBase::hasDynamicRateFor(audio_format_t format) const
{
for (const auto &profile : *this) {
if (profile->getFormat() == format && profile->isDynamicRate()) {
@@ -510,77 +208,14 @@
return false;
}
-void AudioProfileVector::setFormats(const FormatVector &formats)
+void AudioProfileVectorBase::dump(std::string *dst, int spaces) const
{
- // Only allow to change the format of dynamic profile
- sp<AudioProfile> dynamicFormatProfile = getProfileFor(gDynamicFormat);
- if (dynamicFormatProfile == 0) {
- return;
- }
- for (const auto &format : formats) {
- sp<AudioProfile> profile = new AudioProfile(format,
- dynamicFormatProfile->getChannels(),
- dynamicFormatProfile->getSampleRates());
- profile->setDynamicFormat(true);
- profile->setDynamicChannels(dynamicFormatProfile->isDynamicChannels());
- profile->setDynamicRate(dynamicFormatProfile->isDynamicRate());
- add(profile);
- }
-}
-
-void AudioProfileVector::dump(String8 *dst, int spaces) const
-{
- dst->appendFormat("%*s- Profiles:\n", spaces, "");
+ dst->append(base::StringPrintf("%*s- Profiles:\n", spaces, ""));
for (size_t i = 0; i < size(); i++) {
- dst->appendFormat("%*sProfile %zu:", spaces + 4, "", i);
- at(i)->dump(dst, spaces + 8);
- }
-}
-
-sp<AudioProfile> AudioProfileVector::getProfileFor(audio_format_t format) const
-{
- for (const auto &profile : *this) {
- if (profile->getFormat() == format) {
- return profile;
- }
- }
- return nullptr;
-}
-
-void AudioProfileVector::setSampleRatesFor(
- const SampleRateSet &sampleRates, audio_format_t format)
-{
- for (const auto &profile : *this) {
- if (profile->getFormat() == format && profile->isDynamicRate()) {
- if (profile->hasValidRates()) {
- // Need to create a new profile with same format
- sp<AudioProfile> profileToAdd = new AudioProfile(format, profile->getChannels(),
- sampleRates);
- profileToAdd->setDynamicFormat(true); // need to set to allow cleaning
- add(profileToAdd);
- } else {
- profile->setSampleRates(sampleRates);
- }
- return;
- }
- }
-}
-
-void AudioProfileVector::setChannelsFor(const ChannelMaskSet &channelMasks, audio_format_t format)
-{
- for (const auto &profile : *this) {
- if (profile->getFormat() == format && profile->isDynamicChannels()) {
- if (profile->hasValidChannels()) {
- // Need to create a new profile with same format
- sp<AudioProfile> profileToAdd = new AudioProfile(format, channelMasks,
- profile->getSampleRates());
- profileToAdd->setDynamicFormat(true); // need to set to allow cleaning
- add(profileToAdd);
- } else {
- profile->setChannels(channelMasks);
- }
- return;
- }
+ dst->append(base::StringPrintf("%*sProfile %zu:", spaces + 4, "", i));
+ std::string profileStr;
+ at(i)->dump(&profileStr, spaces + 8);
+ dst->append(profileStr);
}
}