C2VEA: handle format IMPLEMENTATION_DEFINED on component's end

IMPLEMENTATION_DEFINED is a special format for encoder input frames which is
backed by gralloc.In ChromeOS, IMPLEMENTATION_DEFINED may be backed by
YCBCR_420_888 or RGBX_8888. As for C2AllocationGralloc::map(), any unrecognized
format will be regarded as YCBCR_420_888.

In YUV-backed case, C2AllocationGralloc::map() could map and provide layout
correctly. However in RGB-backed case, map() would fail and the caller will get
an empty layout instance.

ImplDefinedToRGBXMap can provide the layout for RGB-backed
IMPLEMENTATION_DEFINED format case. When the instance is created, it will own
the GraphicBuffer wrapped from input block and lock it, to provide the address,
offset, and rowInc information. The GraphicBuffer will be unlocked and released
under destruction.

Bug: 73059339
Test: android.media.cts.MediaRecorderTest#testSurfaceRecording
Change-Id: Iae8112e9dc8c30d2f0a08ae2a4cc0d7fb9db1c03
(cherry picked from commit 28ae20917491a301ed0122e280ddbb4152151cb2)
diff --git a/C2VEAComponent.cpp b/C2VEAComponent.cpp
index 7683f41..a6e5ead 100644
--- a/C2VEAComponent.cpp
+++ b/C2VEAComponent.cpp
@@ -828,9 +828,26 @@
         // later to get offset and stride information.
     }
 
+    // The above layout() cannot fill layout information and memset 0 instead if the input format is
+    // IMPLEMENTATION_DEFINED and its backed format is RGB. We fill the layout by using
+    // ImplDefinedToRGBXMap in the case.
+    if (static_cast<uint32_t>(layout.type) == 0u) {
+        std::unique_ptr<ImplDefinedToRGBXMap> idMap = ImplDefinedToRGBXMap::Create(inputBlock);
+        if (idMap == nullptr) {
+            ALOGE("Unable to parse RGBX_8888 from IMPLEMENTATION_DEFINED");
+            reportError(C2_CORRUPTED);
+            return;
+        }
+        layout.type = C2PlanarLayout::TYPE_RGB;
+        // These parameters would be used in TYPE_GRB case below.
+        layout.numPlanes = 3;  // same value as in C2AllocationGralloc::map()
+        layout.rootPlanes = 1;  // same value as in C2AllocationGralloc::map()
+        layout.planes[C2PlanarLayout::PLANE_R].offset = idMap->offset();
+        layout.planes[C2PlanarLayout::PLANE_R].rowInc = idMap->rowInc();
+    }
+
     std::vector<uint32_t> offsets(layout.numPlanes, 0u);
     std::vector<uint32_t> strides(layout.numPlanes, 0u);
-    uint32_t passedPlaneNum = layout.numPlanes;
     media::VideoPixelFormat format = media::VideoPixelFormat::PIXEL_FORMAT_UNKNOWN;
     if (layout.type == C2PlanarLayout::TYPE_YUV) {
         // lockYCbCr() stores offsets into the pointers if given usage does not contain
@@ -855,7 +872,6 @@
         bool semiplanar = false;
         if (ycbcr.chroma_step >
             offsets[C2PlanarLayout::PLANE_V] - offsets[C2PlanarLayout::PLANE_U]) {
-            passedPlaneNum -= 1;
             semiplanar = true;
         }
 
@@ -876,7 +892,6 @@
         offsets[C2PlanarLayout::PLANE_R] = layout.planes[C2PlanarLayout::PLANE_R].offset;
         strides[C2PlanarLayout::PLANE_R] =
                 static_cast<uint32_t>(layout.planes[C2PlanarLayout::PLANE_R].rowInc);
-        passedPlaneNum = 1;
         // TODO(johnylin): is PIXEL_FORMAT_ABGR valid?
         format = media::VideoPixelFormat::PIXEL_FORMAT_ARGB;
     }
@@ -889,14 +904,14 @@
 
     if (keyframe) {
         // Print format logs only for keyframes in order to avoid excessive verbosity.
-        for (uint32_t i = 0; i < passedPlaneNum; ++i) {
+        for (uint32_t i = 0; i < layout.rootPlanes; ++i) {
             ALOGV("plane %u: stride: %d, offset: %u", i, strides[i], offsets[i]);
         }
         ALOGV("HAL pixel format: %s", media::VideoPixelFormatToString(format).c_str());
     }
 
     std::vector<VideoFramePlane> passedPlanes;
-    for (uint32_t i = 0; i < passedPlaneNum; ++i) {
+    for (uint32_t i = 0; i < layout.rootPlanes; ++i) {
         passedPlanes.push_back({offsets[i], strides[i]});
     }
 
diff --git a/C2VEAFormatConverter.cpp b/C2VEAFormatConverter.cpp
index cc80de6..7c81d94 100644
--- a/C2VEAFormatConverter.cpp
+++ b/C2VEAFormatConverter.cpp
@@ -50,6 +50,47 @@
 
 }  // namespace
 
+ImplDefinedToRGBXMap::ImplDefinedToRGBXMap(sp<GraphicBuffer> buf, uint8_t* addr, int rowInc)
+      : mBuffer(std::move(buf)), mAddr(addr), mRowInc(rowInc) {
+}
+
+ImplDefinedToRGBXMap::~ImplDefinedToRGBXMap() {
+    mBuffer->unlock();
+}
+
+// static
+std::unique_ptr<ImplDefinedToRGBXMap> ImplDefinedToRGBXMap::Create(
+        const C2ConstGraphicBlock& block) {
+    uint32_t width, height, format, stride, igbpSlot, generation;
+    uint64_t usage, igbpId;
+    android::_UnwrapNativeCodec2GrallocMetadata(block.handle(), &width, &height,
+                                                &format, &usage, &stride, &generation, &igbpId,
+                                                &igbpSlot);
+
+    if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+        ALOGE("The original format (=%u) is not IMPLEMENTATION_DEFINED", format);
+        return nullptr;
+    }
+
+    native_handle_t* grallocHandle = android::UnwrapNativeCodec2GrallocHandle(block.handle());
+    sp<GraphicBuffer> buf = new GraphicBuffer(grallocHandle, GraphicBuffer::CLONE_HANDLE, width,
+                                              height, format, 1, usage, stride);
+    native_handle_delete(grallocHandle);
+
+    void* pointer = nullptr;
+    int32_t status = buf->lock(GRALLOC_USAGE_SW_READ_OFTEN, &pointer);
+    if (status != OK) {
+        ALOGE("Failed to lock buffer as IMPLEMENTATION_DEFINED format");
+        return nullptr;
+    }
+
+    uint8_t* addr = reinterpret_cast<uint8_t*>(pointer);
+    int rowInc = static_cast<int>(stride * 4);  // RGBX 4-byte data per pixel
+    ALOGD("Parsed input format IMPLEMENTATION_DEFINED to RGBX_8888");
+    return std::unique_ptr<ImplDefinedToRGBXMap>(
+            new ImplDefinedToRGBXMap(std::move(buf), addr, rowInc));
+}
+
 // static
 std::unique_ptr<C2VEAFormatConverter> C2VEAFormatConverter::Create(
         media::VideoPixelFormat outFormat, const media::Size& visibleSize, uint32_t inputCount,
@@ -132,6 +173,20 @@
     const C2GraphicView& inputView = inputBlock.map().get();
     C2PlanarLayout inputLayout = inputView.layout();
 
+    // The above layout() cannot fill layout information and memset 0 instead if the input format is
+    // IMPLEMENTATION_DEFINED and its backed format is RGB. We fill the layout by using
+    // ImplDefinedToRGBXMap in the case.
+    std::unique_ptr<ImplDefinedToRGBXMap> idMap;
+    if (static_cast<uint32_t>(inputLayout.type) == 0u) {
+        idMap = ImplDefinedToRGBXMap::Create(inputBlock);
+        if (idMap == nullptr) {
+            ALOGE("Unable to parse RGBX_8888 from IMPLEMENTATION_DEFINED");
+            *status = C2_CORRUPTED;
+            return inputBlock;  // This is actually redundant and should not be used.
+        }
+        inputLayout.type = C2PlanarLayout::TYPE_RGB;
+    }
+
     C2GraphicView outputView = outputBlock->map().get();
     C2PlanarLayout outputLayout = outputView.layout();
     uint8_t* dstY = outputView.data()[C2PlanarLayout::PLANE_Y];
@@ -210,8 +265,11 @@
         // There is only RGBA_8888 specified in C2AllocationGralloc::map(), no BGRA_8888. Maybe
         // BGRA_8888 is not used now?
         inputFormat = media::VideoPixelFormat::PIXEL_FORMAT_ABGR;
-        const uint8_t* srcRGB = inputView.data()[C2PlanarLayout::PLANE_R];
-        const int srcStrideRGB = inputLayout.planes[C2PlanarLayout::PLANE_R].rowInc;
+
+        const uint8_t* srcRGB = (idMap) ? idMap->addr()
+                                        : inputView.data()[C2PlanarLayout::PLANE_R];
+        const int srcStrideRGB = (idMap) ? idMap->rowInc()
+                                         : inputLayout.planes[C2PlanarLayout::PLANE_R].rowInc;
 
         switch (convertMap(inputFormat, mOutFormat)) {
         case convertMap(media::VideoPixelFormat::PIXEL_FORMAT_ABGR,
diff --git a/include/C2VEAFormatConverter.h b/include/C2VEAFormatConverter.h
index 47b1a91..cc49af7 100644
--- a/include/C2VEAFormatConverter.h
+++ b/include/C2VEAFormatConverter.h
@@ -12,12 +12,37 @@
 
 #include <base/macros.h>
 
+#include <ui/GraphicBuffer.h>
+
 #include <limits>
 #include <queue>
 #include <vector>
 
 namespace android {
 
+// ImplDefinedToRGBXMap can provide the layout for RGB-backed IMPLEMENTATION_DEFINED format case,
+// which will be failed to map by C2AllocationGralloc::map(). When the instance is created, it will
+// own the GraphicBuffer wrapped from input block and lock it, to provide the address, offset, and
+// rowInc information. The GraphicBuffer will be unlocked and released under destruction.
+class ImplDefinedToRGBXMap {
+public:
+    ~ImplDefinedToRGBXMap();
+    ImplDefinedToRGBXMap() = delete;
+
+    static std::unique_ptr<ImplDefinedToRGBXMap> Create(const C2ConstGraphicBlock& block);
+
+    const uint8_t* addr() const { return mAddr; }
+    int offset() const { return 0; }
+    int rowInc() const { return mRowInc; }
+
+private:
+    ImplDefinedToRGBXMap(sp<GraphicBuffer> buf, uint8_t* addr, int rowInc);
+
+    const sp<GraphicBuffer> mBuffer;
+    const uint8_t* mAddr;
+    const int mRowInc;
+};
+
 class C2VEAFormatConverter {
 public:
     ~C2VEAFormatConverter() = default;
diff --git a/tests/c2_comp_intf/Android.mk b/tests/c2_comp_intf/Android.mk
index 19d855b..d4b7d6f 100644
--- a/tests/c2_comp_intf/Android.mk
+++ b/tests/c2_comp_intf/Android.mk
@@ -54,6 +54,7 @@
   libcodec2_vndk \
   libcutils \
   liblog \
+  libui \
   libutils \
   libv4l2_codec2 \
   libv4l2_codec2_accel \