Merge "Support more input formats" into main
diff --git a/automotive/evs/aidl/impl/default/include/ConfigManager.h b/automotive/evs/aidl/impl/default/include/ConfigManager.h
index 37a17dc..f6ba2f2 100644
--- a/automotive/evs/aidl/impl/default/include/ConfigManager.h
+++ b/automotive/evs/aidl/impl/default/include/ConfigManager.h
@@ -50,6 +50,7 @@
 class ConfigManager final {
   public:
     static std::unique_ptr<ConfigManager> Create();
+    static std::unique_ptr<ConfigManager> Create(const std::string path);
     ConfigManager(const ConfigManager&) = delete;
     ConfigManager& operator=(const ConfigManager&) = delete;
 
@@ -65,6 +66,15 @@
             UNKNOWN = std::numeric_limits<std::underlying_type_t<DeviceType>>::max(),
         };
 
+        enum class PixelFormat : std::int32_t {
+            NV12 = 0,
+            NV21 = 1,
+            YV12 = 2,
+            I420 = 3,
+
+            UNKNOWN = std::numeric_limits<std::underlying_type_t<DeviceType>>::max(),
+        };
+
         CameraInfo() : characteristics(nullptr) {}
 
         virtual ~CameraInfo();
@@ -82,6 +92,8 @@
 
         static DeviceType deviceTypeFromSV(const std::string_view sv);
 
+        static PixelFormat pixelFormatFromSV(const std::string_view sv);
+
         DeviceType deviceType{DeviceType::NONE};
 
         /*
@@ -105,6 +117,11 @@
 
         /* Camera module characteristics */
         camera_metadata_t* characteristics;
+
+        /* Format of media in a given media container. This field is effective
+         * only for DeviceType::VIDEO.
+         */
+        PixelFormat format;
     };
 
     class CameraGroupInfo : public CameraInfo {
@@ -272,7 +289,7 @@
      * @return bool
      *         True if it completes parsing a file successfully.
      */
-    bool readConfigDataFromXML() noexcept;
+    bool readConfigDataFromXML(const std::string path) noexcept;
 
     /*
      * read the information of the vehicle
diff --git a/automotive/evs/aidl/impl/default/include/EvsCameraBase.h b/automotive/evs/aidl/impl/default/include/EvsCameraBase.h
index c3e9dfc..d9180e8 100644
--- a/automotive/evs/aidl/impl/default/include/EvsCameraBase.h
+++ b/automotive/evs/aidl/impl/default/include/EvsCameraBase.h
@@ -30,6 +30,7 @@
 
     ~EvsCameraBase() override = default;
 
+    virtual std::string getId() = 0;
     virtual void shutdown() = 0;
 
   protected:
diff --git a/automotive/evs/aidl/impl/default/include/EvsMockCamera.h b/automotive/evs/aidl/impl/default/include/EvsMockCamera.h
index cd68532..67de8dc 100644
--- a/automotive/evs/aidl/impl/default/include/EvsMockCamera.h
+++ b/automotive/evs/aidl/impl/default/include/EvsMockCamera.h
@@ -65,7 +65,9 @@
     ndk::ScopedAStatus setPrimaryClient() override;
     ndk::ScopedAStatus unsetPrimaryClient() override;
 
-    const evs::CameraDesc& getDesc() { return mDescription; }
+    std::string getId() override { return mDescription.id; }
+
+    const CameraDesc& getDesc() { return mDescription; }
 
     static std::shared_ptr<EvsMockCamera> Create(const char* deviceName);
     static std::shared_ptr<EvsMockCamera> Create(
diff --git a/automotive/evs/aidl/impl/default/include/EvsVideoEmulatedCamera.h b/automotive/evs/aidl/impl/default/include/EvsVideoEmulatedCamera.h
index 9d1610a..dc70a43 100644
--- a/automotive/evs/aidl/impl/default/include/EvsVideoEmulatedCamera.h
+++ b/automotive/evs/aidl/impl/default/include/EvsVideoEmulatedCamera.h
@@ -27,7 +27,6 @@
 #include <aidl/android/hardware/automotive/evs/ParameterRange.h>
 #include <aidl/android/hardware/automotive/evs/Stream.h>
 #include <media/NdkMediaExtractor.h>
-
 #include <ui/GraphicBuffer.h>
 
 #include <cstdint>
@@ -70,6 +69,8 @@
     // Methods from EvsCameraBase follow.
     void shutdown() override;
 
+    std::string getId() override { return mDescription.id; }
+
     const evs::CameraDesc& getDesc() { return mDescription; }
 
     static std::shared_ptr<EvsVideoEmulatedCamera> Create(const char* deviceName);
@@ -117,6 +118,10 @@
     bool postVideoStreamStop_locked(ndk::ScopedAStatus& status,
                                     std::unique_lock<std::mutex>& lck) override;
 
+    int (*mFillBuffer)(const uint8_t* src_y, int src_stride_y, const uint8_t* src_u,
+                       int src_stride_u, const uint8_t* src_v, int src_stride_v, uint8_t* dst_argb,
+                       int dst_stride_argb, int width, int height);
+
     // The properties of this camera.
     CameraDesc mDescription = {};
 
@@ -149,6 +154,10 @@
     uint64_t mUsage = 0;
     // Bytes per line in the buffers
     uint32_t mStride = 0;
+    // Bytes per line in the output buffer
+    uint32_t mDstStride = 0;
+    // Bytes per line of U/V plane
+    uint32_t mUvStride = 0;
 
     // Camera parameters.
     std::unordered_map<CameraParam, std::shared_ptr<CameraParameterDesc>> mParams;
diff --git a/automotive/evs/aidl/impl/default/src/ConfigManager.cpp b/automotive/evs/aidl/impl/default/src/ConfigManager.cpp
index d8961d0..eea80f4 100644
--- a/automotive/evs/aidl/impl/default/src/ConfigManager.cpp
+++ b/automotive/evs/aidl/impl/default/src/ConfigManager.cpp
@@ -52,6 +52,25 @@
     return search == nameToType.end() ? DeviceType::UNKNOWN : search->second;
 }
 
+ConfigManager::CameraInfo::PixelFormat ConfigManager::CameraInfo::pixelFormatFromSV(
+        const std::string_view sv) {
+    using namespace std::string_view_literals;
+    static const std::unordered_map<std::string_view, PixelFormat> nameToFormat = {
+            // Full resolution Y plane followed by 2x2 subsampled U/V
+            // interleaved plane.
+            {"NV12"sv, PixelFormat::NV12},
+            // Full resolution Y plane followed by 2x2 subsampled V/U
+            // interleaved plane.
+            {"NV21"sv, PixelFormat::NV21},
+            // Full resolution Y plane followed by 2x2 subsampled V plane and then U plane.
+            {"YV12"sv, PixelFormat::YV12},
+            // Full resolution Y plane followed by 2x2 subsampled U plane and then V plane.
+            {"I420"sv, PixelFormat::I420},
+    };
+    const auto search = nameToFormat.find(sv);
+    return search == nameToFormat.end() ? PixelFormat::UNKNOWN : search->second;
+}
+
 void ConfigManager::printElementNames(const XMLElement* rootElem, const std::string& prefix) const {
     const XMLElement* curElem = rootElem;
 
@@ -144,6 +163,10 @@
         aCamera->deviceType = CameraInfo::deviceTypeFromSV(typeAttr->Value());
     }
 
+    if (const auto formatAttr = aDeviceElem->FindAttribute("format")) {
+        aCamera->format = CameraInfo::pixelFormatFromSV(formatAttr->Value());
+    }
+
     /* size information to allocate camera_metadata_t */
     size_t totalEntries = 0;
     size_t totalDataSize = 0;
@@ -474,19 +497,16 @@
     return;
 }
 
-bool ConfigManager::readConfigDataFromXML() noexcept {
+bool ConfigManager::readConfigDataFromXML(const std::string path) noexcept {
     XMLDocument xmlDoc;
 
     const int64_t parsingStart = android::elapsedRealtimeNano();
 
     /* load and parse a configuration file */
-    xmlDoc.LoadFile(sConfigOverridePath.data());
+    xmlDoc.LoadFile(path.c_str());
     if (xmlDoc.ErrorID() != tinyxml2::XML_SUCCESS) {
-        xmlDoc.LoadFile(sConfigDefaultPath.data());
-        if (xmlDoc.ErrorID() != tinyxml2::XML_SUCCESS) {
-            LOG(ERROR) << "Failed to load and/or parse a configuration file, " << xmlDoc.ErrorStr();
-            return false;
-        }
+        LOG(ERROR) << "Failed to load and/or parse a configuration file, " << xmlDoc.ErrorStr();
+        return false;
     }
 
     /* retrieve the root element */
@@ -644,8 +664,7 @@
                     p += count * sizeof(camera_metadata_rational_t);
                     break;
                 default:
-                    LOG(WARNING) << "Type " << type << " is unknown; "
-                                 << "data may be corrupted.";
+                    LOG(WARNING) << "Type " << type << " is unknown; " << "data may be corrupted.";
                     break;
             }
         }
@@ -746,8 +765,7 @@
                     p += count * sizeof(camera_metadata_rational_t);
                     break;
                 default:
-                    LOG(WARNING) << "Type " << type << " is unknown; "
-                                 << "data may be corrupted.";
+                    LOG(WARNING) << "Type " << type << " is unknown; " << "data may be corrupted.";
                     break;
             }
         }
@@ -958,6 +976,16 @@
 }
 
 std::unique_ptr<ConfigManager> ConfigManager::Create() {
+    std::unique_ptr<ConfigManager> mgr = Create(std::string(sConfigOverridePath));
+    if (!mgr) {
+        LOG(DEBUG) << "A configuration override file does not exist. Use a default file instead.";
+        mgr = Create(std::string((sConfigDefaultPath)));
+    }
+
+    return mgr;
+}
+
+std::unique_ptr<ConfigManager> ConfigManager::Create(const std::string path) {
     std::unique_ptr<ConfigManager> cfgMgr(new ConfigManager());
 
     /*
@@ -968,7 +996,7 @@
      * to the filesystem and construct CameraInfo instead; this was
      * evaluated as 10x faster.
      */
-    if (!cfgMgr->readConfigDataFromXML()) {
+    if (!cfgMgr->readConfigDataFromXML(path)) {
         return nullptr;
     } else {
         return cfgMgr;
diff --git a/automotive/evs/aidl/impl/default/src/EvsVideoEmulatedCamera.cpp b/automotive/evs/aidl/impl/default/src/EvsVideoEmulatedCamera.cpp
index 480c28d..310f47e 100644
--- a/automotive/evs/aidl/impl/default/src/EvsVideoEmulatedCamera.cpp
+++ b/automotive/evs/aidl/impl/default/src/EvsVideoEmulatedCamera.cpp
@@ -26,6 +26,7 @@
 #include <utils/SystemClock.h>
 
 #include <fcntl.h>
+#include <libyuv.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -35,12 +36,45 @@
 #include <tuple>
 #include <utility>
 
+// Uncomment below line to dump decoded frames.
+// #define DUMP_FRAMES (1)
+
 namespace aidl::android::hardware::automotive::evs::implementation {
 
 namespace {
+
 struct FormatDeleter {
     void operator()(AMediaFormat* format) const { AMediaFormat_delete(format); }
 };
+
+int fillRGBAFromNv12(const uint8_t* src_y, int src_stride_y, const uint8_t* src_uv,
+                     int src_stride_uv, const uint8_t*, int, uint8_t* dst_abgr, int dst_stride_abgr,
+                     int width, int height) {
+    return libyuv::NV12ToABGR(src_y, src_stride_y, src_uv, src_stride_uv, dst_abgr, dst_stride_abgr,
+                              width, height);
+}
+
+int fillRGBAFromNv21(const uint8_t* src_y, int src_stride_y, const uint8_t* src_vu,
+                     int src_stride_vu, const uint8_t*, int, uint8_t* dst_abgr, int dst_stride_abgr,
+                     int width, int height) {
+    return libyuv::NV21ToABGR(src_y, src_stride_y, src_vu, src_stride_vu, dst_abgr, dst_stride_abgr,
+                              width, height);
+}
+
+int fillRGBAFromYv12(const uint8_t* src_y, int src_stride_y, const uint8_t* src_u, int src_stride_u,
+                     const uint8_t* src_v, int src_stride_v, uint8_t* dst_abgr, int dst_stride_abgr,
+                     int width, int height) {
+    return libyuv::I420ToABGR(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
+                              dst_abgr, dst_stride_abgr, width, height);
+}
+
+int fillRGBAFromI420(const uint8_t* src_y, int src_stride_y, const uint8_t* src_u, int src_stride_u,
+                     const uint8_t* src_v, int src_stride_v, uint8_t* dst_abgr, int dst_stride_abgr,
+                     int width, int height) {
+    return libyuv::I420ToABGR(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
+                              dst_abgr, dst_stride_abgr, width, height);
+}
+
 }  // namespace
 
 EvsVideoEmulatedCamera::EvsVideoEmulatedCamera(Sigil, const char* deviceName,
@@ -123,7 +157,7 @@
     mDescription.vendorFlags = 0xFFFFFFFF;  // Arbitrary test value
     mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
              GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
-    mFormat = HAL_PIXEL_FORMAT_YCBCR_420_888;
+    mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
     AMediaFormat_setInt32(format.get(), AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
     {
         const media_status_t status =
@@ -137,6 +171,30 @@
     format.reset(AMediaCodec_getOutputFormat(mVideoCodec.get()));
     AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_WIDTH, &mWidth);
     AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_HEIGHT, &mHeight);
+
+    switch (mCameraInfo->format) {
+        default:
+        case ConfigManager::CameraInfo::PixelFormat::NV12:
+            mFillBuffer = fillRGBAFromNv12;
+            mUvStride = mWidth;
+            mDstStride = mWidth * 4;
+            break;
+        case ConfigManager::CameraInfo::PixelFormat::NV21:
+            mFillBuffer = fillRGBAFromNv21;
+            mUvStride = mWidth;
+            mDstStride = mWidth * 4;
+            break;
+        case ConfigManager::CameraInfo::PixelFormat::YV12:
+            mFillBuffer = fillRGBAFromYv12;
+            mUvStride = mWidth / 2;
+            mDstStride = mWidth * 4;
+            break;
+        case ConfigManager::CameraInfo::PixelFormat::I420:
+            mFillBuffer = fillRGBAFromI420;
+            mUvStride = mWidth / 2;
+            mDstStride = mWidth * 4;
+            break;
+    }
     return true;
 }
 
@@ -190,6 +248,28 @@
     uint8_t* const codecOutputBuffer =
             AMediaCodec_getOutputBuffer(mVideoCodec.get(), index, &decodedOutSize) + info.offset;
 
+    int color_format = 0;
+    const auto outFormat = AMediaCodec_getOutputFormat(mVideoCodec.get());
+    if (!AMediaFormat_getInt32(outFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, &color_format)) {
+        LOG(ERROR) << "Failed to get the color format.";
+        return;
+    }
+
+    int stride = 0;
+    if (!AMediaFormat_getInt32(outFormat, AMEDIAFORMAT_KEY_STRIDE, &stride)) {
+        LOG(WARNING) << "Cannot find stride in format. Set as frame width.";
+        stride = mWidth;
+    }
+
+    int slice_height = 0;
+    if (!AMediaFormat_getInt32(outFormat, AMEDIAFORMAT_KEY_SLICE_HEIGHT, &slice_height)) {
+        LOG(WARNING) << "Cannot find slice-height in format. Set as frame height.";
+        slice_height = mHeight;
+    }
+
+    LOG(DEBUG) << "COLOR FORMAT: " << color_format << " stride: " << stride
+               << " height: " << slice_height;
+
     std::size_t renderBufferId = static_cast<std::size_t>(-1);
     buffer_handle_t renderBufferHandle = nullptr;
     {
@@ -200,7 +280,7 @@
         std::tie(renderBufferId, renderBufferHandle) = useBuffer_unsafe();
     }
     if (!renderBufferHandle) {
-        LOG(ERROR) << __func__ << ": Camera failed to get an available render buffer.";
+        LOG(DEBUG) << __func__ << ": Camera failed to get an available render buffer.";
         return;
     }
     std::vector<BufferDesc> renderBufferDescs;
@@ -236,19 +316,51 @@
         return;
     }
 
-    std::size_t ySize = mHeight * mStride;
+    // Decoded output is in YUV4:2:0.
+    std::size_t ySize = mHeight * mWidth;
     std::size_t uvSize = ySize / 4;
 
-    std::memcpy(pixels, codecOutputBuffer, ySize);
-    pixels += ySize;
-
     uint8_t* u_head = codecOutputBuffer + ySize;
     uint8_t* v_head = u_head + uvSize;
 
-    for (size_t i = 0; i < uvSize; ++i) {
-        *(pixels++) = *(u_head++);
-        *(pixels++) = *(v_head++);
+#if DUMP_FRAMES
+    // TODO: We may want to keep this "dump" option.
+    static int dumpCount = 0;
+    static bool dumpData = ++dumpCount < 10;
+    if (dumpData) {
+        std::string path = "/data/vendor/dump/";
+        path += "dump_" + std::to_string(dumpCount) + ".bin";
+
+        ::android::base::unique_fd fd(
+                open(path.data(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP));
+        if (fd < 0) {
+            LOG(ERROR) << "Failed to open " << path;
+        } else {
+            auto len = write(fd.get(), codecOutputBuffer, info.size);
+            LOG(ERROR) << "Write " << len << " to " << path;
+        }
     }
+#endif
+    if (auto result = mFillBuffer(codecOutputBuffer, mWidth, u_head, mUvStride, v_head, mUvStride,
+                                  pixels, mDstStride, mWidth, mHeight);
+        result != 0) {
+        LOG(ERROR) << "Failed to convert I420 to BGRA";
+    }
+#if DUMP_FRAMES
+    else if (dumpData) {
+        std::string path = "/data/vendor/dump/";
+        path += "dump_" + std::to_string(dumpCount) + "_rgba.bin";
+
+        ::android::base::unique_fd fd(
+                open(path.data(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP));
+        if (fd < 0) {
+            LOG(ERROR) << "Failed to open " << path;
+        } else {
+            auto len = write(fd.get(), pixels, mStride * mHeight * 4);
+            LOG(ERROR) << "Write " << len << " to " << path;
+        }
+    }
+#endif
 
     // Release our output buffer
     mapper.unlock(renderBufferHandle);
@@ -332,8 +444,8 @@
 ::android::status_t EvsVideoEmulatedCamera::allocateOneFrame(buffer_handle_t* handle) {
     static auto& alloc = ::android::GraphicBufferAllocator::get();
     unsigned pixelsPerLine = 0;
-    const auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, handle, &pixelsPerLine,
-                                       0, "EvsVideoEmulatedCamera");
+    const auto result = alloc.allocate(mWidth, mHeight, HAL_PIXEL_FORMAT_RGBA_8888, 1, mUsage,
+                                       handle, &pixelsPerLine, 0, "EvsVideoEmulatedCamera");
     if (mStride == 0) {
         // Gralloc defines stride in terms of pixels per line
         mStride = pixelsPerLine;
diff --git a/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp b/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp
index 8b4676e..ce0e776 100644
--- a/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp
+++ b/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp
@@ -92,6 +92,7 @@
                 (override));
     MOCK_METHOD(bool, stopVideoStreamImpl_locked,
                 (ndk::ScopedAStatus & status, std::unique_lock<std::mutex>& lck), (override));
+    MOCK_METHOD(std::string, getId, (), (override));
 };
 
 TEST(EvsCameraBufferTest, ChangeBufferPoolSize) {
diff --git a/automotive/evs/aidl/impl/default/tests/EvsCameraStateTest.cpp b/automotive/evs/aidl/impl/default/tests/EvsCameraStateTest.cpp
index 1925c79..e0bd81f 100644
--- a/automotive/evs/aidl/impl/default/tests/EvsCameraStateTest.cpp
+++ b/automotive/evs/aidl/impl/default/tests/EvsCameraStateTest.cpp
@@ -141,6 +141,7 @@
                 (override));
     MOCK_METHOD(::ndk::ScopedAStatus, setPrimaryClient, (), (override));
     MOCK_METHOD(::ndk::ScopedAStatus, unsetPrimaryClient, (), (override));
+    MOCK_METHOD(std::string, getId, (), (override));
 
     bool mStreamStarted = false;
     bool mStreamStopped = false;