v4l2_codec2 encoder: Add support for configuring the bitrate mode.

This CL adds support for the C2_PARAMKEY_BITRATE_MODE parameter to the
v4l2 encoder. This parameter allows configuring the bitrate mode used
to encode a video.

The supported bitrate modes are:
- C2Config::BITRATE_VARIABLE
- C2Config::BITRATE_CONST

Note: Currently the C2 framework does not support configuring the peak
bitrate, which is used when the bitrate mode is set to VBR. Instead we
set the peak bitrate to a multiple of the target bitrate.

Note: Submit after support for V4L2_CID_MPEG_VIDEO_BITRATE_PEAK and
V4L2_CID_MPEG_VIDEO_BITRATE_MODE has been added to the virto encoder in
crrev.com/c/2944506.

BUG: 190336806
BUG: 181514834
Test: arc.VideoEncodeAccel.h264_192p_i420_vm
Change-Id: I95d8f9921c1ba475ea8c65760d3c18e5e2818d5e
diff --git a/common/V4L2Device.cpp b/common/V4L2Device.cpp
index d4fa7f6..1efb4e3 100644
--- a/common/V4L2Device.cpp
+++ b/common/V4L2Device.cpp
@@ -1439,6 +1439,27 @@
 }
 
 // static
+v4l2_mpeg_video_bitrate_mode V4L2Device::C2BitrateModeToV4L2BitrateMode(
+        C2Config::bitrate_mode_t bitrateMode) {
+    switch (bitrateMode) {
+    case C2Config::bitrate_mode_t::BITRATE_CONST_SKIP_ALLOWED:
+        ALOGW("BITRATE_CONST_SKIP_ALLOWED not supported, defaulting to BITRATE_CONST");
+        FALLTHROUGH;
+    case C2Config::bitrate_mode_t::BITRATE_CONST:
+        return V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+    case C2Config::bitrate_mode_t::BITRATE_VARIABLE_SKIP_ALLOWED:
+        ALOGW("BITRATE_VARIABLE_SKIP_ALLOWED not supported, defaulting to BITRATE_VARIABLE");
+        FALLTHROUGH;
+    case C2Config::bitrate_mode_t::BITRATE_VARIABLE:
+        return V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
+    default:
+        ALOGW("Unsupported bitrate mode %u, defaulting to BITRATE_VARIABLE",
+              static_cast<uint32_t>(bitrateMode));
+        return V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
+    }
+}
+
+// static
 ui::Size V4L2Device::allocatedSizeFromV4L2Format(const struct v4l2_format& format) {
     ui::Size codedSize;
     ui::Size visibleSize;
diff --git a/common/include/v4l2_codec2/common/V4L2Device.h b/common/include/v4l2_codec2/common/V4L2Device.h
index b4c909c..77d7ddb 100644
--- a/common/include/v4l2_codec2/common/V4L2Device.h
+++ b/common/include/v4l2_codec2/common/V4L2Device.h
@@ -349,6 +349,8 @@
     // Convert required H264 profile and level to V4L2 enums.
     static int32_t c2ProfileToV4L2H264Profile(C2Config::profile_t profile);
     static int32_t h264LevelIdcToV4L2H264Level(uint8_t levelIdc);
+    static v4l2_mpeg_video_bitrate_mode C2BitrateModeToV4L2BitrateMode(
+            C2Config::bitrate_mode_t bitrateMode);
 
     // Converts v4l2_memory to a string.
     static const char* v4L2MemoryToString(const v4l2_memory memory);
diff --git a/components/V4L2EncodeComponent.cpp b/components/V4L2EncodeComponent.cpp
index 36bc44a..f88a38a 100644
--- a/components/V4L2EncodeComponent.cpp
+++ b/components/V4L2EncodeComponent.cpp
@@ -40,6 +40,9 @@
 
 const VideoPixelFormat kInputPixelFormat = VideoPixelFormat::NV12;
 
+// The peak bitrate in function of the target bitrate, used when the bitrate mode is VBR.
+constexpr uint32_t kPeakBitrateMultiplier = 2u;
+
 // Get the video frame layout from the specified |inputBlock|.
 // TODO(dstaessens): Clean up code extracting layout from a C2GraphicBlock.
 std::optional<std::vector<VideoFramePlane>> getVideoFrameLayout(const C2ConstGraphicBlock& block,
@@ -646,9 +649,15 @@
         return false;
     }
 
+    // Get the requested bitrate mode and bitrate. The C2 framework doesn't offer a parameter to
+    // configure the peak bitrate, so we use a multiple of the target bitrate.
+    mBitrateMode = mInterface->getBitrateMode();
+    mBitrate = mInterface->getBitrate();
+
     mEncoder = V4L2Encoder::create(
             outputProfile, h264Level, mInterface->getInputVisibleSize(), *stride,
-            mInterface->getKeyFramePeriod(),
+            mInterface->getKeyFramePeriod(), mBitrateMode, mBitrate,
+            mBitrate * kPeakBitrateMultiplier,
             ::base::BindRepeating(&V4L2EncodeComponent::fetchOutputBlock, mWeakThis),
             ::base::BindRepeating(&V4L2EncodeComponent::onInputBufferDone, mWeakThis),
             ::base::BindRepeating(&V4L2EncodeComponent::onOutputBufferDone, mWeakThis),
@@ -678,19 +687,10 @@
     ALOGV("%s()", __func__);
     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
 
-    // Query the interface for the encoding parameters requested by the codec 2.0 framework.
-    C2StreamBitrateInfo::output bitrateInfo;
-    C2StreamFrameRateInfo::output framerateInfo;
-    c2_status_t status =
-            mInterface->query({&bitrateInfo, &framerateInfo}, {}, C2_DONT_BLOCK, nullptr);
-    if (status != C2_OK) {
-        ALOGE("Failed to query interface for encoding parameters (error code: %d)", status);
-        reportError(status);
-        return false;
-    }
-
-    // Ask device to change bitrate if it's different from the currently configured bitrate.
-    uint32_t bitrate = bitrateInfo.value;
+    // Ask device to change bitrate if it's different from the currently configured bitrate. The C2
+    // framework doesn't offer a parameter to configure the peak bitrate, so we'll use a multiple of
+    // the target bitrate here. The peak bitrate is only used if the bitrate mode is set to VBR.
+    uint32_t bitrate = mInterface->getBitrate();
     if (mBitrate != bitrate) {
         ALOG_ASSERT(bitrate > 0u);
         ALOGV("Setting bitrate to %u", bitrate);
@@ -699,10 +699,17 @@
             return false;
         }
         mBitrate = bitrate;
+
+        if (mBitrateMode == C2Config::BITRATE_VARIABLE) {
+            ALOGV("Setting peak bitrate to %u", bitrate * kPeakBitrateMultiplier);
+            // TODO(b/190336806): Our stack doesn't support dynamic peak bitrate changes yet, ignore
+            // errors for now.
+            mEncoder->setPeakBitrate(bitrate * kPeakBitrateMultiplier);
+        }
     }
 
     // Ask device to change framerate if it's different from the currently configured framerate.
-    uint32_t framerate = static_cast<uint32_t>(std::round(framerateInfo.value));
+    uint32_t framerate = static_cast<uint32_t>(std::round(mInterface->getFramerate()));
     if (mFramerate != framerate) {
         ALOG_ASSERT(framerate > 0u);
         ALOGV("Setting framerate to %u", framerate);
@@ -717,7 +724,7 @@
     // Check whether an explicit key frame was requested, if so reset the key frame counter to
     // immediately request a key frame.
     C2StreamRequestSyncFrameTuning::output requestKeyFrame;
-    status = mInterface->query({&requestKeyFrame}, {}, C2_DONT_BLOCK, nullptr);
+    c2_status_t status = mInterface->query({&requestKeyFrame}, {}, C2_DONT_BLOCK, nullptr);
     if (status != C2_OK) {
         ALOGE("Failed to query interface for key frame request (error code: %d)", status);
         reportError(status);
diff --git a/components/V4L2EncodeInterface.cpp b/components/V4L2EncodeInterface.cpp
index 7f0fb39..2bdf11b 100644
--- a/components/V4L2EncodeInterface.cpp
+++ b/components/V4L2EncodeInterface.cpp
@@ -310,6 +310,15 @@
                          .withSetter(Setter<decltype(*mBitrate)>::StrictValueWithNoDeps)
                          .build());
 
+    addParameter(
+            DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE)
+                    .withDefault(new C2StreamBitrateModeTuning::output(0u, C2Config::BITRATE_CONST))
+                    .withFields(
+                            {C2F(mBitrateMode, value)
+                                     .oneOf({C2Config::BITRATE_CONST, C2Config::BITRATE_VARIABLE})})
+                    .withSetter(Setter<decltype(*mBitrateMode)>::StrictValueWithNoDeps)
+                    .build());
+
     std::string outputMime;
     if (getCodecFromComponentName(name) == VideoCodec::H264) {
         outputMime = MEDIA_MIMETYPE_VIDEO_AVC;
diff --git a/components/V4L2Encoder.cpp b/components/V4L2Encoder.cpp
index 67d54c8..2dfbe8d 100644
--- a/components/V4L2Encoder.cpp
+++ b/components/V4L2Encoder.cpp
@@ -54,6 +54,7 @@
 std::unique_ptr<VideoEncoder> V4L2Encoder::create(
         C2Config::profile_t outputProfile, std::optional<uint8_t> level,
         const ui::Size& visibleSize, uint32_t stride, uint32_t keyFramePeriod,
+        C2Config::bitrate_mode_t bitrateMode, uint32_t bitrate, std::optional<uint32_t> peakBitrate,
         FetchOutputBufferCB fetchOutputBufferCb, InputBufferDoneCB inputBufferDoneCb,
         OutputBufferDoneCB outputBufferDoneCb, DrainDoneCB drainDoneCb, ErrorCB errorCb,
         scoped_refptr<::base::SequencedTaskRunner> taskRunner) {
@@ -62,7 +63,8 @@
     std::unique_ptr<V4L2Encoder> encoder = ::base::WrapUnique<V4L2Encoder>(new V4L2Encoder(
             std::move(taskRunner), std::move(fetchOutputBufferCb), std::move(inputBufferDoneCb),
             std::move(outputBufferDoneCb), std::move(drainDoneCb), std::move(errorCb)));
-    if (!encoder->initialize(outputProfile, level, visibleSize, stride, keyFramePeriod)) {
+    if (!encoder->initialize(outputProfile, level, visibleSize, stride, keyFramePeriod, bitrateMode,
+                             bitrate, peakBitrate)) {
         return nullptr;
     }
     return encoder;
@@ -161,6 +163,19 @@
     return true;
 }
 
+bool V4L2Encoder::setPeakBitrate(uint32_t peakBitrate) {
+    ALOGV("%s()", __func__);
+    ALOG_ASSERT(mTaskRunner->RunsTasksInCurrentSequence());
+
+    if (!mDevice->setExtCtrls(V4L2_CTRL_CLASS_MPEG,
+                              {V4L2ExtCtrl(V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, peakBitrate)})) {
+        // TODO(b/190336806): Our stack doesn't support dynamic peak bitrate changes yet, ignore
+        // errors for now.
+        ALOGW("Setting peak bitrate to %u failed", peakBitrate);
+    }
+    return true;
+}
+
 bool V4L2Encoder::setFramerate(uint32_t framerate) {
     ALOGV("%s()", __func__);
     ALOG_ASSERT(mTaskRunner->RunsTasksInCurrentSequence());
@@ -189,8 +204,9 @@
 }
 
 bool V4L2Encoder::initialize(C2Config::profile_t outputProfile, std::optional<uint8_t> level,
-                             const ui::Size& visibleSize, uint32_t stride,
-                             uint32_t keyFramePeriod) {
+                             const ui::Size& visibleSize, uint32_t stride, uint32_t keyFramePeriod,
+                             C2Config::bitrate_mode_t bitrateMode, uint32_t bitrate,
+                             std::optional<uint32_t> peakBitrate) {
     ALOGV("%s()", __func__);
     ALOG_ASSERT(mTaskRunner->RunsTasksInCurrentSequence());
     ALOG_ASSERT(keyFramePeriod > 0);
@@ -238,6 +254,12 @@
         return false;
     }
 
+    // Configure the requested bitrate mode and bitrate on the device.
+    if (!configureBitrateMode(bitrateMode) || !setBitrate(bitrate)) return false;
+
+    // If the bitrate mode is VBR we also need to configure the peak bitrate on the device.
+    if ((bitrateMode == C2Config::BITRATE_VARIABLE) && !setPeakBitrate(*peakBitrate)) return false;
+
     // First try to configure the specified output format, as changing the output format can affect
     // the configured input format.
     if (!configureOutputFormat(outputProfile)) return false;
@@ -637,6 +659,21 @@
     return true;
 }
 
+bool V4L2Encoder::configureBitrateMode(C2Config::bitrate_mode_t bitrateMode) {
+    ALOGV("%s()", __func__);
+    ALOG_ASSERT(mTaskRunner->RunsTasksInCurrentSequence());
+
+    v4l2_mpeg_video_bitrate_mode v4l2BitrateMode =
+            V4L2Device::C2BitrateModeToV4L2BitrateMode(bitrateMode);
+    if (!mDevice->setExtCtrls(V4L2_CTRL_CLASS_MPEG,
+                              {V4L2ExtCtrl(V4L2_CID_MPEG_VIDEO_BITRATE_MODE, v4l2BitrateMode)})) {
+        // TODO(b/190336806): Our stack doesn't support bitrate mode changes yet. We default to CBR
+        // which is currently the only supported mode so we can safely ignore this for now.
+        ALOGW("Setting bitrate mode to %u failed", v4l2BitrateMode);
+    }
+    return true;
+}
+
 bool V4L2Encoder::startDevicePoll() {
     ALOGV("%s()", __func__);
     ALOG_ASSERT(mTaskRunner->RunsTasksInCurrentSequence());
diff --git a/components/include/v4l2_codec2/components/V4L2EncodeComponent.h b/components/include/v4l2_codec2/components/V4L2EncodeComponent.h
index 61d43f9..21fe551 100644
--- a/components/include/v4l2_codec2/components/V4L2EncodeComponent.h
+++ b/components/include/v4l2_codec2/components/V4L2EncodeComponent.h
@@ -155,6 +155,8 @@
 
     // The bitrate currently configured on the v4l2 device.
     uint32_t mBitrate = 0;
+    // The bitrate mode currently configured on the v4l2 device.
+    C2Config::bitrate_mode_t mBitrateMode = C2Config::BITRATE_CONST;
     // The framerate currently configured on the v4l2 device.
     uint32_t mFramerate = 0;
     // The timestamp of the last frame encoded, used to dynamically adjust the framerate.
diff --git a/components/include/v4l2_codec2/components/V4L2EncodeInterface.h b/components/include/v4l2_codec2/components/V4L2EncodeInterface.h
index 2a2c54c..fefebf0 100644
--- a/components/include/v4l2_codec2/components/V4L2EncodeInterface.h
+++ b/components/include/v4l2_codec2/components/V4L2EncodeInterface.h
@@ -39,8 +39,15 @@
         return ui::Size(mInputVisibleSize->width, mInputVisibleSize->height);
     }
     C2BlockPool::local_id_t getBlockPoolId() const { return mOutputBlockPoolIds->m.values[0]; }
+
     // Get sync key-frame period in frames.
     uint32_t getKeyFramePeriod() const;
+    // Get the requested bitrate mode.
+    C2Config::bitrate_mode_t getBitrateMode() const { return mBitrateMode->value; }
+    // Get the requested bitrate.
+    uint32_t getBitrate() const { return mBitrate->value; }
+    // Get the requested framerate.
+    float getFramerate() const { return mFrameRate->value; }
 
     // Request changing the framerate to the specified value.
     void setFramerate(uint32_t framerate) { mFrameRate->value = framerate; }
@@ -98,6 +105,8 @@
 
     // The requested bitrate of the encoded output stream, in bits per second.
     std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    // The requested bitrate mode.
+    std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
     // The requested framerate, in frames per second.
     std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
     // The switch-type parameter that will be set to true while client requests keyframe. It
diff --git a/components/include/v4l2_codec2/components/V4L2Encoder.h b/components/include/v4l2_codec2/components/V4L2Encoder.h
index 5abee8f..9954232 100644
--- a/components/include/v4l2_codec2/components/V4L2Encoder.h
+++ b/components/include/v4l2_codec2/components/V4L2Encoder.h
@@ -33,9 +33,10 @@
 
     static std::unique_ptr<VideoEncoder> create(
             C2Config::profile_t profile, std::optional<uint8_t> level, const ui::Size& visibleSize,
-            uint32_t stride, uint32_t keyFramePeriod, FetchOutputBufferCB fetchOutputBufferCb,
-            InputBufferDoneCB inputBufferDoneCb, OutputBufferDoneCB outputBufferDoneCb,
-            DrainDoneCB drainDoneCb, ErrorCB errorCb,
+            uint32_t stride, uint32_t keyFramePeriod, C2Config::bitrate_mode_t bitrateMode,
+            uint32_t bitrate, std::optional<uint32_t> peakBitrate,
+            FetchOutputBufferCB fetchOutputBufferCb, InputBufferDoneCB inputBufferDoneCb,
+            OutputBufferDoneCB outputBufferDoneCb, DrainDoneCB drainDoneCb, ErrorCB errorCb,
             scoped_refptr<::base::SequencedTaskRunner> taskRunner);
     ~V4L2Encoder() override;
 
@@ -44,6 +45,7 @@
     void flush() override;
 
     bool setBitrate(uint32_t bitrate) override;
+    bool setPeakBitrate(uint32_t peakBitrate) override;
     bool setFramerate(uint32_t framerate) override;
     void requestKeyframe() override;
 
@@ -80,7 +82,9 @@
 
     // Initialize the V4L2 encoder for specified parameters.
     bool initialize(C2Config::profile_t outputProfile, std::optional<uint8_t> level,
-                    const ui::Size& visibleSize, uint32_t stride, uint32_t keyFramePeriod);
+                    const ui::Size& visibleSize, uint32_t stride, uint32_t keyFramePeriod,
+                    C2Config::bitrate_mode_t bitrateMode, uint32_t bitrate,
+                    std::optional<uint32_t> peakBitrate);
 
     // Handle the next encode request on the queue.
     void handleEncodeRequest();
@@ -101,6 +105,8 @@
     // Configure required and optional H.264 controls on the V4L2 device.
     bool configureH264(C2Config::profile_t outputProfile,
                        std::optional<const uint8_t> outputH264Level);
+    // Configure the specified bitrate mode on the V4L2 device.
+    bool configureBitrateMode(C2Config::bitrate_mode_t bitrateMode);
 
     // Attempt to start the V4L2 device poller.
     bool startDevicePoll();
diff --git a/components/include/v4l2_codec2/components/VideoEncoder.h b/components/include/v4l2_codec2/components/VideoEncoder.h
index 46bcad1..5f23541 100644
--- a/components/include/v4l2_codec2/components/VideoEncoder.h
+++ b/components/include/v4l2_codec2/components/VideoEncoder.h
@@ -64,8 +64,12 @@
     // Flush the encoder, pending drain operations will be aborted.
     virtual void flush() = 0;
 
-    // Set the bitrate to the specified value, will affect all non-processed frames.
+    // Set the target bitrate to the specified value, will affect all non-processed frames.
     virtual bool setBitrate(uint32_t bitrate) = 0;
+    // Set the peak bitrate to the specified value. The peak bitrate must be larger or equal to the
+    // target bitrate and is ignored if the bitrate mode is constant.
+    virtual bool setPeakBitrate(uint32_t peakBitrate) = 0;
+
     // Set the framerate to the specified value, will affect all non-processed frames.
     virtual bool setFramerate(uint32_t framerate) = 0;
     // Request the next frame encoded to be a key frame, will affect the next non-processed frame.