[{"id":37102,"web_url":"https://patchwork.libcamera.org/comment/37102/","msgid":"<cd01b1f32a8beef2eaa05c965f4d15fe@igalia.com>","date":"2025-11-28T17:25:56","subject":"Re: [PATCH v16 7/9] libcamera: simple: Make raw streams working","submitter":{"id":232,"url":"https://patchwork.libcamera.org/api/people/232/","name":"Umang Jain","email":"uajain@igalia.com"},"content":"On 2025-11-28 02:49, Milan Zamazal wrote:\n> When a raw stream is requested, whether alone or together with a\n> processed stream, its buffers must be handled outside the software ISP\n> machinery.  They serve as output buffers, even when a processed stream\n> is produced.  But when both processed and raw streams are requested, the\n> buffer can be completed only after the processing is finished, to make\n> sure it's untouched by the application as long as the processing runs.\n> \n> At most one raw stream and at most one processed stream are supported\n> and can be combined.  An example of producing both raw and processed\n> files using `cam' application:\n> \n>   cam -c1 -C100 -Ffile# \\\n>     -s role=viewfinder,width=1920,height=1080,pixelformat=RGB888 \\\n>     -s role=raw,width=3280,height=2464,pixelformat=SRGGB8\n> \n> Note the difference in viewfinder and raw stream sizes due to the fact\n> that debayering requires enlarging the image width, which enforces\n> selecting a larger sensor resolution in this case.\n> \n> In order to track whether a raw stream is requested and which one it is,\n> SimpleCameraData::rawStream_ member variable is introduced.\n> \n> This is the final step to make raw streams working.\n> \n> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n\nReviewed-by: Umang Jain <uajain@igalia.com>\n\n> ---\n>  src/libcamera/pipeline/simple/simple.cpp | 47 +++++++++++++++++-------\n>  1 file changed, 34 insertions(+), 13 deletions(-)\n> \n> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n> index 927875586..cbfc06fb6 100644\n> --- a/src/libcamera/pipeline/simple/simple.cpp\n> +++ b/src/libcamera/pipeline/simple/simple.cpp\n> @@ -333,6 +333,7 @@ public:\n>  \t};\n>  \n>  \tstd::vector<Stream> streams_;\n> +\tStream *rawStream_;\n>  \n>  \t/*\n>  \t * All entities in the pipeline, from the camera sensor to the video\n> @@ -468,7 +469,7 @@ private:\n>  SimpleCameraData::SimpleCameraData(SimplePipelineHandler *pipe,\n>  \t\t\t\t   unsigned int numStreams,\n>  \t\t\t\t   MediaEntity *sensor)\n> -\t: Camera::Private(pipe), streams_(numStreams)\n> +\t: Camera::Private(pipe), streams_(numStreams), rawStream_(nullptr)\n>  {\n>  \t/*\n>  \t * Find the shortest path from the camera sensor to a video capture\n> @@ -884,10 +885,13 @@ void SimpleCameraData::imageBufferReady(FrameBuffer *buffer)\n>  \t * point converting an erroneous buffer.\n>  \t */\n>  \tif (buffer->metadata().status != FrameMetadata::FrameSuccess) {\n> -\t\tif (!useConversion_) {\n> +\t\tif (!useConversion_ || rawStream_) {\n>  \t\t\t/* No conversion, just complete the request. */\n>  \t\t\tRequest *request = buffer->request();\n>  \t\t\tpipe->completeBuffer(request, buffer);\n> +\t\t\tSimpleFrameInfo *info = frameInfo_.find(request->sequence());\n> +\t\t\tif (info)\n> +\t\t\t\tinfo->metadataRequired = false;\n>  \t\t\ttryCompleteRequest(request);\n>  \t\t\treturn;\n>  \t\t}\n> @@ -946,7 +950,8 @@ void SimpleCameraData::imageBufferReady(FrameBuffer *buffer)\n>  \t */\n>  \tif (useConversion_) {\n>  \t\tif (conversionQueue_.empty()) {\n> -\t\t\tvideo_->queueBuffer(buffer);\n> +\t\t\tif (!rawStream_)\n> +\t\t\t\tvideo_->queueBuffer(buffer);\n>  \t\t\treturn;\n>  \t\t}\n>  \n> @@ -998,8 +1003,15 @@ void SimpleCameraData::tryCompleteRequest(Request *request)\n>  \n>  void SimpleCameraData::conversionInputDone(FrameBuffer *buffer)\n>  {\n> -\t/* Queue the input buffer back for capture. */\n> -\tvideo_->queueBuffer(buffer);\n> +\tif (rawStream_) {\n> +\t\t/* Complete the input buffer as with raw-only processing. */\n> +\t\tRequest *request = buffer->request();\n> +\t\tif (pipe()->completeBuffer(request, buffer))\n> +\t\t\ttryCompleteRequest(request);\n> +\t} else {\n> +\t\t/* Queue the input buffer back for capture. */\n> +\t\tvideo_->queueBuffer(buffer);\n> +\t}\n>  }\n>  \n>  void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)\n> @@ -1543,13 +1555,18 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)\n>  \tstd::vector<std::reference_wrapper<StreamConfiguration>> outputCfgs;\n>  \tdata->useConversion_ = config->needConversion();\n>  \n> +\tdata->rawStream_ = nullptr;\n>  \tfor (unsigned int i = 0; i < config->size(); ++i) {\n>  \t\tStreamConfiguration &cfg = config->at(i);\n> +\t\tbool rawStream = isRaw(cfg);\n>  \n>  \t\tcfg.setStream(&data->streams_[i]);\n>  \n> -\t\tif (data->useConversion_ && !isRaw(cfg))\n> +\t\tif (data->useConversion_ && !rawStream)\n>  \t\t\toutputCfgs.push_back(cfg);\n> +\n> +\t\tif (rawStream)\n> +\t\t\tdata->rawStream_ = &data->streams_[i];\n>  \t}\n>  \n>  \tif (outputCfgs.empty())\n> @@ -1580,7 +1597,7 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,\n>  \t * Export buffers on the converter or capture video node, depending on\n>  \t * whether the converter is used or not.\n>  \t */\n> -\tif (data->useConversion_)\n> +\tif (data->useConversion_ && stream != data->rawStream_)\n>  \t\treturn data->converter_\n>  \t\t\t       ? data->converter_->exportBuffers(stream, count, buffers)\n>  \t\t\t       : data->swIsp_->exportBuffers(stream, count, buffers);\n> @@ -1603,7 +1620,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL\n>  \t\treturn -EBUSY;\n>  \t}\n>  \n> -\tif (data->useConversion_) {\n> +\tif (data->useConversion_ && !data->rawStream_) {\n>  \t\t/*\n>  \t\t * When using the converter allocate a fixed number of internal\n>  \t\t * buffers.\n> @@ -1611,8 +1628,11 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL\n>  \t\tret = video->allocateBuffers(kNumInternalBuffers,\n>  \t\t\t\t\t     &data->conversionBuffers_);\n>  \t} else {\n> -\t\t/* Otherwise, prepare for using buffers from the only stream. */\n> -\t\tStream *stream = &data->streams_[0];\n> +\t\t/*\n> +\t\t * Otherwise, prepare for using buffers from either the raw stream, if\n> +\t\t * requested, or the only stream configured.\n> +\t\t */\n> +\t\tStream *stream = (data->rawStream_ ? data->rawStream_ : &data->streams_[0]);\n>  \t\tret = video->importBuffers(stream->configuration().bufferCount);\n>  \t}\n>  \tif (ret < 0) {\n> @@ -1653,8 +1673,9 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL\n>  \t\t}\n>  \n>  \t\t/* Queue all internal buffers for capture. */\n> -\t\tfor (std::unique_ptr<FrameBuffer> &buffer : data->conversionBuffers_)\n> -\t\t\tvideo->queueBuffer(buffer.get());\n> +\t\tif (!data->rawStream_)\n> +\t\t\tfor (std::unique_ptr<FrameBuffer> &buffer : data->conversionBuffers_)\n> +\t\t\t\tvideo->queueBuffer(buffer.get());\n>  \t}\n>  \n>  \treturn 0;\n> @@ -1705,7 +1726,7 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)\n>  \t\t * queue, it will be handed to the converter in the capture\n>  \t\t * completion handler.\n>  \t\t */\n> -\t\tif (data->useConversion_) {\n> +\t\tif (data->useConversion_ && stream != data->rawStream_) {\n>  \t\t\tbuffers.emplace(stream, buffer);\n>  \t\t\tmetadataRequired = !!data->swIsp_;\n>  \t\t} else {","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 A4D77C0F2A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 28 Nov 2025 17:26:03 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E3AE1609DE;\n\tFri, 28 Nov 2025 18:26:02 +0100 (CET)","from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 6008E6069A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 28 Nov 2025 18:26:00 +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 1vP2EI-006WyP-GC; Fri, 28 Nov 2025 18:25:58 +0100","from webmail.service.igalia.com ([192.168.21.45])\n\tby mail.igalia.com with esmtp (Exim)\n\tid 1vP2EG-00HOUB-8I; Fri, 28 Nov 2025 18:25:58 +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 1vP2EF-003AQc-2V; Fri, 28 Nov 2025 18:25:56 +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=\"gidsyYdP\"; 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=f1L2q98u8AyAygMwol9LDhhBgjXjHbkuhLTt+Q3+f9U=;\n\tb=gidsyYdPCzsjq+npaYqbwSIWCn\n\tXWJtCpfF8y1J/CJhjH9n+076d5UN2CTNaUu3hHUCv94/bRVrFLVSjZPgUbGprFeshUhZg+5fKWKfo\n\tjwaKOBvdI76Ssm6lf3mP1wDHE2s4avzrzAfLqwBdSyLlWRcH6oMVStPoMPIYvz2rVtX62rE7n/TIk\n\tFiWGZZTXFqTfa4sn9Vz7pl7WHQYRn9W0dBvpg5CuKgc9c1IiIaiGN6Kyk2EKpldD5zYaKQ/bL7wkC\n\tmb4hJcB9JNCSgOPGpAXmTuUsPIwHFzWBFAzOQwmqtM/tjXxlSgl2RBzmXbeLKWpLHrx7pvkQ2dqxb\n\tkZnqZX/w==;","MIME-Version":"1.0","Date":"Fri, 28 Nov 2025 22:55:56 +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>","Subject":"Re: [PATCH v16 7/9] libcamera: simple: Make raw streams working","In-Reply-To":"<20251127211932.122463-8-mzamazal@redhat.com>","References":"<20251127211932.122463-1-mzamazal@redhat.com>\n\t<20251127211932.122463-8-mzamazal@redhat.com>","Message-ID":"<cd01b1f32a8beef2eaa05c965f4d15fe@igalia.com>","X-Sender":"uajain@igalia.com","Content-Type":"text/plain; charset=US-ASCII","Content-Transfer-Encoding":"7bit","X-Spam-Report":"NO, Score=-3.2, Tests=ALL_TRUSTED=-3, AWL=-1.021, BAYES_50=0.8,\n\tURIBL_BLOCKED=0.001, URIBL_DBL_BLOCKED_OPENDNS=0.001,\n\tURIBL_ZEN_BLOCKED_OPENDNS=0.001","X-Spam-Score":"-31","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>"}}]