Merge latest version to Android master.

Bug: None
Test: mmm external/v4l2_codec2
Change-Id: I21d5d30ce32279dfb1cbe2b40d435d2e9707f5aa
diff --git a/README.md b/README.md
index e7ce74b..a381864 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,366 @@
-# V4L2-based Codec2 Component Implementation
+## General Information
 
-## Description of Sub-folders
+### Scope of this document
 
-* accel/
-Core V4L2 API and codec utilities, ported from Chromium project.
+This document is aimed to provide information about the v4l2\_codec2 project.
+The target readers of this document are the developers and following maintainers
+of this project, and the partners who are willing to use the V4L2 components.
 
-* common/
-Common helper classes for components.
+### Introduction
 
-* components/
-The C2Component implementations based on V4L2 API, and the implementation of
-C2ComponentStore for creating all the C2Components.
+v4l2\_codec2 project provides a component implementation of Codec2 framework,
+the next-generation codec framework. The component implementation delegates the
+request to the driver via the V4L2 API.
 
-* service/
-The Codec2's V4L2 IComponentStore service. The service initiates the component
-store implemented at store/ folder, and registers it as the default service.
+## Quick Start Guide
+
+### Prerequisites
+
+*   Gralloc support for graphic buffer allocation
+*   ION or Gralloc support for linear buffer allocation
+*   DRM for buffer identification
+*   [V4L2 stateful decoding API](https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/dev-decoder.html)
+*   [V4L2 encoding API](https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/dev-encoder.html)
+*   Widevine DRM for secure playback
+
+### Enable V4L2 Components
+
+Install the build package and files, and set the parameters in device.mk
+
+```makefile
+# Add the folder to the namespace.
+PRODUCT_SOONG_NAMESPACES += external/v4l2_codec2
+
+# Add the build target.
+PRODUCT_PACKAGES += \
+    android.hardware.media.c2@1.0-service-v4l2 \
+    libc2plugin_store
+
+# If a customized allocator is needed, then add this package.
+# See more detail at "Customized allocator" section.
+PRODUCT_PACKAGES += \
+    libv4l2_codec2_vendor_allocator
+
+# Install media_codecs_c2.xml.
+# The destination is: /vendor/etc/media_codecs_c2.xml
+PRODUCT_COPY_FILES += \
+    <path_to_file>:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_c2.xml
+
+# Set the customized property of v4l2_codec2, including:
+# - The DRM device name and path pattern.
+# - The maximum concurrent instances for decoder/encoder.
+#   It should be the same as "concurrent-instances" at media_codec_c2.xml.
+PRODUCT_PROPERTY_OVERRIDES += \
+    ro.vendor.v4l2_codec2.drm_device_name=virtio_gpu \
+    ro.vendor.v4l2_codec2.drm_device_path=/dev/dri/renderD* \
+    ro.vendor.v4l2_codec2.decode_concurrent_instances=8 \
+    ro.vendor.v4l2_codec2.encode_concurrent_instances=8
+
+# Codec2.0 poolMask:
+#   ION(16)
+#   BUFFERQUEUE(18)
+#   BLOB(19)
+#   V4L2_BUFFERQUEUE(20)
+#   V4L2_BUFFERPOOL(21)
+#   SECURE_LINEAR(22)
+#   SECURE_GRAPHIC(23)
+#
+# For linear buffer allocation:
+#   If ION is chosen, then the mask should be 0xf50000
+#   If BLOB is chosen, then the mask should be 0xfc0000
+PRODUCT_PROPERTY_OVERRIDES += \
+    debug.stagefright.c2-poolmask=0xf50000
+
+# Install extended policy for codec2.
+# The destination is: /vendor/etc/seccomp_policy/codec2.vendor.ext.policy
+PRODUCT_COPY_FILES += \
+    <path_to_policy>:$(TARGET_COPY_OUT_VENDOR)/etc/seccomp_policy/codec2.vendor.ext.policy
+```
+
+Enable codec2 hidl in manifest.xml
+
+```xml
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.media.c2</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+            <name>IComponentStore</name>
+            <instance>default</instance>
+        </interface>
+        <interface>
+            <name>IConfigurable</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
+```
+
+Add decode and encode components in media\_codecs\_c2.xml
+
+```xml
+<?xml version="1.0" encoding="utf-8" ?>
+<MediaCodecs>
+   <Encoders>
+       <MediaCodec name="c2.v4l2.avc.encoder" type="video/avc">
+           <Limit name="size" min="32x32" max="1920x1088" />
+           <Limit name="alignment" value="2x2" />
+           <Limit name="block-size" value="16x16" />
+           <Limit name="blocks-per-second" range="1-244800" />
+           <Limit name="bitrate" range="1-12000000" />
+           <Limit name="concurrent-instances" max="8" />
+           <Limit name="performance-point-1280x720" range="30-30" />
+       </MediaCodec>
+
+       <MediaCodec name="c2.v4l2.vp8.encoder" type="video/x-vnd.on2.vp8">
+           <Limit name="size" min="32x32" max="1920x1088" />
+           <Limit name="alignment" value="2x2" />
+           <Limit name="block-size" value="16x16" />
+           <Limit name="blocks-per-second" range="1-244800" />
+           <Limit name="bitrate" range="1-12000000" />
+           <Limit name="concurrent-instances" max="8" />
+           <Limit name="performance-point-1280x720" range="30-30" />
+       </MediaCodec>
+
+       <MediaCodec name="c2.v4l2.vp9.encoder" type="video/x-vnd.on2.vp9">
+           <Limit name="size" min="32x32" max="1920x1088" />
+           <Limit name="alignment" value="2x2" />
+           <Limit name="block-size" value="16x16" />
+           <Limit name="blocks-per-second" range="1-244800" />
+           <Limit name="bitrate" range="1-12000000" />
+           <Limit name="concurrent-instances" max="8" />
+           <Limit name="performance-point-1280x720" range="30-30" />
+       </MediaCodec>
+   </Encoders>
+
+   <Decoders>
+       <MediaCodec name="c2.v4l2.avc.decoder" type="video/avc" >
+           <Limit name="size" min="16x16" max="4096x4096" />
+           <Limit name="alignment" value="2x2" />
+           <Limit name="block-size" value="16x16" />
+           <Limit name="blocks-per-second" min="1" max="1879200" />
+           <Limit name="bitrate" range="1-62500000" />
+           <Limit name="concurrent-instances" max="8" />
+           <Limit name="performance-point-3840x2160" range="30-30" />
+           <Feature name="adaptive-playback" />
+       </MediaCodec>
+
+       <MediaCodec name="c2.v4l2.vp8.decoder" type="video/x-vnd.on2.vp8" >
+           <Limit name="size" min="16x16" max="4096x4096" />
+           <Limit name="alignment" value="2x2" />
+           <Limit name="block-size" value="16x16" />
+           <Limit name="blocks-per-second" min="1" max="1984500" />
+           <Limit name="bitrate" range="1-62500000" />
+           <Limit name="concurrent-instances" max="8" />
+           <Limit name="performance-point-3840x2160" range="30-30" />
+           <Feature name="adaptive-playback" />
+       </MediaCodec>
+
+       <MediaCodec name="c2.v4l2.vp9.decoder" type="video/x-vnd.on2.vp9" >
+           <Limit name="size" min="16x16" max="4096x4096" />
+           <Limit name="alignment" value="2x2" />
+           <Limit name="block-size" value="16x16" />
+           <Limit name="blocks-per-second" min="1" max="2073600" />
+           <Limit name="bitrate" range="1-62500000" />
+           <Limit name="concurrent-instances" max="8" />
+           <Limit name="performance-point-3840x2160" range="30-30" />
+           <Feature name="adaptive-playback" />
+       </MediaCodec>
+
+       <MediaCodec name="c2.v4l2.avc.decoder.secure" type="video/avc" >
+           <Limit name="size" min="16x16" max="4096x4096" />
+           <Limit name="alignment" value="2x2" />
+           <Limit name="block-size" value="16x16" />
+           <Limit name="blocks-per-second" min="1" max="1879200" />
+           <Limit name="bitrate" range="1-62500000" />
+           <Limit name="concurrent-instances" max="8" />
+           <Limit name="performance-point-3840x2160" range="30-30" />
+           <Feature name="adaptive-playback" />
+           <Feature name="secure-playback" required="true" />
+       </MediaCodec>
+
+       <MediaCodec name="c2.v4l2.vp8.decoder.secure" type="video/x-vnd.on2.vp8" >
+           <Limit name="size" min="16x16" max="4096x4096" />
+           <Limit name="alignment" value="2x2" />
+           <Limit name="block-size" value="16x16" />
+           <Limit name="blocks-per-second" min="1" max="1984500" />
+           <Limit name="bitrate" range="1-62500000" />
+           <Limit name="concurrent-instances" max="8" />
+           <Limit name="performance-point-3840x2160" range="30-30" />
+           <Feature name="adaptive-playback" />
+           <Feature name="secure-playback" required="true" />
+       </MediaCodec>
+
+       <MediaCodec name="c2.v4l2.vp9.decoder.secure" type="video/x-vnd.on2.vp9" >
+           <Limit name="size" min="16x16" max="4096x4096" />
+           <Limit name="alignment" value="2x2" />
+           <Limit name="block-size" value="16x16" />
+           <Limit name="blocks-per-second" min="1" max="2073600" />
+           <Limit name="bitrate" range="1-62500000" />
+           <Limit name="concurrent-instances" max="8" />
+           <Limit name="performance-point-3840x2160" range="30-30" />
+           <Feature name="adaptive-playback" />
+           <Feature name="secure-playback" required="true" />
+       </MediaCodec>
+   </Decoders>
+</MediaCodecs>
+```
+
+Set SELinux file policy in sepolicy/file\_contexts
+
+```
+/vendor/bin/hw/android\.hardware\.media\.c2@1\.0-service-v4l2(.*)?  u:object_r:mediacodec_exec:s0
+```
+
+Add additional permission in codec2.vendor.ext.policy
+
+```
+_llseek: 1
+epoll_create1: 1
+epoll_ctl: 1
+epoll_pwait: 1
+eventfd2: 1
+fstat64: 1
+fstatat64: 1
+fstatfs64: 1
+getcwd: 1
+getdents64: 1
+getuid32: 1
+mmap2: 1
+pselect6: 1
+statfs64: 1
+sysinfo: 1
+ugetrlimit: 1
+```
+
+Set file permission in ueventd.rc
+
+```
+/dev/video*    0600   media      media
+```
+
+### Customized Allocator
+
+The default allocator of the decoder's output buffer is C2AllocatorGralloc.
+However, C2AllocatorGralloc might not fit the requirement for secure playback.
+In this case, we can implement a customized C2Allocator, and load it at
+run-time. Please create a library `libv4l2_codec2_vendor_allocator`, and export
+a function `CreateVendorAllocator() `for creating the customized C2Allocator.
+Here is an example below.
+
+#### Example of Android.bp
+
+```json
+cc_library_shared {
+    name: "libv4l2_codec2_vendor_allocator",
+    vendor: true,
+
+    defaults: [
+        "libcodec2-impl-defaults",
+    ],
+
+    srcs: [
+        "C2VendorAllocatorFactory.cpp",
+    ],
+
+    shared_libs: [
+        "libc2plugin_store",
+    ],
+}
+```
+
+#### Example of C2VendorAllocatorFactory.cpp
+
+```cpp
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2VendorAllocatorFactory"
+
+#include <C2AllocatorGralloc.h>
+#include <utils/Log.h>
+#include <v4l2_codec2/plugin_store/V4L2AllocatorId.h>
+
+namespace android {
+
+::C2Allocator* CreateVendorAllocator(::C2Allocator::id_t allocatorId) {
+    ALOGV("%s(%d)", __func__, allocatorId);
+
+    // Change to create vendor-customized implementation.
+    switch (allocatorId) {
+    case V4L2AllocatorId::V4L2_BUFFERQUEUE:
+        return new C2AllocatorGralloc(V4L2AllocatorId::V4L2_BUFFERQUEUE, true);
+    case V4L2AllocatorId::V4L2_BUFFERPOOL:
+        return new C2AllocatorGralloc(V4L2AllocatorId::V4L2_BUFFERPOOL, true);
+    case V4L2AllocatorId::SECURE_LINEAR:
+        return new C2AllocatorGralloc(V4L2AllocatorId::SECURE_LINEAR, true);
+    case V4L2AllocatorId::SECURE_GRAPHIC:
+        return new C2AllocatorGralloc(V4L2AllocatorId::SECURE_GRAPHIC, true);
+    default:
+        ALOGE("%s(): Unknown allocator ID: %d", __func__, allocatorId);
+    }
+    return nullptr;
+}
+
+}  // namespace android
+
+extern "C" ::C2Allocator* CreateAllocator(::C2Allocator::id_t allocatorId) {
+    return ::android::CreateVendorAllocator(allocatorId);
+}
+```
+
+## V4L2 Encoder
+
+### Supported Codecs
+
+Currently the V4L2 encoder has support for the H.264, VP8 and VP9 codecs. Codec
+selection can be done by selecting the encoder with the appropriate name.
+
+- H26: *c2.v4l2.avc.encoder*
+- VP8: *c2.v4l2.vp8.encoder*
+- VP9: *c2.v4l2.vp9.encoder*
+
+### Supported Parameters:
+
+The following parameters are static and can not be changed at run-time:
+
+- *C2_PARAMKEY_PICTURE_SIZE*: This parameter can be used to configure the
+resolution of the encoded video stream.
+- *C2_PARAMKEY_PROFILE_LEVEL*: This parameter can be used to adjust the desired
+profile level. When using the H.264 codec the profile level might be adjusted to
+conform to the minimum requirements for the specified bitrate and framerate.
+- *C2_PARAMKEY_SYNC_FRAME_INTERVAL*: This parameter can be used to configure the
+desired time between subsequent key frames in microseconds.
+- *C2_PARAMKEY_BITRATE_MODE*: This parameter can be used to switch between
+constant (*C2Config::BITRATE_CONST*) and variable (*C2Config::BITRATE_VARIABLE*)
+bitrate modes. When using CBR the encoder will try to maintain a constant
+bitrate. This mode is preferable for video conferencing where maintaining a
+stable bitrate is more important than quality. When using VBR the encoder will
+be allowed to dynamically adjust the bitrate to maintain a constant quality. As
+the mediacodec framework does not provide facilities to configure the peak
+bitrate when using VBR, it is currently always set to 2x the target bitrate.
+
+The following parameters are dynamic, and can be freely adjusted at run-time:
+
+- *C2_PARAMKEY_BITRATE*: Use this parameter to specify the desired bitrate.
+- *C2_PARAMKEY_FRAME_RATE*: This parameter can be used to configure the desired
+framerate. Note that the encoder will automatically try to adjust the framerate
+if the timestamps on the input video frames don't match the configured
+framerate.
+- *C2_PARAMKEY_REQUEST_SYNC_FRAME*: This parameter can be used to request
+additional key frames in addition to the periodic ones requested through the
+*C2_PARAMKEY_SYNC_FRAME_INTERVAL* parameter.
+
+### Supported Input Pixel Formats:
+
+The V4L2 encoder supports various input pixel formats, however frames are
+currently always passed to the V4L2 encoder in the NV12 format. If a video frame
+using a different pixel format is passed to the encoder, format conversion will
+be performed to convert the frame to the NV12 format.
+
+### Additional Features:
+
+To improve the resilience of H.264 video streams when data is missing, SPS and
+PPS NAL units are prepended to IDR frames by enabling the
+*V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR* control. If the V4L2 driver does not
+support this control the encoder will manually cache and prepend SPS and PPS NAL
+units.
diff --git a/common/EncodeHelpers.cpp b/common/EncodeHelpers.cpp
index 4575197..c07281f 100644
--- a/common/EncodeHelpers.cpp
+++ b/common/EncodeHelpers.cpp
@@ -18,6 +18,32 @@
 
 namespace android {
 
+namespace {
+
+// Android frameworks needs 4 bytes start code.
+constexpr uint8_t kH264StartCode[] = {0x00, 0x00, 0x00, 0x01};
+constexpr size_t kH264StartCodeSize = 4;
+
+// Copy an H.264 NAL unit with size |srcSize| (without a start code) into a buffer with size
+// |dstSize|. An H.264 start code is prepended to the NAL unit. After copying |dst| is adjusted to
+// point to the address immediately following the copied data, and the |dstSize| is updated to
+// reflect the remaining destination buffer size.
+bool copyNALUPrependingStartCode(const uint8_t* src, size_t srcSize, uint8_t** dst,
+                                 size_t* dstSize) {
+    size_t naluSize = srcSize + kH264StartCodeSize;
+    if (naluSize > *dstSize) {
+        ALOGE("Couldn't copy NAL unit, not enough space in destination buffer");
+        return false;
+    }
+    memcpy(*dst, kH264StartCode, kH264StartCodeSize);
+    memcpy(*dst + kH264StartCodeSize, src, srcSize);
+    *dst += naluSize;
+    *dstSize -= naluSize;
+    return true;
+}
+
+}  // namespace
+
 uint8_t c2LevelToV4L2Level(C2Config::level_t level) {
     switch (level) {
     case C2Config::LEVEL_AVC_1:
@@ -84,41 +110,101 @@
     return ycbcr;
 }
 
-void extractCSDInfo(std::unique_ptr<C2StreamInitDataInfo::output>* const csd, const uint8_t* data,
-                    size_t length) {
-    // Android frameworks needs 4 bytes start code.
-    constexpr uint8_t kStartCode[] = {0x00, 0x00, 0x00, 0x01};
-    constexpr int kStartCodeLength = 4;
+bool extractSPSPPS(const uint8_t* data, size_t length, std::vector<uint8_t>* sps,
+                   std::vector<uint8_t>* pps) {
+    bool foundSPS = false;
+    bool foundPPS = false;
+    NalParser parser(data, length);
+    while (!(foundSPS && foundPPS) && parser.locateNextNal()) {
+        switch (parser.type()) {
+        case NalParser::kSPSType:
+            sps->resize(parser.length());
+            memcpy(sps->data(), parser.data(), parser.length());
+            foundSPS = true;
+            break;
+        case NalParser::kPPSType:
+            pps->resize(parser.length());
+            memcpy(pps->data(), parser.data(), parser.length());
+            foundPPS = true;
+            break;
+        }
+    }
+    return foundSPS && foundPPS;
+}
 
+bool extractCSDInfo(std::unique_ptr<C2StreamInitDataInfo::output>* const csd, const uint8_t* data,
+                    size_t length) {
     csd->reset();
 
-    // Temporarily allocate a byte array to copy codec config data. This should be freed after
-    // codec config data extraction is done.
-    auto tmpConfigData = std::make_unique<uint8_t[]>(length);
-    uint8_t* tmpOutput = tmpConfigData.get();
-    uint8_t* tmpConfigDataEnd = tmpOutput + length;
-
-    NalParser parser(data, length);
-    while (parser.locateNextNal()) {
-        if (parser.length() == 0) continue;
-        uint8_t nalType = parser.type();
-        ALOGV("find next NAL: type=%d, length=%zu", nalType, parser.length());
-        if (nalType != NalParser::kSPSType && nalType != NalParser::kPPSType) continue;
-
-        if (tmpOutput + kStartCodeLength + parser.length() > tmpConfigDataEnd) {
-            ALOGE("Buffer overflow on extracting codec config data (length=%zu)", length);
-            return;
-        }
-        std::memcpy(tmpOutput, kStartCode, kStartCodeLength);
-        tmpOutput += kStartCodeLength;
-        std::memcpy(tmpOutput, parser.data(), parser.length());
-        tmpOutput += parser.length();
+    std::vector<uint8_t> sps;
+    std::vector<uint8_t> pps;
+    if (!extractSPSPPS(data, length, &sps, &pps)) {
+        return false;
     }
 
-    size_t configDataLength = tmpOutput - tmpConfigData.get();
+    size_t configDataLength = sps.size() + pps.size() + (2u * kH264StartCodeSize);
     ALOGV("Extracted codec config data: length=%zu", configDataLength);
+
     *csd = C2StreamInitDataInfo::output::AllocUnique(configDataLength, 0u);
-    std::memcpy((*csd)->m.value, tmpConfigData.get(), configDataLength);
+    uint8_t* csdBuffer = (*csd)->m.value;
+    return copyNALUPrependingStartCode(sps.data(), sps.size(), &csdBuffer, &configDataLength) &&
+           copyNALUPrependingStartCode(pps.data(), pps.size(), &csdBuffer, &configDataLength);
+}
+
+size_t prependSPSPPSToIDR(const uint8_t* src, size_t srcSize, uint8_t* dst, size_t dstSize,
+                          std::vector<uint8_t>* sps, std::vector<uint8_t>* pps) {
+    bool foundStreamParams = false;
+    size_t remainingDstSize = dstSize;
+    NalParser parser(src, srcSize);
+    while (parser.locateNextNal()) {
+        switch (parser.type()) {
+        case NalParser::kSPSType:
+            // SPS found, copy to cache.
+            ALOGV("Found SPS (length %zu)", parser.length());
+            sps->resize(parser.length());
+            memcpy(sps->data(), parser.data(), parser.length());
+            foundStreamParams = true;
+            break;
+        case NalParser::kPPSType:
+            // PPS found, copy to cache.
+            ALOGV("Found PPS (length %zu)", parser.length());
+            pps->resize(parser.length());
+            memcpy(pps->data(), parser.data(), parser.length());
+            foundStreamParams = true;
+            break;
+        case NalParser::kIDRType:
+            ALOGV("Found IDR (length %zu)", parser.length());
+            if (foundStreamParams) {
+                ALOGV("Not injecting SPS and PPS before IDR, already present");
+                break;
+            }
+
+            // Prepend the cached SPS and PPS to the IDR NAL unit.
+            if (sps->empty() || pps->empty()) {
+                ALOGE("No cached SPS or PPS NAL unit available to inject before IDR");
+                return 0;
+            }
+            if (!copyNALUPrependingStartCode(sps->data(), sps->size(), &dst, &remainingDstSize)) {
+                ALOGE("Not enough space to inject SPS NAL unit before IDR");
+                return 0;
+            }
+            if (!copyNALUPrependingStartCode(pps->data(), pps->size(), &dst, &remainingDstSize)) {
+                ALOGE("Not enough space to inject PPS NAL unit before IDR");
+                return 0;
+            }
+
+            ALOGV("Stream header injected before IDR");
+            break;
+        }
+
+        // Copy the NAL unit to the new output buffer.
+        if (!copyNALUPrependingStartCode(parser.data(), parser.length(), &dst, &remainingDstSize)) {
+            ALOGE("NAL unit does not fit in the provided output buffer");
+            return 0;
+        }
+    }
+
+    return dstSize - remainingDstSize;
 }
 
 }  // namespace android
diff --git a/common/Fourcc.cpp b/common/Fourcc.cpp
index f7d3efd..11ea31e 100644
--- a/common/Fourcc.cpp
+++ b/common/Fourcc.cpp
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Fourcc"
+
 #include <v4l2_codec2/common/Fourcc.h>
 
 #include <linux/videodev2.h>
@@ -36,7 +39,7 @@
     case MM21:
         return Fourcc(static_cast<Value>(fourcc));
     }
-    ALOGE("Unmapped fourcc: %s", fourccToString(fourcc).c_str());
+    ALOGV("Unmapped fourcc: %s", fourccToString(fourcc).c_str());
     return std::nullopt;
 }
 
diff --git a/common/NalParser.cpp b/common/NalParser.cpp
index 3216574..1df95d4 100644
--- a/common/NalParser.cpp
+++ b/common/NalParser.cpp
@@ -31,28 +31,35 @@
 constexpr uint32_t kYUV444Idc = 3;
 
 // Read unsigned int encoded with exponential-golomb.
-uint32_t parseUE(ABitReader* br) {
+bool parseUE(ABitReader* br, uint32_t* val) {
     uint32_t numZeroes = 0;
-    while (br->getBits(1) == 0) {
+    uint32_t bit;
+    if (!br->getBitsGraceful(1, &bit)) return false;
+    while (bit == 0) {
         ++numZeroes;
+        if (!br->getBitsGraceful(1, &bit)) return false;
     }
-    uint32_t val = br->getBits(numZeroes);
-    return val + (1u << numZeroes) - 1;
+    if (!br->getBitsGraceful(numZeroes, val)) return false;
+    *val += (1u << numZeroes) - 1;
+    return true;
 }
 
 // Read signed int encoded with exponential-golomb.
-int32_t parseSE(ABitReader* br) {
-    uint32_t codeNum = parseUE(br);
-    return (codeNum & 1) ? (codeNum + 1) >> 1 : -static_cast<int32_t>(codeNum >> 1);
+bool parseSE(ABitReader* br, int32_t* val) {
+    uint32_t codeNum;
+    if (!parseUE(br, &codeNum)) return false;
+    *val = (codeNum & 1) ? (codeNum + 1) >> 1 : -static_cast<int32_t>(codeNum >> 1);
+    return true;
 }
 
 // Skip a H.264 sequence scaling list in the specified bitstream.
-void skipScalingList(ABitReader* br, size_t scalingListSize) {
+bool skipScalingList(ABitReader* br, size_t scalingListSize) {
     size_t nextScale = 8;
     size_t lastScale = 8;
     for (size_t j = 0; j < scalingListSize; ++j) {
         if (nextScale != 0) {
-            int32_t deltaScale = parseSE(br);  // delta_sl
+            int32_t deltaScale;
+            if (!parseSE(br, &deltaScale)) return false;  // delta_sl
             if (deltaScale < -128) {
                 ALOGW("delta scale (%d) is below range, capping to -128", deltaScale);
                 deltaScale = -128;
@@ -64,19 +71,24 @@
         }
         lastScale = (nextScale == 0) ? lastScale : nextScale;
     }
+    return true;
 }
 
 // Skip the H.264 sequence scaling matrix in the specified bitstream.
-void skipScalingMatrix(ABitReader* br, size_t numScalingLists) {
+bool skipScalingMatrix(ABitReader* br, size_t numScalingLists) {
     for (size_t i = 0; i < numScalingLists; ++i) {
-        if (br->getBits(1)) {  // seq_scaling_list_present_flag
+        uint32_t seq_scaling_list_present_flag;
+        if (!br->getBitsGraceful(1, &seq_scaling_list_present_flag))
+            return false;  // seq_scaling_list_present_flag
+        if (seq_scaling_list_present_flag) {
             if (i < 6) {
-                skipScalingList(br, 16);
+                if (!skipScalingList(br, 16)) return false;
             } else {
-                skipScalingList(br, 64);
+                if (!skipScalingList(br, 64)) return false;
             }
         }
     }
+    return true;
 }
 
 }  // namespace
@@ -136,82 +148,120 @@
     // Skip first byte containing type.
     ABitReader br(mCurrNalDataPos + 1, length() - 1);
 
-    uint32_t profileIDC = br.getBits(8);  // profile_idc
-    br.skipBits(16);                      // constraint flags + reserved bits + level_idc
-    parseUE(&br);                         // seq_parameter_set_id
+    uint32_t unused;
+    uint32_t profileIDC;
+    if (!br.getBitsGraceful(8, &profileIDC)) return false;  // profile_idc
+    br.skipBits(16);        // constraint flags + reserved bits + level_idc
+    parseUE(&br, &unused);  // seq_parameter_set_id
 
     if (profileIDC == kProfileIDCHigh || profileIDC == kProfileIDHigh10 ||
         profileIDC == kProfileIDHigh422 || profileIDC == kProfileIDHigh444Predictive ||
         profileIDC == kProfileIDCAVLC444 || profileIDC == kProfileIDScalableBaseline ||
         profileIDC == kProfileIDScalableHigh || profileIDC == kProfileIDSMultiviewHigh ||
         profileIDC == kProfileIDStereoHigh) {
-        uint32_t chromaFormatIDC = parseUE(&br);
+        uint32_t chromaFormatIDC;
+        if (!parseUE(&br, &chromaFormatIDC)) return false;
         if (chromaFormatIDC == kYUV444Idc) {  // chroma_format_idc
             br.skipBits(1);                   // separate_colour_plane_flag
         }
-        parseUE(&br);    // bit_depth_luma_minus8
-        parseUE(&br);    // bit_depth_chroma_minus8
-        br.skipBits(1);  // lossless_qpprime_y_zero_flag
 
-        if (br.getBits(1)) {  // seq_scaling_matrix_present_flag
+        parseUE(&br, &unused);  // bit_depth_luma_minus8
+        parseUE(&br, &unused);  // bit_depth_chroma_minus8
+        br.skipBits(1);         // lossless_qpprime_y_zero_flag
+
+        uint32_t seqScalingMatrixPresentFlag;
+        if (!br.getBitsGraceful(1, &seqScalingMatrixPresentFlag))
+            return false;  // seq_scaling_matrix_present_flag
+        if (seqScalingMatrixPresentFlag) {
             const size_t numScalingLists = (chromaFormatIDC != kYUV444Idc) ? 8 : 12;
-            skipScalingMatrix(&br, numScalingLists);
+            if (!skipScalingMatrix(&br, numScalingLists)) return false;
         }
     }
 
-    parseUE(&br);                                   // log2_max_frame_num_minus4
-    uint32_t pictureOrderCountType = parseUE(&br);  // pic_order_cnt_type
+    parseUE(&br, &unused);  // log2_max_frame_num_minus4
+    uint32_t pictureOrderCountType;
+    if (!parseUE(&br, &pictureOrderCountType)) return false;  // pic_order_cnt_type
     if (pictureOrderCountType == 0) {
-        parseUE(&br);  // log2_max_pic_order_cnt_lsb_minus4
+        parseUE(&br, &unused);  // log2_max_pic_order_cnt_lsb_minus4
     } else if (pictureOrderCountType == 1) {
-        br.skipBits(1);                              // delta_pic_order_always_zero_flag
-        parseSE(&br);                                // offset_for_non_ref_pic
-        parseSE(&br);                                // offset_for_top_to_bottom_field
-        uint32_t numReferenceFrames = parseUE(&br);  // num_ref_frames_in_pic_order_cnt_cycle
+        br.skipBits(1);  // delta_pic_order_always_zero_flag
+        int32_t unused_i;
+        parseSE(&br, &unused_i);  // offset_for_non_ref_pic
+        parseSE(&br, &unused_i);  // offset_for_top_to_bottom_field
+        uint32_t numReferenceFrames;
+        if (!parseUE(&br, &numReferenceFrames))
+            return false;  // num_ref_frames_in_pic_order_cnt_cycle
         for (uint32_t i = 0; i < numReferenceFrames; ++i) {
-            parseUE(&br);  // offset_for_ref_frame
+            parseUE(&br, &unused);  // offset_for_ref_frame
         }
     }
 
-    parseUE(&br);          // num_ref_frames
-    br.skipBits(1);        // gaps_in_frame_num_value_allowed_flag
-    parseUE(&br);          // pic_width_in_mbs_minus1
-    parseUE(&br);          // pic_height_in_map_units_minus1
-    if (!br.getBits(1)) {  // frame_mbs_only_flag
-        br.skipBits(1);    // mb_adaptive_frame_field_flag
+    parseUE(&br, &unused);  // num_ref_frames
+    br.skipBits(1);         // gaps_in_frame_num_value_allowed_flag
+    parseUE(&br, &unused);  // pic_width_in_mbs_minus1
+    parseUE(&br, &unused);  // pic_height_in_map_units_minus1
+    uint32_t frameMbsOnlyFlag;
+    if (!br.getBitsGraceful(1, &frameMbsOnlyFlag)) return false;  // frame_mbs_only_flag
+    if (!frameMbsOnlyFlag) {
+        br.skipBits(1);  // mb_adaptive_frame_field_flag
     }
     br.skipBits(1);  // direct_8x8_inference_flag
 
-    if (br.getBits(1)) {  // frame_cropping_flag
-        parseUE(&br);     // frame_cropping_rect_left_offset
-        parseUE(&br);     // frame_cropping_rect_right_offset
-        parseUE(&br);     // frame_cropping_rect_top_offset
-        parseUE(&br);     // frame_cropping_rect_bottom_offset
+    uint32_t frameCroppingFlag;
+    if (!br.getBitsGraceful(1, &frameCroppingFlag)) return false;  // frame_cropping_flag
+    if (frameCroppingFlag) {
+        parseUE(&br, &unused);  // frame_cropping_rect_left_offset
+        parseUE(&br, &unused);  // frame_cropping_rect_right_offset
+        parseUE(&br, &unused);  // frame_cropping_rect_top_offset
+        parseUE(&br, &unused);  // frame_cropping_rect_bottom_offset
     }
 
-    if (br.getBits(1)) {                 // vui_parameters_present_flag
-        if (br.getBits(1)) {             // VUI aspect_ratio_info_present_flag
-            if (br.getBits(8) == 255) {  // VUI aspect_ratio_idc == extended sample aspect ratio
-                br.skipBits(32);         // VUI sar_width + sar_height
+    uint32_t vuiParametersPresentFlag;
+    if (!br.getBitsGraceful(1, &vuiParametersPresentFlag))
+        return false;  // vui_parameters_present_flag
+    if (vuiParametersPresentFlag) {
+        uint32_t aspectRatioInfoPresentFlag;
+        if (!br.getBitsGraceful(1, &aspectRatioInfoPresentFlag))
+            return false;  // VUI aspect_ratio_info_present_flag
+        if (aspectRatioInfoPresentFlag) {
+            uint32_t aspectRatioIdc;
+            if (!br.getBitsGraceful(8, &aspectRatioIdc)) return false;  // VUI aspect_ratio_idc
+            if (aspectRatioIdc == 255) {  // VUI aspect_ratio_idc == extended sample aspect ratio
+                br.skipBits(32);          // VUI sar_width + sar_height
             }
         }
 
-        if (br.getBits(1)) {  // VUI overscan_info_present_flag
-            br.skipBits(1);   // VUI overscan_appropriate_flag
+        uint32_t overscanInfoPresentFlag;
+        if (!br.getBitsGraceful(1, &overscanInfoPresentFlag))
+            return false;  // VUI overscan_info_present_flag
+        if (overscanInfoPresentFlag) {
+            br.skipBits(1);  // VUI overscan_appropriate_flag
         }
-        if (br.getBits(1)) {                              // VUI video_signal_type_present_flag
-            br.skipBits(3);                               // VUI video_format
-            colorAspects->fullRange = br.getBits(1);      // VUI video_full_range_flag
-            if (br.getBits(1)) {                          // VUI color_description_present_flag
-                colorAspects->primaries = br.getBits(8);  // VUI colour_primaries
-                colorAspects->transfer = br.getBits(8);   // VUI transfer_characteristics
-                colorAspects->coeffs = br.getBits(8);     // VUI matrix_coefficients
-                return !br.overRead();
+        uint32_t videoSignalTypePresentFlag;
+        if (!br.getBitsGraceful(1, &videoSignalTypePresentFlag))
+            return false;  // VUI video_signal_type_present_flag
+        if (videoSignalTypePresentFlag) {
+            br.skipBits(3);  // VUI video_format
+            uint32_t videoFullRangeFlag;
+            if (!br.getBitsGraceful(1, &videoFullRangeFlag))
+                return false;  // VUI videoFullRangeFlag
+            colorAspects->fullRange = videoFullRangeFlag;
+            uint32_t color_description_present_flag;
+            if (!br.getBitsGraceful(1, &color_description_present_flag))
+                return false;  // VUI color_description_present_flag
+            if (color_description_present_flag) {
+                if (!br.getBitsGraceful(8, &colorAspects->primaries))
+                    return false;  // VUI colour_primaries
+                if (!br.getBitsGraceful(8, &colorAspects->transfer))
+                    return false;  // VUI transfer_characteristics
+                if (!br.getBitsGraceful(8, &colorAspects->coeffs))
+                    return false;  // VUI matrix_coefficients
+                return true;
             }
         }
     }
 
-    return false;
+    return false;  // The NAL unit doesn't contain color aspects info.
 }
 
 }  // namespace android
diff --git a/common/V4L2Device.cpp b/common/V4L2Device.cpp
index a31d82b..1efb4e3 100644
--- a/common/V4L2Device.cpp
+++ b/common/V4L2Device.cpp
@@ -313,7 +313,7 @@
 V4L2BufferRefBase::V4L2BufferRefBase(const struct v4l2_buffer& v4l2Buffer,
                                      base::WeakPtr<V4L2Queue> queue)
       : mQueue(std::move(queue)), mReturnTo(mQueue->mFreeBuffers) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(V4L2_TYPE_IS_MULTIPLANAR(v4l2Buffer.type));
     ALOG_ASSERT(v4l2Buffer.length <= base::size(mV4l2Planes));
     ALOG_ASSERT(mReturnTo);
@@ -331,7 +331,7 @@
 }
 
 bool V4L2BufferRefBase::queueBuffer() {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     if (!mQueue) return false;
 
@@ -341,7 +341,7 @@
 }
 
 void* V4L2BufferRefBase::getPlaneMapping(const size_t plane) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     if (!mQueue) return nullptr;
 
@@ -349,7 +349,7 @@
 }
 
 bool V4L2BufferRefBase::checkNumFDsForFormat(const size_t numFds) const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     if (!mQueue) return false;
 
@@ -383,24 +383,24 @@
 V4L2WritableBufferRef::V4L2WritableBufferRef(const struct v4l2_buffer& v4l2Buffer,
                                              base::WeakPtr<V4L2Queue> queue)
       : mBufferData(std::make_unique<V4L2BufferRefBase>(v4l2Buffer, std::move(queue))) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 }
 
 V4L2WritableBufferRef::V4L2WritableBufferRef(V4L2WritableBufferRef&& other)
       : mBufferData(std::move(other.mBufferData)) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     DCHECK_CALLED_ON_VALID_SEQUENCE(other.mSequenceChecker);
 }
 
 V4L2WritableBufferRef::~V4L2WritableBufferRef() {
     // Only valid references should be sequence-checked
     if (mBufferData) {
-        ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+        DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     }
 }
 
 V4L2WritableBufferRef& V4L2WritableBufferRef::operator=(V4L2WritableBufferRef&& other) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     DCHECK_CALLED_ON_VALID_SEQUENCE(other.mSequenceChecker);
 
     if (this == &other) return *this;
@@ -411,14 +411,14 @@
 }
 
 enum v4l2_memory V4L2WritableBufferRef::memory() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     return static_cast<enum v4l2_memory>(mBufferData->mV4l2Buffer.memory);
 }
 
 bool V4L2WritableBufferRef::doQueue() && {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     bool queued = mBufferData->queueBuffer();
@@ -430,7 +430,7 @@
 }
 
 bool V4L2WritableBufferRef::queueMMap() && {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     // Move ourselves so our data gets freed no matter when we return
@@ -445,7 +445,7 @@
 }
 
 bool V4L2WritableBufferRef::queueUserPtr(const std::vector<void*>& ptrs) && {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     // Move ourselves so our data gets freed no matter when we return
@@ -471,7 +471,7 @@
 }
 
 bool V4L2WritableBufferRef::queueDMABuf(const std::vector<int>& fds) && {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     // Move ourselves so our data gets freed no matter when we return
@@ -491,14 +491,14 @@
 }
 
 size_t V4L2WritableBufferRef::planesCount() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     return mBufferData->mV4l2Buffer.length;
 }
 
 size_t V4L2WritableBufferRef::getPlaneSize(const size_t plane) const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     if (plane >= planesCount()) {
@@ -510,7 +510,7 @@
 }
 
 void V4L2WritableBufferRef::setPlaneSize(const size_t plane, const size_t size) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     enum v4l2_memory mem = memory();
@@ -529,28 +529,28 @@
 }
 
 void* V4L2WritableBufferRef::getPlaneMapping(const size_t plane) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     return mBufferData->getPlaneMapping(plane);
 }
 
 void V4L2WritableBufferRef::setTimeStamp(const struct timeval& timestamp) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     mBufferData->mV4l2Buffer.timestamp = timestamp;
 }
 
 const struct timeval& V4L2WritableBufferRef::getTimeStamp() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     return mBufferData->mV4l2Buffer.timestamp;
 }
 
 void V4L2WritableBufferRef::setPlaneBytesUsed(const size_t plane, const size_t bytesUsed) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     if (plane >= planesCount()) {
@@ -567,7 +567,7 @@
 }
 
 size_t V4L2WritableBufferRef::getPlaneBytesUsed(const size_t plane) const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     if (plane >= planesCount()) {
@@ -579,7 +579,7 @@
 }
 
 void V4L2WritableBufferRef::setPlaneDataOffset(const size_t plane, const size_t dataOffset) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     if (plane >= planesCount()) {
@@ -591,7 +591,7 @@
 }
 
 size_t V4L2WritableBufferRef::bufferId() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     return mBufferData->mV4l2Buffer.index;
@@ -600,7 +600,7 @@
 V4L2ReadableBuffer::V4L2ReadableBuffer(const struct v4l2_buffer& v4l2Buffer,
                                        base::WeakPtr<V4L2Queue> queue)
       : mBufferData(std::make_unique<V4L2BufferRefBase>(v4l2Buffer, std::move(queue))) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 }
 
 V4L2ReadableBuffer::~V4L2ReadableBuffer() {
@@ -611,42 +611,42 @@
 }
 
 bool V4L2ReadableBuffer::isLast() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     return mBufferData->mV4l2Buffer.flags & V4L2_BUF_FLAG_LAST;
 }
 
 bool V4L2ReadableBuffer::isKeyframe() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     return mBufferData->mV4l2Buffer.flags & V4L2_BUF_FLAG_KEYFRAME;
 }
 
 struct timeval V4L2ReadableBuffer::getTimeStamp() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     return mBufferData->mV4l2Buffer.timestamp;
 }
 
 size_t V4L2ReadableBuffer::planesCount() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     return mBufferData->mV4l2Buffer.length;
 }
 
 const void* V4L2ReadableBuffer::getPlaneMapping(const size_t plane) const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     DCHECK(mBufferData);
 
     return mBufferData->getPlaneMapping(plane);
 }
 
 size_t V4L2ReadableBuffer::getPlaneBytesUsed(const size_t plane) const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     if (plane >= planesCount()) {
@@ -658,7 +658,7 @@
 }
 
 size_t V4L2ReadableBuffer::getPlaneDataOffset(const size_t plane) const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     if (plane >= planesCount()) {
@@ -670,7 +670,7 @@
 }
 
 size_t V4L2ReadableBuffer::bufferId() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(mBufferData);
 
     return mBufferData->mV4l2Buffer.index;
@@ -698,11 +698,11 @@
 V4L2Queue::V4L2Queue(scoped_refptr<V4L2Device> dev, enum v4l2_buf_type type,
                      base::OnceClosure destroyCb)
       : mType(type), mDevice(dev), mDestroyCb(std::move(destroyCb)) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 }
 
 V4L2Queue::~V4L2Queue() {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     if (mIsStreaming) {
         ALOGEQ("Queue is still streaming, trying to stop it...");
@@ -756,7 +756,7 @@
 }
 
 size_t V4L2Queue::allocateBuffers(size_t count, enum v4l2_memory memory) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     ALOG_ASSERT(!mFreeBuffers);
     ALOG_ASSERT(mQueuedBuffers.size() == 0u);
 
@@ -827,7 +827,7 @@
 }
 
 bool V4L2Queue::deallocateBuffers() {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     if (isStreaming()) {
         ALOGEQ("Cannot deallocate buffers while streaming.");
@@ -860,7 +860,7 @@
 }
 
 size_t V4L2Queue::getMemoryUsage() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
     size_t usage = 0;
     for (const auto& buf : mBuffers) {
         usage += buf->getMemoryUsage();
@@ -873,7 +873,7 @@
 }
 
 std::optional<V4L2WritableBufferRef> V4L2Queue::getFreeBuffer() {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     // No buffers allocated at the moment?
     if (!mFreeBuffers) return std::nullopt;
@@ -886,7 +886,7 @@
 }
 
 std::optional<V4L2WritableBufferRef> V4L2Queue::getFreeBuffer(size_t requestedBufferIid) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     // No buffers allocated at the moment?
     if (!mFreeBuffers) return std::nullopt;
@@ -899,7 +899,7 @@
 }
 
 bool V4L2Queue::queueBuffer(struct v4l2_buffer* v4l2Buffer) {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     int ret = mDevice->ioctl(VIDIOC_QBUF, v4l2Buffer);
     if (ret) {
@@ -919,7 +919,7 @@
 }
 
 std::pair<bool, V4L2ReadableBufferRef> V4L2Queue::dequeueBuffer() {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     // No need to dequeue if no buffers queued.
     if (queuedBuffersCount() == 0) return std::make_pair(true, nullptr);
@@ -968,13 +968,13 @@
 }
 
 bool V4L2Queue::isStreaming() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     return mIsStreaming;
 }
 
 bool V4L2Queue::streamon() {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     if (mIsStreaming) return true;
 
@@ -991,7 +991,7 @@
 }
 
 bool V4L2Queue::streamoff() {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     // We do not check the value of IsStreaming(), because we may have queued buffers to the queue
     // and wish to get them back - in such as case, we may need to do a VIDIOC_STREAMOFF on a
@@ -1017,19 +1017,19 @@
 }
 
 size_t V4L2Queue::allocatedBuffersCount() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     return mBuffers.size();
 }
 
 size_t V4L2Queue::freeBuffersCount() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     return mFreeBuffers ? mFreeBuffers->size() : 0;
 }
 
 size_t V4L2Queue::queuedBuffersCount() const {
-    ALOG_ASSERT(mSequenceChecker.CalledOnValidSequence());
+    DCHECK_CALLED_ON_VALID_SEQUENCE(mSequenceChecker);
 
     return mQueuedBuffers.size();
 }
@@ -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;
@@ -1509,7 +1530,7 @@
 
     // Sanity checks. Calculated coded size has to contain given visible size and fulfill buffer
     // byte size requirements.
-    ALOG_ASSERT(Rect(codedSize).Contains(Rect(visibleSize)));
+    ALOG_ASSERT(contains(Rect(codedSize), Rect(visibleSize)));
     ALOG_ASSERT(sizeimage <= allocationSize(frameFormat, codedSize));
 
     return codedSize;
diff --git a/common/include/v4l2_codec2/common/EncodeHelpers.h b/common/include/v4l2_codec2/common/EncodeHelpers.h
index bfbdd05..832ac91 100644
--- a/common/include/v4l2_codec2/common/EncodeHelpers.h
+++ b/common/include/v4l2_codec2/common/EncodeHelpers.h
@@ -42,12 +42,24 @@
 // Get the specified graphics block in YCbCr format.
 android_ycbcr getGraphicBlockInfo(const C2ConstGraphicBlock& block);
 
+// Try to extract SPS and PPS NAL units from the specified H.264 |data| stream. If found the data
+// will be copied (after resizing) into the provided |sps| and |pps| buffers. Returns whether
+// extraction was successful.
+bool extractSPSPPS(const uint8_t* data, size_t length, std::vector<uint8_t>* sps,
+                   std::vector<uint8_t>* pps);
+
 // When encoding a video the codec-specific data (CSD; e.g. SPS and PPS for H264 encoding) will be
 // concatenated to the first encoded slice. This function extracts the CSD out of the bitstream and
-// stores it into |csd|.
-void extractCSDInfo(std::unique_ptr<C2StreamInitDataInfo::output>* const csd, const uint8_t* data,
+// stores it into |csd|. Returns whether extracting CSD info was successful.
+bool extractCSDInfo(std::unique_ptr<C2StreamInitDataInfo::output>* const csd, const uint8_t* data,
                     size_t length);
 
+// Prepend the specified |sps| and |pps| NAL units (without start codes) to the H.264 |data| stream.
+// The result is copied into |dst|. The provided |sps| and |pps| data will be updated if an SPS or
+// PPS NAL unit is encountered. Returns the size of the new data, will be 0 if an error occurred.
+size_t prependSPSPPSToIDR(const uint8_t* src, size_t srcSize, uint8_t* dst, size_t dstSize,
+                          std::vector<uint8_t>* sps, std::vector<uint8_t>* pps);
+
 }  // namespace android
 
 #endif  // ANDROID_V4L2_CODEC2_COMMON_HELPERS_H
diff --git a/common/include/v4l2_codec2/common/NalParser.h b/common/include/v4l2_codec2/common/NalParser.h
index 69f56c3..ec8a876 100644
--- a/common/include/v4l2_codec2/common/NalParser.h
+++ b/common/include/v4l2_codec2/common/NalParser.h
@@ -12,6 +12,8 @@
 // Helper class to parse H264 NAL units from data.
 class NalParser {
 public:
+    // Type of a IDR Slice NAL unit.
+    static constexpr uint8_t kIDRType = 5;
     // Type of a SPS NAL unit.
     static constexpr uint8_t kSPSType = 7;
     // Type of a PPS NAL unit.
@@ -19,9 +21,9 @@
 
     // Parameters related to a video's color aspects.
     struct ColorAspects {
-        int32_t primaries;
-        int32_t transfer;
-        int32_t coeffs;
+        uint32_t primaries;
+        uint32_t transfer;
+        uint32_t coeffs;
         bool fullRange;
     };
 
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/Android.bp b/components/Android.bp
index 16c7d20..5bee73b 100644
--- a/components/Android.bp
+++ b/components/Android.bp
@@ -47,8 +47,6 @@
         "libstagefright_bufferqueue_helper",
         "libstagefright_foundation",
         "libui",
-    ],
-    static_libs: [
         "libv4l2_codec2_common",
     ],
 
diff --git a/components/V4L2DecodeComponent.cpp b/components/V4L2DecodeComponent.cpp
index 400c765..456f3c4 100644
--- a/components/V4L2DecodeComponent.cpp
+++ b/components/V4L2DecodeComponent.cpp
@@ -35,6 +35,28 @@
 namespace android {
 namespace {
 
+// CCBC pauses sending input buffers to the component when all the output slots are filled by
+// pending decoded buffers. If the available output buffers are exhausted before CCBC pauses sending
+// input buffers, CCodec may timeout due to waiting for a available output buffer.
+// This function returns the minimum number of output buffers to prevent the buffers from being
+// exhausted before CCBC pauses sending input buffers.
+size_t getMinNumOutputBuffers(VideoCodec codec) {
+    // The constant values copied from CCodecBufferChannel.cpp.
+    // (b/184020290): Check the value still sync when seeing error message from CCodec:
+    // "previous call to queue exceeded timeout".
+    constexpr size_t kSmoothnessFactor = 4;
+    constexpr size_t kRenderingDepth = 3;
+    // Extra number of needed output buffers for V4L2Decoder.
+    constexpr size_t kExtraNumOutputBuffersForDecoder = 2;
+
+    // The total needed number of output buffers at pipeline are:
+    // - MediaCodec output slots: output delay + kSmoothnessFactor
+    // - Surface: kRenderingDepth
+    // - Component: kExtraNumOutputBuffersForDecoder
+    return V4L2DecodeInterface::getOutputDelay(codec) + kSmoothnessFactor + kRenderingDepth +
+           kExtraNumOutputBuffersForDecoder;
+}
+
 // Mask against 30 bits to avoid (undefined) wraparound on signed integer.
 int32_t frameIndexToBitstreamId(c2_cntr64_t frameIndex) {
     return static_cast<int32_t>(frameIndex.peeku() & 0x3FFFFFFF);
@@ -124,7 +146,7 @@
         const std::string& name, c2_node_id_t id, const std::shared_ptr<C2ReflectorHelper>& helper,
         C2ComponentFactory::ComponentDeleter deleter) {
     static const int32_t kMaxConcurrentInstances =
-            property_get_int32("debug.v4l2_codec2.decode.concurrent-instances", -1);
+            property_get_int32("ro.vendor.v4l2_codec2.decode_concurrent_instances", -1);
     static std::mutex mutex;
 
     std::lock_guard<std::mutex> lock(mutex);
@@ -180,7 +202,6 @@
     }
     mDecoderTaskRunner = mDecoderThread.task_runner();
     mWeakThis = mWeakThisFactory.GetWeakPtr();
-    mStdWeakThis = weak_from_this();
 
     c2_status_t status = C2_CORRUPTED;
     ::base::WaitableEvent done;
@@ -207,9 +228,11 @@
         return;
     }
     const size_t inputBufferSize = mIntfImpl->getInputBufferSize();
+    const size_t minNumOutputBuffers = getMinNumOutputBuffers(*codec);
+
     // ::base::Unretained(this) is safe here because |mDecoder| is always destroyed before
     // |mDecoderThread| is stopped, so |*this| is always valid during |mDecoder|'s lifetime.
-    mDecoder = V4L2Decoder::Create(*codec, inputBufferSize,
+    mDecoder = V4L2Decoder::Create(*codec, inputBufferSize, minNumOutputBuffers,
                                    ::base::BindRepeating(&V4L2DecodeComponent::getVideoFramePool,
                                                          ::base::Unretained(this)),
                                    ::base::BindRepeating(&V4L2DecodeComponent::onOutputFrameReady,
@@ -237,7 +260,7 @@
     ALOGV("%s()", __func__);
     ALOG_ASSERT(mDecoderTaskRunner->RunsTasksInCurrentSequence());
 
-    auto sharedThis = mStdWeakThis.lock();
+    auto sharedThis = weak_from_this().lock();
     if (sharedThis == nullptr) {
         ALOGE("%s(): V4L2DecodeComponent instance is destroyed.", __func__);
         return nullptr;
@@ -324,7 +347,6 @@
     ALOG_ASSERT(mDecoderTaskRunner->RunsTasksInCurrentSequence());
 
     mWeakThisFactory.InvalidateWeakPtrs();
-    mStdWeakThis.reset();
     mDecoder = nullptr;
 }
 
@@ -470,9 +492,8 @@
                 }
             }
 
-            std::unique_ptr<BitstreamBuffer> buffer =
-                    std::make_unique<BitstreamBuffer>(bitstreamId, linearBlock.handle()->data[0],
-                                                      linearBlock.offset(), linearBlock.size());
+            std::unique_ptr<ConstBitstreamBuffer> buffer = std::make_unique<ConstBitstreamBuffer>(
+                    bitstreamId, linearBlock, linearBlock.offset(), linearBlock.size());
             if (!buffer) {
                 reportError(C2_CORRUPTED);
                 return;
@@ -675,12 +696,6 @@
     ALOGV("%s(work=%llu)", __func__, work->input.ordinal.frameIndex.peekull());
     ALOG_ASSERT(mDecoderTaskRunner->RunsTasksInCurrentSequence());
 
-    auto sharedThis = mStdWeakThis.lock();
-    if (sharedThis == nullptr) {
-        ALOGE("%s(): V4L2DecodeComponent instance is destroyed.", __func__);
-        return false;
-    }
-
     if (!mListener) {
         ALOGE("mListener is nullptr, setListener_vb() not called?");
         return false;
@@ -688,7 +703,7 @@
 
     std::list<std::unique_ptr<C2Work>> finishedWorks;
     finishedWorks.emplace_back(std::move(work));
-    mListener->onWorkDone_nb(std::move(sharedThis), std::move(finishedWorks));
+    mListener->onWorkDone_nb(weak_from_this(), std::move(finishedWorks));
     return true;
 }
 
@@ -725,12 +740,6 @@
     ALOGV("%s()", __func__);
     ALOG_ASSERT(mDecoderTaskRunner->RunsTasksInCurrentSequence());
 
-    auto sharedThis = mStdWeakThis.lock();
-    if (sharedThis == nullptr) {
-        ALOGE("%s(): V4L2DecodeComponent instance is destroyed.", __func__);
-        return;
-    }
-
     std::list<std::unique_ptr<C2Work>> abandonedWorks;
     while (!mPendingWorks.empty()) {
         abandonedWorks.emplace_back(std::move(mPendingWorks.front()));
@@ -754,7 +763,7 @@
             ALOGE("mListener is nullptr, setListener_vb() not called?");
             return;
         }
-        mListener->onWorkDone_nb(std::move(sharedThis), std::move(abandonedWorks));
+        mListener->onWorkDone_nb(weak_from_this(), std::move(abandonedWorks));
     }
 }
 
@@ -828,12 +837,6 @@
     ALOGE("%s(error=%u)", __func__, static_cast<uint32_t>(error));
     ALOG_ASSERT(mDecoderTaskRunner->RunsTasksInCurrentSequence());
 
-    auto sharedThis = mStdWeakThis.lock();
-    if (sharedThis == nullptr) {
-        ALOGE("%s(): V4L2DecodeComponent instance is destroyed.", __func__);
-        return;
-    }
-
     if (mComponentState.load() == ComponentState::ERROR) return;
     mComponentState.store(ComponentState::ERROR);
 
@@ -841,7 +844,7 @@
         ALOGE("mListener is nullptr, setListener_vb() not called?");
         return;
     }
-    mListener->onError_nb(std::move(sharedThis), static_cast<uint32_t>(error));
+    mListener->onError_nb(weak_from_this(), static_cast<uint32_t>(error));
 }
 
 c2_status_t V4L2DecodeComponent::announce_nb(const std::vector<C2WorkOutline>& /* items */) {
diff --git a/components/V4L2Decoder.cpp b/components/V4L2Decoder.cpp
index d694837..18d62d2 100644
--- a/components/V4L2Decoder.cpp
+++ b/components/V4L2Decoder.cpp
@@ -9,6 +9,7 @@
 
 #include <stdint.h>
 
+#include <algorithm>
 #include <vector>
 
 #include <base/bind.h>
@@ -48,12 +49,13 @@
 
 // static
 std::unique_ptr<VideoDecoder> V4L2Decoder::Create(
-        const VideoCodec& codec, const size_t inputBufferSize, GetPoolCB getPoolCb,
-        OutputCB outputCb, ErrorCB errorCb, scoped_refptr<::base::SequencedTaskRunner> taskRunner) {
+        const VideoCodec& codec, const size_t inputBufferSize, const size_t minNumOutputBuffers,
+        GetPoolCB getPoolCb, OutputCB outputCb, ErrorCB errorCb,
+        scoped_refptr<::base::SequencedTaskRunner> taskRunner) {
     std::unique_ptr<V4L2Decoder> decoder =
             ::base::WrapUnique<V4L2Decoder>(new V4L2Decoder(taskRunner));
-    if (!decoder->start(codec, inputBufferSize, std::move(getPoolCb), std::move(outputCb),
-                        std::move(errorCb))) {
+    if (!decoder->start(codec, inputBufferSize, minNumOutputBuffers, std::move(getPoolCb),
+                        std::move(outputCb), std::move(errorCb))) {
         return nullptr;
     }
     return decoder;
@@ -89,12 +91,14 @@
     }
 }
 
-bool V4L2Decoder::start(const VideoCodec& codec, const size_t inputBufferSize, GetPoolCB getPoolCb,
-                        OutputCB outputCb, ErrorCB errorCb) {
-    ALOGV("%s(codec=%s, inputBufferSize=%zu)", __func__, VideoCodecToString(codec),
-          inputBufferSize);
+bool V4L2Decoder::start(const VideoCodec& codec, const size_t inputBufferSize,
+                        const size_t minNumOutputBuffers, GetPoolCB getPoolCb, OutputCB outputCb,
+                        ErrorCB errorCb) {
+    ALOGV("%s(codec=%s, inputBufferSize=%zu, minNumOutputBuffers=%zu)", __func__,
+          VideoCodecToString(codec), inputBufferSize, minNumOutputBuffers);
     ALOG_ASSERT(mTaskRunner->RunsTasksInCurrentSequence());
 
+    mMinNumOutputBuffers = minNumOutputBuffers;
     mGetPoolCb = std::move(getPoolCb);
     mOutputCb = std::move(outputCb);
     mErrorCb = std::move(errorCb);
@@ -188,7 +192,7 @@
     return true;
 }
 
-void V4L2Decoder::decode(std::unique_ptr<BitstreamBuffer> buffer, DecodeCB decodeCb) {
+void V4L2Decoder::decode(std::unique_ptr<ConstBitstreamBuffer> buffer, DecodeCB decodeCb) {
     ALOGV("%s(id=%d)", __func__, buffer->id);
     ALOG_ASSERT(mTaskRunner->RunsTasksInCurrentSequence());
 
@@ -296,7 +300,7 @@
         inputBuffer->setPlaneDataOffset(0, request.buffer->offset);
         inputBuffer->setPlaneBytesUsed(0, request.buffer->offset + request.buffer->size);
         std::vector<int> fds;
-        fds.push_back(std::move(request.buffer->dmabuf_fd));
+        fds.push_back(std::move(request.buffer->dmabuf.handle()->data[0]));
         if (!std::move(*inputBuffer).queueDMABuf(fds)) {
             ALOGE("%s(): Failed to QBUF to input queue, bitstreamId=%d", __func__, bitstreamId);
             onError();
@@ -330,6 +334,7 @@
     }
 
     // Streamoff both V4L2 queues to drop input and output buffers.
+    const bool isOutputStreaming = mOutputQueue->isStreaming();
     mDevice->stopPolling();
     mOutputQueue->streamoff();
     mFrameAtDevice.clear();
@@ -337,7 +342,9 @@
 
     // Streamon both V4L2 queues.
     mInputQueue->streamon();
-    mOutputQueue->streamon();
+    if (isOutputStreaming) {
+        mOutputQueue->streamon();
+    }
 
     // If there is no free buffer at mOutputQueue, tryFetchVideoFrame() should be triggerred after
     // a buffer is DQBUF from output queue. Now all the buffers are dropped at mOutputQueue, we
@@ -496,6 +503,7 @@
     if (!format || !numOutputBuffers) {
         return false;
     }
+    *numOutputBuffers = std::max(*numOutputBuffers, mMinNumOutputBuffers);
 
     const ui::Size codedSize(format->fmt.pix_mp.width, format->fmt.pix_mp.height);
     if (!setupOutputFormat(codedSize)) {
diff --git a/components/V4L2EncodeComponent.cpp b/components/V4L2EncodeComponent.cpp
index b4bbc0e..a1b46ab 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,
@@ -186,6 +189,12 @@
                                                      format, index, timestamp);
 }
 
+// Check whether the specified |profile| is an H.264 profile.
+bool IsH264Profile(C2Config::profile_t profile) {
+    return (profile >= C2Config::PROFILE_AVC_BASELINE &&
+            profile <= C2Config::PROFILE_AVC_ENHANCED_MULTIVIEW_DEPTH_HIGH);
+}
+
 }  // namespace
 
 // static
@@ -198,7 +207,7 @@
     ALOGV("%s(%s)", __func__, name.c_str());
 
     static const int32_t kMaxConcurrentInstances =
-            property_get_int32("debug.v4l2_codec2.encode.concurrent-instances", -1);
+            property_get_int32("ro.vendor.v4l2_codec2.encode_concurrent_instances", -1);
 
     static std::mutex mutex;
     std::lock_guard<std::mutex> lock(mutex);
@@ -617,14 +626,16 @@
     ALOG_ASSERT(!mInputFormatConverter);
     ALOG_ASSERT(!mEncoder);
 
-    mCSDSubmitted = false;
+    mLastFrameTime = std::nullopt;
 
     // Get the requested profile and level.
     C2Config::profile_t outputProfile = mInterface->getOutputProfile();
 
+    // CSD only needs to be extracted when using an H.264 profile.
+    mExtractCSD = IsH264Profile(outputProfile);
+
     std::optional<uint8_t> h264Level;
-    if (outputProfile >= C2Config::PROFILE_AVC_BASELINE &&
-        outputProfile <= C2Config::PROFILE_AVC_ENHANCED_MULTIVIEW_DEPTH_HIGH) {
+    if (IsH264Profile(outputProfile)) {
         h264Level = c2LevelToV4L2Level(mInterface->getOutputLevel());
     }
 
@@ -638,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),
@@ -670,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);
@@ -691,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);
@@ -709,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);
@@ -738,6 +753,18 @@
     ALOGV("Encoding input block (index: %" PRIu64 ", timestamp: %" PRId64 ", size: %dx%d)", index,
           timestamp, block.width(), block.height());
 
+    // Dynamically adjust framerate based on the frame's timestamp if required.
+    constexpr int64_t kMaxFramerateDiff = 5;
+    if (mLastFrameTime && (timestamp > *mLastFrameTime)) {
+        int64_t newFramerate =
+                static_cast<int64_t>(std::round(1000000.0 / (timestamp - *mLastFrameTime)));
+        if (abs(mFramerate - newFramerate) > kMaxFramerateDiff) {
+            ALOGV("Adjusting framerate to %" PRId64 " based on frame timestamps", newFramerate);
+            mInterface->setFramerate(static_cast<uint32_t>(newFramerate));
+        }
+    }
+    mLastFrameTime = timestamp;
+
     // Update dynamic encoding parameters (bitrate, framerate, key frame) if requested.
     if (!updateEncodingParameters()) return false;
 
@@ -785,7 +812,7 @@
         mWorkQueue.pop_front();
     }
     if (!abortedWorkItems.empty()) {
-        mListener->onWorkDone_nb(shared_from_this(), std::move(abortedWorkItems));
+        mListener->onWorkDone_nb(weak_from_this(), std::move(abortedWorkItems));
     }
 }
 
@@ -803,13 +830,7 @@
         reportError(status);
     }
 
-    // Store a reference to the block to keep the fds alive.
-    int fd = block->handle()->data[0];
-    ALOG_ASSERT(!mOutputBuffersMap[fd]);
-    mOutputBuffersMap[fd] = std::move(block);
-
-    // TODO(dstaessens) Store the C2LinearBlock directly into the BitstreamBuffer.
-    *buffer = std::make_unique<BitstreamBuffer>(fd, fd, 0, size);
+    *buffer = std::make_unique<BitstreamBuffer>(std::move(block), 0, size);
 }
 
 void V4L2EncodeComponent::onInputBufferDone(uint64_t index) {
@@ -859,22 +880,19 @@
     ALOGV("%s(): output buffer done (timestamp: %" PRId64 ", size: %zu, keyframe: %d)", __func__,
           timestamp, dataSize, keyFrame);
     ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
+    ALOG_ASSERT(buffer->dmabuf);
 
-    std::shared_ptr<C2LinearBlock> outputBlock = std::move(mOutputBuffersMap[buffer->id]);
-    mOutputBuffersMap.erase(buffer->id);
-    ALOG_ASSERT(outputBlock);
-
-    C2ConstLinearBlock constBlock = outputBlock->share(outputBlock->offset(), dataSize, C2Fence());
+    C2ConstLinearBlock constBlock =
+            buffer->dmabuf->share(buffer->dmabuf->offset(), dataSize, C2Fence());
 
     // If no CSD (content-specific-data, e.g. SPS for H.264) has been submitted yet, we expect this
     // output block to contain CSD. We only submit the CSD once, even if it's attached to each key
     // frame.
-    if (!mCSDSubmitted) {
+    if (mExtractCSD) {
         ALOGV("No CSD submitted yet, extracting CSD");
         std::unique_ptr<C2StreamInitDataInfo::output> csd;
         C2ReadView view = constBlock.map().get();
-        extractCSDInfo(&csd, view.data(), view.capacity());
-        if (!csd) {
+        if (!extractCSDInfo(&csd, view.data(), view.capacity())) {
             ALOGE("Failed to extract CSD");
             reportError(C2_CORRUPTED);
             return;
@@ -884,7 +902,7 @@
         LOG_ASSERT(!mWorkQueue.empty());
         C2Work* work = mWorkQueue.front().get();
         work->worklets.front()->output.configUpdate.push_back(std::move(csd));
-        mCSDSubmitted = true;
+        mExtractCSD = false;
     }
 
     // Get the work item associated with the timestamp.
@@ -993,15 +1011,23 @@
 
     std::list<std::unique_ptr<C2Work>> finishedWorkList;
     finishedWorkList.emplace_back(std::move(work));
-    mListener->onWorkDone_nb(shared_from_this(), std::move(finishedWorkList));
+    mListener->onWorkDone_nb(weak_from_this(), std::move(finishedWorkList));
 }
 
 bool V4L2EncodeComponent::getBlockPool() {
+    ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
+
+    auto sharedThis = weak_from_this().lock();
+    if (!sharedThis) {
+        ALOGI("%s(): V4L2EncodeComponent instance is already destroyed", __func__);
+        return false;
+    }
+
     C2BlockPool::local_id_t poolId = mInterface->getBlockPoolId();
     if (poolId == C2BlockPool::BASIC_LINEAR) {
         ALOGW("Using unoptimized linear block pool");
     }
-    c2_status_t status = GetCodec2BlockPool(poolId, shared_from_this(), &mOutputBlockPool);
+    c2_status_t status = GetCodec2BlockPool(poolId, std::move(sharedThis), &mOutputBlockPool);
     if (status != C2_OK || !mOutputBlockPool) {
         ALOGE("Failed to get output block pool, error: %d", status);
         return false;
@@ -1017,7 +1043,7 @@
     std::lock_guard<std::mutex> lock(mComponentLock);
     if (mComponentState != ComponentState::ERROR) {
         setComponentState(ComponentState::ERROR);
-        mListener->onError_nb(shared_from_this(), static_cast<uint32_t>(error));
+        mListener->onError_nb(weak_from_this(), static_cast<uint32_t>(error));
     }
 }
 
diff --git a/components/V4L2EncodeInterface.cpp b/components/V4L2EncodeInterface.cpp
index 7f0fb39..03d8c37 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;
@@ -329,8 +338,8 @@
                                                  C2Config::LEVEL_AVC_2_1, C2Config::LEVEL_AVC_2_2,
                                                  C2Config::LEVEL_AVC_3, C2Config::LEVEL_AVC_3_1,
                                                  C2Config::LEVEL_AVC_3_2, C2Config::LEVEL_AVC_4,
-                                                 C2Config::LEVEL_AVC_4_1, C2Config::LEVEL_AVC_5,
-                                                 C2Config::LEVEL_AVC_5_1})})
+                                                 C2Config::LEVEL_AVC_4_1, C2Config::LEVEL_AVC_4_2,
+                                                 C2Config::LEVEL_AVC_5, C2Config::LEVEL_AVC_5_1})})
                         .withSetter(H264ProfileLevelSetter, mInputVisibleSize, mFrameRate, mBitrate)
                         .build());
     } else if (getCodecFromComponentName(name) == VideoCodec::VP8) {
diff --git a/components/V4L2Encoder.cpp b/components/V4L2Encoder.cpp
index cdd2d89..cd20cb5 100644
--- a/components/V4L2Encoder.cpp
+++ b/components/V4L2Encoder.cpp
@@ -17,6 +17,7 @@
 #include <log/log.h>
 #include <ui/Rect.h>
 
+#include <v4l2_codec2/common/EncodeHelpers.h>
 #include <v4l2_codec2/common/Fourcc.h>
 #include <v4l2_codec2/common/V4L2Device.h>
 #include <v4l2_codec2/components/BitstreamBuffer.h>
@@ -54,6 +55,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 +64,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 +164,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 +205,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 +255,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;
@@ -582,7 +605,7 @@
                                                 V4L2ExtCtrl(V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0)});
 
     // All controls below are H.264-specific, so we can return here if the profile is not H.264.
-    if (outputProfile >= C2Config::PROFILE_AVC_BASELINE ||
+    if (outputProfile >= C2Config::PROFILE_AVC_BASELINE &&
         outputProfile <= C2Config::PROFILE_AVC_ENHANCED_MULTIVIEW_DEPTH_HIGH) {
         return configureH264(outputProfile, outputH264Level);
     }
@@ -594,17 +617,18 @@
                                 std::optional<const uint8_t> outputH264Level) {
     // When encoding H.264 we want to prepend SPS and PPS to each IDR for resilience. Some
     // devices support this through the V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR control.
-    // TODO(b/161495502): V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR is currently not supported
-    // yet, just log a warning if the operation was unsuccessful for now.
+    // Otherwise we have to cache the latest SPS and PPS and inject these manually.
     if (mDevice->isCtrlExposed(V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR)) {
         if (!mDevice->setExtCtrls(V4L2_CTRL_CLASS_MPEG,
                                   {V4L2ExtCtrl(V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR, 1)})) {
             ALOGE("Failed to configure device to prepend SPS and PPS to each IDR");
             return false;
         }
+        mInjectParamsBeforeIDR = false;
         ALOGV("Device supports prepending SPS and PPS to each IDR");
     } else {
-        ALOGW("Device doesn't support prepending SPS and PPS to IDR");
+        mInjectParamsBeforeIDR = true;
+        ALOGV("Device doesn't support prepending SPS and PPS to IDR, injecting manually.");
     }
 
     std::vector<V4L2ExtCtrl> h264Ctrls;
@@ -637,6 +661,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());
@@ -780,7 +819,7 @@
     size_t bufferId = buffer->bufferId();
 
     std::vector<int> fds;
-    fds.push_back(bitstreamBuffer->dmabuf_fd);
+    fds.push_back(bitstreamBuffer->dmabuf->handle()->data[0]);
     if (!std::move(*buffer).queueDMABuf(fds)) {
         ALOGE("Failed to queue output buffer using QueueDMABuf");
         onError();
@@ -875,11 +914,51 @@
         return false;
     }
 
-    std::unique_ptr<BitstreamBuffer> bitstream_buffer =
+    std::unique_ptr<BitstreamBuffer> bitstreamBuffer =
             std::move(mOutputBuffers[buffer->bufferId()]);
     if (encodedDataSize > 0) {
-        mOutputBufferDoneCb.Run(encodedDataSize, timestamp.InMicroseconds(), buffer->isKeyframe(),
-                                std::move(bitstream_buffer));
+        if (!mInjectParamsBeforeIDR) {
+            // No need to inject SPS or PPS before IDR frames, we can just return the buffer as-is.
+            mOutputBufferDoneCb.Run(encodedDataSize, timestamp.InMicroseconds(),
+                                    buffer->isKeyframe(), std::move(bitstreamBuffer));
+        } else if (!buffer->isKeyframe()) {
+            // We need to inject SPS and PPS before IDR frames, but this frame is not a key frame.
+            // We can return the buffer as-is, but need to update our SPS and PPS cache if required.
+            C2ConstLinearBlock constBlock = bitstreamBuffer->dmabuf->share(
+                    bitstreamBuffer->dmabuf->offset(), encodedDataSize, C2Fence());
+            C2ReadView readView = constBlock.map().get();
+            extractSPSPPS(readView.data(), encodedDataSize, &mCachedSPS, &mCachedPPS);
+            mOutputBufferDoneCb.Run(encodedDataSize, timestamp.InMicroseconds(),
+                                    buffer->isKeyframe(), std::move(bitstreamBuffer));
+        } else {
+            // We need to inject our cached SPS and PPS NAL units to the IDR frame. It's possible
+            // this frame already has SPS and PPS NAL units attached, in which case we only need to
+            // update our cached SPS and PPS.
+            C2ConstLinearBlock constBlock = bitstreamBuffer->dmabuf->share(
+                    bitstreamBuffer->dmabuf->offset(), encodedDataSize, C2Fence());
+            C2ReadView readView = constBlock.map().get();
+
+            // Allocate a new buffer to copy the data with prepended SPS and PPS into.
+            std::unique_ptr<BitstreamBuffer> prependedBitstreamBuffer;
+            mFetchOutputBufferCb.Run(mOutputBufferSize, &prependedBitstreamBuffer);
+            if (!prependedBitstreamBuffer) {
+                ALOGE("Failed to fetch output block");
+                onError();
+                return false;
+            }
+            C2WriteView writeView = prependedBitstreamBuffer->dmabuf->map().get();
+
+            // If there is not enough space in the output buffer just return the original buffer.
+            size_t newSize = prependSPSPPSToIDR(readView.data(), encodedDataSize, writeView.data(),
+                                                writeView.size(), &mCachedSPS, &mCachedPPS);
+            if (newSize > 0) {
+                mOutputBufferDoneCb.Run(newSize, timestamp.InMicroseconds(), buffer->isKeyframe(),
+                                        std::move(prependedBitstreamBuffer));
+            } else {
+                mOutputBufferDoneCb.Run(encodedDataSize, timestamp.InMicroseconds(),
+                                        buffer->isKeyframe(), std::move(bitstreamBuffer));
+            }
+        }
     }
 
     // If the buffer is marked as last and we were flushing the encoder, flushing is now done.
diff --git a/components/include/v4l2_codec2/components/BitstreamBuffer.h b/components/include/v4l2_codec2/components/BitstreamBuffer.h
index d61e4f9..97cb203 100644
--- a/components/include/v4l2_codec2/components/BitstreamBuffer.h
+++ b/components/include/v4l2_codec2/components/BitstreamBuffer.h
@@ -7,18 +7,30 @@
 
 #include <stdint.h>
 
+#include <C2Buffer.h>
+
 namespace android {
 
-// The BitstreamBuffer class can be used to store encoded video data.
-// Note: The BitstreamBuffer does not take ownership of the data. The file descriptor is not
-//       duplicated and the caller is responsible for keeping the data alive.
-struct BitstreamBuffer {
-    BitstreamBuffer(const int32_t id, int dmabuf_fd, const size_t offset, const size_t size)
-          : id(id), dmabuf_fd(dmabuf_fd), offset(offset), size(size) {}
-    ~BitstreamBuffer() = default;
+// The ConstBitstreamBuffer class can be used to store non-modifiable encoded video data.
+struct ConstBitstreamBuffer {
+    ConstBitstreamBuffer(const int32_t id, C2ConstLinearBlock dmabuf, const size_t offset,
+                         const size_t size)
+          : id(id), dmabuf(std::move(dmabuf)), offset(offset), size(size) {}
+    ~ConstBitstreamBuffer() = default;
 
     const int32_t id;
-    int dmabuf_fd;
+    C2ConstLinearBlock dmabuf;
+    const size_t offset;
+    const size_t size;
+};
+
+// The BitstreamBuffer class can be used to store modifiable encoded video data.
+struct BitstreamBuffer {
+    BitstreamBuffer(std::shared_ptr<C2LinearBlock> dmabuf, const size_t offset, const size_t size)
+          : dmabuf(std::move(dmabuf)), offset(offset), size(size) {}
+    ~BitstreamBuffer() = default;
+
+    std::shared_ptr<C2LinearBlock> dmabuf;
     const size_t offset;
     const size_t size;
 };
diff --git a/components/include/v4l2_codec2/components/V4L2DecodeComponent.h b/components/include/v4l2_codec2/components/V4L2DecodeComponent.h
index 1e98118..962f7d6 100644
--- a/components/include/v4l2_codec2/components/V4L2DecodeComponent.h
+++ b/components/include/v4l2_codec2/components/V4L2DecodeComponent.h
@@ -140,9 +140,6 @@
     ::base::Thread mDecoderThread{"V4L2DecodeComponentDecoderThread"};
     scoped_refptr<::base::SequencedTaskRunner> mDecoderTaskRunner;
 
-    // Hold a weak_ptr of |*this| when |mDecoderThread| is running.
-    std::weak_ptr<V4L2DecodeComponent> mStdWeakThis;
-
     ::base::WeakPtrFactory<V4L2DecodeComponent> mWeakThisFactory{this};
     ::base::WeakPtr<V4L2DecodeComponent> mWeakThis;
 };
diff --git a/components/include/v4l2_codec2/components/V4L2Decoder.h b/components/include/v4l2_codec2/components/V4L2Decoder.h
index b65bd49..2ecb3bd 100644
--- a/components/include/v4l2_codec2/components/V4L2Decoder.h
+++ b/components/include/v4l2_codec2/components/V4L2Decoder.h
@@ -26,12 +26,12 @@
 class V4L2Decoder : public VideoDecoder {
 public:
     static std::unique_ptr<VideoDecoder> Create(
-            const VideoCodec& codec, const size_t inputBufferSize, GetPoolCB getPoolCB,
-            OutputCB outputCb, ErrorCB errorCb,
+            const VideoCodec& codec, const size_t inputBufferSize, const size_t minNumOutputBuffers,
+            GetPoolCB getPoolCB, OutputCB outputCb, ErrorCB errorCb,
             scoped_refptr<::base::SequencedTaskRunner> taskRunner);
     ~V4L2Decoder() override;
 
-    void decode(std::unique_ptr<BitstreamBuffer> buffer, DecodeCB decodeCb) override;
+    void decode(std::unique_ptr<ConstBitstreamBuffer> buffer, DecodeCB decodeCb) override;
     void drain(DecodeCB drainCb) override;
     void flush() override;
 
@@ -45,18 +45,19 @@
     static const char* StateToString(State state);
 
     struct DecodeRequest {
-        DecodeRequest(std::unique_ptr<BitstreamBuffer> buffer, DecodeCB decodeCb)
+        DecodeRequest(std::unique_ptr<ConstBitstreamBuffer> buffer, DecodeCB decodeCb)
               : buffer(std::move(buffer)), decodeCb(std::move(decodeCb)) {}
         DecodeRequest(DecodeRequest&&) = default;
         ~DecodeRequest() = default;
 
-        std::unique_ptr<BitstreamBuffer> buffer;  // nullptr means Drain
+        std::unique_ptr<ConstBitstreamBuffer> buffer;  // nullptr means Drain
         DecodeCB decodeCb;
     };
 
     V4L2Decoder(scoped_refptr<::base::SequencedTaskRunner> taskRunner);
-    bool start(const VideoCodec& codec, const size_t inputBufferSize, GetPoolCB getPoolCb,
-               OutputCB outputCb, ErrorCB errorCb);
+    bool start(const VideoCodec& codec, const size_t inputBufferSize,
+               const size_t minNumOutputBuffers, GetPoolCB getPoolCb, OutputCB outputCb,
+               ErrorCB errorCb);
     bool setupInputFormat(const uint32_t inputPixelFormat, const size_t inputBufferSize);
     void pumpDecodeRequest();
 
@@ -85,6 +86,7 @@
     std::queue<DecodeRequest> mDecodeRequests;
     std::map<int32_t, DecodeCB> mPendingDecodeCbs;
 
+    size_t mMinNumOutputBuffers = 0;
     GetPoolCB mGetPoolCb;
     OutputCB mOutputCb;
     DecodeCB mDrainCb;
diff --git a/components/include/v4l2_codec2/components/V4L2EncodeComponent.h b/components/include/v4l2_codec2/components/V4L2EncodeComponent.h
index 4665ffa..0b150e4 100644
--- a/components/include/v4l2_codec2/components/V4L2EncodeComponent.h
+++ b/components/include/v4l2_codec2/components/V4L2EncodeComponent.h
@@ -155,18 +155,19 @@
 
     // 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.
+    std::optional<int64_t> mLastFrameTime;
 
-    // Whether we extracted and submitted CSD (codec-specific data, e.g. H.264 SPS) to the framework.
-    bool mCSDSubmitted = false;
+    // Whether we need to extract and submit CSD (codec-specific data, e.g. H.264 SPS).
+    bool mExtractCSD = false;
 
     // The queue of encode work items currently being processed.
     std::deque<std::unique_ptr<C2Work>> mWorkQueue;
 
-    // Map of buffer ids and associated C2LinearBlock buffers. The buffer's fds are used as id.
-    std::unordered_map<int32_t, std::shared_ptr<C2LinearBlock>> mOutputBuffersMap;
-
     // The output block pool.
     std::shared_ptr<C2BlockPool> mOutputBlockPool;
 
diff --git a/components/include/v4l2_codec2/components/V4L2EncodeInterface.h b/components/include/v4l2_codec2/components/V4L2EncodeInterface.h
index 2efbfcc..fefebf0 100644
--- a/components/include/v4l2_codec2/components/V4L2EncodeInterface.h
+++ b/components/include/v4l2_codec2/components/V4L2EncodeInterface.h
@@ -39,8 +39,18 @@
         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; }
 
 protected:
     void Initialize(const C2String& name);
@@ -95,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..d7b55c0 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();
@@ -158,6 +164,12 @@
     // Key frame counter, a key frame will be requested each time it reaches zero.
     uint32_t mKeyFrameCounter = 0;
 
+    // Whether we need to manually cache and prepend SPS and PPS to IDR frames.
+    bool mInjectParamsBeforeIDR = false;
+    // The latest cached SPS and PPS (without H.264 start code).
+    std::vector<uint8_t> mCachedSPS;
+    std::vector<uint8_t> mCachedPPS;
+
     // The V4L2 device and associated queues used to interact with the device.
     scoped_refptr<V4L2Device> mDevice;
     scoped_refptr<V4L2Queue> mInputQueue;
diff --git a/components/include/v4l2_codec2/components/VideoDecoder.h b/components/include/v4l2_codec2/components/VideoDecoder.h
index 9a48562..5b2da41 100644
--- a/components/include/v4l2_codec2/components/VideoDecoder.h
+++ b/components/include/v4l2_codec2/components/VideoDecoder.h
@@ -34,7 +34,7 @@
 
     virtual ~VideoDecoder();
 
-    virtual void decode(std::unique_ptr<BitstreamBuffer> buffer, DecodeCB decodeCb) = 0;
+    virtual void decode(std::unique_ptr<ConstBitstreamBuffer> buffer, DecodeCB decodeCb) = 0;
     virtual void drain(DecodeCB drainCb) = 0;
     virtual void flush() = 0;
 };
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.
diff --git a/plugin_store/C2VdaPooledBlockPool.cpp b/plugin_store/C2VdaPooledBlockPool.cpp
index 48cc2e5..2b9104b 100644
--- a/plugin_store/C2VdaPooledBlockPool.cpp
+++ b/plugin_store/C2VdaPooledBlockPool.cpp
@@ -14,19 +14,6 @@
 #include <log/log.h>
 
 namespace android {
-namespace {
-// The wait time for another try to fetch a buffer from bufferpool.
-const int64_t kFetchRetryDelayUs = 10 * 1000;
-
-int64_t GetNowUs() {
-    struct timespec t;
-    t.tv_sec = 0;
-    t.tv_nsec = 0;
-    clock_gettime(CLOCK_MONOTONIC, &t);
-    int64_t nsecs = static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
-    return nsecs / 1000ll;
-}
-}  // namespace
 
 using android::hardware::media::bufferpool::BufferPoolData;
 
@@ -57,14 +44,6 @@
     ALOG_ASSERT(block != nullptr);
     std::lock_guard<std::mutex> lock(mMutex);
 
-    if (mNextFetchTimeUs != 0) {
-        int delayUs = GetNowUs() - mNextFetchTimeUs;
-        if (delayUs > 0) {
-            ::usleep(delayUs);
-        }
-        mNextFetchTimeUs = 0;
-    }
-
     std::shared_ptr<C2GraphicBlock> fetchBlock;
     c2_status_t err =
             C2PooledBlockPool::fetchGraphicBlock(width, height, format, usage, &fetchBlock);
@@ -89,7 +68,6 @@
         return C2_OK;
     }
     ALOGV("No buffer could be recycled now, wait for another try...");
-    mNextFetchTimeUs = GetNowUs() + kFetchRetryDelayUs;
     return C2_TIMED_OUT;
 }
 
diff --git a/plugin_store/DrmGrallocHelpers.cpp b/plugin_store/DrmGrallocHelpers.cpp
index 0565a69..0e2cca9 100644
--- a/plugin_store/DrmGrallocHelpers.cpp
+++ b/plugin_store/DrmGrallocHelpers.cpp
@@ -8,21 +8,58 @@
 #include <v4l2_codec2/plugin_store/DrmGrallocHelpers.h>
 
 #include <fcntl.h>
-#include <string.h>
+#include <glob.h>
 
+#include <string>
+#include <vector>
+
+#include <cutils/properties.h>
 #include <drm/drm.h>
 #include <log/log.h>
 
 namespace android {
+namespace {
+
+// Return a list of paths that matches |pattern|, the unix style pathname pattern.
+std::vector<std::string> glob(const std::string& pattern) {
+    glob_t glob_result;
+    memset(&glob_result, 0, sizeof(glob_result));
+
+    std::vector<std::string> paths;
+    if (glob(pattern.c_str(), GLOB_ERR | GLOB_NOESCAPE, NULL, &glob_result) == 0) {
+        for (size_t i = 0; i < glob_result.gl_pathc; ++i) {
+            paths.emplace_back(glob_result.gl_pathv[i]);
+        }
+    }
+
+    globfree(&glob_result);
+    return paths;
+}
+
+std::optional<std::string> propertyGetString(const char* key) {
+    char buf[PROPERTY_VALUE_MAX];
+    int len = property_get(key, buf, nullptr);
+    if (len == 0) {
+        return std::nullopt;
+    }
+    return std::string(buf, len);
+}
+
+}  // namespace
 
 std::optional<int> openRenderFd() {
-    const char kVirglName[] = "virtio_gpu";
+    constexpr char kDevNamePropertyKey[] = "ro.vendor.v4l2_codec2.drm_device_name";
+    constexpr char kDevPathPropertyKey[] = "ro.vendor.v4l2_codec2.drm_device_path";
 
-    for (uint32_t i = 128; i < 192; i++) {
-        char devName[32];
-        snprintf(devName, sizeof(devName), "/dev/dri/renderD%d", i);
+    const auto devName = propertyGetString(kDevNamePropertyKey);
+    if (!devName) {
+        ALOGE("Failed to get DRM device name from Android property");
+        return std::nullopt;
+    }
 
-        int fd = open(devName, O_RDWR | O_CLOEXEC);
+    const auto devPath = propertyGetString(kDevPathPropertyKey).value_or("/dev/dri/renderD*");
+    for (const auto filePath : glob(devPath)) {
+        int fd = open(filePath.c_str(), O_RDWR | O_CLOEXEC);
         if (fd < 0) {
             continue;
         }
@@ -37,7 +74,7 @@
             close(fd);
             continue;
         }
-        if (v.name_len != sizeof(kVirglName) - 1 || memcmp(name, kVirglName, v.name_len)) {
+        if (devName->size() != v.name_len || *devName != name) {
             close(fd);
             continue;
         }
diff --git a/plugin_store/include/v4l2_codec2/plugin_store/C2VdaPooledBlockPool.h b/plugin_store/include/v4l2_codec2/plugin_store/C2VdaPooledBlockPool.h
index 5603046..749ff47 100644
--- a/plugin_store/include/v4l2_codec2/plugin_store/C2VdaPooledBlockPool.h
+++ b/plugin_store/include/v4l2_codec2/plugin_store/C2VdaPooledBlockPool.h
@@ -47,9 +47,6 @@
     std::set<uint32_t> mBufferIds GUARDED_BY(mMutex);
     // The maximum count of allocated buffers.
     size_t mBufferCount GUARDED_BY(mMutex){0};
-    // The timestamp for the next fetchGraphicBlock() call.
-    // Set when the previous fetchGraphicBlock() call timed out.
-    int64_t mNextFetchTimeUs GUARDED_BY(mMutex){0};
 };
 
 }  // namespace android