From patchwork Fri May 1 10:52:09 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Bretschneider X-Patchwork-Id: 26590 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 2D03EBDCB5 for ; Fri, 1 May 2026 10:52:15 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C9B6263021; Fri, 1 May 2026 12:52:14 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=protonmail.com header.i=@protonmail.com header.b="hcntd6rX"; dkim-atps=neutral Received: from mail-10699.protonmail.ch (mail-10699.protonmail.ch [79.135.106.99]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0825862FD3 for ; Fri, 1 May 2026 12:52:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=protonmail3; t=1777632733; x=1777891933; bh=iYQHAUY6USdU+wdz5n70UaFjuzvcs5UlQwruuIUMZLg=; 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=hcntd6rXXMonUNqfHDG7CVPbRAYTRvgAAHoqD/G9Y+NniZ5xFcI4GruXjidFN6eL/ ScDWnSFnU8kq0TSQkTYfGvphzGKUjJECNXasz7ClimQNY0tuWBvi5vjHHaOApaoCM9 1kpaIcbUhyzVUZcF4qmCNjaJYW+RopCOelo3OJA/9VJY1kmbChPA5/YPsoSasTK4oo 8NdrC9OKA28YI1AxVlA6T8ZC6aTOnty9JzRl43ea/Hk+Kjbb427uXCMRa6tsBJfKyK aZKbnw3iGrAvfOwyiSdYspIdrilKzKkwAV89LJ0W+E+5WjR0hjiJBo/gCDh2HuMr66 lrpLvU9PyZvDQ== Date: Fri, 01 May 2026 10:52:09 +0000 To: libcamera-devel@lists.libcamera.org From: Max Bretschneider Cc: Max Bretschneider Subject: [RFC PATCH v1 2/3] libcamera: pipeline: virtual: Add raw_frames config parser support Message-ID: <20260501105137.439519-3-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: 8124651386b37c98f5f4c49914ace05c35876a7d 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 Signed-off-by: Max Bretschneider --- .../pipeline/virtual/config_parser.cpp | 100 ++++++++++++++++-- 1 file changed, 94 insertions(+), 6 deletions(-) -- 2.43.0 diff --git a/src/libcamera/pipeline/virtual/config_parser.cpp b/src/libcamera/pipeline/virtual/config_parser.cpp index 5169fd39..fcce70c8 100644 --- a/src/libcamera/pipeline/virtual/config_parser.cpp +++ b/src/libcamera/pipeline/virtual/config_parser.cpp @@ -156,13 +156,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 +185,87 @@ 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; + } + + 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) << "raw_frames directory has no files: " << *path; + return -EINVAL; + } + break; + default: + LOG(Virtual, Error) << "raw_frames path: " << *path << " is not supported"; + 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 */