Merge "Port AudioCapabilities to native." into main
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index e83e9f5..8a962c6 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -258,6 +258,7 @@
name: "libmedia_codeclist_capabilities",
srcs: [
+ "AudioCapabilities.cpp",
"CodecCapabilities.cpp",
"CodecCapabilitiesUtils.cpp",
],
diff --git a/media/libmedia/AudioCapabilities.cpp b/media/libmedia/AudioCapabilities.cpp
new file mode 100644
index 0000000..d6524d5
--- /dev/null
+++ b/media/libmedia/AudioCapabilities.cpp
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2024, 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AudioCapabilities"
+
+#include <android-base/strings.h>
+#include <android-base/properties.h>
+
+#include <media/AudioCapabilities.h>
+#include <media/CodecCapabilities.h>
+#include <media/stagefright/MediaCodecConstants.h>
+
+namespace android {
+
+const Range<int>& AudioCapabilities::getBitrateRange() const {
+ return mBitrateRange;
+}
+
+const std::vector<int>& AudioCapabilities::getSupportedSampleRates() const {
+ return mSampleRates;
+}
+
+const std::vector<Range<int>>&
+ AudioCapabilities::getSupportedSampleRateRanges() const {
+ return mSampleRateRanges;
+}
+
+int AudioCapabilities::getMaxInputChannelCount() const {
+ int overallMax = 0;
+ for (int i = mInputChannelRanges.size() - 1; i >= 0; i--) {
+ int lmax = mInputChannelRanges[i].upper();
+ if (lmax > overallMax) {
+ overallMax = lmax;
+ }
+ }
+ return overallMax;
+}
+
+int AudioCapabilities::getMinInputChannelCount() const {
+ int overallMin = MAX_INPUT_CHANNEL_COUNT;
+ for (int i = mInputChannelRanges.size() - 1; i >= 0; i--) {
+ int lmin = mInputChannelRanges[i].lower();
+ if (lmin < overallMin) {
+ overallMin = lmin;
+ }
+ }
+ return overallMin;
+}
+
+const std::vector<Range<int>>&
+ AudioCapabilities::getInputChannelCountRanges() const {
+ return mInputChannelRanges;
+}
+
+// static
+std::shared_ptr<AudioCapabilities> AudioCapabilities::Create(std::string mediaType,
+ std::vector<ProfileLevel> profLevs, const sp<AMessage> &format) {
+ std::shared_ptr<AudioCapabilities> caps(new AudioCapabilities());
+ caps->init(mediaType, profLevs, format);
+ return caps;
+}
+
+void AudioCapabilities::init(std::string mediaType, std::vector<ProfileLevel> profLevs,
+ const sp<AMessage> &format) {
+ mMediaType = mediaType;
+ mProfileLevels = profLevs;
+
+ initWithPlatformLimits();
+ applyLevelLimits();
+ parseFromInfo(format);
+}
+
+void AudioCapabilities::initWithPlatformLimits() {
+ mBitrateRange = Range<int>(0, INT_MAX);
+ mInputChannelRanges.push_back(Range<int>(1, MAX_INPUT_CHANNEL_COUNT));
+
+ const int minSampleRate = base::GetIntProperty("ro.mediacodec.min_sample_rate", 7350);
+ const int maxSampleRate = base::GetIntProperty("ro.mediacodec.max_sample_rate", 192000);
+ mSampleRateRanges.push_back(Range<int>(minSampleRate, maxSampleRate));
+}
+
+bool AudioCapabilities::supports(int sampleRate, int inputChannels) {
+ // channels and sample rates are checked orthogonally
+ if (inputChannels != 0
+ && !std::any_of(mInputChannelRanges.begin(), mInputChannelRanges.end(),
+ [inputChannels](const Range<int> &a) { return a.contains(inputChannels); })) {
+ return false;
+ }
+ if (sampleRate != 0
+ && !std::any_of(mSampleRateRanges.begin(), mSampleRateRanges.end(),
+ [sampleRate](const Range<int> &a) { return a.contains(sampleRate); })) {
+ return false;
+ }
+ return true;
+}
+
+bool AudioCapabilities::isSampleRateSupported(int sampleRate) {
+ return supports(sampleRate, 0);
+}
+
+void AudioCapabilities::limitSampleRates(std::vector<int> rates) {
+ std::vector<Range<int>> sampleRateRanges;
+ std::sort(rates.begin(), rates.end());
+ for (int rate : rates) {
+ if (supports(rate, 0 /* channels */)) {
+ sampleRateRanges.push_back(Range<int>(rate, rate));
+ }
+ }
+ mSampleRateRanges = intersectSortedDistinctRanges(mSampleRateRanges, sampleRateRanges);
+ createDiscreteSampleRates();
+}
+
+void AudioCapabilities::createDiscreteSampleRates() {
+ mSampleRates.clear();
+ for (int i = 0; i < mSampleRateRanges.size(); i++) {
+ mSampleRates.push_back(mSampleRateRanges[i].lower());
+ }
+}
+
+void AudioCapabilities::limitSampleRates(std::vector<Range<int>> rateRanges) {
+ sortDistinctRanges(&rateRanges);
+ mSampleRateRanges = intersectSortedDistinctRanges(mSampleRateRanges, rateRanges);
+ // check if all values are discrete
+ for (Range<int> range: mSampleRateRanges) {
+ if (range.lower() != range.upper()) {
+ mSampleRates.clear();
+ return;
+ }
+ }
+ createDiscreteSampleRates();
+}
+
+void AudioCapabilities::applyLevelLimits() {
+ std::vector<int> sampleRates;
+ std::optional<Range<int>> sampleRateRange;
+ std::optional<Range<int>> bitRates;
+ int maxChannels = MAX_INPUT_CHANNEL_COUNT;
+
+ // const char *mediaType = mMediaType.c_str();
+ if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_MPEG)) {
+ sampleRates = {
+ 8000, 11025, 12000,
+ 16000, 22050, 24000,
+ 32000, 44100, 48000 };
+ bitRates = Range<int>(8000, 320000);
+ maxChannels = 2;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_NB)) {
+ sampleRates = { 8000 };
+ bitRates = Range<int>(4750, 12200);
+ maxChannels = 1;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_WB)) {
+ sampleRates = { 16000 };
+ bitRates = Range<int>(6600, 23850);
+ maxChannels = 1;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AAC)) {
+ sampleRates = {
+ 7350, 8000,
+ 11025, 12000, 16000,
+ 22050, 24000, 32000,
+ 44100, 48000, 64000,
+ 88200, 96000 };
+ bitRates = Range<int>(8000, 510000);
+ maxChannels = 48;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_VORBIS)) {
+ bitRates = Range<int>(32000, 500000);
+ sampleRateRange = Range<int>(8000, 192000);
+ maxChannels = 255;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_OPUS)) {
+ bitRates = Range<int>(6000, 510000);
+ sampleRates = { 8000, 12000, 16000, 24000, 48000 };
+ maxChannels = 255;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_RAW)) {
+ sampleRateRange = Range<int>(1, 192000);
+ bitRates = Range<int>(1, 10000000);
+ maxChannels = MAX_NUM_CHANNELS;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_FLAC)) {
+ sampleRateRange = Range<int>(1, 655350);
+ // lossless codec, so bitrate is ignored
+ maxChannels = 255;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_ALAW)
+ || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_MLAW)) {
+ sampleRates = { 8000 };
+ bitRates = Range<int>(64000, 64000);
+ // platform allows multiple channels for this format
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_MSGSM)) {
+ sampleRates = { 8000 };
+ bitRates = Range<int>(13000, 13000);
+ maxChannels = 1;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AC3)) {
+ maxChannels = 6;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_EAC3)) {
+ maxChannels = 16;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_EAC3_JOC)) {
+ sampleRates = { 48000 };
+ bitRates = Range<int>(32000, 6144000);
+ maxChannels = 16;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AC4)) {
+ sampleRates = { 44100, 48000, 96000, 192000 };
+ bitRates = Range<int>(16000, 2688000);
+ maxChannels = 24;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_DTS)) {
+ sampleRates = { 44100, 48000 };
+ bitRates = Range<int>(96000, 1524000);
+ maxChannels = 6;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_DTS_HD)) {
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ switch (profileLevel.mProfile) {
+ case DTS_HDProfileLBR:
+ sampleRates = { 22050, 24000, 44100, 48000 };
+ bitRates = Range<int>(32000, 768000);
+ break;
+ case DTS_HDProfileHRA:
+ case DTS_HDProfileMA:
+ sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range<int>(96000, 24500000);
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile,
+ mMediaType.c_str());
+ mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range<int>(96000, 24500000);
+ }
+ }
+ maxChannels = 8;
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_DTS_UHD)) {
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ switch (profileLevel.mProfile) {
+ case DTS_UHDProfileP2:
+ sampleRates = { 48000 };
+ bitRates = Range<int>(96000, 768000);
+ maxChannels = 10;
+ break;
+ case DTS_UHDProfileP1:
+ sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range<int>(96000, 24500000);
+ maxChannels = 32;
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile,
+ mMediaType.c_str());
+ mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ sampleRates = { 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range<int>(96000, 24500000);
+ maxChannels = 32;
+ }
+ }
+ } else {
+ ALOGW("Unsupported mediaType %s", mMediaType.c_str());
+ mError |= ERROR_CAPABILITIES_UNSUPPORTED;
+ }
+
+ // restrict ranges
+ if (!sampleRates.empty()) {
+ limitSampleRates(sampleRates);
+ } else if (sampleRateRange) {
+ std::vector<Range<int>> rateRanges = { sampleRateRange.value() };
+ limitSampleRates(rateRanges);
+ }
+
+ Range<int> channelRange = Range<int>(1, maxChannels);
+ std::vector<Range<int>> inputChannels = { channelRange };
+ applyLimits(inputChannels, bitRates);
+}
+
+void AudioCapabilities::applyLimits(
+ const std::vector<Range<int>> &inputChannels,
+ const std::optional<Range<int>> &bitRates) {
+ // clamp & make a local copy
+ std::vector<Range<int>> inputChannelsCopy(inputChannels.size());
+ for (int i = 0; i < inputChannels.size(); i++) {
+ int lower = inputChannels[i].clamp(1);
+ int upper = inputChannels[i].clamp(MAX_INPUT_CHANNEL_COUNT);
+ inputChannelsCopy[i] = Range<int>(lower, upper);
+ }
+
+ // sort, intersect with existing, & save channel list
+ sortDistinctRanges(&inputChannelsCopy);
+ mInputChannelRanges = intersectSortedDistinctRanges(inputChannelsCopy, mInputChannelRanges);
+
+ if (bitRates) {
+ mBitrateRange = mBitrateRange.intersect(bitRates.value());
+ }
+}
+
+void AudioCapabilities::parseFromInfo(const sp<AMessage> &format) {
+ int maxInputChannels = MAX_INPUT_CHANNEL_COUNT;
+ std::vector<Range<int>> channels = { Range<int>(1, maxInputChannels) };
+ std::optional<Range<int>> bitRates = POSITIVE_INTEGERS;
+
+ AString rateAString;
+ if (format->findString("sample-rate-ranges", &rateAString)) {
+ std::vector<std::string> rateStrings = base::Split(std::string(rateAString.c_str()), ",");
+ std::vector<Range<int>> rateRanges;
+ for (std::string rateString : rateStrings) {
+ std::optional<Range<int>> rateRange = ParseIntRange(rateString);
+ if (!rateRange) {
+ continue;
+ }
+ rateRanges.push_back(rateRange.value());
+ }
+ limitSampleRates(rateRanges);
+ }
+
+ // we will prefer channel-ranges over max-channel-count
+ AString valueStr;
+ if (format->findString("channel-ranges", &valueStr)) {
+ std::vector<std::string> channelStrings = base::Split(std::string(valueStr.c_str()), ",");
+ std::vector<Range<int>> channelRanges;
+ for (std::string channelString : channelStrings) {
+ std::optional<Range<int>> channelRange = ParseIntRange(channelString);
+ if (!channelRange) {
+ continue;
+ }
+ channelRanges.push_back(channelRange.value());
+ }
+ channels = channelRanges;
+ } else if (format->findString("channel-range", &valueStr)) {
+ std::optional<Range<int>> oneRange = ParseIntRange(std::string(valueStr.c_str()));
+ if (oneRange) {
+ channels = { oneRange.value() };
+ }
+ } else if (format->findString("max-channel-count", &valueStr)) {
+ maxInputChannels = std::atoi(valueStr.c_str());
+ if (maxInputChannels == 0) {
+ channels = { Range<int>(0, 0) };
+ } else {
+ channels = { Range<int>(1, maxInputChannels) };
+ }
+ } else if ((mError & ERROR_CAPABILITIES_UNSUPPORTED) != 0) {
+ maxInputChannels = 0;
+ channels = { Range<int>(0, 0) };
+ }
+
+ if (format->findString("bitrate-range", &valueStr)) {
+ std::optional<Range<int>> parsedBitrate = ParseIntRange(valueStr.c_str());
+ if (parsedBitrate) {
+ bitRates = bitRates.value().intersect(parsedBitrate.value());
+ }
+ }
+
+ applyLimits(channels, bitRates);
+}
+
+void AudioCapabilities::getDefaultFormat(sp<AMessage> &format) {
+ // report settings that have only a single choice
+ if (mBitrateRange.lower() == mBitrateRange.upper()) {
+ format->setInt32(KEY_BIT_RATE, mBitrateRange.lower());
+ }
+ if (getMaxInputChannelCount() == 1) {
+ // mono-only format
+ format->setInt32(KEY_CHANNEL_COUNT, 1);
+ }
+ if (!mSampleRates.empty() && mSampleRates.size() == 1) {
+ format->setInt32(KEY_SAMPLE_RATE, mSampleRates[0]);
+ }
+}
+
+bool AudioCapabilities::supportsFormat(const sp<AMessage> &format) {
+ int32_t sampleRate;
+ format->findInt32(KEY_SAMPLE_RATE, &sampleRate);
+ int32_t channels;
+ format->findInt32(KEY_CHANNEL_COUNT, &channels);
+
+ if (!supports(sampleRate, channels)) {
+ return false;
+ }
+
+ if (!CodecCapabilities::SupportsBitrate(mBitrateRange, format)) {
+ return false;
+ }
+
+ // nothing to do for:
+ // KEY_CHANNEL_MASK: codecs don't get this
+ // KEY_IS_ADTS: required feature for all AAC decoders
+ return true;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/media/libmedia/CodecCapabilities.cpp b/media/libmedia/CodecCapabilities.cpp
index db92c84..5bed1c4 100644
--- a/media/libmedia/CodecCapabilities.cpp
+++ b/media/libmedia/CodecCapabilities.cpp
@@ -25,4 +25,33 @@
namespace android {
+bool CodecCapabilities::SupportsBitrate(Range<int> bitrateRange,
+ const sp<AMessage> &format) {
+ // consider max bitrate over average bitrate for support
+ int32_t maxBitrate = 0;
+ format->findInt32(KEY_MAX_BIT_RATE, &maxBitrate);
+ int32_t bitrate = 0;
+ format->findInt32(KEY_BIT_RATE, &bitrate);
+
+ if (bitrate == 0) {
+ bitrate = maxBitrate;
+ } else if (maxBitrate != 0) {
+ bitrate = std::max(bitrate, maxBitrate);
+ }
+
+ if (bitrate > 0) {
+ return bitrateRange.contains(bitrate);
+ }
+
+ return true;
+}
+
+const std::string& CodecCapabilities::getMediaType() {
+ return mMediaType;
+}
+
+const std::vector<ProfileLevel>& CodecCapabilities::getProfileLevels() {
+ return mProfileLevels;
+}
+
} // namespace android
\ No newline at end of file
diff --git a/media/libmedia/CodecCapabilitiesUtils.cpp b/media/libmedia/CodecCapabilitiesUtils.cpp
index 7f1463e..edfc9be 100644
--- a/media/libmedia/CodecCapabilitiesUtils.cpp
+++ b/media/libmedia/CodecCapabilitiesUtils.cpp
@@ -20,6 +20,7 @@
#include <algorithm>
#include <cmath>
+#include <regex>
#include <string>
#include <vector>
@@ -29,4 +30,24 @@
namespace android {
+std::optional<Range<int>> ParseIntRange(const std::string &str) {
+ if (str.empty()) {
+ ALOGW("could not parse empty integer range");
+ return std::nullopt;
+ }
+ int lower, upper;
+ std::regex regex("([0-9]+)-([0-9]+)");
+ std::smatch match;
+ if (std::regex_match(str, match, regex)) {
+ lower = std::atoi(match[1].str().c_str());
+ upper = std::atoi(match[2].str().c_str());
+ } else if (std::atoi(str.c_str()) != 0) {
+ lower = upper = std::atoi(str.c_str());
+ } else {
+ ALOGW("could not parse integer range: %s", str.c_str());
+ return std::nullopt;
+ }
+ return std::make_optional<Range<int>>(lower, upper);
+}
+
} // namespace android
\ No newline at end of file
diff --git a/media/libmedia/include/media/AudioCapabilities.h b/media/libmedia/include/media/AudioCapabilities.h
new file mode 100644
index 0000000..2bc3335
--- /dev/null
+++ b/media/libmedia/include/media/AudioCapabilities.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2024, 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.
+ */
+
+#ifndef AUDIO_CAPABILITIES_H_
+
+#define AUDIO_CAPABILITIES_H_
+
+#include <media/CodecCapabilitiesUtils.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <system/audio.h>
+
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+struct AudioCapabilities {
+ /**
+ * Create AudioCapabilities.
+ */
+ static std::shared_ptr<AudioCapabilities> Create(std::string mediaType,
+ std::vector<ProfileLevel> profLevs, const sp<AMessage> &format);
+
+ /**
+ * Returns the range of supported bitrates in bits/second.
+ */
+ const Range<int>& getBitrateRange() const;
+
+ /**
+ * Returns the array of supported sample rates if the codec
+ * supports only discrete values. Otherwise, it returns an empty array.
+ * The array is sorted in ascending order.
+ */
+ const std::vector<int>& getSupportedSampleRates() const;
+
+ /**
+ * Returns the array of supported sample rate ranges. The
+ * array is sorted in ascending order, and the ranges are
+ * distinct.
+ */
+ const std::vector<Range<int>>& getSupportedSampleRateRanges() const;
+
+ /**
+ * Returns the maximum number of input channels supported.
+ * The returned value should be between 1 and 255.
+ *
+ * Through {@link android.os.Build.VERSION_CODES#R}, this method indicated support
+ * for any number of input channels between 1 and this maximum value.
+ *
+ * As of {@link android.os.Build.VERSION_CODES#S},
+ * the implied lower limit of 1 channel is no longer valid.
+ * As of {@link android.os.Build.VERSION_CODES#S}, {@link #getMaxInputChannelCount} is
+ * superseded by {@link #getInputChannelCountRanges},
+ * which returns an array of ranges of channels.
+ * The {@link #getMaxInputChannelCount} method will return the highest value
+ * in the ranges returned by {@link #getInputChannelCountRanges}
+ */
+ int getMaxInputChannelCount() const;
+
+ /**
+ * Returns the minimum number of input channels supported.
+ * This is often 1, but does vary for certain mime types.
+ *
+ * This returns the lowest channel count in the ranges returned by
+ * {@link #getInputChannelCountRanges}.
+ */
+ int getMinInputChannelCount() const;
+
+ /**
+ * Returns an array of ranges representing the number of input channels supported.
+ * The codec supports any number of input channels within this range.
+ *
+ * This supersedes the {@link #getMaxInputChannelCount} method.
+ *
+ * For many codecs, this will be a single range [1..N], for some N.
+ *
+ * The returned array cannot be empty.
+ */
+ const std::vector<Range<int>>& getInputChannelCountRanges() const;
+
+ /**
+ * Query whether the sample rate is supported by the codec.
+ */
+ bool isSampleRateSupported(int sampleRate);
+
+ /* For internal use only. Not exposed as a public API */
+ void getDefaultFormat(sp<AMessage> &format);
+
+ /* For internal use only. Not exposed as a public API */
+ bool supportsFormat(const sp<AMessage> &format);
+
+private:
+ static constexpr int MAX_INPUT_CHANNEL_COUNT = 30;
+ static constexpr uint32_t MAX_NUM_CHANNELS = FCC_LIMIT;
+
+ int mError;
+ std::string mMediaType;
+ std::vector<ProfileLevel> mProfileLevels;
+
+ Range<int> mBitrateRange;
+
+ std::vector<int> mSampleRates;
+ std::vector<Range<int>> mSampleRateRanges;
+ std::vector<Range<int>> mInputChannelRanges;
+
+ /* no public constructor */
+ AudioCapabilities() {}
+ void init(std::string mediaType, std::vector<ProfileLevel> profLevs,
+ const sp<AMessage> &format);
+ void initWithPlatformLimits();
+ bool supports(int sampleRate, int inputChannels);
+ void limitSampleRates(std::vector<int> rates);
+ void createDiscreteSampleRates();
+ void limitSampleRates(std::vector<Range<int>> rateRanges);
+ void applyLevelLimits();
+ void applyLimits(const std::vector<Range<int>> &inputChannels,
+ const std::optional<Range<int>> &bitRates);
+ void parseFromInfo(const sp<AMessage> &format);
+
+ friend struct CodecCapabilities;
+};
+
+} // namespace android
+
+#endif // AUDIO_CAPABILITIES_H_
\ No newline at end of file
diff --git a/media/libmedia/include/media/CodecCapabilities.h b/media/libmedia/include/media/CodecCapabilities.h
index 374f8bf..9d1c4ea 100644
--- a/media/libmedia/include/media/CodecCapabilities.h
+++ b/media/libmedia/include/media/CodecCapabilities.h
@@ -18,6 +18,7 @@
#define CODEC_CAPABILITIES_H_
+#include <media/AudioCapabilities.h>
#include <media/CodecCapabilitiesUtils.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -32,6 +33,25 @@
namespace android {
struct CodecCapabilities {
+
+ static bool SupportsBitrate(Range<int> bitrateRange,
+ const sp<AMessage> &format);
+
+ /**
+ * Returns the media type for which this codec-capability object was created.
+ */
+ const std::string& getMediaType();
+
+ /**
+ * Returns the supported profile levels.
+ */
+ const std::vector<ProfileLevel>& getProfileLevels();
+
+private:
+ std::string mMediaType;
+ std::vector<ProfileLevel> mProfileLevels;
+
+ std::shared_ptr<AudioCapabilities> mAudioCaps;
};
} // namespace android
diff --git a/media/libmedia/include/media/CodecCapabilitiesUtils.h b/media/libmedia/include/media/CodecCapabilitiesUtils.h
index 81cb43b..2bf822a 100644
--- a/media/libmedia/include/media/CodecCapabilitiesUtils.h
+++ b/media/libmedia/include/media/CodecCapabilitiesUtils.h
@@ -38,6 +38,147 @@
}
};
+/**
+ * Immutable class for describing the range of two numeric values.
+ *
+ * To make it immutable, all data are private and all functions are const.
+ *
+ * From frameworks/base/core/java/android/util/Range.java
+ */
+template<typename T>
+struct Range {
+ Range() : lower_(), upper_() {}
+
+ Range(T l, T u) : lower_(l), upper_(u) {}
+
+ constexpr bool empty() const { return lower_ > upper_; }
+
+ T lower() const { return lower_; }
+
+ T upper() const { return upper_; }
+
+ // Check if a value is in the range.
+ bool contains(T value) const {
+ return lower_ <= value && upper_ >= value;
+ }
+
+ bool contains(Range<T> range) const {
+ return (range.lower_ >= lower_) && (range.upper_ <= upper_);
+ }
+
+ // Clamp a value in the range
+ T clamp(T value) const{
+ if (value < lower_) {
+ return lower_;
+ } else if (value > upper_) {
+ return upper_;
+ } else {
+ return value;
+ }
+ }
+
+ // Return the intersected range
+ Range<T> intersect(Range<T> range) const {
+ if (lower_ >= range.lower() && range.upper() >= upper_) {
+ // range includes this
+ return *this;
+ } else if (range.lower() >= lower_ && range.upper() <= upper_) {
+ // this includes range
+ return range;
+ } else {
+ // if ranges are disjoint returns an empty Range(lower > upper)
+ Range<T> result = Range<T>(std::max(lower_, range.lower_),
+ std::min(upper_, range.upper_));
+ if (result.empty()) {
+ ALOGE("Failed to intersect 2 ranges as they are disjoint");
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Returns the intersection of this range and the inclusive range
+ * specified by {@code [lower, upper]}.
+ * <p>
+ * See {@link #intersect(Range)} for more details.</p>
+ *
+ * @param lower a non-{@code null} {@code T} reference
+ * @param upper a non-{@code null} {@code T} reference
+ * @return the intersection of this range and the other range
+ *
+ * @throws NullPointerException if {@code lower} or {@code upper} was {@code null}
+ * @throws IllegalArgumentException if the ranges are disjoint.
+ */
+ Range<T> intersect(T lower, T upper) {
+ return Range(std::max(lower_, lower), std::min(upper_, upper));
+ }
+
+private:
+ T lower_;
+ T upper_;
+};
+
+static const Range<int> POSITIVE_INTEGERS = Range<int>(1, INT_MAX);
+
+// found stuff that is not supported by framework (=> this should not happen)
+constexpr int ERROR_CAPABILITIES_UNRECOGNIZED = (1 << 0);
+// found profile/level for which we don't have capability estimates
+constexpr int ERROR_CAPABILITIES_UNSUPPORTED = (1 << 1);
+// have not found any profile/level for which we don't have capability estimate
+// constexpr int ERROR_NONE_SUPPORTED = (1 << 2);
+
+/**
+ * Sorts distinct (non-intersecting) range array in ascending order.
+ * From frameworks/base/media/java/android/media/Utils.java
+ */
+template<typename T>
+void sortDistinctRanges(std::vector<Range<T>> *ranges) {
+ std::sort(ranges->begin(), ranges->end(),
+ [](Range<T> r1, Range<T> r2) {
+ if (r1.upper() < r2.lower()) {
+ return true;
+ } else if (r1.lower() > r2.upper()) {
+ return false;
+ } else {
+ ALOGE("sample rate ranges must be distinct.");
+ return false;
+ }
+ });
+}
+
+/**
+ * Returns the intersection of two sets of non-intersecting ranges
+ * From frameworks/base/media/java/android/media/Utils.java
+ * @param one a sorted set of non-intersecting ranges in ascending order
+ * @param another another sorted set of non-intersecting ranges in ascending order
+ * @return the intersection of the two sets, sorted in ascending order
+ */
+template<typename T>
+std::vector<Range<T>> intersectSortedDistinctRanges(
+ const std::vector<Range<T>> &one, const std::vector<Range<T>> &another) {
+ std::vector<Range<T>> result;
+ int ix = 0;
+ for (Range<T> range : another) {
+ while (ix < one.size() && one[ix].upper() < range.lower()) {
+ ++ix;
+ }
+ while (ix < one.size() && one[ix].upper() < range.upper()) {
+ result.push_back(range.intersect(one[ix]));
+ ++ix;
+ }
+ if (ix == one.size()) {
+ break;
+ }
+ if (one[ix].lower() <= range.upper()) {
+ result.push_back(range.intersect(one[ix]));
+ }
+ }
+ return result;
+}
+
+// parse string into int range
+std::optional<Range<int>> ParseIntRange(const std::string &str);
+
} // namespace android
#endif // CODEC_CAPABILITIES__UTILS_H_
\ No newline at end of file
diff --git a/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp b/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp
index 79a33c1..89c9739 100644
--- a/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp
+++ b/media/libmedia/tests/codeccapabilities/CodecCapabilitiesTest.cpp
@@ -34,4 +34,117 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AString.h>
-using namespace android;
\ No newline at end of file
+using namespace android;
+
+class AudioCapsAacTest : public testing::Test {
+protected:
+ AudioCapsAacTest() {
+ std::string mediaType = MIMETYPE_AUDIO_AAC;
+
+ sp<AMessage> details = new AMessage;
+ details->setString("bitrate-range", "8000-960000");
+ details->setString("max-channel-count", "8");
+ details->setString("sample-rate-ranges",
+ "7350,8000,11025,12000,16000,22050,24000,32000,44100,48000");
+
+ std::vector<ProfileLevel> profileLevel{
+ ProfileLevel(2, 0),
+ ProfileLevel(5, 0),
+ ProfileLevel(29, 0),
+ ProfileLevel(23, 0),
+ ProfileLevel(39, 0),
+ ProfileLevel(20, 0),
+ ProfileLevel(42, 0),
+ };
+
+ audioCaps = AudioCapabilities::Create(mediaType, profileLevel, details);
+ }
+
+ std::shared_ptr<AudioCapabilities> audioCaps;
+};
+
+TEST_F(AudioCapsAacTest, AudioCaps_Aac_Bitrate) {
+ const Range<int>& bitrateRange = audioCaps->getBitrateRange();
+ EXPECT_EQ(bitrateRange.lower(), 8000) << "bitrate range1 does not match. lower: "
+ << bitrateRange.lower();
+ EXPECT_EQ(bitrateRange.upper(), 510000) << "bitrate range1 does not match. upper: "
+ << bitrateRange.upper();
+}
+
+TEST_F(AudioCapsAacTest, AudioCaps_Aac_InputChannelCount) {
+ int maxInputChannelCount = audioCaps->getMaxInputChannelCount();
+ EXPECT_EQ(maxInputChannelCount, 8);
+ int minInputChannelCount = audioCaps->getMinInputChannelCount();
+ EXPECT_EQ(minInputChannelCount, 1);
+}
+
+TEST_F(AudioCapsAacTest, AudioCaps_Aac_SupportedSampleRates) {
+ const std::vector<int>& sampleRates = audioCaps->getSupportedSampleRates();
+ EXPECT_EQ(sampleRates, std::vector<int>({7350, 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000}));
+
+ EXPECT_FALSE(audioCaps->isSampleRateSupported(6000))
+ << "isSampleRateSupported returned true for unsupported sample rate";
+ EXPECT_TRUE(audioCaps->isSampleRateSupported(8000))
+ << "isSampleRateSupported returned false for supported sample rate";
+ EXPECT_TRUE(audioCaps->isSampleRateSupported(12000))
+ << "isSampleRateSupported returned false for supported sample rate";
+ EXPECT_FALSE(audioCaps->isSampleRateSupported(44000))
+ << "isSampleRateSupported returned true for unsupported sample rate";
+ EXPECT_TRUE(audioCaps->isSampleRateSupported(48000))
+ << "isSampleRateSupported returned true for unsupported sample rate";
+}
+
+class AudioCapsRawTest : public testing::Test {
+protected:
+ AudioCapsRawTest() {
+ std::string mediaType = MIMETYPE_AUDIO_RAW;
+
+ sp<AMessage> details = new AMessage;
+ details->setString("bitrate-range", "1-10000000");
+ details->setString("channel-ranges", "1,2,3,4,5,6,7,8,9,10,11,12");
+ details->setString("sample-rate-ranges", "8000-192000");
+
+ std::vector<ProfileLevel> profileLevel;
+
+ audioCaps = AudioCapabilities::Create(mediaType, profileLevel, details);
+ }
+
+ std::shared_ptr<AudioCapabilities> audioCaps;
+};
+
+TEST_F(AudioCapsRawTest, AudioCaps_Raw_Bitrate) {
+ const Range<int>& bitrateRange = audioCaps->getBitrateRange();
+ EXPECT_EQ(bitrateRange.lower(), 1);
+ EXPECT_EQ(bitrateRange.upper(), 10000000);
+}
+
+TEST_F(AudioCapsRawTest, AudioCaps_Raw_InputChannelCount) {
+ int maxInputChannelCount = audioCaps->getMaxInputChannelCount();
+ EXPECT_EQ(maxInputChannelCount, 12);
+ int minInputChannelCount = audioCaps->getMinInputChannelCount();
+ EXPECT_EQ(minInputChannelCount, 1);
+}
+
+TEST_F(AudioCapsRawTest, AudioCaps_Raw_InputChannelCountRanges) {
+ const std::vector<Range<int>>& inputChannelCountRanges
+ = audioCaps->getInputChannelCountRanges();
+ std::vector<Range<int>> expectedOutput({{1,1}, {2,2}, {3,3}, {4,4}, {5,5},
+ {6,6}, {7,7}, {8,8}, {9,9}, {10,10}, {11,11}, {12,12}});
+ ASSERT_EQ(inputChannelCountRanges.size(), expectedOutput.size());
+ for (int i = 0; i < inputChannelCountRanges.size(); i++) {
+ EXPECT_EQ(inputChannelCountRanges.at(i).lower(), expectedOutput.at(i).lower());
+ EXPECT_EQ(inputChannelCountRanges.at(i).upper(), expectedOutput.at(i).upper());
+ }
+}
+
+TEST_F(AudioCapsRawTest, AudioCaps_Raw_SupportedSampleRates) {
+ const std::vector<Range<int>>& sampleRateRanges = audioCaps->getSupportedSampleRateRanges();
+ EXPECT_EQ(sampleRateRanges.size(), 1);
+ EXPECT_EQ(sampleRateRanges.at(0).lower(), 8000);
+ EXPECT_EQ(sampleRateRanges.at(0).upper(), 192000);
+
+ EXPECT_EQ(audioCaps->isSampleRateSupported(7000), false);
+ EXPECT_EQ(audioCaps->isSampleRateSupported(10000), true);
+ EXPECT_EQ(audioCaps->isSampleRateSupported(193000), false);
+}
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
index 4e4aa75..b1cf665 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -562,6 +562,30 @@
}
}
+inline constexpr int32_t DTS_HDProfileHRA = 0x1;
+inline constexpr int32_t DTS_HDProfileLBR = 0x2;
+inline constexpr int32_t DTS_HDProfileMA = 0x4;
+
+inline static const char *asString_Dts_HDProfile(int32_t i, const char *def = "??") {
+ switch (i) {
+ case DTS_HDProfileHRA: return "HRA";
+ case DTS_HDProfileLBR: return "LBR";
+ case DTS_HDProfileMA: return "MA";
+ default: return def;
+ }
+}
+
+inline constexpr int32_t DTS_UHDProfileP1 = 0x1;
+inline constexpr int32_t DTS_UHDProfileP2 = 0x2;
+
+inline static const char *asString_Dts_UHDProfile(int32_t i, const char *def = "??") {
+ switch (i) {
+ case DTS_UHDProfileP1: return "P1";
+ case DTS_UHDProfileP2: return "P2";
+ default: return def;
+ }
+}
+
inline constexpr int32_t BITRATE_MODE_CBR = 2;
inline constexpr int32_t BITRATE_MODE_CBR_FD = 3;
inline constexpr int32_t BITRATE_MODE_CQ = 0;
@@ -729,8 +753,13 @@
inline constexpr char MIMETYPE_AUDIO_FLAC[] = "audio/flac";
inline constexpr char MIMETYPE_AUDIO_MSGSM[] = "audio/gsm";
inline constexpr char MIMETYPE_AUDIO_AC3[] = "audio/ac3";
+inline constexpr char MIMETYPE_AUDIO_AC4[] = "audio/ac4";
inline constexpr char MIMETYPE_AUDIO_EAC3[] = "audio/eac3";
+inline constexpr char MIMETYPE_AUDIO_EAC3_JOC[] = "audio/eac3-joc";
inline constexpr char MIMETYPE_AUDIO_SCRAMBLED[] = "audio/scrambled";
+inline constexpr char MIMETYPE_AUDIO_DTS[] = "audio/vnd.dts";
+inline constexpr char MIMETYPE_AUDIO_DTS_HD[] = "audio/vnd.dts.hd";
+inline constexpr char MIMETYPE_AUDIO_DTS_UHD[] = "audio/vnd.dts.uhd";
inline constexpr char MIMETYPE_IMAGE_ANDROID_HEIC[] = "image/vnd.android.heic";