Patch Detail
Show a patch.
GET /api/1.1/patches/18981/?format=api
{ "id": 18981, "url": "https://patchwork.libcamera.org/api/1.1/patches/18981/?format=api", "web_url": "https://patchwork.libcamera.org/patch/18981/", "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": "<20230910175027.23384-3-andrey.konovalov@linaro.org>", "date": "2023-09-10T17:50:26", "name": "[libcamera-devel,RFC,2/3] libcamera: converter: add software converter", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "e1df272fce7dbeec8501c7554d175aa0dd60dfd9", "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/18981/mbox/", "series": [ { "id": 4014, "url": "https://patchwork.libcamera.org/api/1.1/series/4014/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4014", "date": "2023-09-10T17:50:24", "name": "libcamera: converter: generalize Converter to remove MediaDevice dependency", "version": 1, "mbox": "https://patchwork.libcamera.org/series/4014/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/18981/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/18981/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 2CFE0C3260\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 10 Sep 2023 17:52:12 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5E97B628F1;\n\tSun, 10 Sep 2023 19:52:11 +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 8B792628E7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 10 Sep 2023 19:52:10 +0200 (CEST)", "by mail-ej1-x633.google.com with SMTP id\n\ta640c23a62f3a-9a63b2793ecso461580966b.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 10 Sep 2023 10:52:10 -0700 (PDT)", "from Lat-5310.. ([87.116.166.126]) by smtp.gmail.com with ESMTPSA\n\tid\n\tkd27-20020a17090798db00b009a57d30df89sm4090089ejc.132.2023.09.10.10.52.09\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tSun, 10 Sep 2023 10:52:09 -0700 (PDT)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1694368331;\n\tbh=l7PfTNOR6AaHQ3ZDSpXlVuPFfR+LlKy+UHMxbQHeioI=;\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=fukOmB+lB/LAENYO5Qd3QR/L5h8i0xsLaXa5d/A6kk22fpRGLmrclh62nwZMDdvaJ\n\tM4OFGY8z/jc3BHCh+hqQbFuaTo7K2kPNfgm+pF5VZSpBlcy1XndPjlkZCo7qNGC7o5\n\tKKpMw2o+umGJ/nA6uOUlJ9/mPTjFD2Np6BPwkREeecVG7daXNC5RA4jaFiny90lnXM\n\tGl1KAO0zfXztfSgy4bKHb2vREok1/xEACXcnqXhAHvPhM1DdMlmto/DAmrtAj7rENW\n\tz7W9Tkrf+ORTkvL9/DxfOXfm3njN/wrfYZvDkuo6/opNuOoaHPlt8he68hTtYDA1iO\n\thaDUfEUDBFRtA==", "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=linaro.org; s=google; t=1694368330; x=1694973130;\n\tdarn=lists.libcamera.org; \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=xvWNpNkJSK8RV8XrpOdADEp8LJKAmGEfU5DdngUjYV0=;\n\tb=GsMLlNJEiyBAjCmAlKuK3MyHoBPOCEy725Anu+1cWCUu2vrL+C9bXFecm7rdGgOWxg\n\tDohSZuGh7CYqd+wFbEo+xonRmoKtWmvGsqwk3QR/hNhPRcsmnab8Scl9Ht9EOOdguMgZ\n\tPSHV3i59RS6tiTyUHik9WKOx+uqWEMLgSOyEc8cw1ysJkbiNrRcX+jjUBaQs8SdCMtjg\n\tdhA780Olol/rB+haqXna2bPPpvhwRfiRM1AsqNc3dSCdWyUnApcFKGQJtT8DrtpiKm5M\n\ti+Au9zEGfrxvSW5JGNHUroflkMrg4IXrT7vUqiWzBdfEg28GrqAFg8SootcuDyFWxpfw\n\tjopA==" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=linaro.org\n\theader.i=@linaro.org header.b=\"GsMLlNJE\"; \n\tdkim-atps=neutral", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1694368330; x=1694973130;\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=xvWNpNkJSK8RV8XrpOdADEp8LJKAmGEfU5DdngUjYV0=;\n\tb=mZ0VKgBi5HBQsMCNnM5L+fJVRvy5RMNdrTFgwegmmMRTWVxYuhq1b+NIBKsr3Gm6ta\n\tIJxm1LxDDSc1v6YK81u4yZIvtRCkKBWvr+QC53yOfI9Bd2z1GcOxfJ3XxP829JsnOsVu\n\tGU1t4UYXwsubzl08itvwTl2gdus86E/rnAAa7ZcU4Jbipnat5Cd3inXbAGjrFXe1nkz3\n\t0o7TVfZklEsW/J3ZYTuYvLX64dex16Rc2LxjE9xsQBRlKi/30HcS2syyygb8+ZvyjnqZ\n\tIc6GehtNgD0GvL+QSrSpCnNPslWA1bvcb187QETplWJN4X2DFzd0a1cqEBeIxtertwAR\n\tSUXw==", "X-Gm-Message-State": "AOJu0Yz0OOqz64m2XxWiL4CGhzJK8o70NERG7oExE3Nc6g5z1nbr1GVA\n\tIngJ0I1bHPkFBvdj/gAzPeQMaNNKFc3CggBzjCs=", "X-Google-Smtp-Source": "AGHT+IGoGtTq8Ale+IbJWOJAQbxJ70dyNhzkoMMSbNOup/PI5oaNHgPRbhn7ENaxskvD0DzUAb9zOA==", "X-Received": "by 2002:a17:906:10dc:b0:9a5:852f:10ac with SMTP id\n\tv28-20020a17090610dc00b009a5852f10acmr6853976ejv.53.1694368330109; \n\tSun, 10 Sep 2023 10:52:10 -0700 (PDT)", "To": "libcamera-devel@lists.libcamera.org", "Date": "Sun, 10 Sep 2023 20:50:26 +0300", "Message-Id": "<20230910175027.23384-3-andrey.konovalov@linaro.org>", "X-Mailer": "git-send-email 2.34.1", "In-Reply-To": "<20230910175027.23384-1-andrey.konovalov@linaro.org>", "References": "<20230910175027.23384-1-andrey.konovalov@linaro.org>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [RFC PATCH 2/3] 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": "jacopo.mondi@ideasonboard.com, bryan.odonoghue@linaro.org,\n\tsrinivas.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\nThe only output format currently supported is RGB888.\n\nSigned-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>\n---\n .../internal/converter/converter_softw.h | 88 ++++\n .../libcamera/internal/converter/meson.build | 1 +\n src/libcamera/converter/converter_softw.cpp | 412 ++++++++++++++++++\n src/libcamera/converter/meson.build | 3 +-\n 4 files changed, 503 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..aab1a650\n--- /dev/null\n+++ b/include/libcamera/internal/converter/converter_softw.h\n@@ -0,0 +1,88 @@\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 Size;\n+class SizeRange;\n+struct StreamConfiguration;\n+\n+class SwConverter : public Converter\n+{\n+public:\n+\tSwConverter() : Converter() {}\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/include/libcamera/internal/converter/meson.build b/include/libcamera/internal/converter/meson.build\nindex 891e79e7..843a0483 100644\n--- a/include/libcamera/internal/converter/meson.build\n+++ b/include/libcamera/internal/converter/meson.build\n@@ -1,5 +1,6 @@\n # SPDX-License-Identifier: CC0-1.0\n \n libcamera_internal_headers += files([\n+ 'converter_softw.h',\n 'converter_v4l2_m2m.h',\n ])\ndiff --git a/src/libcamera/converter/converter_softw.cpp b/src/libcamera/converter/converter_softw.cpp\nnew file mode 100644\nindex 00000000..c66400b0\n--- /dev/null\n+++ b/src/libcamera/converter/converter_softw.cpp\n@@ -0,0 +1,412 @@\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+#include \"libcamera/internal/mapped_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::RGB888);\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 RGB888 output is currently supported */\n+\tif (pixelFormat == formats::RGB888) {\n+\t\tint stride = size.width * 3;\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) * 3 ||\n+\t outputCfg.pixelFormat != formats::RGB888) {\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_BGR24 aka 'BGR3' for output: */\n+\tunsigned int bufSize = (height_ - 2) * (width_ - 2) * 3;\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\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+\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+\tMappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);\n+\tMappedFrameBuffer out(output, MappedFrameBuffer::MapFlag::Write);\n+\tif (!in.isValid() || !out.isValid()) {\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(out.planes()[0].data(), in.planes()[0].data());\n+\tmetadata.planes()[0].bytesused = out.planes()[0].size();\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 BGR24 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 * 3;\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\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\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\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}\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", "2/3" ] }