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) {