/*
 * 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