Show a patch.

GET /api/patches/11097/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 11097,
    "url": "https://patchwork.libcamera.org/api/patches/11097/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/11097/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20210131224702.8838-19-laurent.pinchart@ideasonboard.com>",
    "date": "2021-01-31T22:47:00",
    "name": "[libcamera-devel,18/20] libcamera: pipeline: simple: Support configuration of multiple streams",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "3eedce3aafc32f087b9e88eb8b23fa6f658214f1",
    "submitter": {
        "id": 2,
        "url": "https://patchwork.libcamera.org/api/people/2/?format=api",
        "name": "Laurent Pinchart",
        "email": "laurent.pinchart@ideasonboard.com"
    },
    "delegate": {
        "id": 14,
        "url": "https://patchwork.libcamera.org/api/users/14/?format=api",
        "username": "pinchartl",
        "first_name": "Laurent",
        "last_name": "Pinchart",
        "email": "laurent.pinchart@ideasonboard.com"
    },
    "mbox": "https://patchwork.libcamera.org/patch/11097/mbox/",
    "series": [
        {
            "id": 1633,
            "url": "https://patchwork.libcamera.org/api/series/1633/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1633",
            "date": "2021-01-31T22:46:42",
            "name": "[libcamera-devel,01/20] libcamera: pipeline: simple: Manage converter with std::unique_ptr<>",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/1633/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/11097/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/11097/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 8F3C8BD808\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 31 Jan 2021 22:47:47 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5304D683F7;\n\tSun, 31 Jan 2021 23:47:47 +0100 (CET)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 6BDAB683F0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 31 Jan 2021 23:47:35 +0100 (CET)",
            "from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id C19B41838; \n\tSun, 31 Jan 2021 23:47:34 +0100 (CET)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"ZKjDDOx4\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1612133255;\n\tbh=c8JhsNI3GFEXO5I0AruI7LmSmioJBCP29agTnw6OfwA=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=ZKjDDOx4hX34YNSAgYRqozEIU1j6YuIheZe1knDtcWhZ2f0O3dJjRzAvAtLy5HhvW\n\tD80Epu5WkJEM47mmrvM2VipN6QNZhsR2MCm2hOHev0iOxWXzaWJRwMNUu5lk3CP9pS\n\t3Og32jtHq6oQ1O//Zus1Cmi6xMDya4fo1Tc87+nw=",
        "From": "Laurent Pinchart <laurent.pinchart@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Mon,  1 Feb 2021 00:47:00 +0200",
        "Message-Id": "<20210131224702.8838-19-laurent.pinchart@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.28.0",
        "In-Reply-To": "<20210131224702.8838-1-laurent.pinchart@ideasonboard.com>",
        "References": "<20210131224702.8838-1-laurent.pinchart@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Subject": "[libcamera-devel] [PATCH 18/20] libcamera: pipeline: simple:\n\tSupport configuration of multiple streams",
        "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>",
        "Cc": "Phi-Bang Nguyen <pnguyen@baylibre.com>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "Extend the SimpleCameraConfiguration to support multiple streams, using\nthe multi-stream capability of the SimpleConverter class. Wiring up\nmulti-stream support in the other pipeline handler operations will come\nin further commits.\n\nTo keep the code simple, require all streams to use the converter if any\nstream needs it. It would be possible to generate one stream without\nconversion (provided the format and size match what the capture device\ncan generate), and this is left as a future optimization.\n\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n src/libcamera/pipeline/simple/simple.cpp | 174 ++++++++++++++---------\n 1 file changed, 104 insertions(+), 70 deletions(-)",
    "diff": "diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\nindex c987e1a0d9cb..58e5f0d23139 100644\n--- a/src/libcamera/pipeline/simple/simple.cpp\n+++ b/src/libcamera/pipeline/simple/simple.cpp\n@@ -538,62 +538,94 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n \t}\n \n \t/* Cap the number of entries to the available streams. */\n-\tif (config_.size() > 1) {\n-\t\tconfig_.resize(1);\n+\tif (config_.size() > data_->streams_.size()) {\n+\t\tconfig_.resize(data_->streams_.size());\n \t\tstatus = Adjusted;\n \t}\n \n-\tStreamConfiguration &cfg = config_[0];\n-\n-\t/* Adjust the pixel format. */\n-\tauto it = data_->formats_.find(cfg.pixelFormat);\n-\tif (it == data_->formats_.end())\n-\t\tit = data_->formats_.begin();\n-\n-\tPixelFormat pixelFormat = it->first;\n-\tif (cfg.pixelFormat != pixelFormat) {\n-\t\tLOG(SimplePipeline, Debug) << \"Adjusting pixel format\";\n-\t\tcfg.pixelFormat = pixelFormat;\n-\t\tstatus = Adjusted;\n-\t}\n-\n-\tpipeConfig_ = it->second;\n-\tif (!pipeConfig_->outputSizes.contains(cfg.size)) {\n-\t\tLOG(SimplePipeline, Debug)\n-\t\t\t<< \"Adjusting size from \" << cfg.size.toString()\n-\t\t\t<< \" to \" << pipeConfig_->captureSize.toString();\n-\t\tcfg.size = pipeConfig_->captureSize;\n-\t\tstatus = Adjusted;\n-\t}\n-\n-\tneedConversion_ = cfg.pixelFormat != pipeConfig_->captureFormat\n-\t\t\t|| cfg.size != pipeConfig_->captureSize;\n-\n-\tcfg.bufferCount = 3;\n-\n-\t/* Set the stride and frameSize. */\n-\tif (!needConversion_) {\n-\t\tV4L2DeviceFormat format;\n-\t\tformat.fourcc = data_->video_->toV4L2PixelFormat(cfg.pixelFormat);\n-\t\tformat.size = cfg.size;\n-\n-\t\tint ret = data_->video_->tryFormat(&format);\n-\t\tif (ret < 0)\n-\t\t\treturn Invalid;\n-\n-\t\tcfg.stride = format.planes[0].bpl;\n-\t\tcfg.frameSize = format.planes[0].size;\n-\n-\t\treturn status;\n+\t/*\n+\t * Pick a configuration for the pipeline based on the pixel format for\n+\t * the streams (ordered from highest to lowest priority). Default to\n+\t * the first pipeline configuration if no streams requests a supported\n+\t * pixel format.\n+\t */\n+\tpipeConfig_ = 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\tpipeConfig_ = it->second;\n+\t\t\tbreak;\n+\t\t}\n \t}\n \n+\t/* Adjust the requested streams. */\n \tSimplePipelineHandler *pipe = static_cast<SimplePipelineHandler *>(data_->pipe_);\n \tSimpleConverter *converter = pipe->converter();\n \n-\tstd::tie(cfg.stride, cfg.frameSize) =\n-\t\tconverter->strideAndFrameSize(cfg.pixelFormat, cfg.size);\n-\tif (cfg.stride == 0)\n-\t\treturn Invalid;\n+\t/*\n+\t * Enable usage of the converter when producing multiple streams, as\n+\t * the video capture device can't capture to multiple buffers.\n+\t *\n+\t * It is possible to produce up to one stream without conversion\n+\t * (provided the format and size match), at the expense of more complex\n+\t * buffer handling (including allocation of internal buffers to be used\n+\t * when a request doesn't contain a buffer for the stream that doesn't\n+\t * require any conversion, similar to raw capture use cases). This is\n+\t * left as a future improvement.\n+\t */\n+\tneedConversion_ = config_.size() > 1;\n+\n+\tfor (unsigned int i = 0; i < config_.size(); ++i) {\n+\t\tStreamConfiguration &cfg = config_[i];\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) << \"Adjusting pixel format\";\n+\t\t\tcfg.pixelFormat = pixelFormat;\n+\t\t\tstatus = Adjusted;\n+\t\t}\n+\n+\t\tif (!pipeConfig_->outputSizes.contains(cfg.size)) {\n+\t\t\tLOG(SimplePipeline, Debug)\n+\t\t\t\t<< \"Adjusting size from \" << cfg.size.toString()\n+\t\t\t\t<< \" to \" << pipeConfig_->captureSize.toString();\n+\t\t\tcfg.size = pipeConfig_->captureSize;\n+\t\t\tstatus = Adjusted;\n+\t\t}\n+\n+\t\tif (cfg.pixelFormat != pipeConfig_->captureFormat ||\n+\t\t    cfg.size != pipeConfig_->captureSize)\n+\t\t\tneedConversion_ = true;\n+\n+\t\t/* Set the stride, frameSize and bufferCount. */\n+\t\tif (needConversion_) {\n+\t\t\tstd::tie(cfg.stride, cfg.frameSize) =\n+\t\t\t\tconverter->strideAndFrameSize(cfg.pixelFormat, cfg.size);\n+\t\t\tif (cfg.stride == 0)\n+\t\t\t\treturn Invalid;\n+\t\t} else {\n+\t\t\tV4L2DeviceFormat format;\n+\t\t\tformat.fourcc = data_->video_->toV4L2PixelFormat(cfg.pixelFormat);\n+\t\t\tformat.size = cfg.size;\n+\n+\t\t\tint ret = data_->video_->tryFormat(&format);\n+\t\t\tif (ret < 0)\n+\t\t\t\treturn Invalid;\n+\n+\t\t\tcfg.stride = format.planes[0].bpl;\n+\t\t\tcfg.frameSize = format.planes[0].size;\n+\t\t}\n+\n+\t\tcfg.bufferCount = 3;\n+\t}\n \n \treturn status;\n }\n@@ -628,16 +660,18 @@ CameraConfiguration *SimplePipelineHandler::generateConfiguration(Camera *camera\n \t\t       });\n \n \t/*\n-\t * Create the stream configuration. Take the first entry in the formats\n+\t * Create the stream configurations. Take the first entry in the formats\n \t * map as the default, for lack of a better option.\n \t *\n \t * \\todo Implement a better way to pick the default format\n \t */\n-\tStreamConfiguration cfg{ StreamFormats{ formats } };\n-\tcfg.pixelFormat = formats.begin()->first;\n-\tcfg.size = formats.begin()->second[0].max;\n+\tfor ([[maybe_unused]] StreamRole role : roles) {\n+\t\tStreamConfiguration cfg{ StreamFormats{ formats } };\n+\t\tcfg.pixelFormat = formats.begin()->first;\n+\t\tcfg.size = formats.begin()->second[0].max;\n \n-\tconfig->addConfiguration(cfg);\n+\t\tconfig->addConfiguration(cfg);\n+\t}\n \n \tconfig->validate();\n \n@@ -650,7 +684,6 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)\n \t\tstatic_cast<SimpleCameraConfiguration *>(c);\n \tSimpleCameraData *data = cameraData(camera);\n \tV4L2VideoDevice *video = data->video_;\n-\tStreamConfiguration &cfg = config->at(0);\n \tint ret;\n \n \t/*\n@@ -694,28 +727,29 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)\n \t\treturn -EINVAL;\n \t}\n \n-\t/* Configure the converter if required. */\n+\t/* Configure the converter if needed. */\n+\tstd::vector<std::reference_wrapper<StreamConfiguration>> outputCfgs;\n \tdata->useConverter_ = config->needConversion();\n-\tif (data->useConverter_) {\n-\t\tStreamConfiguration inputCfg;\n-\t\tinputCfg.pixelFormat = pipeConfig->captureFormat;\n-\t\tinputCfg.size = pipeConfig->captureSize;\n-\t\tinputCfg.stride = captureFormat.planes[0].bpl;\n-\t\tinputCfg.bufferCount = kNumInternalBuffers;\n \n-\t\tret = converter_->configure(inputCfg, { cfg });\n-\t\tif (ret < 0) {\n-\t\t\tLOG(SimplePipeline, Error)\n-\t\t\t\t<< \"Unable to configure converter\";\n-\t\t\treturn ret;\n-\t\t}\n+\tfor (unsigned int i = 0; i < config->size(); ++i) {\n+\t\tStreamConfiguration &cfg = config->at(i);\n \n-\t\tLOG(SimplePipeline, Debug) << \"Using format converter\";\n+\t\tcfg.setStream(&data->streams_[i]);\n+\n+\t\tif (data->useConverter_)\n+\t\t\toutputCfgs.push_back(cfg);\n \t}\n \n-\tcfg.setStream(&data->streams_[0]);\n+\tif (outputCfgs.empty())\n+\t\treturn 0;\n \n-\treturn 0;\n+\tStreamConfiguration inputCfg;\n+\tinputCfg.pixelFormat = pipeConfig->captureFormat;\n+\tinputCfg.size = pipeConfig->captureSize;\n+\tinputCfg.stride = captureFormat.planes[0].bpl;\n+\tinputCfg.bufferCount = kNumInternalBuffers;\n+\n+\treturn converter_->configure(inputCfg, outputCfgs);\n }\n \n int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,\n",
    "prefixes": [
        "libcamera-devel",
        "18/20"
    ]
}