[{"id":37215,"web_url":"https://patchwork.libcamera.org/comment/37215/","msgid":"<b15ed70145928b172fe35f77bc5c48e4@igalia.com>","date":"2025-12-05T04:30:44","subject":"Re: [PATCH v17 3/7] libcamera: simple: Validate raw stream\n\tconfigurations","submitter":{"id":232,"url":"https://patchwork.libcamera.org/api/people/232/","name":"Umang Jain","email":"uajain@igalia.com"},"content":"On 2025-12-04 22:19, Milan Zamazal wrote:\n> SimpleCameraConfiguration::validate() looks for the best configuration.\n> As part of enabling raw stream support, the method must consider raw\n> streams in addition to the processed streams.\n> \n> Raw streams are adjusted from the capture format and size.\n> \n> Configuration validation computes the maximum size of all the requested\n> streams and compares it to the output sizes.  When e.g. only a raw\n> stream is requested then this may result in an invalid adjustment of its\n> size.  This is because the output sizes are computed for processed\n> streams and may be smaller than capture sizes.  If a raw stream with the\n> capture size is requested, it may then be wrongly adjusted to a larger\n> size because the output sizes, which are irrelevant for raw streams\n> anyway, are smaller than the requested capture size.  The problem is\n> resolved by tracking raw and processed streams maximum sizes separately\n> and comparing raw stream sizes against capture rather than output sizes.\n> \n> Note that with both processed and raw streams, the requested sizes must\n> be mutually matching, including resizing due to debayer requirements.\n> For 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> \n> rather than\n> \n>   cam -s role=viewfinder,width=1920,height=1080 \\\n>       -s role=raw,width=1920,height=1080\n> \n> due to the resolution of 1924x1080 actually selected for debayering to\n> 1920x1080.  If the resolutions don't match mutually or don't match the\n> available sizes, validation adjusts them.\n> \n> Setting up the right configurations is still not enough to make the raw\n> streams working.  Buffer handling must be changed in the simple\n> pipeline, which is addressed in followup patches.\n\nNot sure if I go in such detail for the commit message, it could be\nsimply:\n\n```\nThe maximum RAW stream size is tracked separate to ensure appropriate\nsensor capture size(or pipeConfig) selection. In case a processed stream\nis additionally requested, the validation can adjust the RAW stream size\nin order to satisfy the output of both stream sizes (including resizing\ndue to debayer requirements).\n```\n\n> \n> Co-developed-by: Umang Jain <uajain@igalia.com>\n> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n\nNot sure if my R-b is applicable here, but anyway:\n\nReviewed-by: Umang Jain <uajain@igalia.com>\n> ---\n>  src/libcamera/pipeline/simple/simple.cpp | 117 ++++++++++++++++-------\n>  1 file changed, 82 insertions(+), 35 deletions(-)\n> \n> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n> index 6b83254fb..aaafe0571 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> @@ -1138,29 +1140,57 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>  \t\tstatus = Adjusted;\n>  \t}\n>  \n> -\t/* Find the largest stream size. */\n> -\tSize maxStreamSize;\n> -\tfor (const StreamConfiguration &cfg : config_)\n> -\t\tmaxStreamSize.expandTo(cfg.size);\n> +\t/* Find the largest stream sizes. */\n> +\tSize maxProcessedStreamSize;\n> +\tSize maxRawStreamSize;\n> +\tfor (const StreamConfiguration &cfg : config_) {\n> +\t\tif (isRaw(cfg))\n> +\t\t\tmaxRawStreamSize.expandTo(cfg.size);\n> +\t\telse\n> +\t\t\tmaxProcessedStreamSize.expandTo(cfg.size);\n> +\t}\n>  \n>  \tLOG(SimplePipeline, Debug)\n> -\t\t<< \"Largest stream size is \" << maxStreamSize;\n> +\t\t<< \"Largest processed stream size is \" << maxProcessedStreamSize;\n> +\tLOG(SimplePipeline, Debug)\n> +\t\t<< \"Largest raw stream size is \" << maxRawStreamSize;\n> +\n> +\t/* Cap the number of raw stream configurations */\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> @@ -1182,8 +1212,10 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>  \t\tconst Size &captureSize = pipeConfig->captureSize;\n>  \t\tconst Size &maxOutputSize = pipeConfig->outputSizes.max;\n>  \n> -\t\tif (maxOutputSize.width >= maxStreamSize.width &&\n> -\t\t    maxOutputSize.height >= maxStreamSize.height) {\n> +\t\tif (maxOutputSize.width >= maxProcessedStreamSize.width &&\n> +\t\t    maxOutputSize.height >= maxProcessedStreamSize.height &&\n> +\t\t    captureSize.width >= maxRawStreamSize.width &&\n> +\t\t    captureSize.height >= maxRawStreamSize.height) {\n>  \t\t\tif (!pipeConfig_ || captureSize < pipeConfig_->captureSize)\n>  \t\t\t\tpipeConfig_ = pipeConfig;\n>  \t\t}\n> @@ -1201,7 +1233,8 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>  \t\t<< V4L2SubdeviceFormat{ pipeConfig_->code, pipeConfig_->sensorSize, {} }\n>  \t\t<< \" -> \" << pipeConfig_->captureSize\n>  \t\t<< \"-\" << pipeConfig_->captureFormat\n> -\t\t<< \" for max stream size \" << maxStreamSize;\n> +\t\t<< \" for max processed stream size \" << maxProcessedStreamSize\n> +\t\t<< \" and max raw stream size \" << maxRawStreamSize;\n>  \n>  \t/*\n>  \t * Adjust the requested streams.\n> @@ -1220,21 +1253,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> @@ -1245,7 +1292,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> @@ -1265,9 +1312,9 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>  \t\t\t * adjusting a requested one, changes here shouldn't set the status\n>  \t\t\t * to Adjusted.\n>  \t\t\t */\n> -\t\t\tcfg.colorSpace->adjust(pixelFormat);\n> +\t\t\tcfg.colorSpace->adjust(cfg.pixelFormat);\n>  \t\t} else {\n> -\t\t\tif (cfg.colorSpace->adjust(pixelFormat)) {\n> +\t\t\tif (cfg.colorSpace->adjust(cfg.pixelFormat)) {\n>  \t\t\t\tLOG(SimplePipeline, Debug)\n>  \t\t\t\t\t<< \"Color space adjusted to \"\n>  \t\t\t\t\t<< cfg.colorSpace.value().toString();\n> @@ -1275,7 +1322,7 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>  \t\t\t}\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> @@ -1298,7 +1345,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,","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 55D84BD80A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  5 Dec 2025 04:30:52 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5749E613A4;\n\tFri,  5 Dec 2025 05:30:51 +0100 (CET)","from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id E639E609D8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  5 Dec 2025 05:30:48 +0100 (CET)","from maestria.local.igalia.com ([192.168.10.14]\n\thelo=mail.igalia.com) by fanzine2.igalia.com with esmtps \n\t(Cipher TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256)\n\t(Exim) id 1vRNSw-008wHB-MY; Fri, 05 Dec 2025 05:30:46 +0100","from webmail.service.igalia.com ([192.168.21.45])\n\tby mail.igalia.com with esmtp (Exim)\n\tid 1vRNSu-007Eps-Av; Fri, 05 Dec 2025 05:30:46 +0100","from localhost ([127.0.0.1] helo=webmail.igalia.com)\n\tby webmail with esmtp (Exim 4.96) (envelope-from <uajain@igalia.com>)\n\tid 1vRNSt-004VjW-2n; Fri, 05 Dec 2025 05:30:44 +0100"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=igalia.com header.i=@igalia.com\n\theader.b=\"P91rLpTB\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com;\n\ts=20170329;\n\th=Content-Transfer-Encoding:Content-Type:Message-ID:References:\n\tIn-Reply-To:Subject:Cc:To:From:Date:MIME-Version:Sender:Reply-To:Content-ID:\n\tContent-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc\n\t:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe:\n\tList-Post:List-Owner:List-Archive;\n\tbh=bHro6+lLGs0jB/1yaPVIWy7wdAL5kBbrEAloyWJL/ts=;\n\tb=P91rLpTBJgye9pv1GKfSgdVBe6\n\t5ANUFq2Q5asMKX2S4aZj9gP5d7vKQL3kaIRqwY+Nep+jyTihtur54esW3bpyGqzTJaSe0ffOLTd7q\n\t6X0UdIFdQXgcPP2GBXiHWDI6pc48qeg661Bofrovhk++CSyknAULSpTVJUno/WDBdhractM5VOCWq\n\teWQ0QQ+Gn9VQhvqUIZyfOHU4uNIC1lUG56l4z4bG20PhAldU9nJCaA5jlvVm6ygmWzh/YSJESC4Dl\n\t9kUoFWr6X1ZYuJIa+k8oJt3lPW1aiUExvfQWqSQmmMJTw3cJTj69ctMcQj+xQkROQmfQeudFEURi+\n\t19JugPIQ==;","MIME-Version":"1.0","Date":"Fri, 05 Dec 2025 10:00:44 +0530","From":"Umang Jain <uajain@igalia.com>","To":"Milan Zamazal <mzamazal@redhat.com>","Cc":"libcamera-devel@lists.libcamera.org, Laurent Pinchart\n\t<laurent.pinchart@ideasonboard.com>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>, =?utf-8?q?Barnab?=\n\t=?utf-8?b?w6FzIFDFkWN6ZQ==?= <barnabas.pocze@ideasonboard.com>,\n\tPaul Elder <paul.elder@ideasonboard.com>,  Pavel Machek <pavel@ucw.cz>","Subject":"Re: [PATCH v17 3/7] libcamera: simple: Validate raw stream\n\tconfigurations","In-Reply-To":"<20251204164918.83334-4-mzamazal@redhat.com>","References":"<20251204164918.83334-1-mzamazal@redhat.com>\n\t<20251204164918.83334-4-mzamazal@redhat.com>","Message-ID":"<b15ed70145928b172fe35f77bc5c48e4@igalia.com>","X-Sender":"uajain@igalia.com","Content-Type":"text/plain; charset=US-ASCII","Content-Transfer-Encoding":"7bit","X-Spam-Report":"NO, Score=-2.8, Tests=ALL_TRUSTED=-3, AWL=-2.208, BAYES_50=0.8,\n\tSUBJECT_DRUG_GAP_L=1.561","X-Spam-Score":"-27","X-Spam-Bar":"--","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>"}},{"id":37217,"web_url":"https://patchwork.libcamera.org/comment/37217/","msgid":"<85ms3x6yg1.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","date":"2025-12-05T09:59:58","subject":"Re: [PATCH v17 3/7] libcamera: simple: Validate raw stream\n\tconfigurations","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"content":"Hi Umang,\n\nthank you for review.\n\nUmang Jain <uajain@igalia.com> writes:\n\n> On 2025-12-04 22:19, Milan Zamazal wrote:\n>> SimpleCameraConfiguration::validate() looks for the best configuration.\n>> As part of enabling raw stream support, the method must consider raw\n>\n>> streams in addition to the processed streams.\n>> \n>> Raw streams are adjusted from the capture format and size.\n>> \n>> Configuration validation computes the maximum size of all the requested\n>> streams and compares it to the output sizes.  When e.g. only a raw\n>> stream is requested then this may result in an invalid adjustment of its\n>> size.  This is because the output sizes are computed for processed\n>> streams and may be smaller than capture sizes.  If a raw stream with the\n>> capture size is requested, it may then be wrongly adjusted to a larger\n>> size because the output sizes, which are irrelevant for raw streams\n>> anyway, are smaller than the requested capture size.  The problem is\n>> resolved by tracking raw and processed streams maximum sizes separately\n>> and comparing raw stream sizes against capture rather than output sizes.\n>> \n>> Note that with both processed and raw streams, the requested sizes must\n>> be mutually matching, including resizing due to debayer requirements.\n>> For 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>> \n>> rather than\n>> \n>>   cam -s role=viewfinder,width=1920,height=1080 \\\n>>       -s role=raw,width=1920,height=1080\n>> \n>> due to the resolution of 1924x1080 actually selected for debayering to\n>> 1920x1080.  If the resolutions don't match mutually or don't match the\n>> available sizes, validation adjusts them.\n>> \n>> Setting up the right configurations is still not enough to make the raw\n>> streams working.  Buffer handling must be changed in the simple\n>> pipeline, which is addressed in followup patches.\n>\n> Not sure if I go in such detail for the commit message, it could be\n> simply:\n>\n> ```\n> The maximum RAW stream size is tracked separate to ensure appropriate\n> sensor capture size(or pipeConfig) selection. In case a processed stream\n> is additionally requested, the validation can adjust the RAW stream size\n> in order to satisfy the output of both stream sizes (including resizing\n> due to debayer requirements).\n> ```\n\nThank you for the suggestion but I'm not sure the more concise\ndescription would be sufficiently clear to a casual code reader.  I\nthink it wouldn't be to me and I usually forget changes details much\nearlier than I think I will :-).  Commit messages are often the only\nexplanations available and there is no need to save space in them.\n\n>> \n>> Co-developed-by: Umang Jain <uajain@igalia.com>\n>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n>\n> Not sure if my R-b is applicable here, but anyway:\n>\n> Reviewed-by: Umang Jain <uajain@igalia.com>\n>> ---\n>>  src/libcamera/pipeline/simple/simple.cpp | 117 ++++++++++++++++-------\n>>  1 file changed, 82 insertions(+), 35 deletions(-)\n>> \n>> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n>> index 6b83254fb..aaafe0571 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>> @@ -1138,29 +1140,57 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>>  \t\tstatus = Adjusted;\n>>  \t}\n>>  \n>> -\t/* Find the largest stream size. */\n>> -\tSize maxStreamSize;\n>> -\tfor (const StreamConfiguration &cfg : config_)\n>> -\t\tmaxStreamSize.expandTo(cfg.size);\n>> +\t/* Find the largest stream sizes. */\n>> +\tSize maxProcessedStreamSize;\n>> +\tSize maxRawStreamSize;\n>> +\tfor (const StreamConfiguration &cfg : config_) {\n>> +\t\tif (isRaw(cfg))\n>> +\t\t\tmaxRawStreamSize.expandTo(cfg.size);\n>> +\t\telse\n>> +\t\t\tmaxProcessedStreamSize.expandTo(cfg.size);\n>> +\t}\n>>  \n>>  \tLOG(SimplePipeline, Debug)\n>> -\t\t<< \"Largest stream size is \" << maxStreamSize;\n>> +\t\t<< \"Largest processed stream size is \" << maxProcessedStreamSize;\n>> +\tLOG(SimplePipeline, Debug)\n>> +\t\t<< \"Largest raw stream size is \" << maxRawStreamSize;\n>> +\n>> +\t/* Cap the number of raw stream configurations */\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>> @@ -1182,8 +1212,10 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>>  \t\tconst Size &captureSize = pipeConfig->captureSize;\n>>  \t\tconst Size &maxOutputSize = pipeConfig->outputSizes.max;\n>>  \n>> -\t\tif (maxOutputSize.width >= maxStreamSize.width &&\n>> -\t\t    maxOutputSize.height >= maxStreamSize.height) {\n>> +\t\tif (maxOutputSize.width >= maxProcessedStreamSize.width &&\n>> +\t\t    maxOutputSize.height >= maxProcessedStreamSize.height &&\n>> +\t\t    captureSize.width >= maxRawStreamSize.width &&\n>> +\t\t    captureSize.height >= maxRawStreamSize.height) {\n>>  \t\t\tif (!pipeConfig_ || captureSize < pipeConfig_->captureSize)\n>>  \t\t\t\tpipeConfig_ = pipeConfig;\n>>  \t\t}\n>> @@ -1201,7 +1233,8 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>>  \t\t<< V4L2SubdeviceFormat{ pipeConfig_->code, pipeConfig_->sensorSize, {} }\n>>  \t\t<< \" -> \" << pipeConfig_->captureSize\n>>  \t\t<< \"-\" << pipeConfig_->captureFormat\n>> -\t\t<< \" for max stream size \" << maxStreamSize;\n>> +\t\t<< \" for max processed stream size \" << maxProcessedStreamSize\n>> +\t\t<< \" and max raw stream size \" << maxRawStreamSize;\n>>  \n>>  \t/*\n>>  \t * Adjust the requested streams.\n>> @@ -1220,21 +1253,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>> @@ -1245,7 +1292,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>> @@ -1265,9 +1312,9 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>>  \t\t\t * adjusting a requested one, changes here shouldn't set the status\n>>  \t\t\t * to Adjusted.\n>>  \t\t\t */\n>> -\t\t\tcfg.colorSpace->adjust(pixelFormat);\n>> +\t\t\tcfg.colorSpace->adjust(cfg.pixelFormat);\n>>  \t\t} else {\n>> -\t\t\tif (cfg.colorSpace->adjust(pixelFormat)) {\n>> +\t\t\tif (cfg.colorSpace->adjust(cfg.pixelFormat)) {\n>>  \t\t\t\tLOG(SimplePipeline, Debug)\n>>  \t\t\t\t\t<< \"Color space adjusted to \"\n>>  \t\t\t\t\t<< cfg.colorSpace.value().toString();\n>> @@ -1275,7 +1322,7 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>>  \t\t\t}\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>> @@ -1298,7 +1345,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,","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 7F91CBD80A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  5 Dec 2025 10:00:07 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1587361168;\n\tFri,  5 Dec 2025 11:00:06 +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 6F048609DE\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  5 Dec 2025 11:00:04 +0100 (CET)","from mail-wm1-f71.google.com (mail-wm1-f71.google.com\n\t[209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-648-A_pgxj34P0KBfBsSFH0n3w-1; Fri, 05 Dec 2025 05:00:01 -0500","by mail-wm1-f71.google.com with SMTP id\n\t5b1f17b1804b1-477c49f273fso18221755e9.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 05 Dec 2025 02:00:01 -0800 (PST)","from mzamazal-thinkpadp1gen7.tpbc.csb\n\t(ip-77-48-47-2.net.vodafone.cz. [77.48.47.2])\n\tby smtp.gmail.com with ESMTPSA id\n\t5b1f17b1804b1-479311e7142sm76830075e9.11.2025.12.05.01.59.58\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tFri, 05 Dec 2025 01:59:59 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"Q+wdKEX/\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1764928803;\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\tin-reply-to:in-reply-to:references:references;\n\tbh=7MZxUVjgTFKuURPXW/fGaWT/7zzx3QJrZ0LqxxTlbbU=;\n\tb=Q+wdKEX/VqfQLljyOm2kZ1SXadgofvwQZOrPfrOAiKVMnuvIeCqks/IowvYCoav55IvDmb\n\t7Dau/Js6Dhhg5vieSWeEzbBeto+HWwtD2vEzyDkhbTwY9oU5RV8l9EH/n508+Tlopx7nLl\n\tAt73S0R/nYDs7qrOlcUC4PQC+gPGY4Y=","X-MC-Unique":"A_pgxj34P0KBfBsSFH0n3w-1","X-Mimecast-MFC-AGG-ID":"A_pgxj34P0KBfBsSFH0n3w_1764928800","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1764928800; x=1765533600;\n\th=mime-version:user-agent:message-id:date:references:in-reply-to\n\t:subject:cc:to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject\n\t:date:message-id:reply-to;\n\tbh=7MZxUVjgTFKuURPXW/fGaWT/7zzx3QJrZ0LqxxTlbbU=;\n\tb=VadDB1teOUDfIHt61kklbog2IOX7QuxUFgMqvU/Haz2Hkk7LD26QK7PAb6uMsBhRl5\n\t0Bn1P0QUFLplxPLBV3YEGcrBHX6ZKYFfHVayvviK3Z3u3DHHZUrKxEO2TyOuaeVX2Vy8\n\tNKWzRPMVfo9Lr1LvbWbMRwp59lwDbE+JQSyewuYPko1QAE8EzKsfTT/bRqr4wFCiPxVC\n\t4i9CVCRmXE0sWuVKyRY5JvhkdCsn2D4vpWe45Xk1mn8RPNyOu46WvShb3DCQ0Y0UAGVw\n\t4u6PKeS2RO58npbejESVlTmzYSNuvFQWJwUobPb2pFH9co31Qqb1RL933VjqLwhI+69g\n\teWng==","X-Gm-Message-State":"AOJu0YwkERWsxWofnYw5eQb6Hu0dMbuo8PXFwe6vABcq3I2DPqsrmR5p\n\tOgp7fu7srdwxrOrWr4XAPeqOqx89XWCztJ00KFJTK7O/eJN49iGEycpp151mUmHt5Li4OytwTTJ\n\t/tf/qc1XWlac74YZwpEAPXhhh+vZvKENTJLhAjOy8Z58eSSD/Gqh/ErrBD+cmFeqURJjJly940z\n\tIUruUB4+w=","X-Gm-Gg":"ASbGncuYs21Z2iILWzY1J75j1Errqn7W22j/sBvMa5KX0DMRtd1n9+6JPDwYYdokTCQ\n\tUZl06pJI5GAWvJy7j74d0xyesgDQ5kwqvp/uIiuIMFMiJe3O9GHYxUWBBEQMFdDuhSn9ySi4mwN\n\tk16Kdfj9MQWg7xlxdqVrJbQxznbGOJxIZTg74EEDFB/+4WrDtCeE/pO66VoihYkVWoJqc2WvRtz\n\tTj7fl6rZF9Po3qOtmuFOF7+ebFMFav60iXF2SrigRkFOQtKSjWsvDczinXe76taDiNtEyw6t7TV\n\t6PUWX5X6w19gfNW53Y50s7DZdp3JeBe0dWDlqbbpRbRghPtW6nEeKjm7FhSKqrc9dxlTLJKmIYn\n\teS0K4QW6x5EmIo7BB8xlpR6b+9iDOodnq3GJCvXaCEAlUHkaNj+bgvBXE5aioaZ4=","X-Received":["by 2002:a05:600c:c8c:b0:477:7bca:8b2b with SMTP id\n\t5b1f17b1804b1-4792aef7050mr117666865e9.15.1764928799991; \n\tFri, 05 Dec 2025 01:59:59 -0800 (PST)","by 2002:a05:600c:c8c:b0:477:7bca:8b2b with SMTP id\n\t5b1f17b1804b1-4792aef7050mr117666495e9.15.1764928799506; \n\tFri, 05 Dec 2025 01:59:59 -0800 (PST)"],"X-Google-Smtp-Source":"AGHT+IG8wSv8ur7U7GTQ1IUmlnNzHRtNNflI+Hw8hQNBHwIACzD7Q9LmWvyPCwaZkeFBBxgaNZSuPw==","From":"Milan Zamazal <mzamazal@redhat.com>","To":"Umang Jain <uajain@igalia.com>","Cc":"libcamera-devel@lists.libcamera.org,  Laurent Pinchart\n\t<laurent.pinchart@ideasonboard.com>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>, =?utf-8?q?Barnab?=\n\t=?utf-8?b?w6FzIFDFkWN6ZQ==?=\n\t<barnabas.pocze@ideasonboard.com>, Paul Elder\n\t<paul.elder@ideasonboard.com>, Pavel Machek <pavel@ucw.cz>","Subject":"Re: [PATCH v17 3/7] libcamera: simple: Validate raw stream\n\tconfigurations","In-Reply-To":"<b15ed70145928b172fe35f77bc5c48e4@igalia.com> (Umang Jain's\n\tmessage of \"Fri, 05 Dec 2025 10:00:44 +0530\")","References":"<20251204164918.83334-1-mzamazal@redhat.com>\n\t<20251204164918.83334-4-mzamazal@redhat.com>\n\t<b15ed70145928b172fe35f77bc5c48e4@igalia.com>","Date":"Fri, 05 Dec 2025 10:59:58 +0100","Message-ID":"<85ms3x6yg1.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","User-Agent":"Gnus/5.13 (Gnus v5.13)","MIME-Version":"1.0","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"0ODCyRwVwtRvuB_rkLaCkxYRtq3cCFwUD0FWYFBcUwM_1764928800","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain","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>"}},{"id":37218,"web_url":"https://patchwork.libcamera.org/comment/37218/","msgid":"<d66c45d39185e33efcf18fa1d5cc20c0@igalia.com>","date":"2025-12-05T14:08:19","subject":"Re: [PATCH v17 3/7] libcamera: simple: Validate raw stream\n\tconfigurations","submitter":{"id":232,"url":"https://patchwork.libcamera.org/api/people/232/","name":"Umang Jain","email":"uajain@igalia.com"},"content":"On 2025-12-05 15:29, Milan Zamazal wrote:\n> Hi Umang,\n> \n> thank you for review.\n> \n> Umang Jain <uajain@igalia.com> writes:\n> \n>> On 2025-12-04 22:19, Milan Zamazal wrote:\n>>> SimpleCameraConfiguration::validate() looks for the best configuration.\n>>> As part of enabling raw stream support, the method must consider raw\n>>\n>>> streams in addition to the processed streams.\n>>> \n>>> Raw streams are adjusted from the capture format and size.\n>>> \n>>> Configuration validation computes the maximum size of all the requested\n>>> streams and compares it to the output sizes.  When e.g. only a raw\n>>> stream is requested then this may result in an invalid adjustment of its\n>>> size.  This is because the output sizes are computed for processed\n>>> streams and may be smaller than capture sizes.  If a raw stream with the\n>>> capture size is requested, it may then be wrongly adjusted to a larger\n>>> size because the output sizes, which are irrelevant for raw streams\n>>> anyway, are smaller than the requested capture size.  The problem is\n>>> resolved by tracking raw and processed streams maximum sizes separately\n>>> and comparing raw stream sizes against capture rather than output sizes.\n>>> \n>>> Note that with both processed and raw streams, the requested sizes must\n>>> be mutually matching, including resizing due to debayer requirements.\n>>> For 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>>> \n>>> rather than\n>>> \n>>>   cam -s role=viewfinder,width=1920,height=1080 \\\n>>>       -s role=raw,width=1920,height=1080\n>>> \n>>> due to the resolution of 1924x1080 actually selected for debayering to\n>>> 1920x1080.  If the resolutions don't match mutually or don't match the\n>>> available sizes, validation adjusts them.\n>>> \n>>> Setting up the right configurations is still not enough to make the raw\n>>> streams working.  Buffer handling must be changed in the simple\n>>> pipeline, which is addressed in followup patches.\n>>\n>> Not sure if I go in such detail for the commit message, it could be\n>> simply:\n>>\n>> ```\n>> The maximum RAW stream size is tracked separate to ensure appropriate\n>> sensor capture size(or pipeConfig) selection. In case a processed stream\n>> is additionally requested, the validation can adjust the RAW stream size\n>> in order to satisfy the output of both stream sizes (including resizing\n>> due to debayer requirements).\n>> ```\n> \n> Thank you for the suggestion but I'm not sure the more concise\n> description would be sufficiently clear to a casual code reader.  I\n> think it wouldn't be to me and I usually forget changes details much\n> earlier than I think I will :-).  Commit messages are often the only\n> explanations available and there is no need to save space in them.\n> \n\nNot against detailed commit message but in this case, the material looks\nlike it is geared with interaction of Robert's output sizes comparison\npatch. If this series would have merged and there was a patch on top\n(like in v16) the commit message interaction detail made full sense. I\nam OK keeping you as you like, was just giving PoV.\n\n>>> \n>>> Co-developed-by: Umang Jain <uajain@igalia.com>\n>>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n>>\n>> Not sure if my R-b is applicable here, but anyway:\n>>\n>> Reviewed-by: Umang Jain <uajain@igalia.com>\n>>> ---\n>>>  src/libcamera/pipeline/simple/simple.cpp | 117 ++++++++++++++++-------\n>>>  1 file changed, 82 insertions(+), 35 deletions(-)\n>>> \n>>> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n>>> index 6b83254fb..aaafe0571 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>>> @@ -1138,29 +1140,57 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>>>  \t\tstatus = Adjusted;\n>>>  \t}\n>>>  \n>>> -\t/* Find the largest stream size. */\n>>> -\tSize maxStreamSize;\n>>> -\tfor (const StreamConfiguration &cfg : config_)\n>>> -\t\tmaxStreamSize.expandTo(cfg.size);\n>>> +\t/* Find the largest stream sizes. */\n>>> +\tSize maxProcessedStreamSize;\n>>> +\tSize maxRawStreamSize;\n>>> +\tfor (const StreamConfiguration &cfg : config_) {\n>>> +\t\tif (isRaw(cfg))\n>>> +\t\t\tmaxRawStreamSize.expandTo(cfg.size);\n>>> +\t\telse\n>>> +\t\t\tmaxProcessedStreamSize.expandTo(cfg.size);\n>>> +\t}\n>>>  \n>>>  \tLOG(SimplePipeline, Debug)\n>>> -\t\t<< \"Largest stream size is \" << maxStreamSize;\n>>> +\t\t<< \"Largest processed stream size is \" << maxProcessedStreamSize;\n>>> +\tLOG(SimplePipeline, Debug)\n>>> +\t\t<< \"Largest raw stream size is \" << maxRawStreamSize;\n>>> +\n>>> +\t/* Cap the number of raw stream configurations */\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>>> @@ -1182,8 +1212,10 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>>>  \t\tconst Size &captureSize = pipeConfig->captureSize;\n>>>  \t\tconst Size &maxOutputSize = pipeConfig->outputSizes.max;\n>>>  \n>>> -\t\tif (maxOutputSize.width >= maxStreamSize.width &&\n>>> -\t\t    maxOutputSize.height >= maxStreamSize.height) {\n>>> +\t\tif (maxOutputSize.width >= maxProcessedStreamSize.width &&\n>>> +\t\t    maxOutputSize.height >= maxProcessedStreamSize.height &&\n>>> +\t\t    captureSize.width >= maxRawStreamSize.width &&\n>>> +\t\t    captureSize.height >= maxRawStreamSize.height) {\n>>>  \t\t\tif (!pipeConfig_ || captureSize < pipeConfig_->captureSize)\n>>>  \t\t\t\tpipeConfig_ = pipeConfig;\n>>>  \t\t}\n>>> @@ -1201,7 +1233,8 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>>>  \t\t<< V4L2SubdeviceFormat{ pipeConfig_->code, pipeConfig_->sensorSize, {} }\n>>>  \t\t<< \" -> \" << pipeConfig_->captureSize\n>>>  \t\t<< \"-\" << pipeConfig_->captureFormat\n>>> -\t\t<< \" for max stream size \" << maxStreamSize;\n>>> +\t\t<< \" for max processed stream size \" << maxProcessedStreamSize\n>>> +\t\t<< \" and max raw stream size \" << maxRawStreamSize;\n>>>  \n>>>  \t/*\n>>>  \t * Adjust the requested streams.\n>>> @@ -1220,21 +1253,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>>> @@ -1245,7 +1292,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>>> @@ -1265,9 +1312,9 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>>>  \t\t\t * adjusting a requested one, changes here shouldn't set the status\n>>>  \t\t\t * to Adjusted.\n>>>  \t\t\t */\n>>> -\t\t\tcfg.colorSpace->adjust(pixelFormat);\n>>> +\t\t\tcfg.colorSpace->adjust(cfg.pixelFormat);\n>>>  \t\t} else {\n>>> -\t\t\tif (cfg.colorSpace->adjust(pixelFormat)) {\n>>> +\t\t\tif (cfg.colorSpace->adjust(cfg.pixelFormat)) {\n>>>  \t\t\t\tLOG(SimplePipeline, Debug)\n>>>  \t\t\t\t\t<< \"Color space adjusted to \"\n>>>  \t\t\t\t\t<< cfg.colorSpace.value().toString();\n>>> @@ -1275,7 +1322,7 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>>>  \t\t\t}\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>>> @@ -1298,7 +1345,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,","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 BF2ABBD80A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  5 Dec 2025 14:08:27 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8FDBE613D4;\n\tFri,  5 Dec 2025 15:08:26 +0100 (CET)","from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 17FFA606A0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  5 Dec 2025 15:08:23 +0100 (CET)","from maestria.local.igalia.com ([192.168.10.14]\n\thelo=mail.igalia.com) by fanzine2.igalia.com with esmtps \n\t(Cipher TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256)\n\t(Exim) id 1vRWTt-0096E6-SH; Fri, 05 Dec 2025 15:08:21 +0100","from webmail.service.igalia.com ([192.168.21.45])\n\tby mail.igalia.com with esmtp (Exim)\n\tid 1vRWTr-007myr-Ez; Fri, 05 Dec 2025 15:08:21 +0100","from localhost ([127.0.0.1] helo=webmail.igalia.com)\n\tby webmail with esmtp (Exim 4.96) (envelope-from <uajain@igalia.com>)\n\tid 1vRWTq-004bzD-3C; Fri, 05 Dec 2025 15:08:19 +0100"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=igalia.com header.i=@igalia.com\n\theader.b=\"h6rbAkOy\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com;\n\ts=20170329;\n\th=Content-Transfer-Encoding:Content-Type:Message-ID:References:\n\tIn-Reply-To:Subject:Cc:To:From:Date:MIME-Version:Sender:Reply-To:Content-ID:\n\tContent-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc\n\t:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe:\n\tList-Post:List-Owner:List-Archive;\n\tbh=o05QO+PyCJpCeKHurgGUFAnTb2JMkY9vG+E3zSA94HM=;\n\tb=h6rbAkOy5l/R2w3GQtspAihZU2\n\t+6vlrdLD8UpRFfEfA519DEqeVqMCTUKcHh6Pr91A2na6XQapv2lAYFijEoJuvxoCE/G89qn2imUHI\n\t4h/YfO6fmV/vljLOnsNp65uQ1d/R9CfF3TyZ8TWVnby09aBlCR+AzwFGd4ukf+UHjJxc9ijCrCDoZ\n\tC+FCcdPT1iAOC2nyB6udnjQQx8/KCZo3R3WHbxVRtTmGzlp3OlDoG/MvvWhG8kzEB2iHee/13rH4r\n\tqg9oSVwFXfldYpHM0RfSvB1VYaVv8bT8agY0FmiZLWccw+mPG+qHR2503WeakyrAPUuPFf0SePBOV\n\txO/ghTJg==;","MIME-Version":"1.0","Date":"Fri, 05 Dec 2025 19:38:19 +0530","From":"Umang Jain <uajain@igalia.com>","To":"Milan Zamazal <mzamazal@redhat.com>","Cc":"libcamera-devel@lists.libcamera.org, Laurent Pinchart\n\t<laurent.pinchart@ideasonboard.com>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>, =?utf-8?q?Barnab?=\n\t=?utf-8?b?w6FzIFDFkWN6ZQ==?= <barnabas.pocze@ideasonboard.com>,\n\tPaul Elder <paul.elder@ideasonboard.com>,  Pavel Machek <pavel@ucw.cz>","Subject":"Re: [PATCH v17 3/7] libcamera: simple: Validate raw stream\n\tconfigurations","In-Reply-To":"<85ms3x6yg1.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","References":"<20251204164918.83334-1-mzamazal@redhat.com>\n\t<20251204164918.83334-4-mzamazal@redhat.com>\n\t<b15ed70145928b172fe35f77bc5c48e4@igalia.com>\n\t<85ms3x6yg1.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","Message-ID":"<d66c45d39185e33efcf18fa1d5cc20c0@igalia.com>","X-Sender":"uajain@igalia.com","Content-Type":"text/plain; charset=US-ASCII","Content-Transfer-Encoding":"7bit","X-Spam-Report":"NO, Score=-2.7, Tests=ALL_TRUSTED=-3, AWL=-2.091, BAYES_50=0.8,\n\tSUBJECT_DRUG_GAP_L=1.561","X-Spam-Score":"-26","X-Spam-Bar":"--","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>"}},{"id":37359,"web_url":"https://patchwork.libcamera.org/comment/37359/","msgid":"<48d9e76d518bfb31e61b236333f2757b@igalia.com>","date":"2025-12-13T08:48:16","subject":"Re: [PATCH v17 3/7] libcamera: simple: Validate raw stream\n\tconfigurations","submitter":{"id":232,"url":"https://patchwork.libcamera.org/api/people/232/","name":"Umang Jain","email":"uajain@igalia.com"},"content":"On 2025-12-04 22:19, Milan Zamazal wrote:\n> SimpleCameraConfiguration::validate() looks for the best configuration.\n> As part of enabling raw stream support, the method must consider raw\n> streams in addition to the processed streams.\n> \n> Raw streams are adjusted from the capture format and size.\n> \n> Configuration validation computes the maximum size of all the requested\n> streams and compares it to the output sizes.  When e.g. only a raw\n> stream is requested then this may result in an invalid adjustment of its\n> size.  This is because the output sizes are computed for processed\n> streams and may be smaller than capture sizes.  If a raw stream with the\n> capture size is requested, it may then be wrongly adjusted to a larger\n> size because the output sizes, which are irrelevant for raw streams\n> anyway, are smaller than the requested capture size.  The problem is\n> resolved by tracking raw and processed streams maximum sizes separately\n> and comparing raw stream sizes against capture rather than output sizes.\n> \n> Note that with both processed and raw streams, the requested sizes must\n> be mutually matching, including resizing due to debayer requirements.\n> For 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> \n> rather than\n> \n>   cam -s role=viewfinder,width=1920,height=1080 \\\n>       -s role=raw,width=1920,height=1080\n> \n> due to the resolution of 1924x1080 actually selected for debayering to\n> 1920x1080.  If the resolutions don't match mutually or don't match the\n> available sizes, validation adjusts them.\n> \n> Setting up the right configurations is still not enough to make the raw\n> streams working.  Buffer handling must be changed in the simple\n> pipeline, which is addressed in followup patches.\n> \n> Co-developed-by: Umang Jain <uajain@igalia.com>\n> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n\nSigned-off-by: Umang Jain <uajain@igalia.com>\n\n> ---\n>  src/libcamera/pipeline/simple/simple.cpp | 117 ++++++++++++++++-------\n>  1 file changed, 82 insertions(+), 35 deletions(-)\n> \n> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n> index 6b83254fb..aaafe0571 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> @@ -1138,29 +1140,57 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>  \t\tstatus = Adjusted;\n>  \t}\n>  \n> -\t/* Find the largest stream size. */\n> -\tSize maxStreamSize;\n> -\tfor (const StreamConfiguration &cfg : config_)\n> -\t\tmaxStreamSize.expandTo(cfg.size);\n> +\t/* Find the largest stream sizes. */\n> +\tSize maxProcessedStreamSize;\n> +\tSize maxRawStreamSize;\n> +\tfor (const StreamConfiguration &cfg : config_) {\n> +\t\tif (isRaw(cfg))\n> +\t\t\tmaxRawStreamSize.expandTo(cfg.size);\n> +\t\telse\n> +\t\t\tmaxProcessedStreamSize.expandTo(cfg.size);\n> +\t}\n>  \n>  \tLOG(SimplePipeline, Debug)\n> -\t\t<< \"Largest stream size is \" << maxStreamSize;\n> +\t\t<< \"Largest processed stream size is \" << maxProcessedStreamSize;\n> +\tLOG(SimplePipeline, Debug)\n> +\t\t<< \"Largest raw stream size is \" << maxRawStreamSize;\n> +\n> +\t/* Cap the number of raw stream configurations */\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> @@ -1182,8 +1212,10 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>  \t\tconst Size &captureSize = pipeConfig->captureSize;\n>  \t\tconst Size &maxOutputSize = pipeConfig->outputSizes.max;\n>  \n> -\t\tif (maxOutputSize.width >= maxStreamSize.width &&\n> -\t\t    maxOutputSize.height >= maxStreamSize.height) {\n> +\t\tif (maxOutputSize.width >= maxProcessedStreamSize.width &&\n> +\t\t    maxOutputSize.height >= maxProcessedStreamSize.height &&\n> +\t\t    captureSize.width >= maxRawStreamSize.width &&\n> +\t\t    captureSize.height >= maxRawStreamSize.height) {\n>  \t\t\tif (!pipeConfig_ || captureSize < pipeConfig_->captureSize)\n>  \t\t\t\tpipeConfig_ = pipeConfig;\n>  \t\t}\n> @@ -1201,7 +1233,8 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>  \t\t<< V4L2SubdeviceFormat{ pipeConfig_->code, pipeConfig_->sensorSize, {} }\n>  \t\t<< \" -> \" << pipeConfig_->captureSize\n>  \t\t<< \"-\" << pipeConfig_->captureFormat\n> -\t\t<< \" for max stream size \" << maxStreamSize;\n> +\t\t<< \" for max processed stream size \" << maxProcessedStreamSize\n> +\t\t<< \" and max raw stream size \" << maxRawStreamSize;\n>  \n>  \t/*\n>  \t * Adjust the requested streams.\n> @@ -1220,21 +1253,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> @@ -1245,7 +1292,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> @@ -1265,9 +1312,9 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>  \t\t\t * adjusting a requested one, changes here shouldn't set the status\n>  \t\t\t * to Adjusted.\n>  \t\t\t */\n> -\t\t\tcfg.colorSpace->adjust(pixelFormat);\n> +\t\t\tcfg.colorSpace->adjust(cfg.pixelFormat);\n>  \t\t} else {\n> -\t\t\tif (cfg.colorSpace->adjust(pixelFormat)) {\n> +\t\t\tif (cfg.colorSpace->adjust(cfg.pixelFormat)) {\n>  \t\t\t\tLOG(SimplePipeline, Debug)\n>  \t\t\t\t\t<< \"Color space adjusted to \"\n>  \t\t\t\t\t<< cfg.colorSpace.value().toString();\n> @@ -1275,7 +1322,7 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>  \t\t\t}\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> @@ -1298,7 +1345,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,","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 8CED2BD1F1\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 13 Dec 2025 08:48:25 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 80B3361923;\n\tSat, 13 Dec 2025 09:48:24 +0100 (CET)","from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A9425615B2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 13 Dec 2025 09:48:20 +0100 (CET)","from maestria.local.igalia.com ([192.168.10.14]\n\thelo=mail.igalia.com) by fanzine2.igalia.com with esmtps \n\t(Cipher TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256)\n\t(Exim) id 1vULIZ-00CJaJ-3k; Sat, 13 Dec 2025 09:48:19 +0100","from webmail.service.igalia.com ([192.168.21.45])\n\tby mail.igalia.com with esmtp (Exim)\n\tid 1vULIW-00GhAO-Ps; Sat, 13 Dec 2025 09:48:18 +0100","from localhost ([127.0.0.1] helo=webmail.igalia.com)\n\tby webmail with esmtp (Exim 4.96) (envelope-from <uajain@igalia.com>)\n\tid 1vULIW-006ICi-13; Sat, 13 Dec 2025 09:48:16 +0100"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=igalia.com header.i=@igalia.com\n\theader.b=\"fcQiVnM8\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com;\n\ts=20170329;\n\th=Content-Transfer-Encoding:Content-Type:Message-ID:References:\n\tIn-Reply-To:Subject:Cc:To:From:Date:MIME-Version:Sender:Reply-To:Content-ID:\n\tContent-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc\n\t:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe:\n\tList-Post:List-Owner:List-Archive;\n\tbh=Q0trvMqX14iX4+MsBFHd6TUdgNMHbqVaJnjDS9D+ZTk=;\n\tb=fcQiVnM8xAs47pMTwyt/riDeDu\n\tEeuQCJh/6u94/w20XIFya3nwNYOV95lrIhfe0mrSRGxVprB+u6IUw3MrBb6/SK6Azdv034EZbwcR9\n\tzpko1NmNw/c9H3fhTF6xqd/hDXA0mUdgDzlFKWSNok7Nq3Ci/rAs0rHzZadGL4QZZRJiYyE18u6YT\n\tX7OlmCHQvOJDpI1fbSDk1xQQGdCSAxvEmTlXVj63UAk4GBnGoJSFFGhUn2jicpFwiVZy+2fx979dN\n\tmPDqUL2qHr8kNe0ycg00C/26OBxknctfgbb/k68jpfBPoOoqfHgXUw/DtEorY+0MydwrxnMJqnBRg\n\t3Qrvizew==;","MIME-Version":"1.0","Date":"Sat, 13 Dec 2025 14:18:16 +0530","From":"Umang Jain <uajain@igalia.com>","To":"Milan Zamazal <mzamazal@redhat.com>","Cc":"libcamera-devel@lists.libcamera.org, Laurent Pinchart\n\t<laurent.pinchart@ideasonboard.com>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>, =?utf-8?q?Barnab?=\n\t=?utf-8?b?w6FzIFDFkWN6ZQ==?= <barnabas.pocze@ideasonboard.com>,\n\tPaul Elder <paul.elder@ideasonboard.com>,  Pavel Machek <pavel@ucw.cz>","Subject":"Re: [PATCH v17 3/7] libcamera: simple: Validate raw stream\n\tconfigurations","In-Reply-To":"<20251204164918.83334-4-mzamazal@redhat.com>","References":"<20251204164918.83334-1-mzamazal@redhat.com>\n\t<20251204164918.83334-4-mzamazal@redhat.com>","Message-ID":"<48d9e76d518bfb31e61b236333f2757b@igalia.com>","X-Sender":"uajain@igalia.com","Content-Type":"text/plain; charset=US-ASCII","Content-Transfer-Encoding":"7bit","X-Spam-Report":"NO, Score=-2.7, Tests=ALL_TRUSTED=-3, AWL=-2.025, BAYES_50=0.8,\n\tSUBJECT_DRUG_GAP_L=1.561","X-Spam-Score":"-26","X-Spam-Bar":"--","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>"}}]