C2SoftAomEnc: Compute appropriate level based on the configuration

Minimum level required for the given configuration is now computed
and signalled in the output format

Bug : 268574609
Test: atest android.mediav2.cts.EncoderLevelTest

Change-Id: I8191499bcc0898d203bfb88d549d9efe9ee0b7f6
diff --git a/media/codec2/components/aom/C2SoftAomEnc.cpp b/media/codec2/components/aom/C2SoftAomEnc.cpp
index e08bf43..e38735d 100644
--- a/media/codec2/components/aom/C2SoftAomEnc.cpp
+++ b/media/codec2/components/aom/C2SoftAomEnc.cpp
@@ -116,7 +116,7 @@
                                             LEVEL_AV1_3_2, LEVEL_AV1_3_3, LEVEL_AV1_4,
                                             LEVEL_AV1_4_1}),
                          })
-                         .withSetter(ProfileLevelSetter)
+                         .withSetter(ProfileLevelSetter, mSize, mFrameRate, mBitrate)
                          .build());
 
     std::vector<uint32_t> pixelFormats = {HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
@@ -201,12 +201,69 @@
 }
 
 C2R C2SoftAomEnc::IntfImpl::ProfileLevelSetter(bool mayBlock,
-                                               C2P<C2StreamProfileLevelInfo::output>& me) {
+                                               C2P<C2StreamProfileLevelInfo::output>& me,
+                                               const C2P<C2StreamPictureSizeInfo::input>& size,
+                                               const C2P<C2StreamFrameRateInfo::output>& frameRate,
+                                               const C2P<C2StreamBitrateInfo::output>& bitrate) {
     (void)mayBlock;
     if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
         me.set().profile = PROFILE_AV1_0;
     }
+    struct LevelLimits {
+        C2Config::level_t level;
+        float samplesPerSec;
+        uint64_t samples;
+        uint32_t bitrate;
+        size_t maxHSize;
+        size_t maxVSize;
+    };
+    constexpr LevelLimits kLimits[] = {
+            {LEVEL_AV1_2, 4423680, 147456, 1500000, 2048, 1152},
+            {LEVEL_AV1_2_1, 8363520, 278784, 3000000, 2816, 1584},
+            {LEVEL_AV1_3, 19975680, 665856, 6000000, 4352, 2448},
+            {LEVEL_AV1_3_1, 37950720, 1065024, 10000000, 5504, 3096},
+            {LEVEL_AV1_4, 70778880, 2359296, 12000000, 6144, 3456},
+            {LEVEL_AV1_4_1, 141557760, 2359296, 20000000, 6144, 3456},
+    };
+
+    uint64_t samples = size.v.width * size.v.height;
+    float samplesPerSec = float(samples) * frameRate.v.value;
+
+    // Check if the supplied level meets the samples / bitrate requirements.
+    // If not, update the level with the lowest level meeting the requirements.
+    bool found = false;
+
+    // By default needsUpdate = false in case the supplied level does meet
+    // the requirements.
+    bool needsUpdate = false;
     if (!me.F(me.v.level).supportsAtAll(me.v.level)) {
+        needsUpdate = true;
+    }
+    for (const LevelLimits& limit : kLimits) {
+        if (samples <= limit.samples && samplesPerSec <= limit.samplesPerSec &&
+            bitrate.v.value <= limit.bitrate && size.v.width <= limit.maxHSize &&
+            size.v.height <= limit.maxVSize) {
+            // This is the lowest level that meets the requirements, and if
+            // we haven't seen the supplied level yet, that means we don't
+            // need the update.
+            if (needsUpdate) {
+                ALOGD("Given level %x does not cover current configuration: "
+                        "adjusting to %x",
+                        me.v.level, limit.level);
+                me.set().level = limit.level;
+            }
+            found = true;
+            break;
+        }
+        if (me.v.level == limit.level) {
+            // We break out of the loop when the lowest feasible level is
+            // found. The fact that we're here means that our level doesn't
+            // meet the requirement and needs to be updated.
+            needsUpdate = true;
+        }
+    }
+    if (!found) {
+        // We set to the highest supported level.
         me.set().level = LEVEL_AV1_4_1;
     }
     return C2R::Ok();
diff --git a/media/codec2/components/aom/C2SoftAomEnc.h b/media/codec2/components/aom/C2SoftAomEnc.h
index 3067735..af7da20 100644
--- a/media/codec2/components/aom/C2SoftAomEnc.h
+++ b/media/codec2/components/aom/C2SoftAomEnc.h
@@ -120,7 +120,10 @@
     static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::input>& oldMe,
                           C2P<C2StreamPictureSizeInfo::input>& me);
 
-    static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::output>& me);
+    static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::output>& me,
+                                  const C2P<C2StreamPictureSizeInfo::input>& size,
+                                  const C2P<C2StreamFrameRateInfo::output>& frameRate,
+                                  const C2P<C2StreamBitrateInfo::output>& bitrate);
 
     // unsafe getters
     std::shared_ptr<C2StreamPictureSizeInfo::input> getSize_l() const { return mSize; }