Show a patch.

GET /api/1.1/patches/15628/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 15628,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/15628/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/15628/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20220406094130.189862-2-chenghaoyang@chromium.org>",
    "date": "2022-04-06T09:41:30",
    "name": "[libcamera-devel,1/1] Add CrOS JEA implementation",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "78af98af9e5c2290793612e520e946925f17ad2a",
    "submitter": {
        "id": 117,
        "url": "https://patchwork.libcamera.org/api/1.1/people/117/?format=api",
        "name": "Cheng-Hao Yang",
        "email": "chenghaoyang@chromium.org"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/15628/mbox/",
    "series": [
        {
            "id": 3033,
            "url": "https://patchwork.libcamera.org/api/1.1/series/3033/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3033",
            "date": "2022-04-06T09:41:29",
            "name": "Add CrOS JEA implementation in src/android",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/3033/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/15628/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/15628/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 7EA4FC3260\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  6 Apr 2022 10:34:28 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id EC52B604B8;\n\tWed,  6 Apr 2022 12:34:27 +0200 (CEST)",
            "from mail-pj1-x1033.google.com (mail-pj1-x1033.google.com\n\t[IPv6:2607:f8b0:4864:20::1033])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 117A46563F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  6 Apr 2022 11:41:51 +0200 (CEST)",
            "by mail-pj1-x1033.google.com with SMTP id\n\tnt14-20020a17090b248e00b001ca601046a4so5310395pjb.0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 06 Apr 2022 02:41:51 -0700 (PDT)",
            "from chenghaoyang-p920.tpe.corp.google.com\n\t([2401:fa00:1:17:42f3:c4c9:1892:e888])\n\tby smtp.gmail.com with ESMTPSA id\n\tmq6-20020a17090b380600b001c6357f146csm6071855pjb.12.2022.04.06.02.41.47\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 06 Apr 2022 02:41:48 -0700 (PDT)"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1649241268;\n\tbh=79pZ+B1sOBWjlpNvWpS0qZfeCRaBeErcY1fLaUyxn4I=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=ynmd///U2aK+uy2BPqsE1osG3UkTbUMW8Ly7+WicJtOQYCbSRMYNpOGjZYHSpUvrB\n\t2SYWw7PNAg5Ig4mqJBcnClVh5W7SwjQiC1tBz02vMx9vYQakTPAB0QoG+/Gyoa+c/0\n\ttAbSDg1Nyt7U56QE13veDDw3LK6xhbdABuQC8P8Wma2yRMdWsSRltytEqPVReAZ85L\n\txGoBxjMlNT3GS7AO+zEQ2Nqb34C/aKt/xIwXlNfLpvIWd3Fysh9FFMx2PIM6YAlgK0\n\t2rI1LpibhRUWjlI2QIQSQfMXOt1Lpeh8clhRG2pFFGcifb7KP8rDl3IfLQHCqkOq8g\n\tOqVjU58RlK7ow==",
            "v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=from:to:cc:subject:date:message-id:in-reply-to:references\n\t:mime-version:content-transfer-encoding;\n\tbh=+PkXHup3JeH0yN48Y3QrIgJ80QxoxICNR+2RvPT0ZcQ=;\n\tb=cJmVaYRyI8JMETZlCT5z9KWBkUY2AL16Ti7Q8jm3o7gImTw52B4oTTp2SQt8icKPwp\n\t5R9kWRVxrnjF0nGDGSrhS/gXeU2/CiTQVajMAPCZT93RyTLyM0cSaMMD+ZOu2O5srXgR\n\tuUDBpsOGnw12DcCHU8cK8R43mZIYb5TAa+ea4="
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=chromium.org\n\theader.i=@chromium.org header.b=\"cJmVaYRy\"; \n\tdkim-atps=neutral",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references:mime-version:content-transfer-encoding;\n\tbh=+PkXHup3JeH0yN48Y3QrIgJ80QxoxICNR+2RvPT0ZcQ=;\n\tb=UTLP6qbdHfhVbr6Gax59QQcQZmOB8S9j7UTrI+4hjdPUscHBJvmDqYAlBUSihfVz5i\n\tLy4Pdmt4aVUwTsOzRfgPGU3SmhGp1wyCs9ScPea0paYE+B5KQN/c86SmCtggHkS1VkQZ\n\t3nWRBLez7BOwx+0Clg7kFbhphQMTOcNPFsV4NvUDdG1+2V7KEsDUNfruS1yhckcxPrmf\n\t5fyxOi2ETLDRsEvxAruGrDdeo1D1obATGqsGfYr63k0Q0DPYIrtXcfpOXeMG5BIxedIZ\n\tpUjBeYpnJ+DyjuP/gkTRJpU+PWGUrRKFIACgy1pbD50RzlzncailL8gPTMh8BHn9APPW\n\tXwAA==",
        "X-Gm-Message-State": "AOAM533rBwfxq7Et5GapT3y7p1dKrukMdeMU1Qcz2rfa7P1Al2cUuxII\n\toTyzhGtFgFCwtelPXbjk9WEHOTEfJSycLQ==",
        "X-Google-Smtp-Source": "ABdhPJwbSh7Sb2G5uI0uuf4f7z3OSs+0ocVJiFJ1XmX8feETRKuVTIHbDQdDWK5Qoxd3Jy39vDa9KQ==",
        "X-Received": "by 2002:a17:902:b213:b0:156:5a00:325f with SMTP id\n\tt19-20020a170902b21300b001565a00325fmr7572649plr.127.1649238108700; \n\tWed, 06 Apr 2022 02:41:48 -0700 (PDT)",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Wed,  6 Apr 2022 17:41:30 +0800",
        "Message-Id": "<20220406094130.189862-2-chenghaoyang@chromium.org>",
        "X-Mailer": "git-send-email 2.35.1.1094.g7c7d902a7c-goog",
        "In-Reply-To": "<20220406094130.189862-1-chenghaoyang@chromium.org>",
        "References": "<20220406094130.189862-1-chenghaoyang@chromium.org>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-Mailman-Approved-At": "Wed, 06 Apr 2022 12:34:26 +0200",
        "Subject": "[libcamera-devel] [PATCH 1/1] Add CrOS JEA implementation",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "From": "Harvey Yang via libcamera-devel <libcamera-devel@lists.libcamera.org>",
        "Reply-To": "Harvey Yang <chenghaoyang@chromium.org>",
        "Cc": "Harvey Yang <chenghaoyang@chromium.org>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "This CL uses CrOS JpegCompressor with potential HW accelerator to do\nJPEG encoding.\n\nAs CrOS JpegCompressor might need file descriptors to get the source\ndata and pass the jpeg result, this CL extends FrameBuffer in the\nandroid source code as Android_FrameBuffer, which stores the\nbuffer_handle_t when constructing the frame buffer, and adds a\ngetter function to access it.\n\nThis CL also redefines src/android/jpeg/encoder interfaces and adds\nEncoder::generateThumbnail, which might also be accelerated by CrOS\nHW. It simplifies PostProcessorJpeg's logic when generating the\nthumbnail. The original implementation is then moved into the\nEncoderLibJpeg::generateThumbnail.\n---\n include/libcamera/framebuffer.h               |  3 +-\n src/android/android_framebuffer.cpp           | 32 ++++++++\n src/android/android_framebuffer.h             | 28 +++++++\n src/android/camera_device.cpp                 |  3 +-\n src/android/cros/camera3_hal.cpp              |  3 +\n src/android/frame_buffer_allocator.h          | 37 +++++----\n src/android/jpeg/cros_post_processor_jpeg.cpp | 14 ++++\n src/android/jpeg/encoder.h                    |  9 +-\n src/android/jpeg/encoder_jea.cpp              | 82 +++++++++++++++++++\n src/android/jpeg/encoder_jea.h                | 35 ++++++++\n src/android/jpeg/encoder_libjpeg.cpp          | 70 ++++++++++++++++\n src/android/jpeg/encoder_libjpeg.h            | 21 ++++-\n .../jpeg/generic_post_processor_jpeg.cpp      | 14 ++++\n src/android/jpeg/meson.build                  | 16 ++++\n src/android/jpeg/post_processor_jpeg.cpp      | 60 ++------------\n src/android/jpeg/post_processor_jpeg.h        | 11 +--\n src/android/meson.build                       |  6 +-\n .../mm/cros_frame_buffer_allocator.cpp        | 13 +--\n .../mm/generic_frame_buffer_allocator.cpp     | 11 +--\n 19 files changed, 367 insertions(+), 101 deletions(-)\n create mode 100644 src/android/android_framebuffer.cpp\n create mode 100644 src/android/android_framebuffer.h\n create mode 100644 src/android/jpeg/cros_post_processor_jpeg.cpp\n create mode 100644 src/android/jpeg/encoder_jea.cpp\n create mode 100644 src/android/jpeg/encoder_jea.h\n create mode 100644 src/android/jpeg/generic_post_processor_jpeg.cpp\n create mode 100644 src/android/jpeg/meson.build",
    "diff": "diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h\nindex de172d97..c902cc18 100644\n--- a/include/libcamera/framebuffer.h\n+++ b/include/libcamera/framebuffer.h\n@@ -46,7 +46,7 @@ private:\n \tstd::vector<Plane> planes_;\n };\n \n-class FrameBuffer final : public Extensible\n+class FrameBuffer : public Extensible\n {\n \tLIBCAMERA_DECLARE_PRIVATE()\n \n@@ -61,6 +61,7 @@ public:\n \tFrameBuffer(const std::vector<Plane> &planes, unsigned int cookie = 0);\n \tFrameBuffer(std::unique_ptr<Private> d,\n \t\t    const std::vector<Plane> &planes, unsigned int cookie = 0);\n+\tvirtual ~FrameBuffer() {}\n \n \tconst std::vector<Plane> &planes() const { return planes_; }\n \tRequest *request() const;\ndiff --git a/src/android/android_framebuffer.cpp b/src/android/android_framebuffer.cpp\nnew file mode 100644\nindex 00000000..1ff7018e\n--- /dev/null\n+++ b/src/android/android_framebuffer.cpp\n@@ -0,0 +1,32 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2022, Google Inc.\n+ *\n+ * android_framebuffer.cpp - Android Frame buffer handling\n+ */\n+\n+#include \"android_framebuffer.h\"\n+\n+#include <hardware/camera3.h>\n+\n+AndroidFrameBuffer::AndroidFrameBuffer(\n+\tbuffer_handle_t handle,\n+\tstd::unique_ptr<Private> d,\n+\tconst std::vector<Plane> &planes,\n+\tunsigned int cookie)\n+\t: FrameBuffer(std::move(d), planes, cookie), handle_(handle)\n+{\n+}\n+\n+AndroidFrameBuffer::AndroidFrameBuffer(\n+\tbuffer_handle_t handle,\n+\tconst std::vector<Plane> &planes,\n+\tunsigned int cookie)\n+\t: FrameBuffer(planes, cookie), handle_(handle)\n+{\n+}\n+\n+buffer_handle_t AndroidFrameBuffer::getHandle() const\n+{\n+\treturn handle_;\n+}\ndiff --git a/src/android/android_framebuffer.h b/src/android/android_framebuffer.h\nnew file mode 100644\nindex 00000000..49df9756\n--- /dev/null\n+++ b/src/android/android_framebuffer.h\n@@ -0,0 +1,28 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2022, Google Inc.\n+ *\n+ * android_framebuffer.h - Android Frame buffer handling\n+ */\n+\n+#pragma once\n+\n+#include \"libcamera/internal/framebuffer.h\"\n+\n+#include <hardware/camera3.h>\n+\n+class AndroidFrameBuffer final : public libcamera::FrameBuffer\n+{\n+public:\n+\tAndroidFrameBuffer(\n+\t\tbuffer_handle_t handle, std::unique_ptr<Private> d,\n+\t\tconst std::vector<Plane> &planes,\n+\t\tunsigned int cookie = 0);\n+\tAndroidFrameBuffer(buffer_handle_t handle,\n+\t\t\t   const std::vector<Plane> &planes,\n+\t\t\t   unsigned int cookie = 0);\n+\tbuffer_handle_t getHandle() const;\n+\n+private:\n+\tbuffer_handle_t handle_ = nullptr;\n+};\ndiff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\nindex 00d48471..643b4dee 100644\n--- a/src/android/camera_device.cpp\n+++ b/src/android/camera_device.cpp\n@@ -25,6 +25,7 @@\n \n #include \"system/graphics.h\"\n \n+#include \"android_framebuffer.h\"\n #include \"camera_buffer.h\"\n #include \"camera_hal_config.h\"\n #include \"camera_ops.h\"\n@@ -754,7 +755,7 @@ CameraDevice::createFrameBuffer(const buffer_handle_t camera3buffer,\n \t\tplanes[i].length = buf.size(i);\n \t}\n \n-\treturn std::make_unique<FrameBuffer>(planes);\n+\treturn std::make_unique<AndroidFrameBuffer>(camera3buffer, planes);\n }\n \n int CameraDevice::processControls(Camera3RequestDescriptor *descriptor)\ndiff --git a/src/android/cros/camera3_hal.cpp b/src/android/cros/camera3_hal.cpp\nindex fb863b5f..ea5577f0 100644\n--- a/src/android/cros/camera3_hal.cpp\n+++ b/src/android/cros/camera3_hal.cpp\n@@ -9,8 +9,11 @@\n \n #include \"../camera_hal_manager.h\"\n \n+cros::CameraMojoChannelManagerToken *g_cros_camera_token = nullptr;\n+\n static void set_up([[maybe_unused]] cros::CameraMojoChannelManagerToken *token)\n {\n+\tg_cros_camera_token = token;\n }\n \n static void tear_down()\ndiff --git a/src/android/frame_buffer_allocator.h b/src/android/frame_buffer_allocator.h\nindex 5d2eeda1..e26422a3 100644\n--- a/src/android/frame_buffer_allocator.h\n+++ b/src/android/frame_buffer_allocator.h\n@@ -13,9 +13,10 @@\n #include <libcamera/base/class.h>\n \n #include <libcamera/camera.h>\n-#include <libcamera/framebuffer.h>\n #include <libcamera/geometry.h>\n \n+#include \"android_framebuffer.h\"\n+\n class CameraDevice;\n \n class PlatformFrameBufferAllocator : libcamera::Extensible\n@@ -31,25 +32,25 @@ public:\n \t * Note: The returned FrameBuffer needs to be destroyed before\n \t * PlatformFrameBufferAllocator is destroyed.\n \t */\n-\tstd::unique_ptr<libcamera::FrameBuffer> allocate(\n+\tstd::unique_ptr<AndroidFrameBuffer> allocate(\n \t\tint halPixelFormat, const libcamera::Size &size, uint32_t usage);\n };\n \n-#define PUBLIC_FRAME_BUFFER_ALLOCATOR_IMPLEMENTATION\t\t\t\\\n-PlatformFrameBufferAllocator::PlatformFrameBufferAllocator(\t\t\\\n-\tCameraDevice *const cameraDevice)\t\t\t\t\\\n-\t: Extensible(std::make_unique<Private>(cameraDevice))\t\t\\\n-{\t\t\t\t\t\t\t\t\t\\\n-}\t\t\t\t\t\t\t\t\t\\\n-PlatformFrameBufferAllocator::~PlatformFrameBufferAllocator()\t\t\\\n-{\t\t\t\t\t\t\t\t\t\\\n-}\t\t\t\t\t\t\t\t\t\\\n-std::unique_ptr<libcamera::FrameBuffer>\t\t\t\t\t\\\n-PlatformFrameBufferAllocator::allocate(int halPixelFormat,\t\t\\\n-\t\t\t\t       const libcamera::Size &size,\t\\\n-\t\t\t\t       uint32_t usage)\t\t\t\\\n-{\t\t\t\t\t\t\t\t\t\\\n-\treturn _d()->allocate(halPixelFormat, size, usage);\t\t\\\n-}\n+#define PUBLIC_FRAME_BUFFER_ALLOCATOR_IMPLEMENTATION                        \\\n+\tPlatformFrameBufferAllocator::PlatformFrameBufferAllocator(         \\\n+\t\tCameraDevice *const cameraDevice)                           \\\n+\t\t: Extensible(std::make_unique<Private>(cameraDevice))       \\\n+\t{                                                                   \\\n+\t}                                                                   \\\n+\tPlatformFrameBufferAllocator::~PlatformFrameBufferAllocator()       \\\n+\t{                                                                   \\\n+\t}                                                                   \\\n+\tstd::unique_ptr<AndroidFrameBuffer>                                 \\\n+\tPlatformFrameBufferAllocator::allocate(int halPixelFormat,          \\\n+\t\t\t\t\t       const libcamera::Size &size, \\\n+\t\t\t\t\t       uint32_t usage)              \\\n+\t{                                                                   \\\n+\t\treturn _d()->allocate(halPixelFormat, size, usage);         \\\n+\t}\n \n #endif /* __ANDROID_FRAME_BUFFER_ALLOCATOR_H__ */\ndiff --git a/src/android/jpeg/cros_post_processor_jpeg.cpp b/src/android/jpeg/cros_post_processor_jpeg.cpp\nnew file mode 100644\nindex 00000000..7020f0d0\n--- /dev/null\n+++ b/src/android/jpeg/cros_post_processor_jpeg.cpp\n@@ -0,0 +1,14 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2022, Google Inc.\n+ *\n+ * cros_post_processor_jpeg.cpp - CrOS JPEG Post Processor\n+ */\n+\n+#include \"encoder_jea.h\"\n+#include \"post_processor_jpeg.h\"\n+\n+void PostProcessorJpeg::SetEncoder()\n+{\n+\tencoder_ = std::make_unique<EncoderJea>();\n+}\ndiff --git a/src/android/jpeg/encoder.h b/src/android/jpeg/encoder.h\nindex b974d367..6d527d91 100644\n--- a/src/android/jpeg/encoder.h\n+++ b/src/android/jpeg/encoder.h\n@@ -12,14 +12,19 @@\n #include <libcamera/framebuffer.h>\n #include <libcamera/stream.h>\n \n+#include \"../camera_request.h\"\n+\n class Encoder\n {\n public:\n \tvirtual ~Encoder() = default;\n \n \tvirtual int configure(const libcamera::StreamConfiguration &cfg) = 0;\n-\tvirtual int encode(const libcamera::FrameBuffer &source,\n-\t\t\t   libcamera::Span<uint8_t> destination,\n+\tvirtual int encode(Camera3RequestDescriptor::StreamBuffer *streamBuffer,\n \t\t\t   libcamera::Span<const uint8_t> exifData,\n \t\t\t   unsigned int quality) = 0;\n+\tvirtual int generateThumbnail(const libcamera::FrameBuffer &source,\n+\t\t\t\t      const libcamera::Size &targetSize,\n+\t\t\t\t      unsigned int quality,\n+\t\t\t\t      std::vector<unsigned char> *thumbnail) = 0;\n };\ndiff --git a/src/android/jpeg/encoder_jea.cpp b/src/android/jpeg/encoder_jea.cpp\nnew file mode 100644\nindex 00000000..838e8647\n--- /dev/null\n+++ b/src/android/jpeg/encoder_jea.cpp\n@@ -0,0 +1,82 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2022, Google Inc.\n+ *\n+ * encoder_jea.cpp - JPEG encoding using CrOS JEA\n+ */\n+\n+#include \"encoder_jea.h\"\n+\n+#include <libcamera/base/log.h>\n+\n+#include \"libcamera/internal/mapped_framebuffer.h\"\n+\n+#include <cros-camera/camera_mojo_channel_manager_token.h>\n+\n+#include \"../android_framebuffer.h\"\n+\n+extern cros::CameraMojoChannelManagerToken *g_cros_camera_token;\n+\n+EncoderJea::EncoderJea() = default;\n+\n+EncoderJea::~EncoderJea() = default;\n+\n+int EncoderJea::configure(const libcamera::StreamConfiguration &cfg)\n+{\n+\tsize_ = cfg.size;\n+\n+\tif (jpeg_compressor_.get())\n+\t\treturn 0;\n+\n+\tif (g_cros_camera_token == nullptr)\n+\t\treturn -ENOTSUP;\n+\n+\tjpeg_compressor_ = cros::JpegCompressor::GetInstance(g_cros_camera_token);\n+\n+\treturn 0;\n+}\n+\n+int EncoderJea::encode(Camera3RequestDescriptor::StreamBuffer *streamBuffer,\n+\t\t       libcamera::Span<const uint8_t> exifData,\n+\t\t       unsigned int quality)\n+{\n+\tif (!jpeg_compressor_.get())\n+\t\treturn -1;\n+\n+\tuint32_t out_data_size = 0;\n+\n+\tif (!jpeg_compressor_->CompressImageFromHandle(\n+\t\t    dynamic_cast<const AndroidFrameBuffer *>(\n+\t\t\t    streamBuffer->srcBuffer)\n+\t\t\t    ->getHandle(),\n+\t\t    *streamBuffer->camera3Buffer, size_.width, size_.height, quality,\n+\t\t    exifData.data(), exifData.size(), &out_data_size)) {\n+\t\treturn -1;\n+\t}\n+\n+\treturn out_data_size;\n+}\n+\n+int EncoderJea::generateThumbnail(const libcamera::FrameBuffer &source,\n+\t\t\t\t  const libcamera::Size &targetSize,\n+\t\t\t\t  unsigned int quality,\n+\t\t\t\t  std::vector<unsigned char> *thumbnail)\n+{\n+\tif (!jpeg_compressor_.get())\n+\t\treturn -1;\n+\n+\tlibcamera::MappedFrameBuffer frame(&source, libcamera::MappedFrameBuffer::MapFlag::Read);\n+\n+\tif (frame.planes().empty())\n+\t\treturn 0;\n+\n+\tuint32_t out_data_size = 0;\n+\n+\tif (!jpeg_compressor_->GenerateThumbnail(frame.planes()[0].data(),\n+\t\t\t\t\t\t size_.width, size_.height, targetSize.width, targetSize.height,\n+\t\t\t\t\t\t quality, thumbnail->size(), thumbnail->data(), &out_data_size)) {\n+\t\treturn -1;\n+\t}\n+\n+\treturn out_data_size;\n+}\ndiff --git a/src/android/jpeg/encoder_jea.h b/src/android/jpeg/encoder_jea.h\nnew file mode 100644\nindex 00000000..d5c9f1f7\n--- /dev/null\n+++ b/src/android/jpeg/encoder_jea.h\n@@ -0,0 +1,35 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2022, Google Inc.\n+ *\n+ * encoder_jea.h - JPEG encoding using CrOS JEA\n+ */\n+\n+#pragma once\n+\n+#include <libcamera/geometry.h>\n+\n+#include <cros-camera/jpeg_compressor.h>\n+\n+#include \"encoder.h\"\n+\n+class EncoderJea : public Encoder\n+{\n+public:\n+\tEncoderJea();\n+\t~EncoderJea();\n+\n+\tint configure(const libcamera::StreamConfiguration &cfg) override;\n+\tint encode(Camera3RequestDescriptor::StreamBuffer *streamBuffer,\n+\t\t   libcamera::Span<const uint8_t> exifData,\n+\t\t   unsigned int quality) override;\n+\tint generateThumbnail(const libcamera::FrameBuffer &source,\n+\t\t\t      const libcamera::Size &targetSize,\n+\t\t\t      unsigned int quality,\n+\t\t\t      std::vector<unsigned char> *thumbnail) override;\n+\n+private:\n+\tlibcamera::Size size_;\n+\n+\tstd::unique_ptr<cros::JpegCompressor> jpeg_compressor_;\n+};\ndiff --git a/src/android/jpeg/encoder_libjpeg.cpp b/src/android/jpeg/encoder_libjpeg.cpp\nindex 21a3b33d..b5591e33 100644\n--- a/src/android/jpeg/encoder_libjpeg.cpp\n+++ b/src/android/jpeg/encoder_libjpeg.cpp\n@@ -24,6 +24,8 @@\n #include \"libcamera/internal/formats.h\"\n #include \"libcamera/internal/mapped_framebuffer.h\"\n \n+#include \"../camera_buffer.h\"\n+\n using namespace libcamera;\n \n LOG_DECLARE_CATEGORY(JPEG)\n@@ -82,8 +84,17 @@ EncoderLibJpeg::~EncoderLibJpeg()\n }\n \n int EncoderLibJpeg::configure(const StreamConfiguration &cfg)\n+{\n+\tthumbnailer_.configure(cfg.size, cfg.pixelFormat);\n+\tcfg_ = cfg;\n+\n+\treturn internalConfigure(cfg);\n+}\n+\n+int EncoderLibJpeg::internalConfigure(const StreamConfiguration &cfg)\n {\n \tconst struct JPEGPixelFormatInfo info = findPixelInfo(cfg.pixelFormat);\n+\n \tif (info.colorSpace == JCS_UNKNOWN)\n \t\treturn -ENOTSUP;\n \n@@ -178,6 +189,65 @@ void EncoderLibJpeg::compressNV(const std::vector<Span<uint8_t>> &planes)\n \t}\n }\n \n+int EncoderLibJpeg::encode(Camera3RequestDescriptor::StreamBuffer *streamBuffer,\n+\t\t\t   libcamera::Span<const uint8_t> exifData,\n+\t\t\t   unsigned int quality)\n+{\n+\tinternalConfigure(cfg_);\n+\treturn encode(*streamBuffer->srcBuffer, streamBuffer->dstBuffer.get()->plane(0), exifData, quality);\n+}\n+\n+int EncoderLibJpeg::generateThumbnail(\n+\tconst libcamera::FrameBuffer &source,\n+\tconst libcamera::Size &targetSize,\n+\tunsigned int quality,\n+\tstd::vector<unsigned char> *thumbnail)\n+{\n+\t/* Stores the raw scaled-down thumbnail bytes. */\n+\tstd::vector<unsigned char> rawThumbnail;\n+\n+\tthumbnailer_.createThumbnail(source, targetSize, &rawThumbnail);\n+\n+\tStreamConfiguration thCfg;\n+\tthCfg.size = targetSize;\n+\tthCfg.pixelFormat = thumbnailer_.pixelFormat();\n+\tint ret = internalConfigure(thCfg);\n+\n+\tif (!rawThumbnail.empty() && !ret) {\n+\t\t/*\n+\t\t * \\todo Avoid value-initialization of all elements of the\n+\t\t * vector.\n+\t\t */\n+\t\tthumbnail->resize(rawThumbnail.size());\n+\n+\t\t/*\n+\t\t * Split planes manually as the encoder expects a vector of\n+\t\t * planes.\n+\t\t *\n+\t\t * \\todo Pass a vector of planes directly to\n+\t\t * Thumbnailer::createThumbnailer above and remove the manual\n+\t\t * planes split from here.\n+\t\t */\n+\t\tstd::vector<Span<uint8_t>> thumbnailPlanes;\n+\t\tconst PixelFormatInfo &formatNV12 = PixelFormatInfo::info(formats::NV12);\n+\t\tsize_t yPlaneSize = formatNV12.planeSize(targetSize, 0);\n+\t\tsize_t uvPlaneSize = formatNV12.planeSize(targetSize, 1);\n+\t\tthumbnailPlanes.push_back({ rawThumbnail.data(), yPlaneSize });\n+\t\tthumbnailPlanes.push_back({ rawThumbnail.data() + yPlaneSize, uvPlaneSize });\n+\n+\t\tint jpeg_size = encode(thumbnailPlanes, *thumbnail, {}, quality);\n+\t\tthumbnail->resize(jpeg_size);\n+\n+\t\tLOG(JPEG, Debug)\n+\t\t\t<< \"Thumbnail compress returned \"\n+\t\t\t<< jpeg_size << \" bytes\";\n+\n+\t\treturn jpeg_size;\n+\t}\n+\n+\treturn -1;\n+}\n+\n int EncoderLibJpeg::encode(const FrameBuffer &source, Span<uint8_t> dest,\n \t\t\t   Span<const uint8_t> exifData, unsigned int quality)\n {\ndiff --git a/src/android/jpeg/encoder_libjpeg.h b/src/android/jpeg/encoder_libjpeg.h\nindex 1b3ac067..56b27bae 100644\n--- a/src/android/jpeg/encoder_libjpeg.h\n+++ b/src/android/jpeg/encoder_libjpeg.h\n@@ -15,6 +15,8 @@\n \n #include <jpeglib.h>\n \n+#include \"thumbnailer.h\"\n+\n class EncoderLibJpeg : public Encoder\n {\n public:\n@@ -22,19 +24,32 @@ public:\n \t~EncoderLibJpeg();\n \n \tint configure(const libcamera::StreamConfiguration &cfg) override;\n+\tint encode(Camera3RequestDescriptor::StreamBuffer *streamBuffer,\n+\t\t   libcamera::Span<const uint8_t> exifData,\n+\t\t   unsigned int quality) override;\n+\tint generateThumbnail(\n+\t\tconst libcamera::FrameBuffer &source,\n+\t\tconst libcamera::Size &targetSize,\n+\t\tunsigned int quality,\n+\t\tstd::vector<unsigned char> *thumbnail) override;\n+\n+private:\n+\tint internalConfigure(const libcamera::StreamConfiguration &cfg);\n+\n \tint encode(const libcamera::FrameBuffer &source,\n \t\t   libcamera::Span<uint8_t> destination,\n \t\t   libcamera::Span<const uint8_t> exifData,\n-\t\t   unsigned int quality) override;\n+\t\t   unsigned int quality);\n \tint encode(const std::vector<libcamera::Span<uint8_t>> &planes,\n \t\t   libcamera::Span<uint8_t> destination,\n \t\t   libcamera::Span<const uint8_t> exifData,\n \t\t   unsigned int quality);\n \n-private:\n \tvoid compressRGB(const std::vector<libcamera::Span<uint8_t>> &planes);\n \tvoid compressNV(const std::vector<libcamera::Span<uint8_t>> &planes);\n \n+\tlibcamera::StreamConfiguration cfg_;\n+\n \tstruct jpeg_compress_struct compress_;\n \tstruct jpeg_error_mgr jerr_;\n \n@@ -42,4 +57,6 @@ private:\n \n \tbool nv_;\n \tbool nvSwap_;\n+\n+\tThumbnailer thumbnailer_;\n };\ndiff --git a/src/android/jpeg/generic_post_processor_jpeg.cpp b/src/android/jpeg/generic_post_processor_jpeg.cpp\nnew file mode 100644\nindex 00000000..890f6972\n--- /dev/null\n+++ b/src/android/jpeg/generic_post_processor_jpeg.cpp\n@@ -0,0 +1,14 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2022, Google Inc.\n+ *\n+ * generic_post_processor_jpeg.cpp - Generic JPEG Post Processor\n+ */\n+\n+#include \"encoder_libjpeg.h\"\n+#include \"post_processor_jpeg.h\"\n+\n+void PostProcessorJpeg::SetEncoder()\n+{\n+\tencoder_ = std::make_unique<EncoderLibJpeg>();\n+}\ndiff --git a/src/android/jpeg/meson.build b/src/android/jpeg/meson.build\nnew file mode 100644\nindex 00000000..8606acc4\n--- /dev/null\n+++ b/src/android/jpeg/meson.build\n@@ -0,0 +1,16 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+android_hal_sources += files([\n+    'exif.cpp',\n+    'post_processor_jpeg.cpp'])\n+\n+platform = get_option('android_platform')\n+if platform == 'generic'\n+    android_hal_sources += files(['encoder_libjpeg.cpp',\n+                                  'generic_post_processor_jpeg.cpp',\n+                                  'thumbnailer.cpp'])\n+elif platform == 'cros'\n+    android_hal_sources += files(['cros_post_processor_jpeg.cpp',\n+                                  'encoder_jea.cpp'])\n+    android_deps += [dependency('libcros_camera')]\n+endif\ndiff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp\nindex d72ebc3c..7ceba60e 100644\n--- a/src/android/jpeg/post_processor_jpeg.cpp\n+++ b/src/android/jpeg/post_processor_jpeg.cpp\n@@ -9,10 +9,10 @@\n \n #include <chrono>\n \n+#include \"../android_framebuffer.h\"\n #include \"../camera_device.h\"\n #include \"../camera_metadata.h\"\n #include \"../camera_request.h\"\n-#include \"encoder_libjpeg.h\"\n #include \"exif.h\"\n \n #include <libcamera/base/log.h>\n@@ -44,60 +44,11 @@ int PostProcessorJpeg::configure(const StreamConfiguration &inCfg,\n \n \tstreamSize_ = outCfg.size;\n \n-\tthumbnailer_.configure(inCfg.size, inCfg.pixelFormat);\n-\n-\tencoder_ = std::make_unique<EncoderLibJpeg>();\n+\tSetEncoder();\n \n \treturn encoder_->configure(inCfg);\n }\n \n-void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source,\n-\t\t\t\t\t  const Size &targetSize,\n-\t\t\t\t\t  unsigned int quality,\n-\t\t\t\t\t  std::vector<unsigned char> *thumbnail)\n-{\n-\t/* Stores the raw scaled-down thumbnail bytes. */\n-\tstd::vector<unsigned char> rawThumbnail;\n-\n-\tthumbnailer_.createThumbnail(source, targetSize, &rawThumbnail);\n-\n-\tStreamConfiguration thCfg;\n-\tthCfg.size = targetSize;\n-\tthCfg.pixelFormat = thumbnailer_.pixelFormat();\n-\tint ret = thumbnailEncoder_.configure(thCfg);\n-\n-\tif (!rawThumbnail.empty() && !ret) {\n-\t\t/*\n-\t\t * \\todo Avoid value-initialization of all elements of the\n-\t\t * vector.\n-\t\t */\n-\t\tthumbnail->resize(rawThumbnail.size());\n-\n-\t\t/*\n-\t\t * Split planes manually as the encoder expects a vector of\n-\t\t * planes.\n-\t\t *\n-\t\t * \\todo Pass a vector of planes directly to\n-\t\t * Thumbnailer::createThumbnailer above and remove the manual\n-\t\t * planes split from here.\n-\t\t */\n-\t\tstd::vector<Span<uint8_t>> thumbnailPlanes;\n-\t\tconst PixelFormatInfo &formatNV12 = PixelFormatInfo::info(formats::NV12);\n-\t\tsize_t yPlaneSize = formatNV12.planeSize(targetSize, 0);\n-\t\tsize_t uvPlaneSize = formatNV12.planeSize(targetSize, 1);\n-\t\tthumbnailPlanes.push_back({ rawThumbnail.data(), yPlaneSize });\n-\t\tthumbnailPlanes.push_back({ rawThumbnail.data() + yPlaneSize, uvPlaneSize });\n-\n-\t\tint jpeg_size = thumbnailEncoder_.encode(thumbnailPlanes,\n-\t\t\t\t\t\t\t *thumbnail, {}, quality);\n-\t\tthumbnail->resize(jpeg_size);\n-\n-\t\tLOG(JPEG, Debug)\n-\t\t\t<< \"Thumbnail compress returned \"\n-\t\t\t<< jpeg_size << \" bytes\";\n-\t}\n-}\n-\n void PostProcessorJpeg::process(Camera3RequestDescriptor::StreamBuffer *streamBuffer)\n {\n \tASSERT(encoder_);\n@@ -164,8 +115,8 @@ void PostProcessorJpeg::process(Camera3RequestDescriptor::StreamBuffer *streamBu\n \n \t\tif (thumbnailSize != Size(0, 0)) {\n \t\t\tstd::vector<unsigned char> thumbnail;\n-\t\t\tgenerateThumbnail(source, thumbnailSize, quality, &thumbnail);\n-\t\t\tif (!thumbnail.empty())\n+\t\t\tret = encoder_->generateThumbnail(source, thumbnailSize, quality, &thumbnail);\n+\t\t\tif (ret > 0 && !thumbnail.empty())\n \t\t\t\texif.setThumbnail(thumbnail, Exif::Compression::JPEG);\n \t\t}\n \n@@ -194,8 +145,7 @@ void PostProcessorJpeg::process(Camera3RequestDescriptor::StreamBuffer *streamBu\n \tconst uint8_t quality = ret ? *entry.data.u8 : 95;\n \tresultMetadata->addEntry(ANDROID_JPEG_QUALITY, quality);\n \n-\tint jpeg_size = encoder_->encode(source, destination->plane(0),\n-\t\t\t\t\t exif.data(), quality);\n+\tint jpeg_size = encoder_->encode(streamBuffer, exif.data(), quality);\n \tif (jpeg_size < 0) {\n \t\tLOG(JPEG, Error) << \"Failed to encode stream image\";\n \t\tprocessComplete.emit(streamBuffer, PostProcessor::Status::Error);\ndiff --git a/src/android/jpeg/post_processor_jpeg.h b/src/android/jpeg/post_processor_jpeg.h\nindex 98309b01..a09f8798 100644\n--- a/src/android/jpeg/post_processor_jpeg.h\n+++ b/src/android/jpeg/post_processor_jpeg.h\n@@ -8,11 +8,11 @@\n #pragma once\n \n #include \"../post_processor.h\"\n-#include \"encoder_libjpeg.h\"\n-#include \"thumbnailer.h\"\n \n #include <libcamera/geometry.h>\n \n+#include \"encoder.h\"\n+\n class CameraDevice;\n \n class PostProcessorJpeg : public PostProcessor\n@@ -25,14 +25,9 @@ public:\n \tvoid process(Camera3RequestDescriptor::StreamBuffer *streamBuffer) override;\n \n private:\n-\tvoid generateThumbnail(const libcamera::FrameBuffer &source,\n-\t\t\t       const libcamera::Size &targetSize,\n-\t\t\t       unsigned int quality,\n-\t\t\t       std::vector<unsigned char> *thumbnail);\n+\tvoid SetEncoder();\n \n \tCameraDevice *const cameraDevice_;\n \tstd::unique_ptr<Encoder> encoder_;\n \tlibcamera::Size streamSize_;\n-\tEncoderLibJpeg thumbnailEncoder_;\n-\tThumbnailer thumbnailer_;\n };\ndiff --git a/src/android/meson.build b/src/android/meson.build\nindex 75b4bf20..026b8b3c 100644\n--- a/src/android/meson.build\n+++ b/src/android/meson.build\n@@ -38,6 +38,7 @@ endif\n android_deps += [libyuv_dep]\n \n android_hal_sources = files([\n+    'android_framebuffer.cpp',\n     'camera3_hal.cpp',\n     'camera_capabilities.cpp',\n     'camera_device.cpp',\n@@ -47,10 +48,6 @@ android_hal_sources = files([\n     'camera_ops.cpp',\n     'camera_request.cpp',\n     'camera_stream.cpp',\n-    'jpeg/encoder_libjpeg.cpp',\n-    'jpeg/exif.cpp',\n-    'jpeg/post_processor_jpeg.cpp',\n-    'jpeg/thumbnailer.cpp',\n     'yuv/post_processor_yuv.cpp'\n ])\n \n@@ -58,6 +55,7 @@ android_cpp_args = []\n \n subdir('cros')\n subdir('mm')\n+subdir('jpeg')\n \n android_camera_metadata_sources = files([\n     'metadata/camera_metadata.c',\ndiff --git a/src/android/mm/cros_frame_buffer_allocator.cpp b/src/android/mm/cros_frame_buffer_allocator.cpp\nindex 52e8c180..163c5d75 100644\n--- a/src/android/mm/cros_frame_buffer_allocator.cpp\n+++ b/src/android/mm/cros_frame_buffer_allocator.cpp\n@@ -14,6 +14,7 @@\n \n #include \"libcamera/internal/framebuffer.h\"\n \n+#include \"../android_framebuffer.h\"\n #include \"../camera_device.h\"\n #include \"../frame_buffer_allocator.h\"\n #include \"cros-camera/camera_buffer_manager.h\"\n@@ -47,11 +48,11 @@ public:\n \t{\n \t}\n \n-\tstd::unique_ptr<libcamera::FrameBuffer>\n+\tstd::unique_ptr<AndroidFrameBuffer>\n \tallocate(int halPixelFormat, const libcamera::Size &size, uint32_t usage);\n };\n \n-std::unique_ptr<libcamera::FrameBuffer>\n+std::unique_ptr<AndroidFrameBuffer>\n PlatformFrameBufferAllocator::Private::allocate(int halPixelFormat,\n \t\t\t\t\t\tconst libcamera::Size &size,\n \t\t\t\t\t\tuint32_t usage)\n@@ -80,9 +81,11 @@ PlatformFrameBufferAllocator::Private::allocate(int halPixelFormat,\n \t\tplane.length = cros::CameraBufferManager::GetPlaneSize(handle, i);\n \t}\n \n-\treturn std::make_unique<FrameBuffer>(\n-\t\tstd::make_unique<CrosFrameBufferData>(std::move(scopedHandle)),\n-\t\tplanes);\n+\tauto fb = std::make_unique<AndroidFrameBuffer>(handle,\n+\t\t\t\t\t\t       std::make_unique<CrosFrameBufferData>(std::move(scopedHandle)),\n+\t\t\t\t\t\t       planes);\n+\n+\treturn fb;\n }\n \n PUBLIC_FRAME_BUFFER_ALLOCATOR_IMPLEMENTATION\ndiff --git a/src/android/mm/generic_frame_buffer_allocator.cpp b/src/android/mm/generic_frame_buffer_allocator.cpp\nindex acb2fa2b..c79b7b10 100644\n--- a/src/android/mm/generic_frame_buffer_allocator.cpp\n+++ b/src/android/mm/generic_frame_buffer_allocator.cpp\n@@ -18,6 +18,7 @@\n #include <hardware/gralloc.h>\n #include <hardware/hardware.h>\n \n+#include \"../android_framebuffer.h\"\n #include \"../camera_device.h\"\n #include \"../frame_buffer_allocator.h\"\n \n@@ -77,7 +78,7 @@ public:\n \n \t~Private() override;\n \n-\tstd::unique_ptr<libcamera::FrameBuffer>\n+\tstd::unique_ptr<AndroidFrameBuffer>\n \tallocate(int halPixelFormat, const libcamera::Size &size, uint32_t usage);\n \n private:\n@@ -92,7 +93,7 @@ PlatformFrameBufferAllocator::Private::~Private()\n \t\tgralloc_close(allocDevice_);\n }\n \n-std::unique_ptr<libcamera::FrameBuffer>\n+std::unique_ptr<AndroidFrameBuffer>\n PlatformFrameBufferAllocator::Private::allocate(int halPixelFormat,\n \t\t\t\t\t\tconst libcamera::Size &size,\n \t\t\t\t\t\tuint32_t usage)\n@@ -135,9 +136,9 @@ PlatformFrameBufferAllocator::Private::allocate(int halPixelFormat,\n \t\toffset += planeSize;\n \t}\n \n-\treturn std::make_unique<FrameBuffer>(\n-\t\tstd::make_unique<GenericFrameBufferData>(allocDevice_, handle),\n-\t\tplanes);\n+\treturn std::make_unique<AndroidFrameBuffer>(handle,\n+\t\t\t\t\t\t    std::make_unique<GenericFrameBufferData>(allocDevice_, handle),\n+\t\t\t\t\t\t    planes);\n }\n \n PUBLIC_FRAME_BUFFER_ALLOCATOR_IMPLEMENTATION\n",
    "prefixes": [
        "libcamera-devel",
        "1/1"
    ]
}