{"id":24971,"url":"https://patchwork.libcamera.org/api/1.1/patches/24971/?format=json","web_url":"https://patchwork.libcamera.org/patch/24971/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20251104153501.34362-5-mzamazal@redhat.com>","date":"2025-11-04T15:34:55","name":"[v15,4/8] libcamera: simple: Validate raw stream configurations","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"9437e8adf5b93990ba972dddadb36d11211a4113","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/1.1/people/177/?format=json","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/24971/mbox/","series":[{"id":5563,"url":"https://patchwork.libcamera.org/api/1.1/series/5563/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5563","date":"2025-11-04T15:34:51","name":"Enable raw streams with software ISP","version":15,"mbox":"https://patchwork.libcamera.org/series/5563/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/24971/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/24971/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 4BD6DC3241\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  4 Nov 2025 15:35:37 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F024F60AA0;\n\tTue,  4 Nov 2025 16:35:36 +0100 (CET)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.133.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 25D5260A7B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  4 Nov 2025 16:35:35 +0100 (CET)","from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com\n\t(ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97])\n\tby relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n\tcipher=TLS_AES_256_GCM_SHA384) id us-mta-84-M7JbgyBbNbumnRNin-vtpA-1;\n\tTue, 04 Nov 2025 10:35:29 -0500","from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com\n\t(mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com\n\t[10.30.177.17])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\tkey-exchange X25519 server-signature RSA-PSS (2048 bits)\n\tserver-digest SHA256) (No client certificate requested)\n\tby mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTPS id D585E180035A; Tue,  4 Nov 2025 15:35:27 +0000 (UTC)","from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.45.225.23])\n\tby mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTP id F01C019540DE; Tue,  4 Nov 2025 15:35:24 +0000 (UTC)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"E2SF2AIg\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1762270534;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tcontent-transfer-encoding:content-transfer-encoding:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=mtKPlHH1fnav8HofrCbcoz1kzXHi2KHbTyYEyw4Mgus=;\n\tb=E2SF2AIgKUEWknfKt/g4RnlMhA5BxZI1/wmiaOAV3DkFROR5lO2maW/nHV3993sUiuTe2/\n\tN0aaa9KTurNetvFfe07MPayx5PvxSQrkoGtgxXh4qXwl9LNq2t9jXLy8sg3e+3yOa1i6zz\n\t6HE/t3gGbuIUQdZOcnVwCzOnfx95oPY=","X-MC-Unique":"M7JbgyBbNbumnRNin-vtpA-1","X-Mimecast-MFC-AGG-ID":"M7JbgyBbNbumnRNin-vtpA_1762270528","From":"Milan Zamazal <mzamazal@redhat.com>","To":"libcamera-devel@lists.libcamera.org","Cc":"Milan Zamazal <mzamazal@redhat.com>, Laurent Pinchart\n\t<laurent.pinchart@ideasonboard.com>, Kieran Bingham\n\t<kieran.bingham@ideasonboard.com>, =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?=\n\t<barnabas.pocze@ideasonboard.com>, Paul Elder\n\t<paul.elder@ideasonboard.com>, Umang Jain <uajain@igalia.com>","Subject":"[PATCH v15 4/8] libcamera: simple: Validate raw stream\n\tconfigurations","Date":"Tue,  4 Nov 2025 16:34:55 +0100","Message-ID":"<20251104153501.34362-5-mzamazal@redhat.com>","In-Reply-To":"<20251104153501.34362-1-mzamazal@redhat.com>","References":"<20251104153501.34362-1-mzamazal@redhat.com>","MIME-Version":"1.0","X-Scanned-By":"MIMEDefang 3.0 on 10.30.177.17","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"gUANpa_dmYjyiOj-9dFz-M2LC_YJ4WpPA11g0y6AcDw_1762270528","X-Mimecast-Originator":"redhat.com","Content-Transfer-Encoding":"8bit","content-type":"text/plain; charset=\"US-ASCII\"; x-default=true","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":"SimpleCameraConfiguration::validate() looks for the best configuration.\nAs part of enabling raw stream support, the method must consider raw\nstreams in addition to the processed streams.\n\nRaw streams are adjusted from the capture format and size.\n\nNote that with both processed and raw streams, the requested sizes must\nbe mutually matching, including resizing due to debayer requirements.\nFor example, the following `cam' setup is valid for imx219\n\n  cam -s role=viewfinder,width=1920,height=1080 \\\n      -s role=raw,width=3280,height=2464\n\nrather than\n\n  cam -s role=viewfinder,width=1920,height=1080 \\\n      -s role=raw,width=1920,height=1080\n\ndue to the resolution of 1924x1080 actually selected for debayering to\n1920x1080.  If the resolutions don't match mutually or don't match the\navailable sizes, validation adjusts them.\n\nSetting up the right configurations is still not enough to make the raw\nstreams working.  Buffer handling must be changed in the simple\npipeline, which is addressed in followup patches.\n\nSigned-off-by: Milan Zamazal <mzamazal@redhat.com>\nCo-developed-by: Umang Jain <uajain@igalia.com>\nSigned-off-by: Milan Zamazal <mzamazal@redhat.com>\n---\n src/libcamera/pipeline/simple/simple.cpp | 89 +++++++++++++++++-------\n 1 file changed, 63 insertions(+), 26 deletions(-)","diff":"diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\nindex 7961a9f02..9dc862f89 100644\n--- a/src/libcamera/pipeline/simple/simple.cpp\n+++ b/src/libcamera/pipeline/simple/simple.cpp\n@@ -11,6 +11,7 @@\n #include <list>\n #include <map>\n #include <memory>\n+#include <optional>\n #include <queue>\n #include <set>\n #include <stdint.h>\n@@ -27,6 +28,7 @@\n #include <libcamera/camera.h>\n #include <libcamera/color_space.h>\n #include <libcamera/control_ids.h>\n+#include <libcamera/geometry.h>\n #include <libcamera/pixel_format.h>\n #include <libcamera/request.h>\n #include <libcamera/stream.h>\n@@ -1140,21 +1142,42 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n \tLOG(SimplePipeline, Debug)\n \t\t<< \"Largest stream size is \" << maxStreamSize;\n \n+\t/* Cap the number of raw stream configuration */\n+\tunsigned int rawCount = 0;\n+\tPixelFormat requestedRawFormat;\n+\tfor (const StreamConfiguration &cfg : config_) {\n+\t\tif (!isRaw(cfg))\n+\t\t\tcontinue;\n+\t\trequestedRawFormat = cfg.pixelFormat;\n+\t\trawCount++;\n+\t}\n+\n+\tif (rawCount > 1) {\n+\t\tLOG(SimplePipeline, Error)\n+\t\t\t<< \"Camera configuration with multiple raw streams not supported\";\n+\t\treturn Invalid;\n+\t}\n+\n \t/*\n \t * Find the best configuration for the pipeline using a heuristic.\n-\t * First select the pixel format based on the streams (which are\n-\t * considered ordered from highest to lowest priority). Default to the\n-\t * first pipeline configuration if no streams request a supported pixel\n-\t * format.\n+\t * First select the pixel format based on the raw streams followed by\n+\t * non-raw streams (which are considered ordered from highest to lowest\n+\t * priority). Default to the first pipeline configuration if no streams\n+\t * request a supported pixel format.\n \t */\n \tconst std::vector<const SimpleCameraData::Configuration *> *configs =\n \t\t&data_->formats_.begin()->second;\n \n-\tfor (const StreamConfiguration &cfg : config_) {\n-\t\tauto it = data_->formats_.find(cfg.pixelFormat);\n-\t\tif (it != data_->formats_.end()) {\n-\t\t\tconfigs = &it->second;\n-\t\t\tbreak;\n+\tauto rawIter = data_->formats_.find(requestedRawFormat);\n+\tif (rawIter != data_->formats_.end()) {\n+\t\tconfigs = &rawIter->second;\n+\t} else {\n+\t\tfor (const StreamConfiguration &cfg : config_) {\n+\t\t\tauto it = data_->formats_.find(cfg.pixelFormat);\n+\t\t\tif (it != data_->formats_.end()) {\n+\t\t\t\tconfigs = &it->second;\n+\t\t\t\tbreak;\n+\t\t\t}\n \t\t}\n \t}\n \n@@ -1214,21 +1237,35 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n \n \tfor (unsigned int i = 0; i < config_.size(); ++i) {\n \t\tStreamConfiguration &cfg = config_[i];\n+\t\tconst bool raw = isRaw(cfg);\n \n \t\t/* Adjust the pixel format and size. */\n-\t\tauto it = std::find(pipeConfig_->outputFormats.begin(),\n-\t\t\t\t    pipeConfig_->outputFormats.end(),\n-\t\t\t\t    cfg.pixelFormat);\n-\t\tif (it == pipeConfig_->outputFormats.end())\n-\t\t\tit = pipeConfig_->outputFormats.begin();\n-\n-\t\tPixelFormat pixelFormat = *it;\n-\t\tif (cfg.pixelFormat != pixelFormat) {\n-\t\t\tLOG(SimplePipeline, Debug)\n-\t\t\t\t<< \"Adjusting pixel format from \"\n-\t\t\t\t<< cfg.pixelFormat << \" to \" << pixelFormat;\n-\t\t\tcfg.pixelFormat = pixelFormat;\n-\t\t\tstatus = Adjusted;\n+\t\tif (raw) {\n+\t\t\tif (cfg.pixelFormat != pipeConfig_->captureFormat ||\n+\t\t\t    cfg.size != pipeConfig_->captureSize) {\n+\t\t\t\tcfg.pixelFormat = pipeConfig_->captureFormat;\n+\t\t\t\tcfg.size = pipeConfig_->captureSize;\n+\n+\t\t\t\tLOG(SimplePipeline, Debug)\n+\t\t\t\t\t<< \"Adjusting raw stream to \"\n+\t\t\t\t\t<< cfg.toString();\n+\t\t\t\tstatus = Adjusted;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tauto it = std::find(pipeConfig_->outputFormats.begin(),\n+\t\t\t\t\t    pipeConfig_->outputFormats.end(),\n+\t\t\t\t\t    cfg.pixelFormat);\n+\t\t\tif (it == pipeConfig_->outputFormats.end())\n+\t\t\t\tit = pipeConfig_->outputFormats.begin();\n+\n+\t\t\tPixelFormat pixelFormat = *it;\n+\t\t\tif (cfg.pixelFormat != pixelFormat) {\n+\t\t\t\tLOG(SimplePipeline, Debug)\n+\t\t\t\t\t<< \"Adjusting processed pixel format from \"\n+\t\t\t\t\t<< cfg.pixelFormat << \" to \" << pixelFormat;\n+\t\t\t\tcfg.pixelFormat = pixelFormat;\n+\t\t\t\tstatus = Adjusted;\n+\t\t\t}\n \t\t}\n \n \t\t/*\n@@ -1239,7 +1276,7 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n \t\t * case, perform the standard pixel format based color space adjustment.\n \t\t */\n \t\tif (!cfg.colorSpace) {\n-\t\t\tconst PixelFormatInfo &info = PixelFormatInfo::info(pixelFormat);\n+\t\t\tconst PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat);\n \t\t\tswitch (info.colourEncoding) {\n \t\t\tcase PixelFormatInfo::ColourEncodingRGB:\n \t\t\t\tcfg.colorSpace = ColorSpace::Srgb;\n@@ -1255,14 +1292,14 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n \t\t\t\t<< cfg.colorSpace.value().toString();\n \t\t\tstatus = Adjusted;\n \t\t}\n-\t\tif (cfg.colorSpace->adjust(pixelFormat)) {\n+\t\tif (cfg.colorSpace->adjust(cfg.pixelFormat)) {\n \t\t\tLOG(SimplePipeline, Debug)\n \t\t\t\t<< \"Color space adjusted to \"\n \t\t\t\t<< cfg.colorSpace.value().toString();\n \t\t\tstatus = Adjusted;\n \t\t}\n \n-\t\tif (!pipeConfig_->outputSizes.contains(cfg.size)) {\n+\t\tif (!raw && !pipeConfig_->outputSizes.contains(cfg.size)) {\n \t\t\tSize adjustedSize = pipeConfig_->captureSize;\n \t\t\t/*\n \t\t\t * The converter (when present) may not be able to output\n@@ -1285,7 +1322,7 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n \t\t\tneedConversion_ = true;\n \n \t\t/* Set the stride and frameSize. */\n-\t\tif (needConversion_) {\n+\t\tif (needConversion_ && !raw) {\n \t\t\tstd::tie(cfg.stride, cfg.frameSize) =\n \t\t\t\tdata_->converter_\n \t\t\t\t\t? data_->converter_->strideAndFrameSize(cfg.pixelFormat,\n","prefixes":["v15","4/8"]}