Show a patch.

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

{
    "id": 18925,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/18925/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/18925/",
    "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": "<20230806180136.304156-2-andrey.konovalov@linaro.org>",
    "date": "2023-08-06T18:01:35",
    "name": "[libcamera-devel,RFC,1/2] libcamera: converter: add software converter",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "dbd889f76d3d8acd484080378800473f2e8c1e7e",
    "submitter": {
        "id": 25,
        "url": "https://patchwork.libcamera.org/api/1.1/people/25/?format=api",
        "name": "Andrey Konovalov",
        "email": "andrey.konovalov@linaro.org"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/18925/mbox/",
    "series": [
        {
            "id": 4001,
            "url": "https://patchwork.libcamera.org/api/1.1/series/4001/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4001",
            "date": "2023-08-06T18:01:34",
            "name": "libcamera: converter: introduce software converter for debayering and AWB",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/4001/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/18925/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/18925/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 9E058BDCBF\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun,  6 Aug 2023 18:02:07 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 51039627EA;\n\tSun,  6 Aug 2023 20:02:07 +0200 (CEST)",
            "from mail-ej1-x633.google.com (mail-ej1-x633.google.com\n\t[IPv6:2a00:1450:4864:20::633])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 96A8B627E9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun,  6 Aug 2023 20:02:05 +0200 (CEST)",
            "by mail-ej1-x633.google.com with SMTP id\n\ta640c23a62f3a-99c93638322so528121366b.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 06 Aug 2023 11:02:05 -0700 (PDT)",
            "from Lat-5310.. ([87.116.165.37]) by smtp.gmail.com with ESMTPSA id\n\tj15-20020a170906430f00b0099b76c3041csm4197396ejm.7.2023.08.06.11.02.04\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tSun, 06 Aug 2023 11:02:04 -0700 (PDT)"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1691344927;\n\tbh=hyKZ1FNjPHaejFx/L2CdUmflaw47Z8sL0Z6XmrV5sIA=;\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=BmfzV+3El69Zi/iyeRvPvqsdpknQHtg1LDG0dVgj/m1fPrskDCYF9bIEnagbqxOif\n\tNaQPpnfiiU2Baq7YCaZoiJqvd5XSckoMCZLNHH0gZE3kY5wlfEi38yWBmUg5KY9BnR\n\ttD2KJsdJDc7MIBghifJAdAKgxt41MsWk8bb6SY0UqhVqmYdJ/gi4IfgsbBRA+/mHUP\n\tknn8fL/8+oURQ4t7/Tdov+hGJmm6NJCcD1kviF7VmDbYAmyK/zNIrJfsgk5/SNSTzy\n\t/1w98zvxWXQGRx0mljjvYB+8Cf8BXtxMYpYwsqTZideCGYbcmrW1NlavB8txf32fyU\n\t65x6tybgeFYPQ==",
            "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=linaro.org; s=google; t=1691344925; x=1691949725;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=MkCvqViWmK1XGUN8O+hfLZoKr0ZhnflyP+K3MZlgvTE=;\n\tb=S9y++uY2Au6Rda3f10zjrTD5Q200An8AgiSIOUKJ7/PKnHgac2SueoFA3jqQpyrD6+\n\tuHTJTUYuovcim/M+BD+C6D9JF2LWJOZun7Z20H9e8zOddidMP2c6Y04p8SoNkDy9eSja\n\tMTi/+as5uGp0r3dD9dTzMD4ojEgAo7YlXu3ZFaRjZIMqeIlnB1RPHXBOnNBGpV6f+FBX\n\tZyhEZgyLGhLIKGPX47VZewjCmyJ3lMhVuFxsM2Rqj1x60T/3BpQM1PgB7Nc9OY+dxuCf\n\tEhxt4P53fZIruqatb2yA1wWu5Vhuy10oQgazpGxkcEBvqHg14PhkRsrwoP8CIfw16ryt\n\tf7UA=="
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=linaro.org\n\theader.i=@linaro.org header.b=\"S9y++uY2\"; \n\tdkim-atps=neutral",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20221208; t=1691344925; x=1691949725;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n\t:subject:date:message-id:reply-to;\n\tbh=MkCvqViWmK1XGUN8O+hfLZoKr0ZhnflyP+K3MZlgvTE=;\n\tb=lNRsk2qi1v9jQjILy02VeAdxIAFqtUQiBprqFvYAwaAun1hS4qH504WhPDCQYWvkmj\n\t4NCZc+L/VwatidpwhBaH2ZVX4+VlFp9I9HD673tWCbqHwS+dpXmLWp+3H9tKD3ve80Ak\n\tIH6izHgF44AjPGcMz3v4o32z5CrB1gVd+hp0cnIChgEiuxYhRRVQxrCRnY8wrENg1a35\n\t06htGK1Ff7I2kZ/Ku55YSYa7rvzHmy904v4pQB9TDvPRPqrVuQg8bdjymSBrau7TZupP\n\tM/iG5BAezVmMc30Xcf2YdHkf25D+VWBY0MZUgZ4QMwRypyQhusifi43SnV9iYmgLn3Sl\n\tlgWw==",
        "X-Gm-Message-State": "AOJu0Yx9XAK4p68ieTEo2pXbwJPlFCihCS4mxZv544jDAhYZZeMHGVNV\n\t1HKU0cmikcCOojWZcHnz8gDiQWZGncmvs7RxYa0=",
        "X-Google-Smtp-Source": "AGHT+IHaHiGEY/6Dk2sUtD2Fmw/bYtBRgwnDQT+uJJylH74r0OXV/gOPepdG+yBeUIgbnR9G/SzEfQ==",
        "X-Received": "by 2002:a17:906:8a44:b0:99b:cdfd:fb44 with SMTP id\n\tgx4-20020a1709068a4400b0099bcdfdfb44mr5379077ejc.9.1691344925106; \n\tSun, 06 Aug 2023 11:02:05 -0700 (PDT)",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Sun,  6 Aug 2023 21:01:35 +0300",
        "Message-Id": "<20230806180136.304156-2-andrey.konovalov@linaro.org>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20230806180136.304156-1-andrey.konovalov@linaro.org>",
        "References": "<20230806180136.304156-1-andrey.konovalov@linaro.org>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [RFC PATCH 1/2] libcamera: converter: add\n\tsoftware converter",
        "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": "Andrey Konovalov via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>",
        "Reply-To": "Andrey Konovalov <andrey.konovalov@linaro.org>",
        "Cc": "bryan.odonoghue@linaro.org, srinivas.kandagatla@linaro.org",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "Use the Converter interface to implement bilinear Bayer demosaic\nfiltering and very simple AWB on CPU.\n\nSigned-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>\n---\n .../internal/converter/converter_softw.h      |  90 ++++\n src/libcamera/converter/converter_softw.cpp   | 430 ++++++++++++++++++\n src/libcamera/converter/meson.build           |   3 +-\n 3 files changed, 522 insertions(+), 1 deletion(-)\n create mode 100644 include/libcamera/internal/converter/converter_softw.h\n create mode 100644 src/libcamera/converter/converter_softw.cpp",
    "diff": "diff --git a/include/libcamera/internal/converter/converter_softw.h b/include/libcamera/internal/converter/converter_softw.h\nnew file mode 100644\nindex 00000000..73814c77\n--- /dev/null\n+++ b/include/libcamera/internal/converter/converter_softw.h\n@@ -0,0 +1,90 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2023, Linaro Ltd\n+ *\n+ * converter_softw.h - interface of software converter (runs 100% on CPU)\n+ */\n+\n+#include <map>\n+#include <tuple>\n+#include <vector>\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/base/signal.h>\n+#include <libcamera/base/thread.h>\n+\n+#include <libcamera/pixel_format.h>\n+\n+#include \"libcamera/internal/converter.h\"\n+\n+namespace libcamera {\n+\n+class FrameBuffer;\n+class MediaDevice;\n+class Size;\n+class SizeRange;\n+struct StreamConfiguration;\n+\n+class SwConverter : public Converter\n+{\n+public:\n+\tSwConverter(MediaDevice *media)\n+\t\t: Converter(media) {}\n+\n+\tint loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }\n+\tbool isValid() const { return true; }\n+\n+\tstd::vector<PixelFormat> formats(PixelFormat input);\n+\tSizeRange sizes(const Size &input);\n+\n+\tstd::tuple<unsigned int, unsigned int>\n+\tstrideAndFrameSize(const PixelFormat &pixelFormat, const Size &size);\n+\n+\tint configure(const StreamConfiguration &inputCfg,\n+\t\t      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfg);\n+\tint exportBuffers(unsigned int ouput, unsigned int count,\n+\t\t\t  std::vector<std::unique_ptr<FrameBuffer>> *buffers);\n+\n+\tvoid process(FrameBuffer *input, FrameBuffer *output);\n+\tint start();\n+\tvoid stop();\n+\n+\tint queueBuffers(FrameBuffer *input,\n+\t\t\t const std::map<unsigned int, FrameBuffer *> &outputs);\n+\n+private:\n+\tclass Isp : public Object\n+\t{\n+\tpublic:\n+\t\tIsp(SwConverter *converter)\n+\t\t\t: converter_(converter) {}\n+\n+\t\tint configure(const StreamConfiguration &inputCfg,\n+\t\t\t      const StreamConfiguration &outputCfg);\n+\t\tint exportBuffers(unsigned int count,\n+\t\t\t\t  std::vector<std::unique_ptr<FrameBuffer>> *buffers);\n+\t\tvoid process(FrameBuffer *input, FrameBuffer *output);\n+\t\tint start();\n+\t\tvoid stop();\n+\n+\tprivate:\n+\t\tvoid debayer(uint8_t *dst, const uint8_t *src);\n+\n+\t\tSwConverter *converter_;\n+\n+\t\tThread thread_;\n+\n+\t\tunsigned int width_;\n+\t\tunsigned int height_;\n+\t\tunsigned int stride_;\n+\t\tPoint red_shift_;\n+\n+\t\tunsigned long rNumerat_, rDenomin_; /* red gain for AWB */\n+\t\tunsigned long bNumerat_, bDenomin_; /* blue gain for AWB */\n+\t\tunsigned long gNumerat_, gDenomin_; /* green gain for AWB */\n+\t};\n+\n+\tstd::unique_ptr<Isp> isp_;\n+};\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/converter/converter_softw.cpp b/src/libcamera/converter/converter_softw.cpp\nnew file mode 100644\nindex 00000000..ab00113a\n--- /dev/null\n+++ b/src/libcamera/converter/converter_softw.cpp\n@@ -0,0 +1,430 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2023, Linaro Ltd\n+ *\n+ * converter_softw.h - interface of software converter (runs 100% on CPU)\n+ */\n+\n+#include \"libcamera/internal/converter/converter_softw.h\"\n+\n+#include <sys/mman.h>\n+#include <sys/types.h>\n+#include <tuple>\n+#include <unistd.h>\n+#include <vector>\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/base/signal.h>\n+#include <libcamera/base/thread.h>\n+\n+#include <libcamera/formats.h>\n+#include <libcamera/geometry.h>\n+#include <libcamera/pixel_format.h>\n+#include <libcamera/stream.h>\n+\n+#include \"libcamera/internal/bayer_format.h\"\n+#include \"libcamera/internal/framebuffer.h\"\n+\n+namespace libcamera {\n+\n+LOG_DECLARE_CATEGORY(Converter)\n+\n+std::vector<PixelFormat> SwConverter::formats(PixelFormat input)\n+{\n+\tstd::vector<PixelFormat> pixelFormats;\n+\tBayerFormat inputFormat = BayerFormat::fromPixelFormat(input);\n+\n+\t/* Only RAW10P is currently supported */\n+\tif (inputFormat.bitDepth == 10 && inputFormat.packing == BayerFormat::Packing::CSI2)\n+\t\tpixelFormats.push_back(formats::ARGB8888);\n+\n+\tif (pixelFormats.empty())\n+\t\tLOG(Converter, Info)\n+\t\t\t<< \"Unsupported input format \" << input.toString();\n+\n+\treturn pixelFormats;\n+}\n+\n+SizeRange SwConverter::sizes(const Size &input)\n+{\n+\tif (input.width < 2 || input.height < 2) {\n+\t\tLOG(Converter, Error)\n+\t\t\t<< \"Input format size too small: \" << input.toString();\n+\t\treturn {};\n+\t}\n+\n+\treturn SizeRange(Size(input.width - 2, input.height - 2));\n+}\n+\n+std::tuple<unsigned int, unsigned int>\n+SwConverter::strideAndFrameSize(const PixelFormat &pixelFormat,\n+\t\t\t\tconst Size &size)\n+{\n+\t/* Only ARGB8888 output is currently supported */\n+\tif (pixelFormat == formats::ARGB8888) {\n+\t\tint stride = size.width * 4;\n+\t\treturn std::make_tuple(stride, stride * (size.height));\n+\t}\n+\n+\treturn std::make_tuple(0, 0);\n+}\n+\n+int SwConverter::configure(const StreamConfiguration &inputCfg,\n+\t\t\t   const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs)\n+{\n+\tif (outputCfgs.size() != 1) {\n+\t\tLOG(Converter, Error)\n+\t\t\t<< \"Unsupported number of output streams: \"\n+\t\t\t<< outputCfgs.size();\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tisp_ = std::make_unique<SwConverter::Isp>(this);\n+\n+\treturn isp_->configure(inputCfg, outputCfgs[0]);\n+}\n+\n+int SwConverter::Isp::configure(const StreamConfiguration &inputCfg,\n+\t\t\t\tconst StreamConfiguration &outputCfg)\n+{\n+\tBayerFormat bayerFormat =\n+\t\tBayerFormat::fromPixelFormat(inputCfg.pixelFormat);\n+\twidth_ = inputCfg.size.width;\n+\theight_ = inputCfg.size.height;\n+\tstride_ = inputCfg.stride;\n+\n+\tif (bayerFormat.bitDepth != 10 ||\n+\t    bayerFormat.packing != BayerFormat::Packing::CSI2 ||\n+\t    width_ < 2 || height_ < 2) {\n+\t\tLOG(Converter, Error) << \"Input format \"\n+\t\t\t\t      << inputCfg.size << \"-\"\n+\t\t\t\t      << inputCfg.pixelFormat\n+\t\t\t\t      << \"not supported\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tswitch (bayerFormat.order) {\n+\tcase BayerFormat::BGGR:\n+\t\tred_shift_ = Point(0, 0);\n+\t\tbreak;\n+\tcase BayerFormat::GBRG:\n+\t\tred_shift_ = Point(1, 0);\n+\t\tbreak;\n+\tcase BayerFormat::GRBG:\n+\t\tred_shift_ = Point(0, 1);\n+\t\tbreak;\n+\tcase BayerFormat::RGGB:\n+\tdefault:\n+\t\tred_shift_ = Point(1, 1);\n+\t\tbreak;\n+\t}\n+\n+\tif (outputCfg.size.width != width_ - 2 ||\n+\t    outputCfg.size.height != height_ - 2 ||\n+\t    outputCfg.stride != (width_ - 2) * 4 ||\n+\t    outputCfg.pixelFormat != formats::ARGB8888) {\n+\t\tLOG(Converter, Error)\n+\t\t\t<< \"Output format not supported\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tLOG(Converter, Info) << \"SwConverter configuration: \"\n+\t\t\t     << inputCfg.size << \"-\" << inputCfg.pixelFormat\n+\t\t\t     << \" -> \"\n+\t\t\t     << outputCfg.size << \"-\" << outputCfg.pixelFormat;\n+\n+\t/* set r/g/b gains to 1.0 until frame data collected */\n+\trNumerat_ = rDenomin_ = 1;\n+\tbNumerat_ = bDenomin_ = 1;\n+\tgNumerat_ = gDenomin_ = 1;\n+\n+\treturn 0;\n+}\n+\n+int SwConverter::exportBuffers(unsigned int output, unsigned int count,\n+\t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n+{\n+\t/* single output for now */\n+\tif (output >= 1)\n+\t\treturn -EINVAL;\n+\n+\treturn isp_->exportBuffers(count, buffers);\n+}\n+\n+int SwConverter::Isp::exportBuffers(unsigned int count,\n+\t\t\t\t    std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n+{\n+\t/* V4L2_PIX_FMT_ABGR32 aka BGR32 for output: */\n+\tunsigned int bufSize = (height_ - 2) * (width_ - 2) * 4;\n+\n+\tfor (unsigned int i = 0; i < count; i++) {\n+\t\tstd::string name = \"frame-\" + std::to_string(i);\n+\n+\t\tconst int ispFd = memfd_create(name.c_str(), 0);\n+\t\tint ret = ftruncate(ispFd, bufSize);\n+\t\tif (ret < 0) {\n+\t\t\tLOG(Converter, Error) << \"ftruncate() for memfd failed \"\n+\t\t\t\t\t      << strerror(-ret);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tFrameBuffer::Plane outPlane;\n+\t\toutPlane.fd = SharedFD(std::move(ispFd));\n+\t\toutPlane.offset = 0;\n+\t\toutPlane.length = bufSize;\n+\n+\t\tstd::vector<FrameBuffer::Plane> planes{ outPlane };\n+\t\tbuffers->emplace_back(std::make_unique<FrameBuffer>(std::move(planes)));\n+\t}\n+\n+\treturn count;\n+}\n+\n+int SwConverter::Isp::start()\n+{\n+\tmoveToThread(&thread_);\n+\tthread_.start();\n+\treturn 0;\n+}\n+\n+void SwConverter::Isp::stop()\n+{\n+\tthread_.exit();\n+\tthread_.wait();\n+}\n+\n+int SwConverter::start()\n+{\n+\treturn isp_->start();\n+}\n+\n+void SwConverter::stop()\n+{\n+\treturn isp_->stop();\n+}\n+\n+int SwConverter::queueBuffers(FrameBuffer *input,\n+\t\t\t      const std::map<unsigned int, FrameBuffer *> &outputs)\n+{\n+\tunsigned int mask = 0;\n+\n+\t/*\n+\t * Validate the outputs as a sanity check: at least one output is\n+\t * required, all outputs must reference a valid stream and no two\n+\t * outputs can reference the same stream.\n+\t */\n+\tif (outputs.empty())\n+\t\treturn -EINVAL;\n+\n+\tfor (auto [index, buffer] : outputs) {\n+\t\tif (!buffer)\n+\t\t\treturn -EINVAL;\n+\t\tif (index >= 1) /* only single stream atm */\n+\t\t\treturn -EINVAL;\n+\t\tif (mask & (1 << index))\n+\t\t\treturn -EINVAL;\n+\n+\t\tmask |= 1 << index;\n+\t}\n+\n+\tprocess(input, outputs.at(0));\n+\n+\treturn 0;\n+}\n+\n+void SwConverter::process(FrameBuffer *input, FrameBuffer *output)\n+{\n+\tisp_->invokeMethod(&SwConverter::Isp::process,\n+\t\t\t   ConnectionTypeQueued, input, output);\n+}\n+\n+void SwConverter::Isp::process(FrameBuffer *input, FrameBuffer *output)\n+{\n+\tconst uint8_t *rawData;\n+\tuint8_t *rgbData;\n+\n+\t/* Copy metadata from the input buffer */\n+\tFrameMetadata &metadata = output->_d()->metadata();\n+\tmetadata.status = input->metadata().status;\n+\tmetadata.sequence = input->metadata().sequence;\n+\tmetadata.timestamp = input->metadata().timestamp;\n+\n+\tconst FrameBuffer::Plane &plane = input->planes()[0];\n+\t/* TODO: use MappedBuffer class for mapping the frame buffers */\n+\trawData = (const uint8_t *)mmap(NULL, plane.length, PROT_READ,\n+\t\t\t\t\tMAP_SHARED, plane.fd.get(), 0);\n+\tconst FrameBuffer::Plane &rgbPlane = output->planes()[0];\n+\trgbData = (uint8_t *)mmap(NULL, rgbPlane.length, PROT_READ | PROT_WRITE,\n+\t\t\t\t  MAP_SHARED, rgbPlane.fd.get(), 0);\n+\tLOG(Converter, Debug) << \"raw length = \" << plane.length\n+\t\t\t      << \", rgb length = \" << rgbPlane.length;\n+\tif (rawData == MAP_FAILED || rgbData == MAP_FAILED) {\n+\t\tLOG(Converter, Error) << \"mmap-ing buffer(s) failed\";\n+\t\tmetadata.status = FrameMetadata::FrameError;\n+\t\tconverter_->outputBufferReady.emit(output);\n+\t\tconverter_->inputBufferReady.emit(input);\n+\t\treturn;\n+\t}\n+\n+\tdebayer(rgbData, rawData);\n+\n+\tmetadata.planes()[0].bytesused = rgbPlane.length;\n+\n+\tconverter_->outputBufferReady.emit(output);\n+\tconverter_->inputBufferReady.emit(input);\n+}\n+\n+void SwConverter::Isp::debayer(uint8_t *dst, const uint8_t *src)\n+{\n+\t/* RAW10P input format is assumed */\n+\n+\t/* output buffer is in BGR32 format and is of (width-2)*(height-2) */\n+\n+\tint w_out = width_ - 2;\n+\tint h_out = height_ - 2;\n+\n+\tunsigned long sumR = 0;\n+\tunsigned long sumB = 0;\n+\tunsigned long sumG = 0;\n+\n+\tfor (int y = 0; y < h_out; y++) {\n+\t\tconst uint8_t *pin_base = src + (y + 1) * stride_;\n+\t\tuint8_t *pout = dst + y * w_out * 4;\n+\t\tint phase_y = (y + red_shift_.y) % 2;\n+\n+\t\tfor (int x = 0; x < w_out; x++) {\n+\t\t\tint phase_x = (x + red_shift_.x) % 2;\n+\t\t\tint phase = 2 * phase_y + phase_x;\n+\n+\t\t\t/* x part of the offset in the input buffer: */\n+\t\t\tint x_m1 = x + x / 4;\t\t/* offset for (x-1) */\n+\t\t\tint x_0 = x + 1 + (x + 1) / 4;\t/* offset for x */\n+\t\t\tint x_p1 = x + 2 + (x + 2) / 4;\t/* offset for (x+1) */\n+\t\t\t/* the colour component value to write to the output */\n+\t\t\tunsigned val;\n+\n+\t\t\tswitch (phase) {\n+\t\t\tcase 0: /* at R pixel */\n+\t\t\t\t/* blue: ((-1,-1)+(1,-1)+(-1,1)+(1,1)) / 4 */\n+\t\t\t\tval = ( *(pin_base + x_m1 - stride_)\n+\t\t\t\t\t+ *(pin_base + x_p1 - stride_)\n+\t\t\t\t\t+ *(pin_base + x_m1 + stride_)\n+\t\t\t\t\t+ *(pin_base + x_p1 + stride_) ) >> 2;\n+\t\t\t\tval = val * bNumerat_ / bDenomin_;\n+\t\t\t\t*pout++ = (uint8_t)std::min(val, 0xffU);\n+\t\t\t\t/* green: ((0,-1)+(-1,0)+(1,0)+(0,1)) / 4 */\n+\t\t\t\tval = ( *(pin_base + x_0 - stride_)\n+\t\t\t\t\t+ *(pin_base + x_p1)\n+\t\t\t\t\t+ *(pin_base + x_m1)\n+\t\t\t\t\t+ *(pin_base + x_0 + stride_) ) >> 2;\n+\t\t\t\tval = val * gNumerat_ / gDenomin_;\n+\t\t\t\t*pout++ = (uint8_t)std::min(val, 0xffU);\n+\t\t\t\t/* red: (0,0) */\n+\t\t\t\tval = *(pin_base + x_0);\n+\t\t\t\tsumR += val;\n+\t\t\t\tval = val * rNumerat_ / rDenomin_;\n+\t\t\t\t*pout++ = (uint8_t)std::min(val, 0xffU);\n+\t\t\t\t/* alpha */\n+\t\t\t\t*pout++ = 0xff;\n+\t\t\t\tbreak;\n+\t\t\tcase 1: /* at Gr pixel */\n+\t\t\t\t/* blue: ((0,-1) + (0,1)) / 2 */\n+\t\t\t\tval = ( *(pin_base + x_0 - stride_)\n+\t\t\t\t\t+ *(pin_base + x_0 + stride_) ) >> 1;\n+\t\t\t\tval = val * bNumerat_ / bDenomin_;\n+\t\t\t\t*pout++ = (uint8_t)std::min(val, 0xffU);\n+\t\t\t\t/* green: (0,0) */\n+\t\t\t\tval = *(pin_base + x_0);\n+\t\t\t\tsumG += val;\n+\t\t\t\tval = val * gNumerat_ / gDenomin_;\n+\t\t\t\t*pout++ = (uint8_t)std::min(val, 0xffU);\n+\t\t\t\t/* red: ((-1,0) + (1,0)) / 2 */\n+\t\t\t\tval = ( *(pin_base + x_m1)\n+\t\t\t\t\t+ *(pin_base + x_p1) ) >> 1;\n+\t\t\t\tval = val * rNumerat_ / rDenomin_;\n+\t\t\t\t*pout++ = (uint8_t)std::min(val, 0xffU);\n+\t\t\t\t/* alpha */\n+\t\t\t\t*pout++ = 0xff;\n+\t\t\t\tbreak;\n+\t\t\tcase 2: /* at Gb pixel */\n+\t\t\t\t/* blue: ((-1,0) + (1,0)) / 2 */\n+\t\t\t\tval = ( *(pin_base + x_m1)\n+\t\t\t\t\t+ *(pin_base + x_p1) ) >> 1;\n+\t\t\t\tval = val * bNumerat_ / bDenomin_;\n+\t\t\t\t*pout++ = (uint8_t)std::min(val, 0xffU);\n+\t\t\t\t/* green: (0,0) */\n+\t\t\t\tval = *(pin_base + x_0);\n+\t\t\t\tsumG += val;\n+\t\t\t\tval = val * gNumerat_ / gDenomin_;\n+\t\t\t\t*pout++ = (uint8_t)std::min(val, 0xffU);\n+\t\t\t\t/* red: ((0,-1) + (0,1)) / 2 */\n+\t\t\t\tval = ( *(pin_base + x_0 - stride_)\n+\t\t\t\t\t+ *(pin_base + x_0 + stride_) ) >> 1;\n+\t\t\t\tval = val * rNumerat_ / rDenomin_;\n+\t\t\t\t*pout++ = (uint8_t)std::min(val, 0xffU);\n+\t\t\t\t/* alpha */\n+\t\t\t\t*pout++ = 0xff;\n+\t\t\t\tbreak;\n+\t\t\tdefault: /* at B pixel */\n+\t\t\t\t/* blue: (0,0) */\n+\t\t\t\tval = *(pin_base + x_0);\n+\t\t\t\tsumB += val;\n+\t\t\t\tval = val * bNumerat_ / bDenomin_;\n+\t\t\t\t*pout++ = (uint8_t)std::min(val, 0xffU);\n+\t\t\t\t/* green: ((0,-1)+(-1,0)+(1,0)+(0,1)) / 4 */\n+\t\t\t\tval = ( *(pin_base + x_0 - stride_)\n+\t\t\t\t\t+ *(pin_base + x_p1)\n+\t\t\t\t\t+ *(pin_base + x_m1)\n+\t\t\t\t\t+ *(pin_base + x_0 + stride_) ) >> 2;\n+\t\t\t\tval = val * gNumerat_ / gDenomin_;\n+\t\t\t\t*pout++ = (uint8_t)std::min(val, 0xffU);\n+\t\t\t\t/* red: ((-1,-1)+(1,-1)+(-1,1)+(1,1)) / 4 */\n+\t\t\t\tval = ( *(pin_base + x_m1 - stride_)\n+\t\t\t\t\t+ *(pin_base + x_p1 - stride_)\n+\t\t\t\t\t+ *(pin_base + x_m1 + stride_)\n+\t\t\t\t\t+ *(pin_base + x_p1 + stride_) ) >> 2;\n+\t\t\t\tval = val * rNumerat_ / rDenomin_;\n+\t\t\t\t*pout++ = (uint8_t)std::min(val, 0xffU);\n+\t\t\t\t/* alpha */\n+\t\t\t\t*pout++ = 0xff;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/* calculate red and blue gains for simple AWB */\n+\tLOG(Converter, Debug) << \"sumR = \" << sumR\n+\t\t\t      << \", sumB = \" << sumB << \", sumG = \" << sumG;\n+\n+\tsumG /= 2; /* the number of G pixels is twice as big vs R and B ones */\n+\n+\t/* normalize red, blue, and green sums to fit into 22-bit value */\n+\tunsigned long fRed = sumR / 0x400000;\n+\tunsigned long fBlue = sumB / 0x400000;\n+\tunsigned long fGreen = sumG / 0x400000;\n+\tunsigned long fNorm = std::max({ 1UL, fRed, fBlue, fGreen });\n+\tsumR /= fNorm;\n+\tsumB /= fNorm;\n+\tsumG /= fNorm;\n+\n+\tLOG(Converter, Debug) << \"fNorm = \" << fNorm;\n+\tLOG(Converter, Debug) << \"Normalized: sumR = \" << sumR\n+\t\t\t      << \", sumB= \" << sumB << \", sumG = \" << sumG;\n+\n+\t/* make sure red/blue gains never exceed approximately 256 */\n+\tunsigned long minDenom;\n+\trNumerat_ = (sumR + sumB + sumG) / 3;\n+\tminDenom = rNumerat_ / 0x100;\n+\trDenomin_ = std::max(minDenom, sumR);\n+\tbNumerat_ = rNumerat_;\n+\tbDenomin_ = std::max(minDenom, sumB);\n+\tgNumerat_ = rNumerat_;\n+\tgDenomin_ = std::max(minDenom, sumG);\n+\n+\tLOG(Converter, Debug) << \"rGain = [ \"\n+\t\t\t      << rNumerat_ << \" / \" << rDenomin_\n+\t\t\t      << \" ], bGain = [ \" << bNumerat_ << \" / \" << bDenomin_\n+\t\t\t      << \" ], gGain = [ \" << gNumerat_ << \" / \" << gDenomin_\n+\t\t\t      << \" (minDenom = \" << minDenom << \")\";\n+}\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/converter/meson.build b/src/libcamera/converter/meson.build\nindex 2aa72fe4..1f1e0ea4 100644\n--- a/src/libcamera/converter/meson.build\n+++ b/src/libcamera/converter/meson.build\n@@ -1,5 +1,6 @@\n # SPDX-License-Identifier: CC0-1.0\n \n libcamera_sources += files([\n-        'converter_v4l2_m2m.cpp'\n+        'converter_softw.cpp',\n+        'converter_v4l2_m2m.cpp',\n ])\n",
    "prefixes": [
        "libcamera-devel",
        "RFC",
        "1/2"
    ]
}