{"id":25361,"url":"https://patchwork.libcamera.org/api/1.1/patches/25361/?format=json","web_url":"https://patchwork.libcamera.org/patch/25361/","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":"<20251204164918.83334-8-mzamazal@redhat.com>","date":"2025-12-04T16:49:16","name":"[v17,7/7] libcamera: simple: Make raw streams working","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"c19b12093a92cae83b4e74a3549e05e4b29661bc","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/25361/mbox/","series":[{"id":5637,"url":"https://patchwork.libcamera.org/api/1.1/series/5637/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5637","date":"2025-12-04T16:49:09","name":"Enable raw streams with software ISP","version":17,"mbox":"https://patchwork.libcamera.org/series/5637/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/25361/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/25361/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 C3182C3257\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  4 Dec 2025 16:50:00 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 749B861168;\n\tThu,  4 Dec 2025 17:50:00 +0100 (CET)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 72A8C61130\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  4 Dec 2025 17:49:58 +0100 (CET)","from mx-prod-mc-06.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-65-1l66faZpPFm6ro5Ex34jmQ-1;\n\tThu, 04 Dec 2025 11:49:54 -0500","from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com\n\t(mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4])\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-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTPS id 8369C1801215; Thu,  4 Dec 2025 16:49:52 +0000 (UTC)","from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.71])\n\tby mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTP id 84955300F965; Thu,  4 Dec 2025 16:49:49 +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=\"Y/V2PaZ3\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1764866997;\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=zpMYkjK2rdwL6bm50gkXp0mnyPRrXqxVagNhGxPjEBU=;\n\tb=Y/V2PaZ3VOn7R/Kki+M7p0ORqYJm6v8URXYmSu6x8t3a5obOL2ZFEVfG+5EIE4afGcB8eE\n\tqFkc8GIsYGSBWB9xB476+pGXIlQ9q+Alaf2zN1VJtAuWbu1YCxwQK1qP8pM9XsmgWLgrO/\n\tjuM/3kfHyq6guFj+cHt6TEm9CkMypsA=","X-MC-Unique":"1l66faZpPFm6ro5Ex34jmQ-1","X-Mimecast-MFC-AGG-ID":"1l66faZpPFm6ro5Ex34jmQ_1764866992","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>,\n\tPavel Machek <pavel@ucw.cz>","Subject":"[PATCH v17 7/7] libcamera: simple: Make raw streams working","Date":"Thu,  4 Dec 2025 17:49:16 +0100","Message-ID":"<20251204164918.83334-8-mzamazal@redhat.com>","In-Reply-To":"<20251204164918.83334-1-mzamazal@redhat.com>","References":"<20251204164918.83334-1-mzamazal@redhat.com>","MIME-Version":"1.0","X-Scanned-By":"MIMEDefang 3.4.1 on 10.30.177.4","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"kPAGXRKdPpefS_ek8-kgoPU96OjwifQtNV3bdxdsLe4_1764866992","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":"When a raw stream is requested, whether alone or together with a\nprocessed stream, its buffers must be handled outside the software ISP\nmachinery.  They serve as output buffers, even when a processed stream\nis produced.  But when both processed and raw streams are requested, the\nbuffer can be completed only after the processing is finished, to make\nsure it's untouched by the application as long as the processing runs.\n\nAt most one raw stream and at most one processed stream are supported\nand can be combined.  An example of producing both raw and processed\nfiles 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\nNote the difference in viewfinder and raw stream sizes due to the fact\nthat debayering requires enlarging the image width, which enforces\nselecting a larger sensor resolution in this case.\n\nIn order to track whether a raw stream is requested and which one it is,\nSimpleCameraData::rawStream_ member variable is introduced.\n\nThis is the final step to make raw streams working.\n\nReviewed-by: Umang Jain <uajain@igalia.com>\nSigned-off-by: Milan Zamazal <mzamazal@redhat.com>\n---\n src/libcamera/pipeline/simple/simple.cpp | 47 +++++++++++++++++-------\n 1 file changed, 34 insertions(+), 13 deletions(-)","diff":"diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\nindex 42ac69503..c0dbccca7 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@@ -1553,13 +1565,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@@ -1590,7 +1607,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@@ -1613,7 +1630,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@@ -1621,8 +1638,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@@ -1663,8 +1683,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@@ -1715,7 +1736,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 {\n","prefixes":["v17","7/7"]}