/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define LOG_TAG "AudioProfile" //#define LOG_NDEBUG 0 #include #include #include #include #include namespace android { using media::audio::common::AudioChannelLayout; bool operator == (const AudioProfile &left, const AudioProfile &right) { return (left.getFormat() == right.getFormat()) && (left.getChannels() == right.getChannels()) && (left.getSampleRates() == right.getSampleRates()); } // static sp AudioProfile::createFullDynamic(audio_format_t dynamicFormat) { AudioProfile* dynamicProfile = new AudioProfile(dynamicFormat, ChannelMaskSet(), SampleRateSet()); dynamicProfile->setDynamicFormat(true); dynamicProfile->setDynamicChannels(true); dynamicProfile->setDynamicRate(true); return dynamicProfile; } AudioProfile::AudioProfile(audio_format_t format, audio_channel_mask_t channelMasks, uint32_t samplingRate) : mName(""), mFormat(format) { mChannelMasks.insert(channelMasks); mSamplingRates.insert(samplingRate); } AudioProfile::AudioProfile(audio_format_t format, const ChannelMaskSet &channelMasks, const SampleRateSet &samplingRateCollection) : AudioProfile(format, channelMasks, samplingRateCollection, AUDIO_ENCAPSULATION_TYPE_NONE) {} AudioProfile::AudioProfile(audio_format_t format, const ChannelMaskSet &channelMasks, const SampleRateSet &samplingRateCollection, audio_encapsulation_type_t encapsulationType) : mName(""), mFormat(format), mChannelMasks(channelMasks), mSamplingRates(samplingRateCollection), mEncapsulationType(encapsulationType) {} void AudioProfile::setChannels(const ChannelMaskSet &channelMasks) { if (mIsDynamicChannels) { mChannelMasks = channelMasks; } } void AudioProfile::setSampleRates(const SampleRateSet &sampleRates) { if (mIsDynamicRate) { mSamplingRates = sampleRates; } } void AudioProfile::clear() { if (mIsDynamicChannels) { mChannelMasks.clear(); } if (mIsDynamicRate) { mSamplingRates.clear(); } } void AudioProfile::dump(std::string *dst, int spaces) const { dst->append(base::StringPrintf("\"%s\"; ", mName.c_str())); dst->append(base::StringPrintf("%s%s%s%s", mIsDynamicFormat ? "[dynamic format]" : "", mIsDynamicChannels ? "[dynamic channels]" : "", mIsDynamicRate ? "[dynamic rates]" : "", isDynamic() ? "; " : "")); dst->append(base::StringPrintf("%s (0x%x)\n", audio_format_to_string(mFormat), mFormat)); if (!mSamplingRates.empty()) { dst->append(base::StringPrintf("%*ssampling rates: ", spaces, "")); for (auto it = mSamplingRates.begin(); it != mSamplingRates.end();) { dst->append(base::StringPrintf("%d", *it)); dst->append(++it == mSamplingRates.end() ? "" : ", "); } dst->append("\n"); } if (!mChannelMasks.empty()) { dst->append(base::StringPrintf("%*schannel masks: ", spaces, "")); for (auto it = mChannelMasks.begin(); it != mChannelMasks.end();) { dst->append(base::StringPrintf("0x%04x", *it)); dst->append(++it == mChannelMasks.end() ? "" : ", "); } dst->append("\n"); } dst->append(base::StringPrintf( "%*s%s\n", spaces, "", audio_encapsulation_type_to_string(mEncapsulationType))); } bool AudioProfile::equals(const sp& other, bool ignoreDynamicFlags) const { return other != nullptr && mName.compare(other->mName) == 0 && mFormat == other->getFormat() && mChannelMasks == other->getChannels() && mSamplingRates == other->getSampleRates() && (ignoreDynamicFlags || (mIsDynamicFormat == other->isDynamicFormat() && mIsDynamicChannels == other->isDynamicChannels() && mIsDynamicRate == other->isDynamicRate())) && mEncapsulationType == other->getEncapsulationType(); } AudioProfile& AudioProfile::operator=(const AudioProfile& other) { mName = other.mName; mFormat = other.mFormat; mChannelMasks = other.mChannelMasks; mSamplingRates = other.mSamplingRates; mEncapsulationType = other.mEncapsulationType; mIsDynamicFormat = other.mIsDynamicFormat; mIsDynamicChannels = other.mIsDynamicChannels; mIsDynamicRate = other.mIsDynamicRate; return *this; } ConversionResult AudioProfile::toParcelable(bool isInput) const { media::audio::common::AudioProfile parcelable = VALUE_OR_RETURN(toCommonParcelable(isInput)); media::AudioProfileSys parcelableSys; parcelableSys.isDynamicFormat = mIsDynamicFormat; parcelableSys.isDynamicChannels = mIsDynamicChannels; parcelableSys.isDynamicRate = mIsDynamicRate; return std::make_pair(parcelable, parcelableSys); } ConversionResult> AudioProfile::fromParcelable( const AudioProfile::Aidl& aidl, bool isInput) { sp legacy = VALUE_OR_RETURN(fromCommonParcelable(aidl.first, isInput)); const auto& parcelableSys = aidl.second; legacy->mIsDynamicFormat = parcelableSys.isDynamicFormat; legacy->mIsDynamicChannels = parcelableSys.isDynamicChannels; legacy->mIsDynamicRate = parcelableSys.isDynamicRate; return legacy; } ConversionResult AudioProfile::toCommonParcelable(bool isInput) const { media::audio::common::AudioProfile parcelable; parcelable.name = mName; parcelable.format = VALUE_OR_RETURN( legacy2aidl_audio_format_t_AudioFormatDescription(mFormat)); // Note: legacy 'audio_profile' imposes a limit on the number of // channel masks and sampling rates. That's why it's not used here // and conversions are performed directly on the fields instead // of using 'legacy2aidl_audio_profile_AudioProfile' from AidlConversion. parcelable.channelMasks = VALUE_OR_RETURN( convertContainer>( mChannelMasks, [isInput](audio_channel_mask_t m) { return legacy2aidl_audio_channel_mask_t_AudioChannelLayout(m, isInput); })); parcelable.sampleRates = VALUE_OR_RETURN( convertContainer>(mSamplingRates, convertIntegral)); parcelable.encapsulationType = VALUE_OR_RETURN( legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType(mEncapsulationType)); return parcelable; } ConversionResult> AudioProfile::fromCommonParcelable( const media::audio::common::AudioProfile& aidl, bool isInput) { sp legacy = new AudioProfile(); legacy->mName = aidl.name; legacy->mFormat = VALUE_OR_RETURN( aidl2legacy_AudioFormatDescription_audio_format_t(aidl.format)); legacy->mChannelMasks = VALUE_OR_RETURN( convertContainer(aidl.channelMasks, [isInput](const AudioChannelLayout& l) { return aidl2legacy_AudioChannelLayout_audio_channel_mask_t(l, isInput); })); legacy->mSamplingRates = VALUE_OR_RETURN( convertContainer(aidl.sampleRates, convertIntegral)); legacy->mEncapsulationType = VALUE_OR_RETURN( aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t( aidl.encapsulationType)); return legacy; } ConversionResult> aidl2legacy_AudioProfile(const AudioProfile::Aidl& aidl, bool isInput) { return AudioProfile::fromParcelable(aidl, isInput); } ConversionResult legacy2aidl_AudioProfile(const sp& legacy, bool isInput) { return legacy->toParcelable(isInput); } ConversionResult> aidl2legacy_AudioProfile_common(const media::audio::common::AudioProfile& aidl, bool isInput) { return AudioProfile::fromCommonParcelable(aidl, isInput); } ConversionResult legacy2aidl_AudioProfile_common(const sp& legacy, bool isInput) { return legacy->toCommonParcelable(isInput); } ssize_t AudioProfileVector::add(const sp &profile) { ssize_t index = size(); push_back(profile); return index; } void AudioProfileVector::clearProfiles() { for (auto it = begin(); it != end();) { if ((*it)->isDynamicFormat() && (*it)->hasValidFormat()) { it = erase(it); } else { (*it)->clear(); ++it; } } } sp AudioProfileVector::getFirstValidProfile() const { for (const auto &profile : *this) { if (profile->isValid()) { return profile; } } return nullptr; } sp AudioProfileVector::getFirstValidProfileFor(audio_format_t format) const { for (const auto &profile : *this) { if (profile->isValid() && profile->getFormat() == format) { return profile; } } return nullptr; } FormatVector AudioProfileVector::getSupportedFormats() const { FormatVector supportedFormats; for (const auto &profile : *this) { if (profile->hasValidFormat()) { supportedFormats.push_back(profile->getFormat()); } } return supportedFormats; } bool AudioProfileVector::hasDynamicChannelsFor(audio_format_t format) const { for (const auto &profile : *this) { if (profile->getFormat() == format && profile->isDynamicChannels()) { return true; } } return false; } bool AudioProfileVector::hasDynamicFormat() const { for (const auto &profile : *this) { if (profile->isDynamicFormat()) { return true; } } return false; } bool AudioProfileVector::hasDynamicProfile() const { for (const auto &profile : *this) { if (profile->isDynamic()) { return true; } } return false; } bool AudioProfileVector::hasDynamicRateFor(audio_format_t format) const { for (const auto &profile : *this) { if (profile->getFormat() == format && profile->isDynamicRate()) { return true; } } return false; } const SampleRateSet AudioProfileVector::getSampleRatesFor(audio_format_t format) const { for (const auto& profile : *this) { if (profile->getFormat() == format) { return profile->getSampleRates(); } } return {}; } const ChannelMaskSet AudioProfileVector::getChannelMasksFor(audio_format_t format) const { for (const auto& profile : *this) { if (profile->getFormat() == format) { return profile->getChannels(); } } return {}; } bool AudioProfileVector::contains(const sp& profile, bool ignoreDynamicFlags) const { for (const auto& audioProfile : *this) { if (audioProfile->equals(profile, ignoreDynamicFlags)) { return true; } } return false; } void AudioProfileVector::dump(std::string *dst, int spaces) const { dst->append(base::StringPrintf("%*s- Profiles (%zu):\n", spaces - 2, "", size())); for (size_t i = 0; i < size(); i++) { const std::string prefix = base::StringPrintf("%*s %zu. ", spaces, "", i + 1); dst->append(prefix); std::string profileStr; at(i)->dump(&profileStr, prefix.size()); dst->append(profileStr); } } bool AudioProfileVector::equals(const AudioProfileVector& other) const { return std::equal(begin(), end(), other.begin(), other.end(), [](const sp& left, const sp& right) { return left->equals(right); }); } void AudioProfileVector::addAllValidProfiles(const AudioProfileVector& audioProfiles) { for (const auto& audioProfile : audioProfiles) { if (audioProfile->isValid() && !contains(audioProfile, true /*ignoreDynamicFlags*/)) { add(audioProfile); } } } ChannelMaskSet AudioProfileVector::getSupportedChannelMasks() const { ChannelMaskSet channelMasks; for (const auto& profile : *this) { if (profile->isValid()) { channelMasks.insert(profile->getChannels().begin(), profile->getChannels().end()); } } return channelMasks; } ConversionResult aidl2legacy_AudioProfileVector(const AudioProfileVector::Aidl& aidl, bool isInput) { return convertContainers(aidl.first, aidl.second, [isInput](const media::audio::common::AudioProfile& p, const media::AudioProfileSys& ps) { return aidl2legacy_AudioProfile(std::make_pair(p, ps), isInput); }); } ConversionResult legacy2aidl_AudioProfileVector(const AudioProfileVector& legacy, bool isInput) { return convertContainerSplit< std::vector, std::vector>(legacy, [isInput](const sp& p) { return legacy2aidl_AudioProfile(p, isInput); }); } AudioProfileVector intersectAudioProfiles(const AudioProfileVector& profiles1, const AudioProfileVector& profiles2) { std::map> infos2; for (const auto& profile : profiles2) { infos2.emplace(profile->getFormat(), std::make_pair(profile->getChannels(), profile->getSampleRates())); } AudioProfileVector profiles; for (const auto& profile : profiles1) { const auto it = infos2.find(profile->getFormat()); if (it == infos2.end()) { continue; } ChannelMaskSet channelMasks = SetIntersection(profile->getChannels(), it->second.first); if (channelMasks.empty()) { continue; } SampleRateSet sampleRates = SetIntersection(profile->getSampleRates(), it->second.second); if (sampleRates.empty()) { continue; } profiles.push_back(new AudioProfile(profile->getFormat(), channelMasks, sampleRates)); } return profiles; } } // namespace android