Patch Detail
Show a patch.
GET /api/patches/26589/?format=api
{ "id": 26589, "url": "https://patchwork.libcamera.org/api/patches/26589/?format=api", "web_url": "https://patchwork.libcamera.org/patch/26589/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/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": "<20260501105137.439519-2-maxbretschneider@protonmail.com>", "date": "2026-05-01T10:52:06", "name": "[RFC,v1,1/3] libcamera: pipeline: virtual: Add RawFrameGenerator", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "ccb4b723517fb331837ff1e6e082b7bd9ebf6106", "submitter": { "id": 266, "url": "https://patchwork.libcamera.org/api/people/266/?format=api", "name": "Max Bretschneider", "email": "maxbretschneider@protonmail.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/26589/mbox/", "series": [ { "id": 5892, "url": "https://patchwork.libcamera.org/api/series/5892/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5892", "date": "2026-05-01T10:52:02", "name": "libcamera: pipeline: virtual: Add raw Bayer frame support", "version": 1, "mbox": "https://patchwork.libcamera.org/series/5892/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/26589/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/26589/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 2445BC32F6\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 1 May 2026 10:52:13 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3A69062FE8;\n\tFri, 1 May 2026 12:52:11 +0200 (CEST)", "from mail-24431.protonmail.ch (mail-24431.protonmail.ch\n\t[109.224.244.31])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id DB13B62FB1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 1 May 2026 12:52:09 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=protonmail.com header.i=@protonmail.com\n\theader.b=\"tHHOeJyh\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com;\n\ts=protonmail3; t=1777632729; x=1777891929;\n\tbh=A2NHwKtarGnPMpJP6OwQv3aTHbgDJHF4mxqq8z6MZgQ=;\n\th=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References:\n\tFeedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID:\n\tMessage-ID:BIMI-Selector;\n\tb=tHHOeJyhq1+OVumfnlT6LrpJIWOPqrTdN/tr3aQMALjqzEAqRImMc+lCmrhmP3P5e\n\t2T/nAhCw117exUrJXN3SG/NIhzFtUZq83y9pe4LK6DILUdZIuIMi3yN342cb+FvRoG\n\tkTR912SetckG9a9lrTJKXJOFIPOKFKOAX83tR/CSwfvvqWRuwchpIg0sFynF6nT7Nl\n\twgfw9zwCzZlvbJChSBoIyLWQKIjLoAbNKM+YqqTJw9HbQrEahXVRJN0iBoLac47fag\n\tYt7CL9k4MGBTeIZZclaVWXotOyazzmZiG2814U2CDGBcAiIeQLe0TbetOC+/LuxUP6\n\ti6Og2+XnKDH7A==", "Date": "Fri, 01 May 2026 10:52:06 +0000", "To": "libcamera-devel@lists.libcamera.org", "From": "Max Bretschneider <maxbretschneider@protonmail.com>", "Cc": "Max Bretschneider <maxbretschneider@protonmail.com>", "Subject": "[RFC PATCH v1 1/3] libcamera: pipeline: virtual: Add\n\tRawFrameGenerator", "Message-ID": "<20260501105137.439519-2-maxbretschneider@protonmail.com>", "In-Reply-To": "<20260501105137.439519-1-maxbretschneider@protonmail.com>", "References": "<20260501105137.439519-1-maxbretschneider@protonmail.com>", "Feedback-ID": "122687743:user:proton", "X-Pm-Message-ID": "fc042eac895cfb37f83f64a2de269e9db950978e", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=utf-8", "Content-Transfer-Encoding": "quoted-printable", "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>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Added a new FrameGenerator subclass which reads binary raw Bayer\nfiles from disk and copies them into the output FrameBuffers unchanged.\nNo format conversion is performed. Input files must be plain binary\nsensor dumps without a header, and they must match the declared\nbayer_order and bit_depht. Modelled on ImageFrameGenerator.\n\nSigned-off-by: Max Bretschneider <maxbretschneider@protonmail.com>\n---\n src/libcamera/pipeline/virtual/meson.build | 1 +\n .../pipeline/virtual/raw_frame_generator.cpp | 131 ++++++++++++++++++\n .../pipeline/virtual/raw_frame_generator.h | 47 +++++++\n 3 files changed, 179 insertions(+)\n create mode 100644 src/libcamera/pipeline/virtual/raw_frame_generator.cpp\n create mode 100644 src/libcamera/pipeline/virtual/raw_frame_generator.h\n\n--\n2.43.0", "diff": "diff --git a/src/libcamera/pipeline/virtual/meson.build b/src/libcamera/pipeline/virtual/meson.build\nindex c8434593..c6576142 100644\n--- a/src/libcamera/pipeline/virtual/meson.build\n+++ b/src/libcamera/pipeline/virtual/meson.build\n@@ -3,6 +3,7 @@\n libcamera_internal_sources += files([\n 'config_parser.cpp',\n 'image_frame_generator.cpp',\n+ 'raw_frame_generator.cpp',\n 'test_pattern_generator.cpp',\n 'virtual.cpp',\n ])\ndiff --git a/src/libcamera/pipeline/virtual/raw_frame_generator.cpp b/src/libcamera/pipeline/virtual/raw_frame_generator.cpp\nnew file mode 100644\nindex 00000000..e0be28cf\n--- /dev/null\n+++ b/src/libcamera/pipeline/virtual/raw_frame_generator.cpp\n@@ -0,0 +1,131 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026, max.bretschneider@leica-geosystems.com\n+ *\n+ * Derived class of FrameGenerator for generating raw Bayer frames\n+ */\n+\n+#include \"raw_frame_generator.h\"\n+\n+#include <errno.h>\n+#include <string.h>\n+\n+#include <libcamera/base/file.h>\n+#include <libcamera/base/log.h>\n+\n+#include <libcamera/framebuffer.h>\n+\n+#include \"libcamera/internal/mapped_framebuffer.h\"\n+\n+namespace libcamera {\n+\n+LOG_DECLARE_CATEGORY(Virtual)\n+\n+/*\n+ * Factory function to create a RawFrameGenerator object.\n+ * Read the raw Bayer frames from disk and store them in memory.\n+ */\n+std::unique_ptr<RawFrameGenerator>\n+RawFrameGenerator::create(RawFrames &rawFrames)\n+{\n+\tstd::unique_ptr<RawFrameGenerator> rawFrameGenerator =\n+\t\tstd::make_unique<RawFrameGenerator>();\n+\n+\t/*\n+\t * For each file in the directory, load the raw frame\n+\t * and store it. No format conversion is performed, but\n+\t * raw Bayer bytes are stored as-is.\n+\t */\n+\tfor (const auto &path : rawFrames.files) {\n+\t\tFile file(path);\n+\t\tif (!file.open(File::OpenModeFlag::ReadOnly)) {\n+\t\t\tLOG(Virtual, Error) << \"Failed to open raw frame file \"\n+\t\t\t\t\t << file.fileName()\n+\t\t\t\t\t << \": \" << strerror(file.error());\n+\t\t\treturn nullptr;\n+\t\t}\n+\n+\t\tauto fileSize = file.size();\n+\t\tauto buffer = std::make_unique<uint8_t[]>(fileSize);\n+\t\tif (file.read({ buffer.get(), static_cast<size_t>(fileSize) }) != fileSize) {\n+\t\t\tLOG(Virtual, Error) << \"Failed to read raw frame file \"\n+\t\t\t\t\t << file.fileName()\n+\t\t\t\t\t << \": \" << strerror(file.error());\n+\t\t\treturn nullptr;\n+\t\t}\n+\n+\t\trawFrameGenerator->framesDatas_.emplace_back(\n+\t\t\tRawFrameData{ std::move(buffer), static_cast<size_t>(fileSize) });\n+\t}\n+\n+\tASSERT(!rawFrameGenerator->framesDatas_.empty());\n+\n+\treturn rawFrameGenerator;\n+}\n+\n+void RawFrameGenerator::configure(const Size & /*size*/)\n+{\n+\t/*\n+\t * Raw frames cannot be scaled, the configured size is not used for\n+\t * processing but mismatches are caught in generateFrame().\n+\t */\n+\tframeIndex_ = 0;\n+\tparameter_ = 0;\n+}\n+\n+int RawFrameGenerator::generateFrame(const Size & /*size*/, const FrameBuffer *buffer)\n+{\n+\tASSERT(!framesDatas_.empty());\n+\n+\tMappedFrameBuffer mappedFrameBuffer(buffer,\n+\t\t\t\t\t MappedFrameBuffer::MapFlag::Write);\n+\n+\tconst auto &planes = mappedFrameBuffer.planes();\n+\n+\t/* Loop only around the number of frames available */\n+\tframeIndex_ %= framesDatas_.size();\n+\n+\tconst auto &frame = framesDatas_[frameIndex_];\n+\n+\t/*\n+\t * Raw Bayer frames must exactly match the configured output size.\n+\t * They cannot be scaled to fit.\n+\t */\n+\tif (frame.size != planes[0].size()) {\n+\t\tLOG(Virtual, Error) << \"Raw frame size mismatch: file has \"\n+\t\t\t\t << frame.size << \" bytes, buffer expects \"\n+\t\t\t\t << planes[0].size() << \" bytes\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tmemcpy(planes[0].data(), frame.data.get(), frame.size);\n+\n+\t/* Proceed to the next frame on every request */\n+\tparameter_++;\n+\tif (parameter_ % frameRepeat == 0) {\n+\t\tframeIndex_++;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * \\var RawFrameGenerator::frameRepeat\n+ * \\brief Number of frames to repeat before proceeding to the next frame\n+ */\n+\n+/*\n+ * \\var RawFrameGenerator::framesDatas_\n+ * \\brief List of raw Bayer frame buffers loaded from disk\n+ */\n+\n+/* \\var RawFrameGenerator::frameIndex_\n+ * \\brief Index of the current frame in framesDatas_\n+ */\n+\n+/*\n+ * \\var RawFrameGenerator::parameter_\n+ * \\brief Counter used to implement frameRepeat behaviour\n+ */\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/pipeline/virtual/raw_frame_generator.h b/src/libcamera/pipeline/virtual/raw_frame_generator.h\nnew file mode 100644\nindex 00000000..d711a47d\n--- /dev/null\n+++ b/src/libcamera/pipeline/virtual/raw_frame_generator.h\n@@ -0,0 +1,47 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026, max.bretschneider@leica-geosystems.com\n+ *\n+ * Raw Bayer frame generator for the virtual pipeline handler\n+ */\n+\n+#pragma once\n+\n+#include <filesystem>\n+#include <memory>\n+#include <stdint.h>\n+#include <vector>\n+\n+#include \"frame_generator.h\"\n+\n+namespace libcamera {\n+\n+/* Frame configuration provided by the config file */\n+struct RawFrames {\n+\tstd::vector<std::filesystem::path> files;\n+\tuint32_t cfaPattern;\n+\tunsigned int bitDepth;\n+};\n+\n+class RawFrameGenerator : public FrameGenerator\n+{\n+public:\n+\tstatic std::unique_ptr<RawFrameGenerator> create(RawFrames &rawFrames);\n+\n+private:\n+\tstatic constexpr unsigned int frameRepeat = 1; /*advance every frame*/\n+\n+\tstruct RawFrameData {\n+\t\tstd::unique_ptr<uint8_t[]> data;\n+\t\tsize_t size;\n+\t};\n+\n+\tvoid configure(const Size &size) override;\n+\tint generateFrame(const Size &size, const FrameBuffer *buffer) override;\n+\n+\tstd::vector<RawFrameData> framesDatas_;\n+\tunsigned int frameIndex_;\n+\tunsigned int parameter_;\n+};\n+\n+} /* namespace libcamera */\n", "prefixes": [ "RFC", "v1", "1/3" ] }