Update mediaformatshaper library

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

Bug: 182827840
Test: manual
Change-Id: I557ea3d82814dd5758396181210fd46524a26a30
diff --git a/media/libmediaformatshaper/Android.bp b/media/libmediaformatshaper/Android.bp
index 731ff4c..b283bc8 100644
--- a/media/libmediaformatshaper/Android.bp
+++ b/media/libmediaformatshaper/Android.bp
@@ -36,6 +36,7 @@
     name: "libmediaformatshaper_defaults",
     srcs: [
         "CodecProperties.cpp",
+        "CodecSeeding.cpp",
         "FormatShaper.cpp",
         "ManageShapingCodecs.cpp",
         "VideoShaper.cpp",
diff --git a/media/libmediaformatshaper/CodecProperties.cpp b/media/libmediaformatshaper/CodecProperties.cpp
index dccfd95..d733c57 100644
--- a/media/libmediaformatshaper/CodecProperties.cpp
+++ b/media/libmediaformatshaper/CodecProperties.cpp
@@ -26,6 +26,7 @@
 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 @@
     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
diff --git a/media/libmediaformatshaper/CodecSeeding.cpp b/media/libmediaformatshaper/CodecSeeding.cpp
new file mode 100644
index 0000000..629b405
--- /dev/null
+++ b/media/libmediaformatshaper/CodecSeeding.cpp
@@ -0,0 +1,127 @@
+/*
+ * 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
+
diff --git a/media/libmediaformatshaper/FormatShaper.cpp b/media/libmediaformatshaper/FormatShaper.cpp
index ca4dc72..a52edc2 100644
--- a/media/libmediaformatshaper/FormatShaper.cpp
+++ b/media/libmediaformatshaper/FormatShaper.cpp
@@ -93,19 +93,9 @@
         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 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 @@
         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;
 }
diff --git a/media/libmediaformatshaper/VQApply.cpp b/media/libmediaformatshaper/VQApply.cpp
index 6f6f33c..39a5e19 100644
--- a/media/libmediaformatshaper/VQApply.cpp
+++ b/media/libmediaformatshaper/VQApply.cpp
@@ -44,42 +44,60 @@
 #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);
 
-    if (codec->supportedMinimumQuality() > 0) {
-        // allow the codec provided minimum quality behavior to work at it
-        ALOGD("minquality(codec): codec says %d", codec->supportedMinimumQuality());
+    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;
     }
 
-    ALOGD("considering other ways to improve quality...");
+    if (codec->supportedMinimumQuality() > 0) {
+        // allow the codec provided minimum quality behavior to work at it
+        ALOGD("minquality: codec claims to implement minquality=%d",
+              codec->supportedMinimumQuality());
+        return 0;
+    }
 
     //
     // 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");
         }
-        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());
+        // 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);
+        }
     }
 
     double bpp = codec->getBpp();
@@ -108,7 +126,7 @@
               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 @@
 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 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);
diff --git a/media/libmediaformatshaper/VideoShaper.cpp b/media/libmediaformatshaper/VideoShaper.cpp
index fecd3a1..f772a66 100644
--- a/media/libmediaformatshaper/VideoShaper.cpp
+++ b/media/libmediaformatshaper/VideoShaper.cpp
@@ -83,7 +83,7 @@
     // apply any quality transforms in here..
     (void) VQApply(codec, info, inFormat, flags);
 
-    // We must always spread and map any QP parameters.
+    // We must 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);
diff --git a/media/libmediaformatshaper/include/media/formatshaper/CodecProperties.h b/media/libmediaformatshaper/include/media/formatshaper/CodecProperties.h
index f7177a4..e5cc9cf 100644
--- a/media/libmediaformatshaper/include/media/formatshaper/CodecProperties.h
+++ b/media/libmediaformatshaper/include/media/formatshaper/CodecProperties.h
@@ -31,6 +31,12 @@
   public:
     CodecProperties(std::string name, std::string mediaType);
 
+    // seed the codec with some preconfigured values
+    // (e.g. mediaType-granularity defaults)
+    // runs from the constructor
+    void Seed();
+    void Finish();
+
     std::string getName();
     std::string getMediaType();
 
@@ -46,8 +52,9 @@
     // and 'reverse' describes which strings are to be on which side.
     const char **getMappings(std::string kind, bool reverse);
 
-    // debugging of what's in the mapping dictionary
-    void showMappings();
+    // keep a map of all features and their parameters
+    void setFeatureValue(std::string key, int32_t value);
+    bool getFeatureValue(std::string key, int32_t *valuep);
 
     // does the codec support the Android S minimum quality rules
     void setSupportedMinimumQuality(int vmaf);
@@ -88,10 +95,14 @@
     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)*/ ;
-    std::map<std::string, std::string> mUnMappings /*GUARDED_BY(mMappingLock)*/ ;
+
+    std::map<std::string, int32_t> mFeatures /*GUARDED_BY(mMappingLock)*/ ;
 
     bool mIsRegistered = false;
 
+    // debugging of what's in the mapping dictionary
+    void showMappings();
+
     // DISALLOW_EVIL_CONSTRUCTORS(CodecProperties);
 };