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

Commit 970f1c8a authored by Ray Essick's avatar Ray Essick
Browse files

Update mediaformatshaper library

better default handling
defer library load until time of use (memory vs latency)

Bug: 182827840
Test: manual
Change-Id: I557ea3d82814dd5758396181210fd46524a26a30
parent 32ec8e85
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ cc_defaults {
    name: "libmediaformatshaper_defaults",
    srcs: [
        "CodecProperties.cpp",
        "CodecSeeding.cpp",
        "FormatShaper.cpp",
        "ManageShapingCodecs.cpp",
        "VideoShaper.cpp",
+33 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ namespace android {
namespace mediaformatshaper {

CodecProperties::CodecProperties(std::string name, std::string mediaType) {
    ALOGV("CodecProperties(%s, %s)", name.c_str(), mediaType.c_str());
    mName = name;
    mMediaType = mediaType;
}
@@ -58,6 +59,38 @@ int CodecProperties::supportedApi() {
    return mApi;
}

void CodecProperties::setFeatureValue(std::string key, int32_t value) {
    ALOGD("setFeatureValue(%s,%d)", key.c_str(), value);
    mFeatures.insert({key, value});

    if (!strcmp(key.c_str(), "vq-minimum-quality")) {
        setSupportedMinimumQuality(value);
    } else if (!strcmp(key.c_str(), "vq-supports-qp")) {      // key from prototyping
        setSupportsQp(1);
    } else if (!strcmp(key.c_str(), "qp-bounds")) {           // official key
        setSupportsQp(1);
    } else if (!strcmp(key.c_str(), "vq-target-qpmax")) {
        setTargetQpMax(value);
    } else if (!strcmp(key.c_str(), "vq-target-bppx100")) {
        double bpp = value / 100.0;
        setBpp(bpp);
    }
}

bool CodecProperties::getFeatureValue(std::string key, int32_t *valuep) {
    ALOGV("getFeatureValue(%s)", key.c_str());
    if (valuep == nullptr) {
        return false;
    }
    auto mapped = mFeatures.find(key);
    if (mapped != mFeatures.end()) {
        *valuep = mapped->second;
        return true;
    }
    return false;
}


std::string CodecProperties::getMapping(std::string key, std::string kind) {
    ALOGV("getMapping(key %s, kind %s )", key.c_str(), kind.c_str());
    //play with mMappings
+127 −0
Original line number Diff line number Diff line
/*
 * Copyright 2021, 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 "CodecSeeding"
#include <utils/Log.h>

#include <string>

#include <media/formatshaper/CodecProperties.h>

namespace android {
namespace mediaformatshaper {

/*
 * a block of pre-loads; things the library seeds into the codecproperties based
 * on the mediaType.
 * XXX: parsing from a file is likely better than embedding in code.
 */
typedef struct {
    const char *key;
    int32_t value;
} preloadFeature_t;

typedef struct {
    const char *mediaType;
    preloadFeature_t *features;
} preloadProperties_t;

/*
 * 240 = 2.4 bits per pixel-per-second == 5mbps@1080, 2.3mbps@720p, which is about where
 * we want our initial floor for now.
 */

static preloadFeature_t featuresAvc[] = {
      {"vq-target-bppx100", 240},
      {nullptr, 0}
};

static preloadFeature_t featuresHevc[] = {
      {"vq-target-bppx100", 240},
      {nullptr, 0}
};

static preloadFeature_t featuresGenericVideo[] = {
      {"vq-target-bppx100", 240},
      {nullptr, 0}
};

static preloadProperties_t preloadProperties[] = {
    { "video/avc", featuresAvc},
    { "video/hevc", &featuresHevc[0]},

    // wildcard for any video format not already captured
    { "video/*", &featuresGenericVideo[0]},
    { nullptr, nullptr}
};

void CodecProperties::Seed() {
    ALOGV("Seed: for codec %s, mediatype %s", mName.c_str(), mMediaType.c_str());

    // load me up with initial configuration data
    int count = 0;
    for (int i=0;; i++) {
        preloadProperties_t *p = &preloadProperties[i];
        if (p->mediaType == nullptr) {
            break;
        }
        bool found = false;
        if (strcmp(p->mediaType, mMediaType.c_str()) == 0) {
            found = true;
        }
        const char *r;
        if (!found && (r = strchr(p->mediaType, '*')) != NULL) {
            // wildcard; check the prefix
            size_t len = r - p->mediaType;
            if (strncmp(p->mediaType, mMediaType.c_str(), len) == 0) {
                found = true;
            }
        }

        if (!found) {
            continue;
        }
        ALOGV("seeding from mediaType '%s'", p->mediaType);

        // walk through, filling things
        if (p->features != nullptr) {
            for (int j=0;; j++) {
                preloadFeature_t *q = &p->features[j];
                if (q->key == nullptr) {
                    break;
                }
                setFeatureValue(q->key, q->value);
                count++;
            }
            break;
        }
    }
    ALOGV("loaded %d preset values", count);
}

// a chance, as we register the codec and accept no further updates, to
// override any poor configuration that arrived from the device's XML files.
//
void CodecProperties::Finish() {
    ALOGV("Finish: for codec %s, mediatype %s", mName.c_str(), mMediaType.c_str());

    // currently a no-op
}

} // namespace mediaformatshaper
} // namespace android
+12 −13
Original line number Diff line number Diff line
@@ -93,19 +93,9 @@ int setFeature(shaperHandle_t shaper, const char *feature, int value) {
        return -1;
    }

    if (!strcmp(feature, "vq-minimum-quality")) {
        codec->setSupportedMinimumQuality(value);
    } else if (!strcmp(feature, "vq-supports-qp")) {
        codec->setSupportsQp(value != 0);
    } else if (!strcmp(feature, "vq-target-qpmax")) {
        codec->setTargetQpMax(value);
    } else if (!strcmp(feature, "vq-target-bppx100")) {
        double bpp = value / 100.0;
        codec->setBpp(bpp);
    } else {
        // changed nothing, don't mark as configured
        return 0;
    }
    // save a map of all features
    codec->setFeatureValue(feature, value);

    return 0;
}

@@ -120,6 +110,9 @@ shaperHandle_t findShaper(const char *codecName, const char *mediaType) {

shaperHandle_t createShaper(const char *codecName, const char *mediaType) {
    CodecProperties *codec = new CodecProperties(codecName, mediaType);
    if (codec != nullptr) {
        codec->Seed();
    }
    return (shaperHandle_t) codec;
}

@@ -134,6 +127,12 @@ shaperHandle_t registerShaper(shaperHandle_t shaper, const char *codecName, cons
        return nullptr;
    }

    // any final cleanup for the parameters. This allows us to override
    // bad parameters from a devices XML file.
    codec->Finish();

    // may return a different codec, if we lost a race.
    // if so, registerCodec() reclaims the one we tried to register for us.
    codec = registerCodec(codec, codecName, mediaType);
    return (shaperHandle_t) codec;
}
+43 −25
Original line number Diff line number Diff line
@@ -44,42 +44,60 @@ namespace mediaformatshaper {
#define	AMEDIAFORMAT_VIDEO_QP_P_MAX	"video-qp-p-max"
#define	AMEDIAFORMAT_VIDEO_QP_P_MIN	"video-qp-p-min"

// defined in the SDK, but not in the NDK
//
static const int BITRATE_MODE_VBR = 1;

//
// Caller retains ownership of and responsibility for inFormat
//
int VQApply(CodecProperties *codec, vqOps_t *info, AMediaFormat* inFormat, int flags) {
    ALOGV("codecName %s inFormat %p flags x%x", codec->getName().c_str(), inFormat, flags);

    int32_t bitRateMode = -1;
    if (AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BITRATE_MODE, &bitRateMode)
        && bitRateMode != BITRATE_MODE_VBR) {
        ALOGD("minquality: applies only to VBR encoding");
        return 0;
    }

    if (codec->supportedMinimumQuality() > 0) {
        // allow the codec provided minimum quality behavior to work at it
        ALOGD("minquality(codec): codec says %d", codec->supportedMinimumQuality());
        ALOGD("minquality: codec claims to implement minquality=%d",
              codec->supportedMinimumQuality());
        return 0;
    }

    ALOGD("considering other ways to improve quality...");

    //
    // apply any and all tools that we have.
    // -- qp
    // -- minimum bits-per-pixel
    //
    if (codec->supportsQp()) {
    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
        //
        // XXX: augment this so that we don't lower an existing QP setting
        // (e.g. if user set it to 40, we don't want to set it back to 45)
        int qpmax = codec->targetQpMax();
        if (qpmax <= 0) {
                qpmax = 45;
                ALOGD("use default substitute QpMax == %d", qpmax);
        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

        if (qpmax > 0) {
            ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpmax);
            AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpmax);

            // force spreading the QP across frame types, since we imposing a value
            qpSpreadMaxPerFrameType(inFormat, info->qpDelta, info->qpMax, /* override */ true);
    } else {
        ALOGD("codec %s: no qp bounding", codec->getName().c_str());
        }
    }

    double bpp = codec->getBpp();
@@ -108,7 +126,7 @@ int VQApply(CodecProperties *codec, vqOps_t *info, AMediaFormat* inFormat, int f
              bitrateConfigured, bitrateFloor, codec->getBpp(), height, width);

        if (bitrateConfigured < bitrateFloor) {
            ALOGD("minquality/target bitrate raised from %d to %" PRId64 " to maintain quality",
            ALOGD("minquality/target bitrate raised from %d to %" PRId64 " bps",
                  bitrateConfigured, bitrateFloor);
            AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateFloor);
        }
@@ -121,16 +139,16 @@ int VQApply(CodecProperties *codec, vqOps_t *info, AMediaFormat* inFormat, int f
bool hasQpPerFrameType(AMediaFormat *format) {
    int32_t value;

    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &value)
        || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &value)) {
    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &value)
        || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &value)) {
        return true;
    }
    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &value)
        || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &value)) {
    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &value)
        || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &value)) {
        return true;
    }
    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &value)
        || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &value)) {
    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &value)
        || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &value)) {
        return true;
    }
    return false;
@@ -138,8 +156,8 @@ bool hasQpPerFrameType(AMediaFormat *format) {

bool hasQp(AMediaFormat *format) {
    int32_t value;
    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &value)
        || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &value)) {
    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &value)
        || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &value)) {
        return true;
    }
    return hasQpPerFrameType(format);
Loading