Update MediaFormatShaper

-- differentiate internal vs exported header files
-- handle when both shaper and app specify QP
-- use defined constant for QP feature parameters instead of literal
-- HEVC values for bits-per-pixel based on experimentation
-- tune upper end of range where we shape (change fewer encodings)

Bug: 183211971
Test: VQ test suite
Change-Id: I6ba5b993878dffdaba12509cdea7a4cecbb819f2
diff --git a/media/libmediaformatshaper/Android.bp b/media/libmediaformatshaper/Android.bp
index 3107e12..bdd1465 100644
--- a/media/libmediaformatshaper/Android.bp
+++ b/media/libmediaformatshaper/Android.bp
@@ -56,6 +56,10 @@
         "include",
     ],
 
+    header_libs: [
+        "libstagefright_headers",
+    ],
+
     shared_libs: [
         "liblog",
         "libutils",
diff --git a/media/libmediaformatshaper/CodecProperties.cpp b/media/libmediaformatshaper/CodecProperties.cpp
index e6b3c46..315b3ec 100644
--- a/media/libmediaformatshaper/CodecProperties.cpp
+++ b/media/libmediaformatshaper/CodecProperties.cpp
@@ -21,7 +21,9 @@
 #include <string>
 #include <stdlib.h>
 
-#include <media/formatshaper/CodecProperties.h>
+#include "CodecProperties.h"
+
+#include <media/stagefright/MediaCodecConstants.h>
 
 
 // we aren't going to mess with shaping points dimensions beyond this
@@ -68,11 +70,11 @@
     ALOGD("setFeatureValue(%s,%d)", key.c_str(), value);
     mFeatures.insert({key, value});
 
-    if (!strcmp(key.c_str(), "qp-bounds")) {               // official key
+    if (!strcmp(key.c_str(), FEATURE_QpBounds)) {
         setSupportsQp(1);
-    } else if (!strcmp(key.c_str(), "vq-supports-qp")) {   // key from prototyping
-        setSupportsQp(1);
-    } else if (!strcmp(key.c_str(), "vq-minimum-quality")) {
+    } else if (!strcmp(key.c_str(), "video-minimum-quality")) {
+        setSupportedMinimumQuality(1);
+    } else if (!strcmp(key.c_str(), "vq-minimum-quality")) { // from prototyping
         setSupportedMinimumQuality(1);
     }
 }
diff --git a/media/libmediaformatshaper/include/media/formatshaper/CodecProperties.h b/media/libmediaformatshaper/CodecProperties.h
similarity index 100%
rename from media/libmediaformatshaper/include/media/formatshaper/CodecProperties.h
rename to media/libmediaformatshaper/CodecProperties.h
diff --git a/media/libmediaformatshaper/CodecSeeding.cpp b/media/libmediaformatshaper/CodecSeeding.cpp
index a7fcc66..7fe1075 100644
--- a/media/libmediaformatshaper/CodecSeeding.cpp
+++ b/media/libmediaformatshaper/CodecSeeding.cpp
@@ -20,7 +20,7 @@
 
 #include <string>
 
-#include <media/formatshaper/CodecProperties.h>
+#include "CodecProperties.h"
 
 namespace android {
 namespace mediaformatshaper {
@@ -49,7 +49,7 @@
  */
 
 static preloadTuning_t featuresAvc[] = {
-      {true, "vq-target-bpp", "2.45"},
+      // {true, "vq-target-bpp", "2.45"},
       {true, "vq-target-bpp-1080p", "2.40"},
       {true, "vq-target-bpp-540p", "2.60"},
       {true, "vq-target-bpp-480p", "3.00"},
@@ -58,8 +58,11 @@
 };
 
 static preloadTuning_t featuresHevc[] = {
-      {true, "vq-target-bpp", "2.30"},
-      {true, "vq-target-qpmax", "40"}, // nop, since hevc codecs don't declare qp support
+      // {true, "vq-target-bpp", "1.80"},
+      {true, "vq-target-bpp-1080p", "1.50"},
+      {true, "vq-target-bpp-720p", "1.80"},
+      {true, "vq-target-bpp-540p", "2.10"},
+      // no qp for hevc, at least for now
       {true, nullptr, 0}
 };
 
diff --git a/media/libmediaformatshaper/FormatShaper.cpp b/media/libmediaformatshaper/FormatShaper.cpp
index 42502e0..451f772 100644
--- a/media/libmediaformatshaper/FormatShaper.cpp
+++ b/media/libmediaformatshaper/FormatShaper.cpp
@@ -23,10 +23,11 @@
 
 #include <media/NdkMediaFormat.h>
 
-#include <media/formatshaper/VQops.h>
-#include <media/formatshaper/CodecProperties.h>
+#include "CodecProperties.h"
+#include "VideoShaper.h"
+#include "VQops.h"
+
 #include <media/formatshaper/FormatShaper.h>
-#include <media/formatshaper/VideoShaper.h>
 
 namespace android {
 namespace mediaformatshaper {
diff --git a/media/libmediaformatshaper/ManageShapingCodecs.cpp b/media/libmediaformatshaper/ManageShapingCodecs.cpp
index bdc395f..3061d0b 100644
--- a/media/libmediaformatshaper/ManageShapingCodecs.cpp
+++ b/media/libmediaformatshaper/ManageShapingCodecs.cpp
@@ -23,7 +23,8 @@
 #include <inttypes.h>
 
 #include <media/NdkMediaFormat.h>
-#include <media/formatshaper/CodecProperties.h>
+
+#include "CodecProperties.h"
 
 namespace android {
 namespace mediaformatshaper {
diff --git a/media/libmediaformatshaper/VQApply.cpp b/media/libmediaformatshaper/VQApply.cpp
index 08e23cc..4f6a6c3 100644
--- a/media/libmediaformatshaper/VQApply.cpp
+++ b/media/libmediaformatshaper/VQApply.cpp
@@ -23,9 +23,9 @@
 
 #include <media/NdkMediaFormat.h>
 
-#include <media/formatshaper/VQops.h>
-#include <media/formatshaper/CodecProperties.h>
-#include <media/formatshaper/VideoShaper.h>
+#include "VQops.h"
+#include "CodecProperties.h"
+#include "VideoShaper.h"
 
 namespace android {
 namespace mediaformatshaper {
@@ -51,17 +51,18 @@
 
 // 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
+constexpr double BITRATE_LEAVE_UNTOUCHED = 1.75;
+
+// 20% bump if QP is configured but it is unavailable
+constexpr double BITRATE_QP_UNAVAILABLE_BOOST = 0.20;
+
 
 //
 // 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);
+    (void) info; // unused for now
 
     int32_t bitRateMode = -1;
     if (AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BITRATE_MODE, &bitRateMode)
@@ -99,14 +100,16 @@
     double minimumBpp = codec->getBpp(width, height);
 
     int64_t bitrateFloor = pixels * minimumBpp;
+    int64_t bitrateCeiling = bitrateFloor * BITRATE_LEAVE_UNTOUCHED;
     if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX;
+    if (bitrateCeiling > INT32_MAX) bitrateCeiling = INT32_MAX;
 
     // 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);
+    if (bitrateConfigured >= bitrateCeiling) {
+        ALOGV("high enough bitrate: configured %" PRId64 " >= ceiling %" PRId64,
+                bitrateConfigured, bitrateCeiling);
         return 0;
     }
 
@@ -117,38 +120,54 @@
         bitrateChosen = bitrateFloor;
     }
 
-    bool qpPresent = hasQp(inFormat);
+    bool qpPresent = hasQpMax(inFormat);
 
-    // add QP, if not already present
+    // calculate a target QP value
+    int32_t qpmax = codec->targetQpMax();
     if (!qpPresent) {
-        int32_t qpmax = codec->targetQpMax();
+        // user didn't, so shaper wins
         if (qpmax != INT32_MAX) {
             ALOGV("choosing qp=%d", qpmax);
             qpChosen = qpmax;
         }
+    } else if (qpmax == INT32_MAX) {
+        // shaper didn't so user wins
+        qpChosen = INT32_MAX;
+        AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &qpChosen);
+    } else {
+        // both sides want it, choose most restrictive
+        int32_t value = INT32_MAX;
+        AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &value);
+        qpChosen = std::min(qpmax, value);
     }
 
     // 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;
+        if (qpChosen != INT32_MAX) {
+            int64_t boost = 0;
+            boost = bitrateChosen * BITRATE_QP_UNAVAILABLE_BOOST;
+            ALOGD("minquality: requested QP unsupported, boost bitrate %" PRId64 " by %" PRId64,
+                bitrateChosen, boost);
+            bitrateChosen =  bitrateChosen + boost;
             qpChosen = INT32_MAX;
         }
     }
 
+    // limits
     // 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);
+        // caller (VideoShaper) handles spreading this across the subframes
     }
 
     if (bitrateChosen != bitrateConfigured) {
+        if (bitrateChosen > bitrateCeiling) {
+            ALOGD("minquality: bitrate clamped at ceiling %" PRId64,  bitrateCeiling);
+            bitrateChosen = bitrateCeiling;
+        }
         ALOGD("minquality/target bitrate raised from %" PRId64 " to %" PRId64 " bps",
               bitrateConfigured, bitrateChosen);
         AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateChosen);
@@ -158,7 +177,7 @@
 }
 
 
-bool hasQpPerFrameType(AMediaFormat *format) {
+bool hasQpMaxPerFrameType(AMediaFormat *format) {
     int32_t value;
 
     if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &value)
@@ -176,19 +195,29 @@
     return false;
 }
 
-bool hasQp(AMediaFormat *format) {
+bool hasQpMaxGlobal(AMediaFormat *format) {
     int32_t value;
     if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &value)
         || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &value)) {
         return true;
     }
-    return hasQpPerFrameType(format);
+    return false;
+}
+
+bool hasQpMax(AMediaFormat *format) {
+    if (hasQpMaxGlobal(format)) {
+        return true;
+    }
+    return hasQpMaxPerFrameType(format);
 }
 
 void qpSpreadPerFrameType(AMediaFormat *format, int delta,
                            int qplow, int qphigh, bool override) {
-     qpSpreadMaxPerFrameType(format, delta, qphigh, override);
+
      qpSpreadMinPerFrameType(format, qplow, override);
+     qpSpreadMaxPerFrameType(format, delta, qphigh, override);
+     // make sure that min<max for all the QP fields.
+     qpVerifyMinMaxOrdering(format);
 }
 
 void qpSpreadMaxPerFrameType(AMediaFormat *format, int delta, int qphigh, bool override) {
@@ -196,20 +225,26 @@
 
     int32_t qpOffered = 0;
     if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &qpOffered)) {
-        // propagate to otherwise unspecified frame-specific keys
-        int32_t maxI;
-        if (override || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI)) {
-            int32_t value = std::min(qphigh, qpOffered);
+        // propagate to frame-specific keys, choosing most restrictive
+        // ensure that we don't violate min<=max rules
+        {
+            int32_t maxI = INT32_MAX;
+            AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI);
+            int32_t value = std::min({qpOffered, qphigh, maxI});
             AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, value);
         }
-        int32_t maxP;
-        if (override || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP)) {
-            int32_t value = std::min(qphigh, (std::min(qpOffered, INT32_MAX-delta) + delta));
+        {
+            int32_t maxP = INT32_MAX;
+            AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP);
+            int32_t value = std::min({(std::min(qpOffered, INT32_MAX-1*delta) + 1*delta),
+                                     qphigh, maxP});
             AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, value);
         }
-        int32_t maxB;
-        if (override || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB)) {
-            int32_t value = std::min(qphigh, (std::min(qpOffered, INT32_MAX-2*delta) + 2*delta));
+        {
+            int32_t maxB = INT32_MAX;
+            AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB);
+            int32_t value = std::min({(std::min(qpOffered, INT32_MAX-2*delta) + 2*delta),
+                                     qphigh, maxB});
             AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, value);
         }
     }
@@ -221,19 +256,47 @@
     int32_t qpOffered = 0;
     if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &qpOffered)) {
         int value = std::max(qplow, qpOffered);
-        // propagate to otherwise unspecified frame-specific keys
-        int32_t minI;
-        if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI)) {
-            AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, value);
-        }
-        int32_t minP;
-        if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP)) {
-            AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, value);
-        }
-        int32_t minB;
-        if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB)) {
-            AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, value);
-        }
+        // propagate to frame-specific keys, use lowest of this and existing per-frame value
+        int32_t minI = INT32_MAX;
+        AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI);
+        int32_t setI = std::min(value, minI);
+        AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, setI);
+
+        int32_t minP = INT32_MAX;
+        AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP);
+        int32_t setP = std::min(value, minP);
+        AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, setP);
+
+        int32_t minB = INT32_MAX;
+        AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB);
+        int32_t setB = std::min(value, minB);
+        AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, setB);
+    }
+}
+
+// XXX whether we allow min==max, or if we'll insist that min<max
+void qpVerifyMinMaxOrdering(AMediaFormat *format) {
+    // ensure that we don't violate min<=max rules
+    int32_t maxI = INT32_MAX;
+    int32_t minI = INT32_MIN;
+    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI)
+        && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI)
+        && minI > maxI) {
+        AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, maxI);
+    }
+    int32_t maxP = INT32_MAX;
+    int32_t minP = INT32_MIN;
+    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP)
+        && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP)
+        && minP > maxP) {
+        AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, maxP);
+    }
+    int32_t maxB = INT32_MAX;
+    int32_t minB = INT32_MIN;
+    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB)
+        && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB)
+        && minB > maxB) {
+        AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, maxB);
     }
 }
 
diff --git a/media/libmediaformatshaper/include/media/formatshaper/VQops.h b/media/libmediaformatshaper/VQops.h
similarity index 88%
rename from media/libmediaformatshaper/include/media/formatshaper/VQops.h
rename to media/libmediaformatshaper/VQops.h
index 807e8af..74cce18 100644
--- a/media/libmediaformatshaper/include/media/formatshaper/VQops.h
+++ b/media/libmediaformatshaper/VQops.h
@@ -17,7 +17,7 @@
 #ifndef LIBMEDIAFORMATSHAPER_VQOPS_H_
 #define LIBMEDIAFORMATSHAPER_VQOPS_H_
 
-#include <media/formatshaper/CodecProperties.h>
+#include "CodecProperties.h"
 #include <media/NdkMediaFormat.h>
 
 namespace android {
@@ -39,10 +39,12 @@
 void qpSpreadPerFrameType(AMediaFormat *format, int delta, int qplow, int qphigh, bool override);
 void qpSpreadMaxPerFrameType(AMediaFormat *format, int delta, int qphigh, bool override);
 void qpSpreadMinPerFrameType(AMediaFormat *format, int qplow, bool override);
+void qpVerifyMinMaxOrdering(AMediaFormat *format);
 
 // does the format have QP bounding entries
-bool hasQp(AMediaFormat *format);
-bool hasQpPerFrameType(AMediaFormat *format);
+bool hasQpMax(AMediaFormat *format);
+bool hasQpMaxGlobal(AMediaFormat *format);
+bool hasQpMaxPerFrameType(AMediaFormat *format);
 
 }  // namespace mediaformatshaper
 }  // namespace android
diff --git a/media/libmediaformatshaper/VideoShaper.cpp b/media/libmediaformatshaper/VideoShaper.cpp
index f772a66..cf8b50f 100644
--- a/media/libmediaformatshaper/VideoShaper.cpp
+++ b/media/libmediaformatshaper/VideoShaper.cpp
@@ -23,9 +23,9 @@
 
 #include <media/NdkMediaFormat.h>
 
-#include <media/formatshaper/VQops.h>
-#include <media/formatshaper/CodecProperties.h>
-#include <media/formatshaper/VideoShaper.h>
+#include "CodecProperties.h"
+#include "VideoShaper.h"
+#include "VQops.h"
 
 namespace android {
 namespace mediaformatshaper {
@@ -83,10 +83,10 @@
     // apply any quality transforms in here..
     (void) VQApply(codec, info, inFormat, flags);
 
-    // We must always spread any QP parameters.
+    // We always spread any QP parameters.
     // Sometimes it's something we inserted here, sometimes it's a value that the user injected.
     //
-    qpSpreadPerFrameType(inFormat, info->qpDelta, info->qpMin, info->qpMax, /* override */ false);
+    qpSpreadPerFrameType(inFormat, info->qpDelta, info->qpMin, info->qpMax, /* override */ true);
 
     //
     return 0;
diff --git a/media/libmediaformatshaper/include/media/formatshaper/VideoShaper.h b/media/libmediaformatshaper/VideoShaper.h
similarity index 100%
rename from media/libmediaformatshaper/include/media/formatshaper/VideoShaper.h
rename to media/libmediaformatshaper/VideoShaper.h