CodecCapabilities: Port VideoCapabilities from Java to Native.
Test: atest VideoCapsHevcTest
Bug: b/306023029
Change-Id: If849e4bb6ded7c70af294c4b8fe8e3f79d378394
diff --git a/media/libmedia/VideoCapabilities.cpp b/media/libmedia/VideoCapabilities.cpp
new file mode 100644
index 0000000..bd26b8c
--- /dev/null
+++ b/media/libmedia/VideoCapabilities.cpp
@@ -0,0 +1,1702 @@
+/*
+ * 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 "VideoCapabilities"
+
+#include <android-base/strings.h>
+
+#include <media/CodecCapabilities.h>
+#include <media/VideoCapabilities.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaCodecConstants.h>
+
+#include <utils/Errors.h>
+
+namespace android {
+
+static const Range<int64_t> POSITIVE_INT64 = Range((int64_t)1, INT64_MAX);
+static const Range<int32_t> BITRATE_RANGE = Range<int32_t>(0, 500000000);
+static const Range<int32_t> FRAME_RATE_RANGE = Range<int32_t>(0, 960);
+static const Range<Rational> POSITIVE_RATIONALS =
+ Range<Rational>(Rational((int32_t)1, INT32_MAX), Rational(INT32_MAX, (int32_t)1));
+
+const Range<int32_t>& VideoCapabilities::getBitrateRange() const {
+ return mBitrateRange;
+}
+
+const Range<int32_t>& VideoCapabilities::getSupportedWidths() const {
+ return mWidthRange;
+}
+
+const Range<int32_t>& VideoCapabilities::getSupportedHeights() const {
+ return mHeightRange;
+}
+
+int32_t VideoCapabilities::getWidthAlignment() const {
+ return mWidthAlignment;
+}
+
+int32_t VideoCapabilities::getHeightAlignment() const {
+ return mHeightAlignment;
+}
+
+int32_t VideoCapabilities::getSmallerDimensionUpperLimit() const {
+ return mSmallerDimensionUpperLimit;
+}
+
+const Range<int32_t>& VideoCapabilities::getSupportedFrameRates() const {
+ return mFrameRateRange;
+}
+
+std::optional<Range<int32_t>> VideoCapabilities::getSupportedWidthsFor(int32_t height) const {
+ Range<int32_t> range = mWidthRange;
+ if (!mHeightRange.contains(height)
+ || (height % mHeightAlignment) != 0) {
+ ALOGE("unsupported height");
+ return std::nullopt;
+ }
+ const int32_t heightInBlocks = divUp(height, mBlockHeight);
+
+ // constrain by block count and by block aspect ratio
+ const int32_t minWidthInBlocks = std::max(
+ divUp(mBlockCountRange.lower(), heightInBlocks),
+ (int32_t)std::ceil(mBlockAspectRatioRange.lower().asDouble()
+ * heightInBlocks));
+ const int32_t maxWidthInBlocks = std::min(
+ mBlockCountRange.upper() / heightInBlocks,
+ (int32_t)(mBlockAspectRatioRange.upper().asDouble()
+ * heightInBlocks));
+ range = range.intersect(
+ (minWidthInBlocks - 1) * mBlockWidth + mWidthAlignment,
+ maxWidthInBlocks * mBlockWidth);
+
+ // constrain by smaller dimension limit
+ if (height > mSmallerDimensionUpperLimit) {
+ range = range.intersect(1, mSmallerDimensionUpperLimit);
+ }
+
+ // constrain by aspect ratio
+ range = range.intersect(
+ (int32_t)std::ceil(mAspectRatioRange.lower().asDouble()
+ * height),
+ (int32_t)(mAspectRatioRange.upper().asDouble() * height));
+ return range;
+}
+
+std::optional<Range<int32_t>> VideoCapabilities::getSupportedHeightsFor(int32_t width) const {
+ Range<int32_t> range = mHeightRange;
+ if (!mWidthRange.contains(width)
+ || (width % mWidthAlignment) != 0) {
+ ALOGE("unsupported width");
+ return std::nullopt;
+ }
+ const int32_t widthInBlocks = divUp(width, mBlockWidth);
+
+ // constrain by block count and by block aspect ratio
+ const int32_t minHeightInBlocks = std::max(
+ divUp(mBlockCountRange.lower(), widthInBlocks),
+ (int32_t)std::ceil(widthInBlocks /
+ mBlockAspectRatioRange.upper().asDouble()));
+ const int32_t maxHeightInBlocks = std::min(
+ mBlockCountRange.upper() / widthInBlocks,
+ (int32_t)(widthInBlocks /
+ mBlockAspectRatioRange.lower().asDouble()));
+ range = range.intersect(
+ (minHeightInBlocks - 1) * mBlockHeight + mHeightAlignment,
+ maxHeightInBlocks * mBlockHeight);
+
+ // constrain by smaller dimension limit
+ if (width > mSmallerDimensionUpperLimit) {
+ range = range.intersect(1, mSmallerDimensionUpperLimit);
+ }
+
+ // constrain by aspect ratio
+ range = range.intersect(
+ (int32_t)std::ceil(width /
+ mAspectRatioRange.upper().asDouble()),
+ (int32_t)(width / mAspectRatioRange.lower().asDouble()));
+ return range;
+}
+
+std::optional<Range<double>> VideoCapabilities::getSupportedFrameRatesFor(
+ int32_t width, int32_t height) const {
+ if (!supports(std::make_optional<int32_t>(width), std::make_optional<int32_t>(height),
+ std::nullopt /* rate */)) {
+ ALOGE("Unsupported size. width: %d, height: %d", width, height);
+ return std::nullopt;
+ }
+
+ const int32_t blockCount =
+ divUp(width, mBlockWidth) * divUp(height, mBlockHeight);
+
+ return std::make_optional(Range(
+ std::max(mBlocksPerSecondRange.lower() / (double) blockCount,
+ (double) mFrameRateRange.lower()),
+ std::min(mBlocksPerSecondRange.upper() / (double) blockCount,
+ (double) mFrameRateRange.upper())));
+}
+
+int32_t VideoCapabilities::getBlockCount(int32_t width, int32_t height) const {
+ return divUp(width, mBlockWidth) * divUp(height, mBlockHeight);
+}
+
+std::optional<VideoSize> VideoCapabilities::findClosestSize(
+ int32_t width, int32_t height) const {
+ int32_t targetBlockCount = getBlockCount(width, height);
+ std::optional<VideoSize> closestSize;
+ int32_t minDiff = INT32_MAX;
+ for (const auto &[size, range] : mMeasuredFrameRates) {
+ int32_t diff = std::abs(targetBlockCount -
+ getBlockCount(size.getWidth(), size.getHeight()));
+ if (diff < minDiff) {
+ minDiff = diff;
+ closestSize = size;
+ }
+ }
+ return closestSize;
+}
+
+std::optional<Range<double>> VideoCapabilities::estimateFrameRatesFor(
+ int32_t width, int32_t height) const {
+ std::optional<VideoSize> size = findClosestSize(width, height);
+ if (!size) {
+ return std::nullopt;
+ }
+ auto rangeItr = mMeasuredFrameRates.find(size.value());
+ if (rangeItr == mMeasuredFrameRates.end()) {
+ return std::nullopt;
+ }
+ Range<int64_t> range = rangeItr->second;
+ double ratio = getBlockCount(size.value().getWidth(), size.value().getHeight())
+ / (double)std::max(getBlockCount(width, height), 1);
+ return std::make_optional(Range(range.lower() * ratio, range.upper() * ratio));
+}
+
+std::optional<Range<double>> VideoCapabilities::getAchievableFrameRatesFor(
+ int32_t width, int32_t height) const {
+ if (!supports(std::make_optional<int32_t>(width), std::make_optional<int32_t>(height),
+ std::nullopt /* rate */)) {
+ ALOGE("Unsupported size. width: %d, height: %d", width, height);
+ return std::nullopt;
+ }
+
+ if (mMeasuredFrameRates.empty()) {
+ ALOGW("Codec did not publish any measurement data.");
+ return std::nullopt;
+ }
+
+ return estimateFrameRatesFor(width, height);
+}
+
+// VideoCapabilities::PerformancePoint
+
+int32_t VideoCapabilities::PerformancePoint::getMaxMacroBlocks() const {
+ return saturateInt64ToInt32(mWidth * (int64_t)mHeight);
+}
+
+int32_t VideoCapabilities::PerformancePoint::getWidth() const {
+ return mWidth;
+}
+
+int32_t VideoCapabilities::PerformancePoint::getHeight() const {
+ return mHeight;
+}
+
+int32_t VideoCapabilities::PerformancePoint::getMaxFrameRate() const {
+ return mMaxFrameRate;
+}
+
+int64_t VideoCapabilities::PerformancePoint::getMaxMacroBlockRate() const {
+ return mMaxMacroBlockRate;
+}
+
+VideoSize VideoCapabilities::PerformancePoint::getBlockSize() const {
+ return mBlockSize;
+}
+
+std::string VideoCapabilities::PerformancePoint::toString() const {
+ int64_t blockWidth = 16 * (int64_t)mBlockSize.getWidth();
+ int64_t blockHeight = 16 * (int64_t)mBlockSize.getHeight();
+ int32_t origRate = (int32_t)divUp(mMaxMacroBlockRate, (int64_t)getMaxMacroBlocks());
+ std::string info = std::to_string(mWidth * (int64_t)16) + "x"
+ + std::to_string(mHeight * (int64_t)16) + "@" + std::to_string(origRate);
+ if (origRate < mMaxFrameRate) {
+ info += ", max " + std::to_string(mMaxFrameRate) + "fps";
+ }
+ if (blockWidth > 16 || blockHeight > 16) {
+ info += ", " + std::to_string(blockWidth) + "x"
+ + std::to_string(blockHeight) + " blocks";
+ }
+ return "PerformancePoint(" + info + ")";
+}
+
+void VideoCapabilities::PerformancePoint::init(int32_t width, int32_t height,
+ int32_t frameRate, int32_t maxFrameRate, VideoSize blockSize) {
+ mBlockSize = VideoSize(divUp(blockSize.getWidth(), (int32_t)16),
+ divUp(blockSize.getHeight(), (int32_t)16));
+ // Use IsPowerOfTwoStrict as we do not want width and height to be 0;
+ if (!IsPowerOfTwoStrict(blockSize.getWidth()) || !IsPowerOfTwoStrict(blockSize.getHeight())) {
+ ALOGE("The width and height of a PerformancePoint must be the power of two and not zero."
+ " width: %d, height: %d", blockSize.getWidth(), blockSize.getHeight());
+ }
+
+ // these are guaranteed not to overflow as we decimate by 16
+ mWidth = (int32_t)(divUp(std::max(width, 1),
+ std::max(blockSize.getWidth(), 16))
+ * mBlockSize.getWidth());
+ mHeight = (int32_t)(divUp(std::max(height, 1),
+ std::max(blockSize.getHeight(), 16))
+ * mBlockSize.getHeight());
+ mMaxFrameRate = std::max(std::max(frameRate, maxFrameRate), 1);
+ mMaxMacroBlockRate = std::max(frameRate, 1) * (int64_t)getMaxMacroBlocks();
+}
+
+VideoCapabilities::PerformancePoint::PerformancePoint(int32_t width, int32_t height,
+ int32_t frameRate, int32_t maxFrameRate, VideoSize blockSize) {
+ init(width, height, frameRate, maxFrameRate, blockSize);
+}
+
+VideoCapabilities::PerformancePoint::PerformancePoint(VideoSize blockSize, int32_t width,
+ int32_t height, int32_t maxFrameRate, int64_t maxMacroBlockRate) :
+ mBlockSize(blockSize), mWidth(width), mHeight(height), mMaxFrameRate(maxFrameRate),
+ mMaxMacroBlockRate(maxMacroBlockRate) {}
+
+VideoCapabilities::PerformancePoint::PerformancePoint(
+ const PerformancePoint &pp, VideoSize newBlockSize) {
+ init(16 * pp.mWidth, 16 * pp.mHeight,
+ // guaranteed not to overflow as these were multiplied at construction
+ (int32_t)divUp(pp.mMaxMacroBlockRate, (int64_t)pp.getMaxMacroBlocks()),
+ pp.mMaxFrameRate,
+ VideoSize(std::max(newBlockSize.getWidth(), 16 * pp.mBlockSize.getWidth()),
+ std::max(newBlockSize.getHeight(), 16 * pp.mBlockSize.getHeight())));
+}
+
+VideoCapabilities::PerformancePoint::PerformancePoint(
+ int32_t width, int32_t height, int32_t frameRate) {
+ init(width, height, frameRate, frameRate /* maxFrameRate */, VideoSize(16, 16));
+}
+
+int32_t VideoCapabilities::PerformancePoint::saturateInt64ToInt32(int64_t value) const {
+ if (value < INT32_MIN) {
+ return INT32_MIN;
+ } else if (value > INT32_MAX) {
+ return INT32_MAX;
+ } else {
+ return (int32_t)value;
+ }
+}
+
+/* This method may overflow */
+int32_t VideoCapabilities::PerformancePoint::align(
+ int32_t value, int32_t alignment) const {
+ return divUp(value, alignment) * alignment;
+}
+
+bool VideoCapabilities::PerformancePoint::covers(
+ const sp<AMessage> &format) const {
+ int32_t width, height;
+ format->findInt32(KEY_WIDTH, &width);
+ format->findInt32(KEY_HEIGHT, &height);
+ double frameRate;
+ format->findDouble(KEY_FRAME_RATE, &frameRate);
+ PerformancePoint other = PerformancePoint(
+ width, height,
+ // safely convert ceil(double) to int through float cast and std::round
+ std::round((float)(std::ceil(frameRate)))
+ );
+ return covers(other);
+}
+
+bool VideoCapabilities::PerformancePoint::covers(
+ const PerformancePoint &other) const {
+ // convert performance points to common block size
+ VideoSize commonSize = getCommonBlockSize(other);
+ PerformancePoint aligned = PerformancePoint(*this, commonSize);
+ PerformancePoint otherAligned = PerformancePoint(other, commonSize);
+
+ return (aligned.getMaxMacroBlocks() >= otherAligned.getMaxMacroBlocks()
+ && aligned.mMaxFrameRate >= otherAligned.mMaxFrameRate
+ && aligned.mMaxMacroBlockRate >= otherAligned.mMaxMacroBlockRate);
+}
+
+VideoSize VideoCapabilities::PerformancePoint::getCommonBlockSize(
+ const PerformancePoint &other) const {
+ return VideoSize(
+ 16 * std::max(mBlockSize.getWidth(), other.mBlockSize.getWidth()),
+ 16 * std::max(mBlockSize.getHeight(), other.mBlockSize.getHeight()));
+}
+
+bool VideoCapabilities::PerformancePoint::equals(
+ const PerformancePoint &other) const {
+ // convert performance points to common block size
+ VideoSize commonSize = getCommonBlockSize(other);
+ PerformancePoint aligned = PerformancePoint(*this, commonSize);
+ PerformancePoint otherAligned = PerformancePoint(other, commonSize);
+
+ return (aligned.getMaxMacroBlocks() == otherAligned.getMaxMacroBlocks()
+ && aligned.mMaxFrameRate == otherAligned.mMaxFrameRate
+ && aligned.mMaxMacroBlockRate == otherAligned.mMaxMacroBlockRate);
+}
+
+// VideoCapabilities
+
+const std::vector<VideoCapabilities::PerformancePoint>&
+ VideoCapabilities::getSupportedPerformancePoints() const {
+ return mPerformancePoints;
+}
+
+bool VideoCapabilities::areSizeAndRateSupported(
+ int32_t width, int32_t height, double frameRate) const {
+ return supports(std::make_optional<int32_t>(width), std::make_optional<int32_t>(height),
+ std::make_optional<double>(frameRate));
+}
+
+bool VideoCapabilities::isSizeSupported(int32_t width, int32_t height) const {
+ return supports(std::make_optional<int32_t>(width), std::make_optional<int32_t>(height),
+ std::nullopt /* rate */);
+}
+
+bool VideoCapabilities::supports(std::optional<int32_t> width, std::optional<int32_t> height,
+ std::optional<double> rate) const {
+ bool ok = true;
+
+ if (width) {
+ ok &= mWidthRange.contains(width.value())
+ && (width.value() % mWidthAlignment == 0);
+ }
+ if (height) {
+ ok &= mHeightRange.contains(height.value())
+ && (height.value() % mHeightAlignment == 0);
+ }
+ if (rate) {
+ ok &= mFrameRateRange.contains(Range<int32_t>::RangeFor(rate.value()));
+ }
+ if (height && width) {
+ ok &= std::min(height.value(), width.value()) <= mSmallerDimensionUpperLimit;
+
+ const int32_t widthInBlocks = divUp(width.value(), mBlockWidth);
+ const int32_t heightInBlocks = divUp(height.value(), mBlockHeight);
+ const int32_t blockCount = widthInBlocks * heightInBlocks;
+ ok &= mBlockCountRange.contains(blockCount)
+ && mBlockAspectRatioRange.contains(
+ Rational(widthInBlocks, heightInBlocks))
+ && mAspectRatioRange.contains(Rational(width.value(), height.value()));
+ if (rate) {
+ double blocksPerSec = blockCount * rate.value();
+ ok &= mBlocksPerSecondRange.contains(
+ Range<int64_t>::RangeFor(blocksPerSec));
+ }
+ }
+ return ok;
+}
+
+bool VideoCapabilities::supportsFormat(const sp<AMessage> &format) const {
+ int32_t widthVal, heightVal;
+ std::optional<int32_t> width = format->findInt32(KEY_WIDTH, &widthVal)
+ ? std::make_optional<int32_t>(widthVal) : std::nullopt;
+ std::optional<int32_t> height = format->findInt32(KEY_HEIGHT, &heightVal)
+ ? std::make_optional<int32_t>(heightVal) : std::nullopt;
+ double rateVal;
+ std::optional<double> rate = format->findDouble(KEY_FRAME_RATE, &rateVal)
+ ? std::make_optional<double>(rateVal) : std::nullopt;
+
+ if (!supports(width, height, rate)) {
+ return false;
+ }
+
+ if (!CodecCapabilities::SupportsBitrate(mBitrateRange, format)) {
+ return false;
+ }
+
+ // we ignore color-format for now as it is not reliably reported by codec
+ return true;
+}
+
+// static
+std::shared_ptr<VideoCapabilities> VideoCapabilities::Create(std::string mediaType,
+ std::vector<ProfileLevel> profLevs, const sp<AMessage> &format) {
+ std::shared_ptr<VideoCapabilities> caps(new VideoCapabilities());
+ caps->init(mediaType, profLevs, format);
+ return caps;
+}
+
+void VideoCapabilities::init(std::string mediaType, std::vector<ProfileLevel> profLevs,
+ const sp<AMessage> &format) {
+ mMediaType = mediaType;
+ mProfileLevels = profLevs;
+ mError = 0;
+
+ initWithPlatformLimits();
+ applyLevelLimits();
+ parseFromInfo(format);
+ updateLimits();
+}
+
+VideoSize VideoCapabilities::getBlockSize() const {
+ return VideoSize(mBlockWidth, mBlockHeight);
+}
+
+const Range<int32_t>& VideoCapabilities::getBlockCountRange() const {
+ return mBlockCountRange;
+}
+
+const Range<int64_t>& VideoCapabilities::getBlocksPerSecondRange() const {
+ return mBlocksPerSecondRange;
+}
+
+Range<Rational> VideoCapabilities::getAspectRatioRange(bool blocks) const {
+ return blocks ? mBlockAspectRatioRange : mAspectRatioRange;
+}
+
+void VideoCapabilities::initWithPlatformLimits() {
+ mBitrateRange = BITRATE_RANGE;
+
+ mWidthRange = VideoSize::GetAllowedDimensionRange();
+ mHeightRange = VideoSize::GetAllowedDimensionRange();
+ mFrameRateRange = FRAME_RATE_RANGE;
+
+ mHorizontalBlockRange = VideoSize::GetAllowedDimensionRange();
+ mVerticalBlockRange = VideoSize::GetAllowedDimensionRange();
+
+ // full positive ranges are supported as these get calculated
+ mBlockCountRange = POSITIVE_INT32;
+ mBlocksPerSecondRange = POSITIVE_INT64;
+
+ mBlockAspectRatioRange = POSITIVE_RATIONALS;
+ mAspectRatioRange = POSITIVE_RATIONALS;
+
+ // YUV 4:2:0 requires 2:2 alignment
+ mWidthAlignment = 2;
+ mHeightAlignment = 2;
+ mBlockWidth = 2;
+ mBlockHeight = 2;
+ mSmallerDimensionUpperLimit = VideoSize::GetAllowedDimensionRange().upper();
+}
+
+std::vector<VideoCapabilities::PerformancePoint>
+ VideoCapabilities::getPerformancePoints(
+ const sp<AMessage> &format) const {
+ std::vector<PerformancePoint> ret;
+ AMessage::Type type;
+ for (int i = 0; i < format->countEntries(); i++) {
+ const char *name = format->getEntryNameAt(i, &type);
+ AString rangeStr;
+ if (!format->findString(name, &rangeStr)) {
+ continue;
+ }
+
+ const std::string key = std::string(name);
+ // looking for: performance-point-WIDTHxHEIGHT-range
+
+ // check none performance point
+ if (key == "performance-point-none" && ret.size() == 0) {
+ // This means that component knowingly did not publish performance points.
+ // This is different from when the component forgot to publish performance
+ // points.
+ return ret;
+ }
+
+ // parse size from key
+ std::regex sizeRegex("performance-point-(.+)-range");
+ std::smatch sizeMatch;
+ if (!std::regex_match(key, sizeMatch, sizeRegex)) {
+ continue;
+ }
+ std::optional<VideoSize> size = VideoSize::ParseSize(sizeMatch[1].str());
+ if (!size || size.value().getWidth() * size.value().getHeight() <= 0) {
+ continue;
+ }
+
+ // parse range from value
+ std::optional<Range<int64_t>> range = Range<int64_t>::Parse(std::string(rangeStr.c_str()));
+ if (!range || range.value().lower() < 0 || range.value().upper() < 0) {
+ continue;
+ }
+
+ PerformancePoint given = PerformancePoint(
+ size.value().getWidth(), size.value().getHeight(), (int32_t)range.value().lower(),
+ (int32_t)range.value().upper(), VideoSize(mBlockWidth, mBlockHeight));
+ PerformancePoint rotated = PerformancePoint(
+ size.value().getHeight(), size.value().getWidth(), (int32_t)range.value().lower(),
+ (int32_t)range.value().upper(), VideoSize(mBlockWidth, mBlockHeight));
+ ret.push_back(given);
+ if (!given.covers(rotated)) {
+ ret.push_back(rotated);
+ }
+ }
+
+ // check if the component specified no performance point indication
+ if (ret.size() == 0) {
+ return ret;
+ }
+
+ // sort reversed by area first, then by frame rate
+ std::sort(ret.begin(), ret.end(), [](const PerformancePoint &a, const PerformancePoint &b) {
+ return -((a.getMaxMacroBlocks() != b.getMaxMacroBlocks()) ?
+ (a.getMaxMacroBlocks() < b.getMaxMacroBlocks() ? -1 : 1) :
+ (a.getMaxMacroBlockRate() != b.getMaxMacroBlockRate()) ?
+ (a.getMaxMacroBlockRate() < b.getMaxMacroBlockRate() ? -1 : 1) :
+ (a.getMaxFrameRate() != b.getMaxFrameRate()) ?
+ (a.getMaxFrameRate() < b.getMaxFrameRate() ? -1 : 1) : 0);
+ });
+
+ return ret;
+}
+
+std::map<VideoSize, Range<int64_t>, VideoSizeCompare> VideoCapabilities
+ ::getMeasuredFrameRates(const sp<AMessage> &format) const {
+ std::map<VideoSize, Range<int64_t>, VideoSizeCompare> ret;
+ AMessage::Type type;
+ for (int i = 0; i < format->countEntries(); i++) {
+ const char *name = format->getEntryNameAt(i, &type);
+ AString rangeStr;
+ if (!format->findString(name, &rangeStr)) {
+ continue;
+ }
+
+ const std::string key = std::string(name);
+ // looking for: measured-frame-rate-WIDTHxHEIGHT-range
+
+ std::regex sizeRegex("measured-frame-rate-(.+)-range");
+ std::smatch sizeMatch;
+ if (!std::regex_match(key, sizeMatch, sizeRegex)) {
+ continue;
+ }
+
+ std::optional<VideoSize> size = VideoSize::ParseSize(sizeMatch[1].str());
+ if (!size || size.value().getWidth() * size.value().getHeight() <= 0) {
+ continue;
+ }
+
+ std::optional<Range<int64_t>> range = Range<int64_t>::Parse(std::string(rangeStr.c_str()));
+ if (!range || range.value().lower() < 0 || range.value().upper() < 0) {
+ continue;
+ }
+
+ ret.emplace(size.value(), range.value());
+ }
+ return ret;
+}
+
+// static
+std::optional<std::pair<Range<int32_t>, Range<int32_t>>> VideoCapabilities
+ ::ParseWidthHeightRanges(const std::string &str) {
+ std::optional<std::pair<VideoSize, VideoSize>> range = VideoSize::ParseSizeRange(str);
+ if (!range) {
+ ALOGW("could not parse size range: %s", str.c_str());
+ return std::nullopt;
+ }
+
+ return std::make_optional(std::pair(
+ Range(range.value().first.getWidth(), range.value().second.getWidth()),
+ Range(range.value().first.getHeight(), range.value().second.getHeight())));
+}
+
+// static
+int32_t VideoCapabilities::EquivalentVP9Level(const sp<AMessage> &format) {
+ int32_t blockSizeWidth = 8;
+ int32_t blockSizeHeight = 8;
+ // VideoSize *blockSizePtr = &VideoSize(8, 8);
+ AString blockSizeStr;
+ if (format->findString("block-size", &blockSizeStr)) {
+ std::optional<VideoSize> parsedBlockSize
+ = VideoSize::ParseSize(std::string(blockSizeStr.c_str()));
+ if (parsedBlockSize) {
+ // blockSize = parsedBlockSize.value();
+ blockSizeWidth = parsedBlockSize.value().getWidth();
+ blockSizeHeight = parsedBlockSize.value().getHeight();
+ }
+ }
+ int32_t BS = blockSizeWidth * blockSizeHeight;
+
+ int32_t FS = 0;
+ AString blockCountRangeStr;
+ if (format->findString("block-count-range", &blockCountRangeStr)) {
+ std::optional<Range<int>> counts = Range<int32_t>::Parse(
+ std::string(blockCountRangeStr.c_str()));
+ if (counts) {
+ FS = BS * counts.value().upper();
+ }
+ }
+
+ int64_t SR = 0;
+ AString blockRatesStr;
+ if (format->findString("blocks-per-second-range", &blockRatesStr)) {
+ std::optional<Range<int64_t>> blockRates
+ = Range<int64_t>::Parse(std::string(blockRatesStr.c_str()));
+ if (blockRates) {
+ // ToDo: Catch the potential overflow issue.
+ SR = BS * blockRates.value().upper();
+ }
+ }
+
+ int32_t D = 0;
+ AString dimensionRangesStr;
+ if (format->findString("size-range", &dimensionRangesStr)) {
+ std::optional<std::pair<Range<int>, Range<int>>> dimensionRanges =
+ ParseWidthHeightRanges(std::string(dimensionRangesStr.c_str()));
+ if (dimensionRanges) {
+ D = std::max(dimensionRanges.value().first.upper(),
+ dimensionRanges.value().second.upper());
+ }
+ }
+
+ int32_t BR = 0;
+ AString bitrateRangeStr;
+ if (format->findString("bitrate-range", &bitrateRangeStr)) {
+ std::optional<Range<int>> bitRates = Range<int32_t>::Parse(
+ std::string(bitrateRangeStr.c_str()));
+ if (bitRates) {
+ BR = divUp(bitRates.value().upper(), 1000);
+ }
+ }
+
+ if (SR <= 829440 && FS <= 36864 && BR <= 200 && D <= 512)
+ return VP9Level1;
+ if (SR <= 2764800 && FS <= 73728 && BR <= 800 && D <= 768)
+ return VP9Level11;
+ if (SR <= 4608000 && FS <= 122880 && BR <= 1800 && D <= 960)
+ return VP9Level2;
+ if (SR <= 9216000 && FS <= 245760 && BR <= 3600 && D <= 1344)
+ return VP9Level21;
+ if (SR <= 20736000 && FS <= 552960 && BR <= 7200 && D <= 2048)
+ return VP9Level3;
+ if (SR <= 36864000 && FS <= 983040 && BR <= 12000 && D <= 2752)
+ return VP9Level31;
+ if (SR <= 83558400 && FS <= 2228224 && BR <= 18000 && D <= 4160)
+ return VP9Level4;
+ if (SR <= 160432128 && FS <= 2228224 && BR <= 30000 && D <= 4160)
+ return VP9Level41;
+ if (SR <= 311951360 && FS <= 8912896 && BR <= 60000 && D <= 8384)
+ return VP9Level5;
+ if (SR <= 588251136 && FS <= 8912896 && BR <= 120000 && D <= 8384)
+ return VP9Level51;
+ if (SR <= 1176502272 && FS <= 8912896 && BR <= 180000 && D <= 8384)
+ return VP9Level52;
+ if (SR <= 1176502272 && FS <= 35651584 && BR <= 180000 && D <= 16832)
+ return VP9Level6;
+ if (SR <= 2353004544L && FS <= 35651584 && BR <= 240000 && D <= 16832)
+ return VP9Level61;
+ if (SR <= 4706009088L && FS <= 35651584 && BR <= 480000 && D <= 16832)
+ return VP9Level62;
+ // returning largest level
+ return VP9Level62;
+}
+
+void VideoCapabilities::parseFromInfo(const sp<AMessage> &format) {
+ VideoSize blockSize = VideoSize(mBlockWidth, mBlockHeight);
+ VideoSize alignment = VideoSize(mWidthAlignment, mHeightAlignment);
+ std::optional<Range<int32_t>> counts, widths, heights;
+ std::optional<Range<int32_t>> frameRates, bitRates;
+ std::optional<Range<int64_t>> blockRates;
+ std::optional<Range<Rational>> ratios, blockRatios;
+
+ AString blockSizeStr;
+ if (format->findString("block-size", &blockSizeStr)) {
+ std::optional<VideoSize> parsedBlockSize
+ = VideoSize::ParseSize(std::string(blockSizeStr.c_str()));
+ blockSize = parsedBlockSize.value_or(blockSize);
+ }
+ AString alignmentStr;
+ if (format->findString("alignment", &alignmentStr)) {
+ std::optional<VideoSize> parsedAlignment
+ = VideoSize::ParseSize(std::string(alignmentStr.c_str()));
+ alignment = parsedAlignment.value_or(alignment);
+ }
+ AString blockCountRangeStr;
+ if (format->findString("block-count-range", &blockCountRangeStr)) {
+ std::optional<Range<int>> parsedBlockCountRange =
+ Range<int32_t>::Parse(std::string(blockCountRangeStr.c_str()));
+ if (parsedBlockCountRange) {
+ counts = parsedBlockCountRange.value();
+ }
+ }
+ AString blockRatesStr;
+ if (format->findString("blocks-per-second-range", &blockRatesStr)) {
+ blockRates = Range<int64_t>::Parse(std::string(blockRatesStr.c_str()));
+ }
+ mMeasuredFrameRates = getMeasuredFrameRates(format);
+ mPerformancePoints = getPerformancePoints(format);
+ AString sizeRangesStr;
+ if (format->findString("size-range", &sizeRangesStr)) {
+ std::optional<std::pair<Range<int>, Range<int>>> sizeRanges =
+ ParseWidthHeightRanges(std::string(sizeRangesStr.c_str()));
+ if (sizeRanges) {
+ widths = sizeRanges.value().first;
+ heights = sizeRanges.value().second;
+ }
+ }
+ // for now this just means using the smaller max size as 2nd
+ // upper limit.
+ // for now we are keeping the profile specific "width/height
+ // in macroblocks" limits.
+ if (format->contains("feature-can-swap-width-height")) {
+ if (widths && heights) {
+ mSmallerDimensionUpperLimit =
+ std::min(widths.value().upper(), heights.value().upper());
+ widths = heights = widths.value().extend(heights.value());
+ } else {
+ ALOGW("feature can-swap-width-height is best used with size-range");
+ mSmallerDimensionUpperLimit =
+ std::min(mWidthRange.upper(), mHeightRange.upper());
+ mWidthRange = mHeightRange = mWidthRange.extend(mHeightRange);
+ }
+ }
+
+ AString ratioStr;
+ if (format->findString("block-aspect-ratio-range", &ratioStr)) {
+ ratios = Rational::ParseRange(std::string(ratioStr.c_str()));
+ }
+ AString blockRatiosStr;
+ if (format->findString("pixel-aspect-ratio-range", &blockRatiosStr)) {
+ blockRatios = Rational::ParseRange(std::string(blockRatiosStr.c_str()));
+ }
+ AString frameRatesStr;
+ if (format->findString("frame-rate-range", &frameRatesStr)) {
+ frameRates = Range<int32_t>::Parse(std::string(frameRatesStr.c_str()));
+ if (frameRates) {
+ frameRates = frameRates.value().intersect(FRAME_RATE_RANGE);
+ if (frameRates.value().empty()) {
+ ALOGW("frame rate range is out of limits");
+ frameRates = std::nullopt;
+ }
+ }
+ }
+ AString bitRatesStr;
+ if (format->findString("bitrate-range", &bitRatesStr)) {
+ bitRates = Range<int32_t>::Parse(std::string(bitRatesStr.c_str()));
+ if (bitRates) {
+ bitRates = bitRates.value().intersect(BITRATE_RANGE);
+ if (bitRates.value().empty()) {
+ ALOGW("bitrate range is out of limits");
+ bitRates = std::nullopt;
+ }
+ }
+ }
+
+ if (!IsPowerOfTwo(blockSize.getWidth()) || !IsPowerOfTwo(blockSize.getHeight())
+ || !IsPowerOfTwo(alignment.getWidth()) || !IsPowerOfTwo(alignment.getHeight())) {
+ ALOGE("The widths and heights of blockSizes and alignments must be the power of two."
+ " blockSize width: %d; blockSize height: %d;"
+ " alignment width: %d; alignment height: %d.",
+ blockSize.getWidth(), blockSize.getHeight(),
+ alignment.getWidth(), alignment.getHeight());
+ mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ return;
+ }
+
+ // update block-size and alignment
+ applyMacroBlockLimits(
+ INT32_MAX, INT32_MAX, INT32_MAX, INT64_MAX,
+ blockSize.getWidth(), blockSize.getHeight(),
+ alignment.getWidth(), alignment.getHeight());
+
+ if ((mError & ERROR_CAPABILITIES_UNSUPPORTED) != 0 || mAllowMbOverride) {
+ // codec supports profiles that we don't know.
+ // Use supplied values clipped to platform limits
+ if (widths) {
+ mWidthRange = VideoSize::GetAllowedDimensionRange().intersect(widths.value());
+ }
+ if (heights) {
+ mHeightRange = VideoSize::GetAllowedDimensionRange().intersect(heights.value());
+ }
+ if (counts) {
+ mBlockCountRange = POSITIVE_INT32.intersect(
+ counts.value().factor(mBlockWidth * mBlockHeight
+ / blockSize.getWidth() / blockSize.getHeight()));
+ }
+ if (blockRates) {
+ mBlocksPerSecondRange = POSITIVE_INT64.intersect(
+ blockRates.value().factor(mBlockWidth * mBlockHeight
+ / blockSize.getWidth() / blockSize.getHeight()));
+ }
+ if (blockRatios) {
+ mBlockAspectRatioRange = POSITIVE_RATIONALS.intersect(
+ Rational::ScaleRange(blockRatios.value(),
+ mBlockHeight / blockSize.getHeight(),
+ mBlockWidth / blockSize.getWidth()));
+ }
+ if (ratios) {
+ mAspectRatioRange = POSITIVE_RATIONALS.intersect(ratios.value());
+ }
+ if (frameRates) {
+ mFrameRateRange = FRAME_RATE_RANGE.intersect(frameRates.value());
+ }
+ if (bitRates) {
+ // only allow bitrate override if unsupported profiles were encountered
+ if ((mError & ERROR_CAPABILITIES_UNSUPPORTED) != 0) {
+ mBitrateRange = BITRATE_RANGE.intersect(bitRates.value());
+ } else {
+ mBitrateRange = mBitrateRange.intersect(bitRates.value());
+ }
+ }
+ } else {
+ // no unsupported profile/levels, so restrict values to known limits
+ if (widths) {
+ mWidthRange = mWidthRange.intersect(widths.value());
+ }
+ if (heights) {
+ mHeightRange = mHeightRange.intersect(heights.value());
+ }
+ if (counts) {
+ mBlockCountRange = mBlockCountRange.intersect(
+ counts.value().factor(mBlockWidth * mBlockHeight
+ / blockSize.getWidth() / blockSize.getHeight()));
+ }
+ if (blockRates) {
+ mBlocksPerSecondRange = mBlocksPerSecondRange.intersect(
+ blockRates.value().factor(mBlockWidth * mBlockHeight
+ / blockSize.getWidth() / blockSize.getHeight()));
+ }
+ if (blockRatios) {
+ mBlockAspectRatioRange = mBlockAspectRatioRange.intersect(
+ Rational::ScaleRange(blockRatios.value(),
+ mBlockHeight / blockSize.getHeight(),
+ mBlockWidth / blockSize.getWidth()));
+ }
+ if (ratios) {
+ mAspectRatioRange = mAspectRatioRange.intersect(ratios.value());
+ }
+ if (frameRates) {
+ mFrameRateRange = mFrameRateRange.intersect(frameRates.value());
+ }
+ if (bitRates) {
+ mBitrateRange = mBitrateRange.intersect(bitRates.value());
+ }
+ }
+ updateLimits();
+}
+
+void VideoCapabilities::applyBlockLimits(
+ int32_t blockWidth, int32_t blockHeight,
+ Range<int32_t> counts, Range<int64_t> rates, Range<Rational> ratios) {
+
+ if (!IsPowerOfTwo(blockWidth) || !IsPowerOfTwo(blockHeight)) {
+ ALOGE("blockWidth and blockHeight must be the power of two."
+ " blockWidth: %d; blockHeight: %d", blockWidth, blockHeight);
+ mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ return;
+ }
+
+ const int32_t newBlockWidth = std::max(blockWidth, mBlockWidth);
+ const int32_t newBlockHeight = std::max(blockHeight, mBlockHeight);
+
+ // factor will always be a power-of-2
+ int32_t factor =
+ newBlockWidth * newBlockHeight / mBlockWidth / mBlockHeight;
+ if (factor != 1) {
+ mBlockCountRange = mBlockCountRange.factor(factor);
+ mBlocksPerSecondRange = mBlocksPerSecondRange.factor(factor);
+ mBlockAspectRatioRange = Rational::ScaleRange(
+ mBlockAspectRatioRange,
+ newBlockHeight / mBlockHeight,
+ newBlockWidth / mBlockWidth);
+ mHorizontalBlockRange = mHorizontalBlockRange.factor(newBlockWidth / mBlockWidth);
+ mVerticalBlockRange = mVerticalBlockRange.factor(newBlockHeight / mBlockHeight);
+ }
+ factor = newBlockWidth * newBlockHeight / blockWidth / blockHeight;
+ if (factor != 1) {
+ counts = counts.factor(factor);
+ rates = rates.factor((int64_t)factor);
+ ratios = Rational::ScaleRange(
+ ratios, newBlockHeight / blockHeight,
+ newBlockWidth / blockWidth);
+ }
+ mBlockCountRange = mBlockCountRange.intersect(counts);
+ mBlocksPerSecondRange = mBlocksPerSecondRange.intersect(rates);
+ mBlockAspectRatioRange = mBlockAspectRatioRange.intersect(ratios);
+ mBlockWidth = newBlockWidth;
+ mBlockHeight = newBlockHeight;
+}
+
+void VideoCapabilities::applyAlignment(
+ int32_t widthAlignment, int32_t heightAlignment) {
+ if (!IsPowerOfTwo(widthAlignment) || !IsPowerOfTwo(heightAlignment)) {
+ ALOGE("width and height alignments must be the power of two."
+ " widthAlignment: %d; heightAlignment: %d", widthAlignment, heightAlignment);
+ mError |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ return;
+ }
+
+ if (widthAlignment > mBlockWidth || heightAlignment > mBlockHeight) {
+ // maintain assumption that 0 < alignment <= block-size
+ applyBlockLimits(
+ std::max(widthAlignment, mBlockWidth),
+ std::max(heightAlignment, mBlockHeight),
+ POSITIVE_INT32, POSITIVE_INT64, POSITIVE_RATIONALS);
+ }
+
+ mWidthAlignment = std::max(widthAlignment, mWidthAlignment);
+ mHeightAlignment = std::max(heightAlignment, mHeightAlignment);
+
+ mWidthRange = mWidthRange.align(mWidthAlignment);
+ mHeightRange = mHeightRange.align(mHeightAlignment);
+}
+
+void VideoCapabilities::updateLimits() {
+ // pixels -> blocks <- counts
+ mHorizontalBlockRange = mHorizontalBlockRange.intersect(
+ mWidthRange.factor(mBlockWidth));
+ mHorizontalBlockRange = mHorizontalBlockRange.intersect(
+ Range( mBlockCountRange.lower() / mVerticalBlockRange.upper(),
+ mBlockCountRange.upper() / mVerticalBlockRange.lower()));
+ mVerticalBlockRange = mVerticalBlockRange.intersect(
+ mHeightRange.factor(mBlockHeight));
+ mVerticalBlockRange = mVerticalBlockRange.intersect(
+ Range( mBlockCountRange.lower() / mHorizontalBlockRange.upper(),
+ mBlockCountRange.upper() / mHorizontalBlockRange.lower()));
+ mBlockCountRange = mBlockCountRange.intersect(
+ Range( mHorizontalBlockRange.lower()
+ * mVerticalBlockRange.lower(),
+ mHorizontalBlockRange.upper()
+ * mVerticalBlockRange.upper()));
+ mBlockAspectRatioRange = mBlockAspectRatioRange.intersect(
+ Rational(mHorizontalBlockRange.lower(), mVerticalBlockRange.upper()),
+ Rational(mHorizontalBlockRange.upper(), mVerticalBlockRange.lower()));
+
+ // blocks -> pixels
+ mWidthRange = mWidthRange.intersect(
+ (mHorizontalBlockRange.lower() - 1) * mBlockWidth + mWidthAlignment,
+ mHorizontalBlockRange.upper() * mBlockWidth);
+ mHeightRange = mHeightRange.intersect(
+ (mVerticalBlockRange.lower() - 1) * mBlockHeight + mHeightAlignment,
+ mVerticalBlockRange.upper() * mBlockHeight);
+ mAspectRatioRange = mAspectRatioRange.intersect(
+ Rational(mWidthRange.lower(), mHeightRange.upper()),
+ Rational(mWidthRange.upper(), mHeightRange.lower()));
+
+ mSmallerDimensionUpperLimit = std::min(
+ mSmallerDimensionUpperLimit,
+ std::min(mWidthRange.upper(), mHeightRange.upper()));
+
+ // blocks -> rate
+ mBlocksPerSecondRange = mBlocksPerSecondRange.intersect(
+ mBlockCountRange.lower() * (int64_t)mFrameRateRange.lower(),
+ mBlockCountRange.upper() * (int64_t)mFrameRateRange.upper());
+ mFrameRateRange = mFrameRateRange.intersect(
+ (int32_t)(mBlocksPerSecondRange.lower()
+ / mBlockCountRange.upper()),
+ (int32_t)(mBlocksPerSecondRange.upper()
+ / (double)mBlockCountRange.lower()));
+}
+
+void VideoCapabilities::applyMacroBlockLimits(
+ int32_t maxHorizontalBlocks, int32_t maxVerticalBlocks,
+ int32_t maxBlocks, int64_t maxBlocksPerSecond,
+ int32_t blockWidth, int32_t blockHeight,
+ int32_t widthAlignment, int32_t heightAlignment) {
+ applyMacroBlockLimits(
+ 1 /* minHorizontalBlocks */, 1 /* minVerticalBlocks */,
+ maxHorizontalBlocks, maxVerticalBlocks,
+ maxBlocks, maxBlocksPerSecond,
+ blockWidth, blockHeight, widthAlignment, heightAlignment);
+}
+
+void VideoCapabilities::applyMacroBlockLimits(
+ int32_t minHorizontalBlocks, int32_t minVerticalBlocks,
+ int32_t maxHorizontalBlocks, int32_t maxVerticalBlocks,
+ int32_t maxBlocks, int64_t maxBlocksPerSecond,
+ int32_t blockWidth, int32_t blockHeight,
+ int32_t widthAlignment, int32_t heightAlignment) {
+ applyAlignment(widthAlignment, heightAlignment);
+ applyBlockLimits(
+ blockWidth, blockHeight, Range((int32_t)1, maxBlocks),
+ Range((int64_t)1, maxBlocksPerSecond),
+ Range(Rational(1, maxVerticalBlocks), Rational(maxHorizontalBlocks, 1)));
+ mHorizontalBlockRange =
+ mHorizontalBlockRange.intersect(
+ divUp(minHorizontalBlocks, (mBlockWidth / blockWidth)),
+ maxHorizontalBlocks / (mBlockWidth / blockWidth));
+ mVerticalBlockRange =
+ mVerticalBlockRange.intersect(
+ divUp(minVerticalBlocks, (mBlockHeight / blockHeight)),
+ maxVerticalBlocks / (mBlockHeight / blockHeight));
+}
+
+void VideoCapabilities::applyLevelLimits() {
+ int64_t maxBlocksPerSecond = 0;
+ int32_t maxBlocks = 0;
+ int32_t maxBps = 0;
+ int32_t maxDPBBlocks = 0;
+
+ int errors = ERROR_CAPABILITIES_NONE_SUPPORTED;
+ const char *mediaType = mMediaType.c_str();
+ if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_AVC)) {
+ maxBlocks = 99;
+ maxBlocksPerSecond = 1485;
+ maxBps = 64000;
+ maxDPBBlocks = 396;
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ int32_t MBPS = 0, FS = 0, BR = 0, DPB = 0;
+ bool supported = true;
+ switch (profileLevel.mLevel) {
+ case AVCLevel1:
+ MBPS = 1485; FS = 99; BR = 64; DPB = 396; break;
+ case AVCLevel1b:
+ MBPS = 1485; FS = 99; BR = 128; DPB = 396; break;
+ case AVCLevel11:
+ MBPS = 3000; FS = 396; BR = 192; DPB = 900; break;
+ case AVCLevel12:
+ MBPS = 6000; FS = 396; BR = 384; DPB = 2376; break;
+ case AVCLevel13:
+ MBPS = 11880; FS = 396; BR = 768; DPB = 2376; break;
+ case AVCLevel2:
+ MBPS = 11880; FS = 396; BR = 2000; DPB = 2376; break;
+ case AVCLevel21:
+ MBPS = 19800; FS = 792; BR = 4000; DPB = 4752; break;
+ case AVCLevel22:
+ MBPS = 20250; FS = 1620; BR = 4000; DPB = 8100; break;
+ case AVCLevel3:
+ MBPS = 40500; FS = 1620; BR = 10000; DPB = 8100; break;
+ case AVCLevel31:
+ MBPS = 108000; FS = 3600; BR = 14000; DPB = 18000; break;
+ case AVCLevel32:
+ MBPS = 216000; FS = 5120; BR = 20000; DPB = 20480; break;
+ case AVCLevel4:
+ MBPS = 245760; FS = 8192; BR = 20000; DPB = 32768; break;
+ case AVCLevel41:
+ MBPS = 245760; FS = 8192; BR = 50000; DPB = 32768; break;
+ case AVCLevel42:
+ MBPS = 522240; FS = 8704; BR = 50000; DPB = 34816; break;
+ case AVCLevel5:
+ MBPS = 589824; FS = 22080; BR = 135000; DPB = 110400; break;
+ case AVCLevel51:
+ MBPS = 983040; FS = 36864; BR = 240000; DPB = 184320; break;
+ case AVCLevel52:
+ MBPS = 2073600; FS = 36864; BR = 240000; DPB = 184320; break;
+ case AVCLevel6:
+ MBPS = 4177920; FS = 139264; BR = 240000; DPB = 696320; break;
+ case AVCLevel61:
+ MBPS = 8355840; FS = 139264; BR = 480000; DPB = 696320; break;
+ case AVCLevel62:
+ MBPS = 16711680; FS = 139264; BR = 800000; DPB = 696320; break;
+ default:
+ ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ switch (profileLevel.mProfile) {
+ case AVCProfileConstrainedHigh:
+ case AVCProfileHigh:
+ BR *= 1250; break;
+ case AVCProfileHigh10:
+ BR *= 3000; break;
+ case AVCProfileExtended:
+ case AVCProfileHigh422:
+ case AVCProfileHigh444:
+ ALOGW("Unsupported profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ supported = false;
+ FALLTHROUGH_INTENDED;
+ // fall through - treat as base profile
+ case AVCProfileConstrainedBaseline:
+ FALLTHROUGH_INTENDED;
+ case AVCProfileBaseline:
+ FALLTHROUGH_INTENDED;
+ case AVCProfileMain:
+ BR *= 1000; break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ BR *= 1000;
+ }
+ if (supported) {
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ }
+ maxBlocksPerSecond = std::max((int64_t)MBPS, maxBlocksPerSecond);
+ maxBlocks = std::max(FS, maxBlocks);
+ maxBps = std::max(BR, maxBps);
+ maxDPBBlocks = std::max(maxDPBBlocks, DPB);
+ }
+
+ int32_t maxLengthInBlocks = (int32_t)(std::sqrt(8 * maxBlocks));
+ applyMacroBlockLimits(
+ maxLengthInBlocks, maxLengthInBlocks,
+ maxBlocks, maxBlocksPerSecond,
+ 16 /* blockWidth */, 16 /* blockHeight */,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_MPEG2)) {
+ int32_t maxWidth = 11, maxHeight = 9, maxRate = 15;
+ maxBlocks = 99;
+ maxBlocksPerSecond = 1485;
+ maxBps = 64000;
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ int32_t MBPS = 0, FS = 0, BR = 0, FR = 0, W = 0, H = 0;
+ bool supported = true;
+ switch (profileLevel.mProfile) {
+ case MPEG2ProfileSimple:
+ switch (profileLevel.mLevel) {
+ case MPEG2LevelML:
+ FR = 30; W = 45; H = 36; MBPS = 40500; FS = 1620; BR = 15000; break;
+ default:
+ ALOGW("Unrecognized profile/level %d/%d for %s",
+ profileLevel.mProfile, profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ break;
+ case MPEG2ProfileMain:
+ switch (profileLevel.mLevel) {
+ case MPEG2LevelLL:
+ FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 4000; break;
+ case MPEG2LevelML:
+ FR = 30; W = 45; H = 36; MBPS = 40500; FS = 1620; BR = 15000; break;
+ case MPEG2LevelH14:
+ FR = 60; W = 90; H = 68; MBPS = 183600; FS = 6120; BR = 60000; break;
+ case MPEG2LevelHL:
+ FR = 60; W = 120; H = 68; MBPS = 244800; FS = 8160; BR = 80000; break;
+ case MPEG2LevelHP:
+ FR = 60; W = 120; H = 68; MBPS = 489600; FS = 8160; BR = 80000; break;
+ default:
+ ALOGW("Unrecognized profile/level %d / %d for %s",
+ profileLevel.mProfile, profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ break;
+ case MPEG2Profile422:
+ case MPEG2ProfileSNR:
+ case MPEG2ProfileSpatial:
+ case MPEG2ProfileHigh:
+ ALOGW("Unsupported profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNSUPPORTED;
+ supported = false;
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ if (supported) {
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ }
+ maxBlocksPerSecond = std::max((int64_t)MBPS, maxBlocksPerSecond);
+ maxBlocks = std::max(FS, maxBlocks);
+ maxBps = std::max(BR * 1000, maxBps);
+ maxWidth = std::max(W, maxWidth);
+ maxHeight = std::max(H, maxHeight);
+ maxRate = std::max(FR, maxRate);
+ }
+ applyMacroBlockLimits(maxWidth, maxHeight,
+ maxBlocks, maxBlocksPerSecond,
+ 16 /* blockWidth */, 16 /* blockHeight */,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ mFrameRateRange = mFrameRateRange.intersect(12, maxRate);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_MPEG4)) {
+ int32_t maxWidth = 11, maxHeight = 9, maxRate = 15;
+ maxBlocks = 99;
+ maxBlocksPerSecond = 1485;
+ maxBps = 64000;
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ int32_t MBPS = 0, FS = 0, BR = 0, FR = 0, W = 0, H = 0;
+ bool strict = false; // true: W, H and FR are individual max limits
+ bool supported = true;
+ switch (profileLevel.mProfile) {
+ case MPEG4ProfileSimple:
+ switch (profileLevel.mLevel) {
+ case MPEG4Level0:
+ strict = true;
+ FR = 15; W = 11; H = 9; MBPS = 1485; FS = 99; BR = 64; break;
+ case MPEG4Level1:
+ FR = 30; W = 11; H = 9; MBPS = 1485; FS = 99; BR = 64; break;
+ case MPEG4Level0b:
+ strict = true;
+ FR = 15; W = 11; H = 9; MBPS = 1485; FS = 99; BR = 128; break;
+ case MPEG4Level2:
+ FR = 30; W = 22; H = 18; MBPS = 5940; FS = 396; BR = 128; break;
+ case MPEG4Level3:
+ FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 384; break;
+ case MPEG4Level4a:
+ FR = 30; W = 40; H = 30; MBPS = 36000; FS = 1200; BR = 4000; break;
+ case MPEG4Level5:
+ FR = 30; W = 45; H = 36; MBPS = 40500; FS = 1620; BR = 8000; break;
+ case MPEG4Level6:
+ FR = 30; W = 80; H = 45; MBPS = 108000; FS = 3600; BR = 12000; break;
+ default:
+ ALOGW("Unrecognized profile/level %d/%d for %s",
+ profileLevel.mProfile, profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ break;
+ case MPEG4ProfileAdvancedSimple:
+ switch (profileLevel.mLevel) {
+ case MPEG4Level0:
+ case MPEG4Level1:
+ FR = 30; W = 11; H = 9; MBPS = 2970; FS = 99; BR = 128; break;
+ case MPEG4Level2:
+ FR = 30; W = 22; H = 18; MBPS = 5940; FS = 396; BR = 384; break;
+ case MPEG4Level3:
+ FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 768; break;
+ case MPEG4Level3b:
+ FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 1500; break;
+ case MPEG4Level4:
+ FR = 30; W = 44; H = 36; MBPS = 23760; FS = 792; BR = 3000; break;
+ case MPEG4Level5:
+ FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 8000; break;
+ default:
+ ALOGW("Unrecognized profile/level %d/%d for %s",
+ profileLevel.mProfile, profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ break;
+ case MPEG4ProfileMain: // 2-4
+ case MPEG4ProfileNbit: // 2
+ case MPEG4ProfileAdvancedRealTime: // 1-4
+ case MPEG4ProfileCoreScalable: // 1-3
+ case MPEG4ProfileAdvancedCoding: // 1-4
+ case MPEG4ProfileCore: // 1-2
+ case MPEG4ProfileAdvancedCore: // 1-4
+ case MPEG4ProfileSimpleScalable: // 0-2
+ case MPEG4ProfileHybrid: // 1-2
+
+ // Studio profiles are not supported by our codecs.
+
+ // Only profiles that can decode simple object types are considered.
+ // The following profiles are not able to.
+ case MPEG4ProfileBasicAnimated: // 1-2
+ case MPEG4ProfileScalableTexture: // 1
+ case MPEG4ProfileSimpleFace: // 1-2
+ case MPEG4ProfileAdvancedScalable: // 1-3
+ case MPEG4ProfileSimpleFBA: // 1-2
+ ALOGV("Unsupported profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNSUPPORTED;
+ supported = false;
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ if (supported) {
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ }
+ maxBlocksPerSecond = std::max((int64_t)MBPS, maxBlocksPerSecond);
+ maxBlocks = std::max(FS, maxBlocks);
+ maxBps = std::max(BR * 1000, maxBps);
+ if (strict) {
+ maxWidth = std::max(W, maxWidth);
+ maxHeight = std::max(H, maxHeight);
+ maxRate = std::max(FR, maxRate);
+ } else {
+ // assuming max 60 fps frame rate and 1:2 aspect ratio
+ int32_t maxDim = (int32_t)std::sqrt(2 * FS);
+ maxWidth = std::max(maxDim, maxWidth);
+ maxHeight = std::max(maxDim, maxHeight);
+ maxRate = std::max(std::max(FR, 60), maxRate);
+ }
+ }
+ applyMacroBlockLimits(maxWidth, maxHeight,
+ maxBlocks, maxBlocksPerSecond,
+ 16 /* blockWidth */, 16 /* blockHeight */,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ mFrameRateRange = mFrameRateRange.intersect(12, maxRate);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_H263)) {
+ int32_t maxWidth = 11, maxHeight = 9, maxRate = 15;
+ int32_t minWidth = maxWidth, minHeight = maxHeight;
+ int32_t minAlignment = 16;
+ maxBlocks = 99;
+ maxBlocksPerSecond = 1485;
+ maxBps = 64000;
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ int32_t MBPS = 0, BR = 0, FR = 0, W = 0, H = 0, minW = minWidth, minH = minHeight;
+ bool strict = false; // true: support only sQCIF, QCIF (maybe CIF)
+ switch (profileLevel.mLevel) {
+ case H263Level10:
+ strict = true; // only supports sQCIF & QCIF
+ FR = 15; W = 11; H = 9; BR = 1; MBPS = W * H * FR; break;
+ case H263Level20:
+ strict = true; // only supports sQCIF, QCIF & CIF
+ FR = 30; W = 22; H = 18; BR = 2; MBPS = W * H * 15; break;
+ case H263Level30:
+ strict = true; // only supports sQCIF, QCIF & CIF
+ FR = 30; W = 22; H = 18; BR = 6; MBPS = W * H * FR; break;
+ case H263Level40:
+ strict = true; // only supports sQCIF, QCIF & CIF
+ FR = 30; W = 22; H = 18; BR = 32; MBPS = W * H * FR; break;
+ case H263Level45:
+ // only implies level 10 support
+ strict = profileLevel.mProfile == H263ProfileBaseline
+ || profileLevel.mProfile ==
+ H263ProfileBackwardCompatible;
+ if (!strict) {
+ minW = 1; minH = 1; minAlignment = 4;
+ }
+ FR = 15; W = 11; H = 9; BR = 2; MBPS = W * H * FR; break;
+ case H263Level50:
+ // only supports 50fps for H > 15
+ minW = 1; minH = 1; minAlignment = 4;
+ FR = 60; W = 22; H = 18; BR = 64; MBPS = W * H * 50; break;
+ case H263Level60:
+ // only supports 50fps for H > 15
+ minW = 1; minH = 1; minAlignment = 4;
+ FR = 60; W = 45; H = 18; BR = 128; MBPS = W * H * 50; break;
+ case H263Level70:
+ // only supports 50fps for H > 30
+ minW = 1; minH = 1; minAlignment = 4;
+ FR = 60; W = 45; H = 36; BR = 256; MBPS = W * H * 50; break;
+ default:
+ ALOGW("Unrecognized profile/level %d/%d for %s",
+ profileLevel.mProfile, profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ switch (profileLevel.mProfile) {
+ case H263ProfileBackwardCompatible:
+ case H263ProfileBaseline:
+ case H263ProfileH320Coding:
+ case H263ProfileHighCompression:
+ case H263ProfileHighLatency:
+ case H263ProfileInterlace:
+ case H263ProfileInternet:
+ case H263ProfileISWV2:
+ case H263ProfileISWV3:
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ if (strict) {
+ // Strict levels define sub-QCIF min size and enumerated sizes. We cannot
+ // express support for "only sQCIF & QCIF (& CIF)" using VideoCapabilities
+ // but we can express "only QCIF (& CIF)", so set minimume size at QCIF.
+ // minW = 8; minH = 6;
+ minW = 11; minH = 9;
+ } else {
+ // any support for non-strict levels (including unrecognized profiles or
+ // levels) allow custom frame size support beyond supported limits
+ // (other than bitrate)
+ mAllowMbOverride = true;
+ }
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ maxBlocksPerSecond = std::max((int64_t)MBPS, maxBlocksPerSecond);
+ maxBlocks = std::max(W * H, maxBlocks);
+ maxBps = std::max(BR * 64000, maxBps);
+ maxWidth = std::max(W, maxWidth);
+ maxHeight = std::max(H, maxHeight);
+ maxRate = std::max(FR, maxRate);
+ minWidth = std::min(minW, minWidth);
+ minHeight = std::min(minH, minHeight);
+ }
+ // unless we encountered custom frame size support, limit size to QCIF and CIF
+ // using aspect ratio.
+ if (!mAllowMbOverride) {
+ mBlockAspectRatioRange =
+ Range(Rational(11, 9), Rational(11, 9));
+ }
+ applyMacroBlockLimits(
+ minWidth, minHeight,
+ maxWidth, maxHeight,
+ maxBlocks, maxBlocksPerSecond,
+ 16 /* blockWidth */, 16 /* blockHeight */,
+ minAlignment /* widthAlignment */, minAlignment /* heightAlignment */);
+ mFrameRateRange = Range(1, maxRate);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_VP8)) {
+ maxBlocks = INT_MAX;
+ maxBlocksPerSecond = INT_MAX;
+
+ // TODO: set to 100Mbps for now, need a number for VP8
+ maxBps = 100000000;
+
+ // profile levels are not indicative for VPx, but verify
+ // them nonetheless
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ switch (profileLevel.mLevel) {
+ case VP8Level_Version0:
+ case VP8Level_Version1:
+ case VP8Level_Version2:
+ case VP8Level_Version3:
+ break;
+ default:
+ ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ switch (profileLevel.mProfile) {
+ case VP8ProfileMain:
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ }
+
+ const int32_t blockSize = 16;
+ applyMacroBlockLimits(SHRT_MAX, SHRT_MAX,
+ maxBlocks, maxBlocksPerSecond, blockSize, blockSize,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_VP9)) {
+ maxBlocksPerSecond = 829440;
+ maxBlocks = 36864;
+ maxBps = 200000;
+ int32_t maxDim = 512;
+
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ int64_t SR = 0; // luma sample rate
+ int32_t FS = 0; // luma picture size
+ int32_t BR = 0; // bit rate kbps
+ int32_t D = 0; // luma dimension
+ switch (profileLevel.mLevel) {
+ case VP9Level1:
+ SR = 829440; FS = 36864; BR = 200; D = 512; break;
+ case VP9Level11:
+ SR = 2764800; FS = 73728; BR = 800; D = 768; break;
+ case VP9Level2:
+ SR = 4608000; FS = 122880; BR = 1800; D = 960; break;
+ case VP9Level21:
+ SR = 9216000; FS = 245760; BR = 3600; D = 1344; break;
+ case VP9Level3:
+ SR = 20736000; FS = 552960; BR = 7200; D = 2048; break;
+ case VP9Level31:
+ SR = 36864000; FS = 983040; BR = 12000; D = 2752; break;
+ case VP9Level4:
+ SR = 83558400; FS = 2228224; BR = 18000; D = 4160; break;
+ case VP9Level41:
+ SR = 160432128; FS = 2228224; BR = 30000; D = 4160; break;
+ case VP9Level5:
+ SR = 311951360; FS = 8912896; BR = 60000; D = 8384; break;
+ case VP9Level51:
+ SR = 588251136; FS = 8912896; BR = 120000; D = 8384; break;
+ case VP9Level52:
+ SR = 1176502272; FS = 8912896; BR = 180000; D = 8384; break;
+ case VP9Level6:
+ SR = 1176502272; FS = 35651584; BR = 180000; D = 16832; break;
+ case VP9Level61:
+ SR = 2353004544L; FS = 35651584; BR = 240000; D = 16832; break;
+ case VP9Level62:
+ SR = 4706009088L; FS = 35651584; BR = 480000; D = 16832; break;
+ default:
+ ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ switch (profileLevel.mProfile) {
+ case VP9Profile0:
+ case VP9Profile1:
+ case VP9Profile2:
+ case VP9Profile3:
+ case VP9Profile2HDR:
+ case VP9Profile3HDR:
+ case VP9Profile2HDR10Plus:
+ case VP9Profile3HDR10Plus:
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ maxBlocksPerSecond = std::max(SR, maxBlocksPerSecond);
+ maxBlocks = std::max(FS, maxBlocks);
+ maxBps = std::max(BR * 1000, maxBps);
+ maxDim = std::max(D, maxDim);
+ }
+
+ const int32_t blockSize = 8;
+ int32_t maxLengthInBlocks = divUp(maxDim, blockSize);
+ maxBlocks = divUp(maxBlocks, blockSize * blockSize);
+ maxBlocksPerSecond = divUp(maxBlocksPerSecond, blockSize * (int64_t)blockSize);
+
+ applyMacroBlockLimits(
+ maxLengthInBlocks, maxLengthInBlocks,
+ maxBlocks, maxBlocksPerSecond,
+ blockSize, blockSize,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_HEVC)) {
+ // CTBs are at least 8x8 so use 8x8 block size
+ maxBlocks = 36864 >> 6; // 192x192 pixels == 576 8x8 blocks
+ maxBlocksPerSecond = maxBlocks * 15;
+ maxBps = 128000;
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ double FR = 0;
+ int32_t FS = 0, BR = 0;
+ switch (profileLevel.mLevel) {
+ /* The HEVC spec talks only in a very convoluted manner about the
+ existence of levels 1-3.1 for High tier, which could also be
+ understood as 'decoders and encoders should treat these levels
+ as if they were Main tier', so we do that. */
+ case HEVCMainTierLevel1:
+ case HEVCHighTierLevel1:
+ FR = 15; FS = 36864; BR = 128; break;
+ case HEVCMainTierLevel2:
+ case HEVCHighTierLevel2:
+ FR = 30; FS = 122880; BR = 1500; break;
+ case HEVCMainTierLevel21:
+ case HEVCHighTierLevel21:
+ FR = 30; FS = 245760; BR = 3000; break;
+ case HEVCMainTierLevel3:
+ case HEVCHighTierLevel3:
+ FR = 30; FS = 552960; BR = 6000; break;
+ case HEVCMainTierLevel31:
+ case HEVCHighTierLevel31:
+ FR = 33.75; FS = 983040; BR = 10000; break;
+ case HEVCMainTierLevel4:
+ FR = 30; FS = 2228224; BR = 12000; break;
+ case HEVCHighTierLevel4:
+ FR = 30; FS = 2228224; BR = 30000; break;
+ case HEVCMainTierLevel41:
+ FR = 60; FS = 2228224; BR = 20000; break;
+ case HEVCHighTierLevel41:
+ FR = 60; FS = 2228224; BR = 50000; break;
+ case HEVCMainTierLevel5:
+ FR = 30; FS = 8912896; BR = 25000; break;
+ case HEVCHighTierLevel5:
+ FR = 30; FS = 8912896; BR = 100000; break;
+ case HEVCMainTierLevel51:
+ FR = 60; FS = 8912896; BR = 40000; break;
+ case HEVCHighTierLevel51:
+ FR = 60; FS = 8912896; BR = 160000; break;
+ case HEVCMainTierLevel52:
+ FR = 120; FS = 8912896; BR = 60000; break;
+ case HEVCHighTierLevel52:
+ FR = 120; FS = 8912896; BR = 240000; break;
+ case HEVCMainTierLevel6:
+ FR = 30; FS = 35651584; BR = 60000; break;
+ case HEVCHighTierLevel6:
+ FR = 30; FS = 35651584; BR = 240000; break;
+ case HEVCMainTierLevel61:
+ FR = 60; FS = 35651584; BR = 120000; break;
+ case HEVCHighTierLevel61:
+ FR = 60; FS = 35651584; BR = 480000; break;
+ case HEVCMainTierLevel62:
+ FR = 120; FS = 35651584; BR = 240000; break;
+ case HEVCHighTierLevel62:
+ FR = 120; FS = 35651584; BR = 800000; break;
+ default:
+ ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ switch (profileLevel.mProfile) {
+ case HEVCProfileMain:
+ case HEVCProfileMain10:
+ case HEVCProfileMainStill:
+ case HEVCProfileMain10HDR10:
+ case HEVCProfileMain10HDR10Plus:
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+
+ /* DPB logic:
+ if (width * height <= FS / 4) DPB = 16;
+ else if (width * height <= FS / 2) DPB = 12;
+ else if (width * height <= FS * 0.75) DPB = 8;
+ else DPB = 6;
+ */
+
+ FS >>= 6; // convert pixels to blocks
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ maxBlocksPerSecond = std::max((int64_t)(FR * FS), maxBlocksPerSecond);
+ maxBlocks = std::max(FS, maxBlocks);
+ maxBps = std::max(1000 * BR, maxBps);
+ }
+
+ int32_t maxLengthInBlocks = (int32_t)(std::sqrt(8 * maxBlocks));
+ applyMacroBlockLimits(
+ maxLengthInBlocks, maxLengthInBlocks,
+ maxBlocks, maxBlocksPerSecond,
+ 8 /* blockWidth */, 8 /* blockHeight */,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_VIDEO_AV1)) {
+ maxBlocksPerSecond = 829440;
+ maxBlocks = 36864;
+ maxBps = 200000;
+ int32_t maxDim = 512;
+
+ // Sample rate, Picture Size, Bit rate and luma dimension for AV1 Codec,
+ // corresponding to the definitions in
+ // "AV1 Bitstream & Decoding Process Specification", Annex A
+ // found at https://aomedia.org/av1-bitstream-and-decoding-process-specification/
+ for (ProfileLevel profileLevel: mProfileLevels) {
+ int64_t SR = 0; // luma sample rate
+ int32_t FS = 0; // luma picture size
+ int32_t BR = 0; // bit rate kbps
+ int32_t D = 0; // luma D
+ switch (profileLevel.mLevel) {
+ case AV1Level2:
+ SR = 5529600; FS = 147456; BR = 1500; D = 2048; break;
+ case AV1Level21:
+ case AV1Level22:
+ case AV1Level23:
+ SR = 10454400; FS = 278784; BR = 3000; D = 2816; break;
+
+ case AV1Level3:
+ SR = 24969600; FS = 665856; BR = 6000; D = 4352; break;
+ case AV1Level31:
+ case AV1Level32:
+ case AV1Level33:
+ SR = 39938400; FS = 1065024; BR = 10000; D = 5504; break;
+
+ case AV1Level4:
+ SR = 77856768; FS = 2359296; BR = 12000; D = 6144; break;
+ case AV1Level41:
+ case AV1Level42:
+ case AV1Level43:
+ SR = 155713536; FS = 2359296; BR = 20000; D = 6144; break;
+
+ case AV1Level5:
+ SR = 273715200; FS = 8912896; BR = 30000; D = 8192; break;
+ case AV1Level51:
+ SR = 547430400; FS = 8912896; BR = 40000; D = 8192; break;
+ case AV1Level52:
+ SR = 1094860800; FS = 8912896; BR = 60000; D = 8192; break;
+ case AV1Level53:
+ SR = 1176502272; FS = 8912896; BR = 60000; D = 8192; break;
+
+ case AV1Level6:
+ SR = 1176502272; FS = 35651584; BR = 60000; D = 16384; break;
+ case AV1Level61:
+ SR = 2189721600L; FS = 35651584; BR = 100000; D = 16384; break;
+ case AV1Level62:
+ SR = 4379443200L; FS = 35651584; BR = 160000; D = 16384; break;
+ case AV1Level63:
+ SR = 4706009088L; FS = 35651584; BR = 160000; D = 16384; break;
+
+ default:
+ ALOGW("Unrecognized level %d for %s", profileLevel.mLevel, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ switch (profileLevel.mProfile) {
+ case AV1ProfileMain8:
+ case AV1ProfileMain10:
+ case AV1ProfileMain10HDR10:
+ case AV1ProfileMain10HDR10Plus:
+ break;
+ default:
+ ALOGW("Unrecognized profile %d for %s", profileLevel.mProfile, mediaType);
+ errors |= ERROR_CAPABILITIES_UNRECOGNIZED;
+ }
+ errors &= ~ERROR_CAPABILITIES_NONE_SUPPORTED;
+ maxBlocksPerSecond = std::max(SR, maxBlocksPerSecond);
+ maxBlocks = std::max(FS, maxBlocks);
+ maxBps = std::max(BR * 1000, maxBps);
+ maxDim = std::max(D, maxDim);
+ }
+
+ const int32_t blockSize = 8;
+ int32_t maxLengthInBlocks = divUp(maxDim, blockSize);
+ maxBlocks = divUp(maxBlocks, blockSize * blockSize);
+ maxBlocksPerSecond = divUp(maxBlocksPerSecond, blockSize * (int64_t)blockSize);
+ applyMacroBlockLimits(
+ maxLengthInBlocks, maxLengthInBlocks,
+ maxBlocks, maxBlocksPerSecond,
+ blockSize, blockSize,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ } else {
+ ALOGW("Unsupported mime %s", mediaType);
+ // using minimal bitrate here. should be overridden by
+ // info from media_codecs.xml
+ maxBps = 64000;
+ errors |= ERROR_CAPABILITIES_UNSUPPORTED;
+ }
+ mBitrateRange = Range(1, maxBps);
+ mError |= errors;
+}
+
+} // namespace android
\ No newline at end of file