From patchwork Mon May 11 20:23:43 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Bretschneider X-Patchwork-Id: 26721 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 24C21BDCBD for ; Mon, 11 May 2026 20:23:52 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BF8E863030; Mon, 11 May 2026 22:23:51 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=protonmail.com header.i=@protonmail.com header.b="rhb7LThS"; dkim-atps=neutral Received: from mail-4316.protonmail.ch (mail-4316.protonmail.ch [185.70.43.16]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C341963021 for ; Mon, 11 May 2026 22:23:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=protonmail3; t=1778531029; x=1778790229; bh=peBw+jWE9ZWUT6bsesMlaUiHFLcrmfAFAta+Czgmol4=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=rhb7LThSpFoIXbnnmLw7+ZSV++F45CbgQ+adJogGkCO4jvh6cKSlzZX2RMqvMoW4F 2hkLoygRG30KhPp0C86m955VqcHX0Kryl63msoSTXZz9DpybqbfaznNt7vjk7pgWaE DKaSSPbL2576BbJmu7Els+Z7CjLMolL9kPqfm9N4t5SGBSdwQNBhpusOu+YxWeOWB3 HrfJe+fMJJtk/Bp7FUgXil56LPJpCiZVNI1psmIa45L7g2MltHHuv517GHVXivBXgt QtctVkKT4eyYRG9FNahEb5wwrD9Kv8laA9J/dfiYwdVnHUfsVeVWjqTkWPlNgx8vYp XwG9PMN/abo9g== Date: Mon, 11 May 2026 20:23:43 +0000 To: libcamera-devel@lists.libcamera.org From: Max Bretschneider Cc: Max Bretschneider Subject: [RFC PATCH v3 1/2] libcamera: pipeline: virtual: Add RawFrameGenerator Message-ID: <20260511202327.40191-2-maxbretschneider@protonmail.com> In-Reply-To: <20260511202327.40191-1-maxbretschneider@protonmail.com> References: <20260501105137.439519-1-maxbretschneider@protonmail.com> <20260504091623.3354474-1-maxbretschneider@protonmail.com> <20260511202327.40191-1-maxbretschneider@protonmail.com> Feedback-ID: 122687743:user:proton X-Pm-Message-ID: 814d31fb981abff955e4104fe2f034b5ed47c871 MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Added a new FrameGenerator subclass which reads plain binary raw Bayer files from disk and copies them into the output FrameBuffers unchanged. No format conversion is performed. Input files must be plain binary sensor dumps without a header, and they must match the declared bayer_order and bit_depth. Modelled on ImageFrameGenerator. Signed-off-by: Max Bretschneider --- src/libcamera/pipeline/virtual/meson.build | 1 + .../pipeline/virtual/raw_frame_generator.cpp | 131 ++++++++++++++++++ .../pipeline/virtual/raw_frame_generator.h | 47 +++++++ 3 files changed, 179 insertions(+) create mode 100644 src/libcamera/pipeline/virtual/raw_frame_generator.cpp create mode 100644 src/libcamera/pipeline/virtual/raw_frame_generator.h diff --git a/src/libcamera/pipeline/virtual/meson.build b/src/libcamera/pipeline/virtual/meson.build index c8434593..c6576142 100644 --- a/src/libcamera/pipeline/virtual/meson.build +++ b/src/libcamera/pipeline/virtual/meson.build @@ -3,6 +3,7 @@ libcamera_internal_sources += files([ 'config_parser.cpp', 'image_frame_generator.cpp', + 'raw_frame_generator.cpp', 'test_pattern_generator.cpp', 'virtual.cpp', ]) diff --git a/src/libcamera/pipeline/virtual/raw_frame_generator.cpp b/src/libcamera/pipeline/virtual/raw_frame_generator.cpp new file mode 100644 index 00000000..e0be28cf --- /dev/null +++ b/src/libcamera/pipeline/virtual/raw_frame_generator.cpp @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, max.bretschneider@leica-geosystems.com + * + * Derived class of FrameGenerator for generating raw Bayer frames + */ + +#include "raw_frame_generator.h" + +#include +#include + +#include +#include + +#include + +#include "libcamera/internal/mapped_framebuffer.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(Virtual) + +/* + * Factory function to create a RawFrameGenerator object. + * Read the raw Bayer frames from disk and store them in memory. + */ +std::unique_ptr +RawFrameGenerator::create(RawFrames &rawFrames) +{ + std::unique_ptr rawFrameGenerator = + std::make_unique(); + + /* + * For each file in the directory, load the raw frame + * and store it. No format conversion is performed, but + * raw Bayer bytes are stored as-is. + */ + for (const auto &path : rawFrames.files) { + File file(path); + if (!file.open(File::OpenModeFlag::ReadOnly)) { + LOG(Virtual, Error) << "Failed to open raw frame file " + << file.fileName() + << ": " << strerror(file.error()); + return nullptr; + } + + auto fileSize = file.size(); + auto buffer = std::make_unique(fileSize); + if (file.read({ buffer.get(), static_cast(fileSize) }) != fileSize) { + LOG(Virtual, Error) << "Failed to read raw frame file " + << file.fileName() + << ": " << strerror(file.error()); + return nullptr; + } + + rawFrameGenerator->framesDatas_.emplace_back( + RawFrameData{ std::move(buffer), static_cast(fileSize) }); + } + + ASSERT(!rawFrameGenerator->framesDatas_.empty()); + + return rawFrameGenerator; +} + +void RawFrameGenerator::configure(const Size & /*size*/) +{ + /* + * Raw frames cannot be scaled, the configured size is not used for + * processing but mismatches are caught in generateFrame(). + */ + frameIndex_ = 0; + parameter_ = 0; +} + +int RawFrameGenerator::generateFrame(const Size & /*size*/, const FrameBuffer *buffer) +{ + ASSERT(!framesDatas_.empty()); + + MappedFrameBuffer mappedFrameBuffer(buffer, + MappedFrameBuffer::MapFlag::Write); + + const auto &planes = mappedFrameBuffer.planes(); + + /* Loop only around the number of frames available */ + frameIndex_ %= framesDatas_.size(); + + const auto &frame = framesDatas_[frameIndex_]; + + /* + * Raw Bayer frames must exactly match the configured output size. + * They cannot be scaled to fit. + */ + if (frame.size != planes[0].size()) { + LOG(Virtual, Error) << "Raw frame size mismatch: file has " + << frame.size << " bytes, buffer expects " + << planes[0].size() << " bytes"; + return -EINVAL; + } + + memcpy(planes[0].data(), frame.data.get(), frame.size); + + /* Proceed to the next frame on every request */ + parameter_++; + if (parameter_ % frameRepeat == 0) { + frameIndex_++; + } + + return 0; +} + +/* + * \var RawFrameGenerator::frameRepeat + * \brief Number of frames to repeat before proceeding to the next frame + */ + +/* + * \var RawFrameGenerator::framesDatas_ + * \brief List of raw Bayer frame buffers loaded from disk + */ + +/* \var RawFrameGenerator::frameIndex_ + * \brief Index of the current frame in framesDatas_ + */ + +/* + * \var RawFrameGenerator::parameter_ + * \brief Counter used to implement frameRepeat behaviour + */ + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/virtual/raw_frame_generator.h b/src/libcamera/pipeline/virtual/raw_frame_generator.h new file mode 100644 index 00000000..d711a47d --- /dev/null +++ b/src/libcamera/pipeline/virtual/raw_frame_generator.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, max.bretschneider@leica-geosystems.com + * + * Raw Bayer frame generator for the virtual pipeline handler + */ + +#pragma once + +#include +#include +#include +#include + +#include "frame_generator.h" + +namespace libcamera { + +/* Frame configuration provided by the config file */ +struct RawFrames { + std::vector files; + uint32_t cfaPattern; + unsigned int bitDepth; +}; + +class RawFrameGenerator : public FrameGenerator +{ +public: + static std::unique_ptr create(RawFrames &rawFrames); + +private: + static constexpr unsigned int frameRepeat = 1; /*advance every frame*/ + + struct RawFrameData { + std::unique_ptr data; + size_t size; + }; + + void configure(const Size &size) override; + int generateFrame(const Size &size, const FrameBuffer *buffer) override; + + std::vector framesDatas_; + unsigned int frameIndex_; + unsigned int parameter_; +}; + +} /* namespace libcamera */ From patchwork Mon May 11 20:23:48 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Bretschneider X-Patchwork-Id: 26722 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 50DEBBDCBD for ; Mon, 11 May 2026 20:23:55 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F1B866302A; Mon, 11 May 2026 22:23:54 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=protonmail.com header.i=@protonmail.com header.b="EA/tfYFa"; dkim-atps=neutral Received: from mail-24417.protonmail.ch (mail-24417.protonmail.ch [109.224.244.17]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 28B3963020 for ; Mon, 11 May 2026 22:23:53 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=protonmail3; t=1778531031; x=1778790231; bh=rGG8gQYP/tCPtHEagfHZLlJ2ekYYmlM6YBiRJIyo9ww=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=EA/tfYFaGIKS7a+A+9mX1gWsiN/KJAltS66JBdryIZXnPv0oAiqXkx1cguEN/tEtL q/Dow0eGduBg1oYQ6m0bAllO0jnLmDJ1kKY7m2mp8wbMj9ifUcxQBg7m8zCh+74+/r jGE79WoD8Xxd4atjBSUSi+AAcWAHNirCyZA15gknt+sYIWQpL6mHky1HJIPtsfaA5i AkWONaBXD0PsUr8qkhNozH+GjWySdwiGmOXpokcWwGgzr+NhbGIe64XwJAEizNftQG MvOa1oRCpRXJsnKyueWe6s8PmDFFgSgChaa0b4/90jkOcfe5ukKDZ+VKhFuxF/gExU ZSOzR/OJvNkWQ== Date: Mon, 11 May 2026 20:23:48 +0000 To: libcamera-devel@lists.libcamera.org From: Max Bretschneider Cc: Max Bretschneider Subject: [RFC PATCH v3 2/2] libcamera: pipeline: virtual: Add raw_frames config and Bayer format support Message-ID: <20260511202327.40191-3-maxbretschneider@protonmail.com> In-Reply-To: <20260511202327.40191-1-maxbretschneider@protonmail.com> References: <20260501105137.439519-1-maxbretschneider@protonmail.com> <20260504091623.3354474-1-maxbretschneider@protonmail.com> <20260511202327.40191-1-maxbretschneider@protonmail.com> Feedback-ID: 122687743:user:proton X-Pm-Message-ID: 42b4aab9f27bf004d0f2731d2526cda323c4247a MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Extend parseFrameGenerator() to handle the new raw_frames YAML key alongside the existing test_pattern and frames keys. File collection logic (e.g. either single file or directory with natural sorting) follows the existing frames path handling. This raw_frames block accepts: - path (single file or directory) - bayer_order (RGGB, BGGR, GRBG, GBRG) - bit_depth (8, 10, 12, 14, 16), in accordance to libcamera/formats.yaml Extend generateConfiguration() to return a Bayer StreamConfiguration when the StreamRole::Raw is requested on a camera configured with raw_frames. Extend validate() to allow Bayer formats, also set the correct stride and frameSize for single plane Bayer buffers. Signed-off-by: Max Bretschneider --- .../pipeline/virtual/config_parser.cpp | 156 ++++++++++++++---- src/libcamera/pipeline/virtual/virtual.cpp | 99 +++++++++-- src/libcamera/pipeline/virtual/virtual.h | 3 +- 3 files changed, 210 insertions(+), 48 deletions(-) diff --git a/src/libcamera/pipeline/virtual/config_parser.cpp b/src/libcamera/pipeline/virtual/config_parser.cpp index 5169fd39..12249c37 100644 --- a/src/libcamera/pipeline/virtual/config_parser.cpp +++ b/src/libcamera/pipeline/virtual/config_parser.cpp @@ -7,6 +7,7 @@ #include "config_parser.h" +#include #include #include @@ -24,6 +25,38 @@ namespace libcamera { LOG_DECLARE_CATEGORY(Virtual) +namespace { + +std::optional> collectFiles(const std::string &path) +{ + std::vector files; + + switch (std::filesystem::symlink_status(path).type()) { + case std::filesystem::file_type::regular: + files.push_back(path); + break; + + case std::filesystem::file_type::directory: + for (const auto &dentry : std::filesystem::directory_iterator{ path }) + if (dentry.is_regular_file()) + files.push_back(dentry.path()); + + std::sort(files.begin(), files.end(), [](const auto &a, const auto &b) { + return ::strverscmp(a.c_str(), b.c_str()) < 0; + }); + + if (files.empty()) + return std::nullopt; + break; + + default: + return std::nullopt; + } + return files; +} + +} /* namespace */ + std::vector> ConfigParser::parseConfigFile(File &file, PipelineHandler *pipe) { @@ -156,13 +189,20 @@ int ConfigParser::parseFrameGenerator(const ValueNode &cameraConfigData, Virtual { const std::string testPatternKey = "test_pattern"; const std::string framesKey = "frames"; - if (cameraConfigData.contains(testPatternKey)) { - if (cameraConfigData.contains(framesKey)) { - LOG(Virtual, Error) << "A camera should use either " - << testPatternKey << " or " << framesKey; - return -EINVAL; - } + const std::string rawFramesKey = "raw_frames"; + + /* Ensure only one frame source is specified */ + int sourcesSpecified = cameraConfigData.contains(testPatternKey) + + cameraConfigData.contains(framesKey) + + cameraConfigData.contains(rawFramesKey); + if (sourcesSpecified > 1) { + LOG(Virtual, Error) << "A camera should use only one of " + << testPatternKey << ", " << framesKey + << ", or " << rawFramesKey; + return -EINVAL; + } + if (cameraConfigData.contains(testPatternKey)) { auto testPattern = cameraConfigData[testPatternKey].get(""); if (testPattern == "bars") { @@ -178,6 +218,75 @@ int ConfigParser::parseFrameGenerator(const ValueNode &cameraConfigData, Virtual return 0; } + if (cameraConfigData.contains(rawFramesKey)) { + const ValueNode &rawFrames = cameraConfigData[rawFramesKey]; + + if (!rawFrames.isDictionary()) { + LOG(Virtual, Error) << "'raw_frames' is not a dictionary."; + return -EINVAL; + } + + auto path = rawFrames["path"].get(); + if (!path) { + LOG(Virtual, Error) << "raw_frames: path must be specified."; + return -EINVAL; + } + + auto type = std::filesystem::symlink_status(*path).type(); + if (type != std::filesystem::file_type::regular && + type != std::filesystem::file_type::directory) { + LOG(Virtual, Error) << "raw_frames path: " << *path << " is not supported"; + return -EINVAL; + } + + auto files = collectFiles(*path); + + if (!files) { + LOG(Virtual, Error) << "raw_frames directory has no files: " << *path; + return -EINVAL; + } + + /* Parse bayer_order */ + auto bayerOrder = rawFrames["bayer_order"].get(); + if (!bayerOrder) { + LOG(Virtual, Error) << "raw_frames: bayer_order must be specified."; + return -EINVAL; + } + + static const std::map bayerOrderMap = { + { "RGGB", properties::draft::ColorFilterArrangementEnum::RGGB }, + { "BGGR", properties::draft::ColorFilterArrangementEnum::BGGR }, + { "GRBG", properties::draft::ColorFilterArrangementEnum::GRBG }, + { "GBRG", properties::draft::ColorFilterArrangementEnum::GBRG }, + }; + + auto it = bayerOrderMap.find(*bayerOrder); + if (it == bayerOrderMap.end()) { + LOG(Virtual, Error) << "raw_frames: unsupported bayer_order: " + << *bayerOrder + << ", must be one of RGGB, BGGR, GRBG, GBRG"; + return -EINVAL; + } + + /* Parse bit_depth */ + auto bitDepth = rawFrames["bit_depth"].get(); + if (!bitDepth) { + LOG(Virtual, Error) << "raw_frames: bit_depth must be specified."; + return -EINVAL; + } + + static const std::set supportedBitDepths = { 8, 10, 12, 14, 16 }; + if (supportedBitDepths.find(*bitDepth) == supportedBitDepths.end()) { + LOG(Virtual, Error) << "raw_frames: bit_depth unsupported: " << *bitDepth + << ", must be one of 8, 10, 12, 14, 16"; + return -EINVAL; + } + + data->config_.frame = RawFrames{ std::move(*files), it->second, *bitDepth }; + + return 0; + } + const ValueNode &frames = cameraConfigData[framesKey]; /* When there is no frames provided in the config file, use color bar test pattern */ @@ -198,35 +307,20 @@ int ConfigParser::parseFrameGenerator(const ValueNode &cameraConfigData, Virtual return -EINVAL; } - std::vector files; - - switch (std::filesystem::symlink_status(*path).type()) { - case std::filesystem::file_type::regular: - files.push_back(*path); - break; - - case std::filesystem::file_type::directory: - for (const auto &dentry : std::filesystem::directory_iterator{ *path }) { - if (dentry.is_regular_file()) - files.push_back(dentry.path()); - } - - std::sort(files.begin(), files.end(), [](const auto &a, const auto &b) { - return ::strverscmp(a.c_str(), b.c_str()) < 0; - }); - - if (files.empty()) { - LOG(Virtual, Error) << "Directory has no files: " << *path; - return -EINVAL; - } - break; - - default: + auto type = std::filesystem::symlink_status(*path).type(); + if (type != std::filesystem::file_type::regular && + type != std::filesystem::file_type::directory) { LOG(Virtual, Error) << "Frame: " << *path << " is not supported"; return -EINVAL; } - data->config_.frame = ImageFrames{ std::move(files) }; + auto files = collectFiles(*path); + if (!files) { + LOG(Virtual, Error) << "Directory has no files: " << *path; + return -EINVAL; + } + + data->config_.frame = ImageFrames{ std::move(*files) }; return 0; } diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp index 81d2ddda..4ef72c2c 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -31,13 +32,13 @@ #include #include +#include "libcamera/internal/bayer_format.h" #include "libcamera/internal/camera.h" #include "libcamera/internal/dma_buf_allocator.h" #include "libcamera/internal/formats.h" #include "libcamera/internal/framebuffer.h" #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/request.h" -#include "libcamera/internal/value_node.h" #include "pipeline/virtual/config_parser.h" @@ -202,21 +203,28 @@ CameraConfiguration::Status VirtualCameraConfiguration::validate() adjusted = true; } - if (cfg.pixelFormat != formats::NV12) { - cfg.pixelFormat = formats::NV12; - status = Adjusted; - adjusted = true; - } + const PixelFormatInfo &fmtInfo = PixelFormatInfo::info(cfg.pixelFormat); + const bool rawStream = fmtInfo.colourEncoding == PixelFormatInfo::ColourEncodingRAW; - if (cfg.colorSpace != ColorSpace::Rec709) { - cfg.colorSpace = ColorSpace::Rec709; - status = Adjusted; - adjusted = true; - } + if (!rawStream) { + if (cfg.pixelFormat != formats::NV12) { + cfg.pixelFormat = formats::NV12; + status = Adjusted; + adjusted = true; + } - if (validateColorSpaces() == Adjusted) { - status = Adjusted; - adjusted = true; + if (cfg.colorSpace != ColorSpace::Rec709) { + cfg.colorSpace = ColorSpace::Rec709; + status = Adjusted; + adjusted = true; + } + + if (validateColorSpaces() == Adjusted) { + status = Adjusted; + adjusted = true; + } + } else { + cfg.colorSpace = ColorSpace::Raw; } if (adjusted) @@ -267,7 +275,54 @@ PipelineHandlerVirtual::generateConfiguration(Camera *camera, case StreamRole::Viewfinder: break; - case StreamRole::Raw: + case StreamRole::Raw: { + const auto *rawFrames = std::get_if(&data->config_.frame); + if (!rawFrames) { + LOG(Virtual, Error) + << "StreamRole::Raw requested but camera is not configured with raw_frames"; + return {}; + } + + static const std::map cfaToOrder = { + { properties::draft::ColorFilterArrangementEnum::RGGB, BayerFormat::RGGB }, + { properties::draft::ColorFilterArrangementEnum::BGGR, BayerFormat::BGGR }, + { properties::draft::ColorFilterArrangementEnum::GRBG, BayerFormat::GRBG }, + { properties::draft::ColorFilterArrangementEnum::GBRG, BayerFormat::GBRG }, + }; + + auto it = cfaToOrder.find(rawFrames->cfaPattern); + if (it == cfaToOrder.end()) { + LOG(Virtual, Error) << "Unsupported CFA pattern"; + return {}; + } + + PixelFormat rawFormat = + BayerFormat(it->second, rawFrames->bitDepth, + BayerFormat::Packing::None) + .toPixelFormat(); + + if (!rawFormat.isValid()) { + LOG(Virtual, Error) << "Could not find PixelFormat for Bayer format"; + return {}; + } + + /* + * Use the Bayer format matching the raw frame + * configuration. + */ + std::map> rawStreamFormats; + rawStreamFormats[rawFormat] = { { data->config_.minResolutionSize, + data->config_.maxResolutionSize } }; + StreamFormats rawFormats(rawStreamFormats); + StreamConfiguration rawCfg(rawFormats); + rawCfg.pixelFormat = rawFormat; + rawCfg.size = data->config_.maxResolutionSize; + rawCfg.bufferCount = VirtualCameraConfiguration::kBufferCount; + rawCfg.colorSpace = ColorSpace::Raw; + config->addConfiguration(rawCfg); + continue; + } + default: LOG(Virtual, Error) << "Requested stream role not supported: " << role; @@ -401,6 +456,13 @@ bool PipelineHandlerVirtual::match([[maybe_unused]] DeviceEnumerator *enumerator std::set streams; for (auto &streamConfig : data->streamConfigs_) streams.insert(&streamConfig.stream); + + if (const auto *rawFrames = std::get_if(&data->config_.frame)) { + data->properties_.set(properties::draft::ColorFilterArrangement, static_cast(rawFrames->cfaPattern)); + data->properties_.set(properties::PixelArraySize, data->config_.maxResolutionSize); + data->properties_.set(properties::UnitCellSize, Size(1000, 1000)); + } + std::string id = data->config_.id; std::shared_ptr camera = Camera::create(std::move(data), id, streams); @@ -434,7 +496,12 @@ bool PipelineHandlerVirtual::initFrameGenerator(Camera *camera) [&](ImageFrames &imageFrames) { for (auto &streamConfig : data->streamConfigs_) streamConfig.frameGenerator = ImageFrameGenerator::create(imageFrames); - } }, + }, + [&](RawFrames &rawFrames) { + for (auto &streamConfig : data->streamConfigs_) + streamConfig.frameGenerator = RawFrameGenerator::create(rawFrames); + }, + }, frame); for (auto &streamConfig : data->streamConfigs_) diff --git a/src/libcamera/pipeline/virtual/virtual.h b/src/libcamera/pipeline/virtual/virtual.h index 215e56fa..48392808 100644 --- a/src/libcamera/pipeline/virtual/virtual.h +++ b/src/libcamera/pipeline/virtual/virtual.h @@ -23,11 +23,12 @@ #include "frame_generator.h" #include "image_frame_generator.h" +#include "raw_frame_generator.h" #include "test_pattern_generator.h" namespace libcamera { -using VirtualFrame = std::variant; +using VirtualFrame = std::variant; class VirtualCameraData : public Camera::Private, public Thread,