Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 10822e63 authored by Fyodor Kyslov's avatar Fyodor Kyslov Committed by Cherrypicker Worker
Browse files

Implement codec capabilities enforcement.

Codecs declare their capabilities via media_codecs.xml files, however
these capabilities are not enforced in the framework. This change
provides mechanism for codecs to opt-in to the enforcement. At the
moment the only enforced capabilities are max block-count and max size.

Bug: b/281585476
Test: manual test on AV1 SW Decoder
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:17300fb926e7afdd603749d36b7e3332ef779960)
Merged-In: Ia3f026c6b604704e5bcff42e17a5f4fc6c7767c5
Change-Id: Ia3f026c6b604704e5bcff42e17a5f4fc6c7767c5
parent 38f74691
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -637,6 +637,10 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
            if (encoder) {
                attrs |= MediaCodecInfo::kFlagIsEncoder;
            }
            if (codec.quirkSet.find("attribute::enforce-xml-capabilities") !=
                codec.quirkSet.end()) {
                attrs |= MediaCodecInfo::kFlagIsEnforceXmlCapabilities;
            }
            if (trait.owner == "software") {
                attrs |= MediaCodecInfo::kFlagIsSoftwareOnly;
            } else {
+105 −0
Original line number Diff line number Diff line
@@ -102,6 +102,111 @@ status_t MediaCodecInfo::Capabilities::writeToParcel(Parcel *parcel) const {
    return OK;
}

static int32_t convertToIntNoSign(const AString &str) {
    char *end;
    unsigned long u = strtoul(str.c_str(), &end, 10);
    if (end == str.c_str() || *end != '\0') {
        // malformed integer
        return -1;
    }
    if (u > INT32_MAX) {
        // The number is too big
        return -1;
    }
    return static_cast<int32_t>(u);
}

static void parseSize(const AString &str, int32_t *width, int32_t *height) {
    ssize_t ix = str.find("x");
    if (ix == -1) {
        ix = str.find("*");
        if (ix == -1) {
            return;
        }
    }
    AString wStr(str, 0, ix);
    AString hStr(str, ix + 1, str.size() - ix - 1);
    *width = convertToIntNoSign(wStr);
    *height = convertToIntNoSign(hStr);
}

static void parseRange(const AString &str, int32_t *min, int32_t *max) {
    ssize_t ix = str.find("-");
    if (ix == -1) {
        return;
    }
    AString minStr(str, 0, ix);
    AString maxStr(str, ix + 1, str.size() - ix - 1);
    *min = convertToIntNoSign(minStr);
    *max = convertToIntNoSign(maxStr);
}

static void parseSizeRange(const AString &str, int32_t *minWidth, int32_t *minHeight,
                           int32_t *maxWidth, int32_t *maxHeight) {
    ssize_t ix = str.find("-");
    if (ix == -1) {
        return;
    }
    AString minSize(str, 0, ix);
    AString maxSize(str, ix + 1, str.size() - ix - 1);
    parseSize(minSize, minWidth, minHeight);
    parseSize(maxSize, maxWidth, maxHeight);
}


bool MediaCodecInfo::Capabilities::isResolutionSupported(int32_t width, int32_t height) {
    AString blockSizeStr;
    AString blockCountStr;
    int32_t blockWidth = -1;
    int32_t blockHeight = -1;
    int32_t maxBlocks = -1;
    int32_t minBlocks = -1;

    if (mDetails->findString("block-size", &blockSizeStr)) {
        parseSize(blockSizeStr, &blockWidth, &blockHeight);
    }
    if (mDetails->findString("block-count-range", &blockCountStr)) {
        parseRange(blockCountStr, &minBlocks, &maxBlocks);
    }
    if (maxBlocks != -1 && blockWidth != -1 && blockHeight != -1) {
        if (maxBlocks < ((width + blockWidth - 1) / blockWidth) *
                         ((height + blockHeight - 1) / blockHeight)) {
            return false;
        }
    }

    AString sizeRangeStr;
    int32_t maxWidth = -1;
    int32_t maxHeight = -1;
    int32_t minWidth = -1;
    int32_t minHeight = -1;

    if (mDetails->findString("size-range", &sizeRangeStr)) {
        parseSizeRange(sizeRangeStr, &minWidth, &minHeight, &maxWidth, &maxHeight);
    }

    if (maxWidth != -1 && maxHeight != -1) {
        // The logic is that the format is not supported if width or height is outside
        // of min-max limits, UNLESS codec allows to swap it and in this case format is
        // not supported if width is outside of min-max height or height is outside of
        // min-max width
        if (width < minWidth || height < minHeight ||
            width > maxWidth || height > maxHeight) {
            int32_t swappable = 0;
            if (!mDetails->findInt32("feature-can-swap-width-height", &swappable) ||
                swappable == 0) {
                return false;
            }
            if (width < minHeight || height < minWidth ||
                width > maxHeight || height > maxWidth) {
                return false;
            }
        }
    }
    return true;
}


void MediaCodecInfo::CapabilitiesWriter::addDetail(
        const char* key, const char* value) {
    mCap->mDetails->setString(key, value);
+3 −0
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ struct MediaCodecInfo : public RefBase {
        kFlagIsVendor = 1 << 1,
        kFlagIsSoftwareOnly = 1 << 2,
        kFlagIsHardwareAccelerated = 1 << 3,
        kFlagIsEnforceXmlCapabilities = 1 << 4,
    };

    struct Capabilities : public RefBase {
@@ -96,6 +97,8 @@ struct MediaCodecInfo : public RefBase {
         */
        const sp<AMessage> getDetails() const;

        bool isResolutionSupported(int32_t width, int32_t height);

    protected:
        Vector<ProfileLevel> mProfileLevels;
        SortedVector<ProfileLevel> mProfileLevelsSorted;
+53 −1
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@

#include "include/SoftwareRenderer.h"

#include <android/api-level.h>
#include <android/binder_manager.h>
#include <android/content/pm/IPackageManagerNative.h>
#include <android/hardware/cas/native/1.0/IDescrambler.h>
@@ -2043,6 +2044,40 @@ status_t MediaCodec::configure(
    return configure(format, nativeWindow, crypto, NULL, flags);
}

bool MediaCodec::isResolutionSupported(const sp<AMessage>& format) {
    int32_t width = -1;
    int32_t height = -1;
    int32_t maxWidth = -1;
    int32_t maxHeight = -1;
    format->findInt32("width", &width);
    format->findInt32("height", &height);
    format->findInt32("max-width", &maxWidth);
    format->findInt32("max-height", &maxHeight);
    AString mediaType;
    if (!format->findString("mime", &mediaType)) {
        ALOGI("Can not check mediaFormat: No MIME set.");
        return true;
    }
    sp<MediaCodecInfo::Capabilities> caps = mCodecInfo->getCapabilitiesFor(mediaType.c_str());
    if (caps == NULL) {
        ALOGI("Can not get Capabilities for MIME %s.", mediaType.c_str());
        return true;
    }
    if (width != -1 && height != -1) {
        if (!caps->isResolutionSupported(width, height)) {
            ALOGD("Frame resolution (%dx%d) is beyond codec capabilities", width, height);
            return false;
        }
    }
    if (maxWidth != -1 && maxHeight != -1) {
        if (!caps->isResolutionSupported(maxWidth, maxHeight)) {
            ALOGD("Max frame resolution (%dx%d) is beyond codec capabilities", maxWidth, maxHeight);
            return false;
        }
    }
    return true;
}

status_t MediaCodec::configure(
        const sp<AMessage> &format,
        const sp<Surface> &surface,
@@ -2130,7 +2165,24 @@ status_t MediaCodec::configure(
            mediametrics_delete(nextMetricsHandle);
            return BAD_VALUE;
        }

        // For applications built with targetSdkVersion of Android U or later (or if MediaCodec's
        // caller is not an app) we enforce codec resolution capabilities if such enforcement is
        // required by 'enforce-xml-capabilities' attribute
        if (android_get_application_target_sdk_version() >= __ANDROID_API_U__) {
            if (mCodecInfo != nullptr &&
                (mCodecInfo->getAttributes() &
                 MediaCodecInfo::kFlagIsEnforceXmlCapabilities)) {
                if (!isResolutionSupported(format)) {
                    mErrorLog.log(LOG_TAG,
                                  base::StringPrintf("The input resolution of %dx%d is not "
                                  "supported for this codec; please query MediaCodecList "
                                  "for the supported formats including the resolution. See "
                                  "CodecCapabilities#isFormatSupported() and "
                                  "VideoCapabilities#isSizeSupported()", mWidth, mHeight));
                    return BAD_VALUE;
                }
            }
        }
    } else {
        if (nextMetricsHandle != 0) {
            int32_t channelCount;
+1 −0
Original line number Diff line number Diff line
@@ -462,6 +462,7 @@ private:
    constexpr const char *asString(TunnelPeekState state, const char *default_string="?");
    void updateTunnelPeek(const sp<AMessage> &msg);
    void processRenderedFrames(const sp<AMessage> &msg);
    bool isResolutionSupported(const sp<AMessage> &format);

    inline void initClientConfigParcel(ClientConfigParcel& clientConfig);