Tuning mediaformatshaper
-- allow per-resolution QPmax setting
-- additional tuning for Android 12 release
Bug: 183211971
Test: videoquality tests
Change-Id: I763c7f56016ab335b4f73fde1eda410991a8c7b5
diff --git a/media/libmediaformatshaper/CodecProperties.cpp b/media/libmediaformatshaper/CodecProperties.cpp
index b118af6..c57b693 100644
--- a/media/libmediaformatshaper/CodecProperties.cpp
+++ b/media/libmediaformatshaper/CodecProperties.cpp
@@ -53,13 +53,6 @@
mMinimumQuality = vmaf;
}
-int CodecProperties::targetQpMax() {
- return mTargetQpMax;
-}
-void CodecProperties::setTargetQpMax(int qpMax) {
- mTargetQpMax = qpMax;
-}
-
void CodecProperties::setMissingQpBoost(double boost) {
mMissingQpBoost = boost;
}
@@ -118,6 +111,11 @@
setTargetQpMax(iValue);
legal = true;
}
+ } else if (!strncmp(key.c_str(), "vq-target-qpmax-", strlen("vq-target-qpmax-"))) {
+ std::string resolution = key.substr(strlen("vq-target-qpmax-"));
+ if (qpMaxPoint(resolution, value)) {
+ legal = true;
+ }
} else if (!strcmp(key.c_str(), "vq-target-bpp")) {
const char *p = value.c_str();
char *q;
@@ -292,6 +290,131 @@
return mBpp;
}
+bool CodecProperties::qpMaxPoint(std::string resolution, std::string value) {
+
+ int32_t width = 0;
+ int32_t height = 0;
+ int qpMax = INT32_MAX;
+
+ // 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;
+ qpMax = strtol(p, &q, 0);
+ if (q == p) {
+ ALOGW("unparseable qpmax '%s'", value.c_str());
+ return false;
+ }
+
+ // convert to our internal 'unspecified' notation
+ if (qpMax == -1)
+ qpMax = INT32_MAX;
+
+ struct qpmax_point *point = (struct qpmax_point*) malloc(sizeof(*point));
+ if (point == nullptr) {
+ ALOGW("unable to allocate memory for qpmax point");
+ return false;
+ }
+
+ point->pixels = width * height;
+ point->width = width;
+ point->height = height;
+ point->qpMax = qpMax;
+
+ if (mQpMaxPoints == nullptr) {
+ point->next = nullptr;
+ mQpMaxPoints = point;
+ } else if (point->pixels < mQpMaxPoints->pixels) {
+ // at the front
+ point->next = mQpMaxPoints;
+ mQpMaxPoints = point;
+ } else {
+ struct qpmax_point *after = mQpMaxPoints;
+ 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;
+}
+
+int CodecProperties::targetQpMax(int32_t width, int32_t height) {
+ // look in the per-resolution list
+
+ int32_t pixels = width * height;
+
+ if (mQpMaxPoints) {
+ struct qpmax_point *point = mQpMaxPoints;
+ while (point && point->pixels < pixels) {
+ point = point->next;
+ }
+ if (point) {
+ ALOGV("targetQpMax(w=%d,h=%d) returns %d from qpmax_point w=%d h=%d",
+ width, height, point->qpMax, point->width, point->height);
+ return point->qpMax;
+ }
+ }
+
+ ALOGV("defaulting to %d qpmax", mTargetQpMax);
+ return mTargetQpMax;
+}
+
+void CodecProperties::setTargetQpMax(int qpMax) {
+ // convert to our internal 'unspecified' notation
+ if (qpMax == -1)
+ qpMax = INT32_MAX;
+ mTargetQpMax = qpMax;
+}
+
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
diff --git a/media/libmediaformatshaper/CodecProperties.h b/media/libmediaformatshaper/CodecProperties.h
index 98ab99e..196a40f 100644
--- a/media/libmediaformatshaper/CodecProperties.h
+++ b/media/libmediaformatshaper/CodecProperties.h
@@ -69,7 +69,7 @@
// qp max bound used to compensate when SupportedMinimumQuality == 0
// 0 == let a system default handle it
void setTargetQpMax(int qpmax);
- int targetQpMax();
+ int targetQpMax(int32_t width, int32_t height);
// target bits-per-pixel (per second) for encoding operations.
// This is used to calculate a minimum bitrate for any particular resolution.
@@ -123,6 +123,19 @@
struct bpp_point *mBppPoints = nullptr;
bool bppPoint(std::string resolution, std::string value);
+ // same thing for qpmax -- allow different ones based on resolution
+ // 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 qpmax_point {
+ struct qpmax_point *next;
+ int32_t pixels;
+ int32_t width, height;
+ int qpMax;
+ };
+ struct qpmax_point *mQpMaxPoints = nullptr;
+ bool qpMaxPoint(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)*/ ;
diff --git a/media/libmediaformatshaper/CodecSeeding.cpp b/media/libmediaformatshaper/CodecSeeding.cpp
index 292b6df..2f2e29d 100644
--- a/media/libmediaformatshaper/CodecSeeding.cpp
+++ b/media/libmediaformatshaper/CodecSeeding.cpp
@@ -49,28 +49,39 @@
*/
static preloadTuning_t featuresAvc[] = {
+ {true, "vq-target-bpp", "0"},
{true, "vq-target-bpp-1080p", "1.90"},
{true, "vq-target-bpp-720p", "2.25"},
{true, "vq-target-bpp-540p", "2.65"},
{true, "vq-target-bpp-480p", "3.00"},
- {true, "vq-target-qpmax", "40"},
+ {true, "vq-target-qpmax", "-1"},
+ {true, "vq-target-qpmax-1080p", "45"},
+ {true, "vq-target-qpmax-720p", "43"},
+ {true, "vq-target-qpmax-540p", "42"},
+ {true, "vq-target-qpmax-480p", "38"},
{true, "vq-bitrate-phaseout", "1.75"},
{true, "vq-boost-missing-qp", "0.20"},
{true, nullptr, 0}
};
static preloadTuning_t featuresHevc[] = {
+ {true, "vq-target-bpp", "0"},
{true, "vq-target-bpp-1080p", "1.50"},
{true, "vq-target-bpp-720p", "1.80"},
{true, "vq-target-bpp-540p", "2.10"},
- {true, "vq-target-qpmax", "40"},
+ {true, "vq-target-qpmax", "-1"},
+ {true, "vq-target-qpmax-1080p", "45"},
+ {true, "vq-target-qpmax-720p", "43"},
+ {true, "vq-target-qpmax-540p", "42"},
+ {true, "vq-target-qpmax-480p", "39"},
{true, "vq-bitrate-phaseout", "1.75"},
{true, "vq-boost-missing-qp", "0.20"},
{true, nullptr, 0}
};
static preloadTuning_t featuresGenericVideo[] = {
- {true, "vq-target-bpp", "2.00"},
+ // 0 == off
+ {true, "vq-target-bpp", "0"},
{true, nullptr, 0}
};
diff --git a/media/libmediaformatshaper/VQApply.cpp b/media/libmediaformatshaper/VQApply.cpp
index 28ce43f..585ec6c 100644
--- a/media/libmediaformatshaper/VQApply.cpp
+++ b/media/libmediaformatshaper/VQApply.cpp
@@ -115,7 +115,7 @@
bool qpPresent = hasQpMax(inFormat);
// calculate a target QP value
- int32_t qpmax = codec->targetQpMax();
+ int32_t qpmax = codec->targetQpMax(width, height);
if (!qpPresent) {
// user didn't, so shaper wins
if (qpmax != INT32_MAX) {