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

Commit ecc690d5 authored by Ray Essick's avatar Ray Essick Committed by Android (Google) Code Review
Browse files

Merge "Updated shaping algorithm" into sc-dev

parents 721f69a8 244aaa5f
Loading
Loading
Loading
Loading
+123 −0
Original line number Diff line number Diff line
@@ -23,6 +23,10 @@

#include <media/formatshaper/CodecProperties.h>


// we aren't going to mess with shaping points dimensions beyond this
static const int32_t DIMENSION_LIMIT = 16384;

namespace android {
namespace mediaformatshaper {

@@ -113,7 +117,13 @@ void CodecProperties::setTuningValue(std::string key, std::string value) {
            setBpp(bpp);
            legal = true;
        }
    } else if (!strncmp(key.c_str(), "vq-target-bpp-", strlen("vq-target-bpp-"))) {
            std::string resolution = key.substr(strlen("vq-target-bpp-"));
            if (bppPoint(resolution, value)) {
                legal = true;
            }
    } else if (!strcmp(key.c_str(), "vq-target-bppx100")) {
        // legacy, prototyping
        const char *p = value.c_str();
        char *q;
        int32_t iValue =  strtol(p, &q, 0);
@@ -143,6 +153,119 @@ bool CodecProperties::getTuningValue(std::string key, std::string &value) {
    return false;
}

bool CodecProperties::bppPoint(std::string resolution, std::string value) {

    int32_t width = 0;
    int32_t height = 0;
    double bpp = -1;

    // resolution is "WxH", "W*H" or a standard name like "720p"
    if (resolution == "1080p") {
        width = 1080; height = 1920;
    } else if (resolution == "720p") {
        width = 720; height = 1280;
    } else if (resolution == "540p") {
        width = 540; height = 960;
    } else if (resolution == "480p") {
        width = 480; height = 854;
    } else {
        size_t sep = resolution.find('x');
        if (sep == std::string::npos) {
            sep = resolution.find('*');
        }
        if (sep == std::string::npos) {
            ALOGW("unable to parse resolution: '%s'", resolution.c_str());
            return false;
        }
        std::string w = resolution.substr(0, sep);
        std::string h = resolution.substr(sep+1);

        char *q;
        const char *p = w.c_str();
        width = strtol(p, &q, 0);
        if (q == p) {
                width = -1;
        }
        p = h.c_str();
        height = strtol(p, &q, 0);
        if (q == p) {
                height = -1;
        }
        if (width <= 0 || height <= 0 || width > DIMENSION_LIMIT || height > DIMENSION_LIMIT) {
            ALOGW("unparseable: width, height '%s'", resolution.c_str());
            return false;
        }
    }

    const char *p = value.c_str();
    char *q;
    bpp = strtod(p, &q);
    if (q == p) {
        ALOGW("unparseable bpp '%s'", value.c_str());
        return false;
    }

    struct bpp_point *point = (struct bpp_point*) malloc(sizeof(*point));
    if (point == nullptr) {
        ALOGW("unable to allocate memory for bpp point");
        return false;
    }

    point->pixels = width * height;
    point->width = width;
    point->height = height;
    point->bpp = bpp;

    if (mBppPoints == nullptr) {
        point->next = nullptr;
        mBppPoints = point;
    } else if (point->pixels < mBppPoints->pixels) {
        // at the front
        point->next = mBppPoints;
        mBppPoints = point;
    } else {
        struct bpp_point *after = mBppPoints;
        while (after->next) {
            if (point->pixels > after->next->pixels) {
                after = after->next;
                continue;
            }

            // insert before after->next
            point->next = after->next;
            after->next = point;
            break;
        }
        if (after->next == nullptr) {
            // hasn't gone in yet
            point->next = nullptr;
            after->next = point;
        }
    }

    return true;
}

double CodecProperties::getBpp(int32_t width, int32_t height) {
    // look in the per-resolution list

    int32_t pixels = width * height;

    if (mBppPoints) {
        struct bpp_point *point = mBppPoints;
        while (point && point->pixels < pixels) {
            point = point->next;
        }
        if (point) {
            ALOGV("getBpp(w=%d,h=%d) returns %f from bpppoint w=%d h=%d",
                width, height, point->bpp, point->width, point->height);
            return point->bpp;
        }
    }

    ALOGV("defaulting to %f bpp", mBpp);
    return mBpp;
}

std::string CodecProperties::getMapping(std::string key, std::string kind) {
    ALOGV("getMapping(key %s, kind %s )", key.c_str(), kind.c_str());
+5 −2
Original line number Diff line number Diff line
@@ -50,13 +50,16 @@ typedef struct {

static preloadTuning_t featuresAvc[] = {
      {true, "vq-target-bpp", "2.45"},
      {true, "vq-target-qpmax", "41"},
      {true, "vq-target-bpp-1080p", "2.40"},
      {true, "vq-target-bpp-540p", "2.60"},
      {true, "vq-target-bpp-480p", "3.00"},
      {true, "vq-target-qpmax", "40"},
      {true, nullptr, 0}
};

static preloadTuning_t featuresHevc[] = {
      {true, "vq-target-bpp", "2.30"},
      {true, "vq-target-qpmax", "42"}, // nop, since hevc codecs don't declare qp support
      {true, "vq-target-qpmax", "40"}, // nop, since hevc codecs don't declare qp support
      {true, nullptr, 0}
};

+72 −50
Original line number Diff line number Diff line
@@ -48,6 +48,15 @@ namespace mediaformatshaper {
//
static const int BITRATE_MODE_VBR = 1;


// constants we use within the calculations
//
constexpr double BITRATE_LEAVE_UNTOUCHED = 2.0;
constexpr double BITRATE_QP_UNAVAILABLE = 1.20;
// 10% didn't work so hot on bonito (with no QP support)
// 15% is next.. still leaves a few short
// 20% ? this is on the edge of what I want do do

//
// Caller retains ownership of and responsibility for inFormat
//
@@ -69,67 +78,80 @@ int VQApply(CodecProperties *codec, vqOps_t *info, AMediaFormat* inFormat, int f
    }

    //
    // apply any and all tools that we have.
    // consider any and all tools available
    // -- qp
    // -- minimum bits-per-pixel
    //
    if (!codec->supportsQp()) {
        ALOGD("minquality: no qp bounding in codec %s", codec->getName().c_str());
    } else {
        // use a (configurable) QP value to force better quality
        //
        int32_t qpmax = codec->targetQpMax();
        int32_t qpmaxUser = INT32_MAX;
        if (hasQp(inFormat)) {
            (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &qpmaxUser);
            ALOGD("minquality by QP: format already sets QP");
        }

        // if the system didn't do one, use what the user provided
        if (qpmax == 0 && qpmaxUser != INT32_MAX) {
                qpmax = qpmaxUser;
        }
        // XXX: if both said something, how do we want to reconcile that
    int64_t bitrateChosen = 0;
    int32_t qpChosen = INT32_MAX;

        if (qpmax > 0) {
            ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpmax);
            AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpmax);
    int64_t bitrateConfigured = 0;
    int32_t bitrateConfiguredTmp = 0;
    (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfiguredTmp);
    bitrateConfigured = bitrateConfiguredTmp;
    bitrateChosen = bitrateConfigured;

            // force spreading the QP across frame types, since we imposing a value
            qpSpreadMaxPerFrameType(inFormat, info->qpDelta, info->qpMax, /* override */ true);
        }
    }

    double bpp = codec->getBpp();
    if (bpp > 0.0) {
        // if we've decided to use bits-per-pixel (per second) to drive the quality
        //
        // (properly phrased as 'bits per second per pixel' so that it's resolution
        // and framerate agnostic
        //
        // all of these is structured so that a missing value cleanly gets us to a
        // non-faulting value of '0' for the minimum bits-per-pixel.
        //
    int32_t width = 0;
    (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_WIDTH, &width);
    int32_t height = 0;
    (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_HEIGHT, &height);
        int32_t bitrateConfigured = 0;
        (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfigured);

    int64_t pixels = ((int64_t)width) * height;
        int64_t bitrateFloor = pixels * bpp;
    double minimumBpp = codec->getBpp(width, height);

    int64_t bitrateFloor = pixels * minimumBpp;
    if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX;

        ALOGD("minquality/bitrate: target %d floor %" PRId64 "(%.3f bpp * (%d w * %d h)",
              bitrateConfigured, bitrateFloor, codec->getBpp(), height, width);
    // if we are far enough above the target bpp, leave it alone
    //
    ALOGV("bitrate: configured %" PRId64 " floor %" PRId64, bitrateConfigured, bitrateFloor);
    if (bitrateConfigured >= BITRATE_LEAVE_UNTOUCHED * bitrateFloor) {
        ALOGV("high enough bitrate: configured %" PRId64 " >= %f * floor %" PRId64,
                bitrateConfigured, BITRATE_LEAVE_UNTOUCHED, bitrateFloor);
        return 0;
    }

    // raise anything below the bitrate floor
    if (bitrateConfigured < bitrateFloor) {
            ALOGD("minquality/target bitrate raised from %d to %" PRId64 " bps",
        ALOGD("raise bitrate: configured %" PRId64 " to floor %" PRId64,
                bitrateConfigured, bitrateFloor);
            AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateFloor);
        bitrateChosen = bitrateFloor;
    }

    bool qpPresent = hasQp(inFormat);

    // add QP, if not already present
    if (!qpPresent) {
        int32_t qpmax = codec->targetQpMax();
        if (qpmax != INT32_MAX) {
            ALOGV("choosing qp=%d", qpmax);
            qpChosen = qpmax;
        }
    }

    // if QP is desired but not supported, compensate with additional bits
    if (!codec->supportsQp()) {
        if (qpPresent || qpChosen != INT32_MAX) {
            ALOGD("minquality: desired QP, but unsupported, boost bitrate %" PRId64 " to %" PRId64,
                bitrateChosen, (int64_t)(bitrateChosen * BITRATE_QP_UNAVAILABLE));
            bitrateChosen =  bitrateChosen * BITRATE_QP_UNAVAILABLE;
            qpChosen = INT32_MAX;
        }
    }

    // apply our chosen values
    //
    if (qpChosen != INT32_MAX) {
        ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
        AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);

        // force spreading the QP across frame types, since we are imposing a value
        qpSpreadMaxPerFrameType(inFormat, info->qpDelta, info->qpMax, /* override */ true);
    }

    if (bitrateChosen != bitrateConfigured) {
        ALOGD("minquality/target bitrate raised from %" PRId64 " to %" PRId64 " bps",
              bitrateConfigured, bitrateChosen);
        AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateChosen);
    }

    return 0;
+16 −2
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@
#include <mutex>
#include <string>

#include <inttypes.h>

#include <utils/RefBase.h>

namespace android {
@@ -73,7 +75,7 @@ class CodecProperties {
    // This is used to calculate a minimum bitrate for any particular resolution.
    // A 1080p (1920*1080 = 2073600 pixels) to be encoded at 5Mbps has a bpp == 2.41
    void setBpp(double bpp) { mBpp = bpp;}
    double getBpp() {return mBpp;}
    double getBpp(int32_t width, int32_t height);

    // Does this codec support QP bounding
    // The getMapping() methods provide any needed mapping to non-standard keys.
@@ -92,10 +94,22 @@ class CodecProperties {
    std::string mMediaType;
    int mApi = 0;
    int mMinimumQuality = 0;
    int mTargetQpMax = 0;
    int mTargetQpMax = INT32_MAX;
    bool mSupportsQp = false;
    double mBpp = 0.0;

    // allow different target bits-per-pixel based on resolution
    // similar to codec 'performance points'
    // uses 'next largest' (by pixel count) point as minimum bpp
    struct bpp_point {
        struct bpp_point *next;
        int32_t pixels;
        int32_t width, height;
        double bpp;
    };
    struct bpp_point *mBppPoints = nullptr;
    bool bppPoint(std::string resolution, std::string value);

    std::mutex mMappingLock;
    // XXX figure out why I'm having problems getting compiler to like GUARDED_BY
    std::map<std::string, std::string> mMappings /*GUARDED_BY(mMappingLock)*/ ;