diff options
author | David Staessens <dstaessens@google.com> | 2021-06-07 14:54:53 +0900 |
---|---|---|
committer | Chih-Yu Huang <akahuang@google.com> | 2021-10-20 12:07:10 +0900 |
commit | df5be4b969430c7d933b1bd9d01be3d4836a589d (patch) | |
tree | 4df62d16231d132c6134855074f6cc4f80898c6b | |
parent | bc5b861c8ef10aef8c3b7c48b350314d8d000b44 (diff) | |
download | v4l2_codec2-df5be4b969430c7d933b1bd9d01be3d4836a589d.tar.gz |
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
-rw-r--r-- | common/V4L2Device.cpp | 21 | ||||
-rw-r--r-- | common/include/v4l2_codec2/common/V4L2Device.h | 2 | ||||
-rw-r--r-- | components/V4L2EncodeComponent.cpp | 39 | ||||
-rw-r--r-- | components/V4L2EncodeInterface.cpp | 9 | ||||
-rw-r--r-- | components/V4L2Encoder.cpp | 43 | ||||
-rw-r--r-- | components/include/v4l2_codec2/components/V4L2EncodeComponent.h | 2 | ||||
-rw-r--r-- | components/include/v4l2_codec2/components/V4L2EncodeInterface.h | 9 | ||||
-rw-r--r-- | components/include/v4l2_codec2/components/V4L2Encoder.h | 14 | ||||
-rw-r--r-- | components/include/v4l2_codec2/components/VideoEncoder.h | 6 |
9 files changed, 121 insertions, 24 deletions
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 @@ int32_t V4L2Device::h264LevelIdcToV4L2H264Level(uint8_t levelIdc) { } // 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 @@ public: // 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 @@ namespace { 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 @@ bool V4L2EncodeComponent::initializeEncoder() { 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 @@ bool V4L2EncodeComponent::updateEncodingParameters() { 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 @@ bool V4L2EncodeComponent::updateEncodingParameters() { 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 @@ bool V4L2EncodeComponent::updateEncodingParameters() { // 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 @@ void V4L2EncodeInterface::Initialize(const C2String& name) { .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 @@ size_t GetMaxOutputBufferSize(const ui::Size& size) { 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<VideoEncoder> V4L2Encoder::create( 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 @@ bool V4L2Encoder::setBitrate(uint32_t bitrate) { 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 @@ VideoPixelFormat V4L2Encoder::inputFormat() const { } 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 @@ bool V4L2Encoder::initialize(C2Config::profile_t outputProfile, std::optional<ui 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 @@ bool V4L2Encoder::configureH264(C2Config::profile_t outputProfile, 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 @@ private: // 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 @@ public: 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 @@ protected: // 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 @@ public: 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 @@ public: 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 @@ private: // 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 @@ private: // 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 @@ public: // 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. |