Rework how QP bounds are passed, AVC encoder

Pass via an (variable length) set of settings for picture types
instead of the fixed I/P/B types. Also makes it possible to
pass a max without also passing a min.
This is for the AVC software encoder; other codecs will be modified
accordingly.

Bug: 181830609
Test: vq testing, ALOG
Change-Id: I9086c0d83e625a7a815ce2c104352fdac165b927
(cherry picked from commit b3e76b60d9fd524681650f1608f606180040a186)
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp
index 43b2c14..940f57c 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.cpp
+++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp
@@ -19,6 +19,8 @@
 #include <log/log.h>
 #include <utils/misc.h>
 
+#include <algorithm>
+
 #include <media/hardware/VideoAPI.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
@@ -121,6 +123,19 @@
                 .build());
 
         addParameter(
+                DefineParam(mPictureQuantization, C2_PARAMKEY_PICTURE_QUANTIZATION)
+                .withDefault(C2StreamPictureQuantizationTuning::output::AllocShared(
+                        0 /* flexCount */, 0u /* stream */))
+                .withFields({C2F(mPictureQuantization, m.values[0].type_).oneOf(
+                                {C2Config::picture_type_t(I_FRAME),
+                                  C2Config::picture_type_t(P_FRAME),
+                                  C2Config::picture_type_t(B_FRAME)}),
+                             C2F(mPictureQuantization, m.values[0].min).any(),
+                             C2F(mPictureQuantization, m.values[0].max).any()})
+                .withSetter(PictureQuantizationSetter)
+                .build());
+
+        addParameter(
                 DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY)
                 .withDefault(new C2PortActualDelayTuning::input(DEFAULT_B_FRAMES))
                 .withFields({C2F(mActualInputDelay, value).inRange(0, MAX_B_FRAMES)})
@@ -186,23 +201,6 @@
                 .build());
 
         addParameter(
-                DefineParam(mQuantization, C2_PARAMKEY_QUANTIZATION)
-                .withDefault(new C2StreamQuantizationInfo::output(0u,
-                                      DEFAULT_QP_MAX, DEFAULT_QP_MIN,
-                                      DEFAULT_QP_MAX, DEFAULT_QP_MIN,
-                                      DEFAULT_QP_MAX, DEFAULT_QP_MIN))
-                .withFields({
-                        C2F(mQuantization, iMax).inRange(1, 51),
-                        C2F(mQuantization, iMin).inRange(1, 51),
-                        C2F(mQuantization, pMax).inRange(1, 51),
-                        C2F(mQuantization, pMin).inRange(1, 51),
-                        C2F(mQuantization, bMax).inRange(1, 51),
-                        C2F(mQuantization, bMin).inRange(1, 51),
-                 })
-                .withSetter(QuantizationSetter)
-                .build());
-
-        addParameter(
                 DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME)
                 .withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE))
                 .withFields({C2F(mRequestSync, value).oneOf({ C2_FALSE, C2_TRUE }) })
@@ -237,70 +235,6 @@
         return res;
     }
 
-    static C2R QuantizationSetter(bool mayBlock, C2P<C2StreamQuantizationInfo::output> &me) {
-        (void)mayBlock;
-        (void)me;
-        C2R res = C2R::Ok();
-
-        ALOGV("QuantizationSetter enters max/min i %d/%d p %d/%d b %d/%d",
-              me.v.iMax, me.v.iMin, me.v.pMax, me.v.pMin, me.v.bMax, me.v.bMin);
-
-        // bounds checking
-        constexpr int qp_lowest = 1;
-        constexpr int qp_highest = 51;
-
-        if (me.v.iMax < qp_lowest) {
-            me.set().iMax = qp_lowest;
-        } else if (me.v.iMax > qp_highest) {
-            me.set().iMax = qp_highest;
-        }
-
-        if (me.v.iMin < qp_lowest) {
-            me.set().iMin = qp_lowest;
-        } else if (me.v.iMin > qp_highest) {
-            me.set().iMin = qp_highest;
-        }
-
-        if (me.v.pMax < qp_lowest) {
-            me.set().pMax = qp_lowest;
-        } else if (me.v.pMax > qp_highest) {
-            me.set().pMax = qp_highest;
-        }
-
-        if (me.v.pMin < qp_lowest) {
-            me.set().pMin = qp_lowest;
-        } else if (me.v.pMin > qp_highest) {
-            me.set().pMin = qp_highest;
-        }
-
-        if (me.v.bMax < qp_lowest) {
-            me.set().bMax = qp_lowest;
-        } else if (me.v.bMax > qp_highest) {
-            me.set().bMax = qp_highest;
-        }
-
-        if (me.v.bMin < qp_lowest) {
-            me.set().bMin = qp_lowest;
-        } else if (me.v.bMin > qp_highest) {
-            me.set().bMin = qp_highest;
-        }
-
-        // consistency checking, e.g. min<max
-        //
-        if (me.v.iMax < me.v.iMin) {
-            me.set().iMax = me.v.iMin;
-        }
-        if (me.v.pMax < me.v.pMin) {
-            me.set().pMax = me.v.pMin;
-        }
-        if (me.v.bMax < me.v.bMin) {
-            me.set().bMax = me.v.bMin;
-        }
-
-        // TODO: enforce any sort of i_max < p_max < b_max?
-
-        return res;
-    }
 
     static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::input> &oldMe,
                           C2P<C2StreamPictureSizeInfo::input> &me) {
@@ -418,6 +352,13 @@
         return C2R::Ok();
     }
 
+    static C2R PictureQuantizationSetter(bool mayBlock,
+                                         C2P<C2StreamPictureQuantizationTuning::output> &me) {
+        (void)mayBlock;
+        (void)me;
+        return C2R::Ok();
+    }
+
     IV_PROFILE_T getProfile_l() const {
         switch (mProfileLevel->profile) {
         case PROFILE_AVC_CONSTRAINED_BASELINE:  [[fallthrough]];
@@ -475,7 +416,8 @@
     std::shared_ptr<C2StreamBitrateInfo::output> getBitrate_l() const { return mBitrate; }
     std::shared_ptr<C2StreamRequestSyncFrameTuning::output> getRequestSync_l() const { return mRequestSync; }
     std::shared_ptr<C2StreamGopTuning::output> getGop_l() const { return mGop; }
-    std::shared_ptr<C2StreamQuantizationInfo::output> getQuantization_l() const { return mQuantization; }
+    std::shared_ptr<C2StreamPictureQuantizationTuning::output> getPictureQuantization_l() const
+    { return mPictureQuantization; }
 
 private:
     std::shared_ptr<C2StreamUsageTuning::input> mUsage;
@@ -487,7 +429,7 @@
     std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
     std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod;
     std::shared_ptr<C2StreamGopTuning::output> mGop;
-    std::shared_ptr<C2StreamQuantizationInfo::output> mQuantization;
+    std::shared_ptr<C2StreamPictureQuantizationTuning::output> mPictureQuantization;
 };
 
 #define ive_api_function  ih264e_api_function
@@ -748,37 +690,68 @@
     ive_ctl_set_qp_op_t s_qp_op;
     IV_STATUS_T status;
 
+    ALOGV("in setQp()");
+
     // set the defaults
     s_qp_ip.e_cmd = IVE_CMD_VIDEO_CTL;
     s_qp_ip.e_sub_cmd = IVE_CMD_CTL_SET_QP;
 
-    s_qp_ip.u4_i_qp = DEFAULT_I_QP;
-    s_qp_ip.u4_i_qp_max = DEFAULT_QP_MAX;
-    s_qp_ip.u4_i_qp_min = DEFAULT_QP_MIN;
+    // these are the ones we're going to set, so want them to default ....
+    // to the DEFAULT values for the codec instea dof CODEC_ bounding
+    int32_t iMin = INT32_MIN, pMin = INT32_MIN, bMin = INT32_MIN;
+    int32_t iMax = INT32_MAX, pMax = INT32_MAX, bMax = INT32_MAX;
 
-    s_qp_ip.u4_p_qp = DEFAULT_P_QP;
-    s_qp_ip.u4_p_qp_max = DEFAULT_QP_MAX;
-    s_qp_ip.u4_p_qp_min = DEFAULT_QP_MIN;
+    std::shared_ptr<C2StreamPictureQuantizationTuning::output> qp =
+                    mIntf->getPictureQuantization_l();
+    for (size_t i = 0; i < qp->flexCount(); ++i) {
+        const C2PictureQuantizationStruct &layer = qp->m.values[i];
 
-    s_qp_ip.u4_b_qp = DEFAULT_P_QP;
-    s_qp_ip.u4_b_qp_max = DEFAULT_QP_MAX;
-    s_qp_ip.u4_b_qp_min = DEFAULT_QP_MIN;
-
-    // parameter parsing ensured proper range 1..51, so only worry about ordering
-    bool valid = true;
-    if (mQuantization->iMax < mQuantization->iMin) valid = false;
-    if (mQuantization->pMax < mQuantization->pMin) valid = false;
-    if (mQuantization->bMax < mQuantization->bMin) valid = false;
-
-    if (valid) {
-        s_qp_ip.u4_i_qp_max = mQuantization->iMax;
-        s_qp_ip.u4_i_qp_min = mQuantization->iMin;
-        s_qp_ip.u4_p_qp_max = mQuantization->pMax;
-        s_qp_ip.u4_p_qp_min = mQuantization->pMin;
-        s_qp_ip.u4_b_qp_max = mQuantization->bMax;
-        s_qp_ip.u4_b_qp_min = mQuantization->bMin;
+        if (layer.type_ == C2Config::picture_type_t(I_FRAME)) {
+            iMax = layer.max;
+            iMin = layer.min;
+            ALOGV("iMin %d iMax %d", iMin, iMax);
+        } else if (layer.type_ == C2Config::picture_type_t(P_FRAME)) {
+            pMax = layer.max;
+            pMin = layer.min;
+            ALOGV("pMin %d pMax %d", pMin, pMax);
+        } else if (layer.type_ == C2Config::picture_type_t(B_FRAME)) {
+            bMax = layer.max;
+            bMin = layer.min;
+            ALOGV("bMin %d bMax %d", bMin, bMax);
+        }
     }
 
+    // INT32_{MIN,MAX} means unspecified, so use the codec's default
+    if (iMax == INT32_MAX) iMax = DEFAULT_I_QP_MAX;
+    if (iMin == INT32_MIN) iMin = DEFAULT_I_QP_MIN;
+    if (pMax == INT32_MAX) pMax = DEFAULT_P_QP_MAX;
+    if (pMin == INT32_MIN) pMin = DEFAULT_P_QP_MIN;
+    if (bMax == INT32_MAX) bMax = DEFAULT_B_QP_MAX;
+    if (bMin == INT32_MIN) bMin = DEFAULT_B_QP_MIN;
+
+    // ensure we have legal values
+    iMax = std::clamp(iMax, CODEC_QP_MIN, CODEC_QP_MAX);
+    iMin = std::clamp(iMin, CODEC_QP_MIN, CODEC_QP_MAX);
+    pMax = std::clamp(pMax, CODEC_QP_MIN, CODEC_QP_MAX);
+    pMin = std::clamp(pMin, CODEC_QP_MIN, CODEC_QP_MAX);
+    bMax = std::clamp(bMax, CODEC_QP_MIN, CODEC_QP_MAX);
+    bMin = std::clamp(bMin, CODEC_QP_MIN, CODEC_QP_MAX);
+
+    s_qp_ip.u4_i_qp_max = iMax;
+    s_qp_ip.u4_i_qp_min = iMin;
+    s_qp_ip.u4_p_qp_max = pMax;
+    s_qp_ip.u4_p_qp_min = pMin;
+    s_qp_ip.u4_b_qp_max = bMax;
+    s_qp_ip.u4_b_qp_min = bMin;
+
+    // ensure initial qp values are within our newly configured bounds...
+    s_qp_ip.u4_i_qp = std::clamp(DEFAULT_I_QP, iMin, iMax);
+    s_qp_ip.u4_p_qp = std::clamp(DEFAULT_P_QP, pMin, pMax);
+    s_qp_ip.u4_b_qp = std::clamp(DEFAULT_B_QP, bMin, bMax);
+
+    ALOGV("setting QP: i %d-%d p %d-%d b %d-%d", iMin, iMax, pMin, pMax, bMin, bMax);
+
+
     s_qp_ip.u4_timestamp_high = -1;
     s_qp_ip.u4_timestamp_low = -1;
 
@@ -1026,7 +999,6 @@
         mIInterval = mIntf->getSyncFramePeriod_l();
         mIDRInterval = mIntf->getSyncFramePeriod_l();
         gop = mIntf->getGop_l();
-        mQuantization = mIntf->getQuantization_l();
     }
     if (gop && gop->flexCount() > 0) {
         uint32_t syncInterval = 1;
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.h b/media/codec2/components/avc/C2SoftAvcEnc.h
index e4bf0b0..ee6d47a 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.h
+++ b/media/codec2/components/avc/C2SoftAvcEnc.h
@@ -100,6 +100,11 @@
 #define STRLENGTH                   500
 #define DEFAULT_CONSTRAINED_INTRA   0
 
+/** limits as specified by h264 */
+#define CODEC_QP_MIN                0
+#define CODEC_QP_MAX                51
+
+
 #define MIN(a, b) ((a) < (b))? (a) : (b)
 #define MAX(a, b) ((a) > (b))? (a) : (b)
 #define ALIGN16(x) ((((x) + 15) >> 4) << 4)
@@ -192,7 +197,6 @@
     std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
     std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
     std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
-    std::shared_ptr<C2StreamQuantizationInfo::output> mQuantization;
 
     uint32_t mOutBufferSize;
     UWORD32 mHeaderGenerated;