[{"id":15393,"web_url":"https://patchwork.libcamera.org/comment/15393/","msgid":"<20210302063016.GD35047@pyrite.rasen.tech>","date":"2021-03-02T06:30:16","subject":"Re: [libcamera-devel] [PATCH 18/20] libcamera: pipeline: simple:\n\tSupport configuration of multiple streams","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Hi Laurent,\n\nOn Mon, Feb 01, 2021 at 12:47:00AM +0200, Laurent Pinchart wrote:\n> Extend the SimpleCameraConfiguration to support multiple streams, using\n> the multi-stream capability of the SimpleConverter class. Wiring up\n> multi-stream support in the other pipeline handler operations will come\n> in further commits.\n> \n> To keep the code simple, require all streams to use the converter if any\n> stream needs it. It would be possible to generate one stream without\n> conversion (provided the format and size match what the capture device\n> can generate), and this is left as a future optimization.\n> \n> Signed-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(-)\n> \n> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n> index 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\nDon't we also needConversion_ if we have only one stream but the\nformat/size doesn't match?\n\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\nIs this the right parameter order (did I miss a change earlier in this\nseries)?\n\n\nPaul\n\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,","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 444D6BD808\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  2 Mar 2021 06:30:26 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A558368A92;\n\tTue,  2 Mar 2021 07:30:25 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B3F9860522\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Mar 2021 07:30:23 +0100 (CET)","from pyrite.rasen.tech (unknown\n\t[IPv6:2400:4051:61:600:2c71:1b79:d06d:5032])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id DDAB445D;\n\tTue,  2 Mar 2021 07:30:21 +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=\"jIMnTMI+\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1614666623;\n\tbh=C//Hhy273ss8IHT6yNPn7c/igb2+4DGCRGEXd5jb26g=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=jIMnTMI+MFxug2DrdKceGNxGQ8nF9sGAsPAdXl8UDeBQsekzFAztH/VfqVtLebGbX\n\t9Pb1INUXgx+GbgYe3GtTXRwplP4MwUcz49WpB2mbgx8OMK6fQw0p6iwnUKpOaofOwH\n\tn2AmYZmUhHYBrr9BV6f/oPo/OsCjpEDCy99gIHao=","Date":"Tue, 2 Mar 2021 15:30:16 +0900","From":"paul.elder@ideasonboard.com","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Message-ID":"<20210302063016.GD35047@pyrite.rasen.tech>","References":"<20210131224702.8838-1-laurent.pinchart@ideasonboard.com>\n\t<20210131224702.8838-19-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20210131224702.8838-19-laurent.pinchart@ideasonboard.com>","Subject":"Re: [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>,\n\tlibcamera-devel@lists.libcamera.org","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>"}},{"id":15408,"web_url":"https://patchwork.libcamera.org/comment/15408/","msgid":"<YD4W36/khzC15VI8@pendragon.ideasonboard.com>","date":"2021-03-02T10:43:43","subject":"Re: [libcamera-devel] [PATCH 18/20] libcamera: pipeline: simple:\n\tSupport configuration of multiple streams","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nOn Tue, Mar 02, 2021 at 03:30:16PM +0900, paul.elder@ideasonboard.com wrote:\n> On Mon, Feb 01, 2021 at 12:47:00AM +0200, Laurent Pinchart wrote:\n> > Extend the SimpleCameraConfiguration to support multiple streams, using\n> > the multi-stream capability of the SimpleConverter class. Wiring up\n> > multi-stream support in the other pipeline handler operations will come\n> > in further commits.\n> > \n> > To keep the code simple, require all streams to use the converter if any\n> > stream needs it. It would be possible to generate one stream without\n> > conversion (provided the format and size match what the capture device\n> > can generate), and this is left as a future optimization.\n> > \n> > Signed-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(-)\n> > \n> > diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n> > index 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> Don't we also needConversion_ if we have only one stream but the\n> format/size doesn't match?\n\nWe do, and that's handled below...\n\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... here.\n\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> \n> Is this the right parameter order (did I miss a change earlier in this\n> series)?\n\nThe compiler doesn't complain :-)\n\nThe change is in \"[PATCH 03/20] libcamera: pipeline: simple: converter:\nGroup query functions together\".\n\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,","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 A1132BD808\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  2 Mar 2021 10:44:13 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 26E8C68A95;\n\tTue,  2 Mar 2021 11:44:13 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B70B568A92\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Mar 2021 11:44:11 +0100 (CET)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 1A6F145D;\n\tTue,  2 Mar 2021 11:44:11 +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=\"jrslKNYs\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1614681851;\n\tbh=0WWtcne0uI+0Axu7P2A4xHPBfdY0sQlcYqOLjty/6fY=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=jrslKNYsg3UklQgig9aeMSR2yvO16z/PcsLXlYHn/LtrmHlj+OugxHvTMonQHrOkm\n\t3Aw+YOXoGyPQ6/kywjeZaDCsDM7tCXw2+ndX5w9ZOi7mBJDflhMSS7kYbKfEZgClkQ\n\tFg3deMnaI0fjIYsNef2L3OLszPFCyAxj0aIFYol8=","Date":"Tue, 2 Mar 2021 12:43:43 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"paul.elder@ideasonboard.com","Message-ID":"<YD4W36/khzC15VI8@pendragon.ideasonboard.com>","References":"<20210131224702.8838-1-laurent.pinchart@ideasonboard.com>\n\t<20210131224702.8838-19-laurent.pinchart@ideasonboard.com>\n\t<20210302063016.GD35047@pyrite.rasen.tech>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20210302063016.GD35047@pyrite.rasen.tech>","Subject":"Re: [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>,\n\tlibcamera-devel@lists.libcamera.org","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>"}},{"id":15409,"web_url":"https://patchwork.libcamera.org/comment/15409/","msgid":"<718a0d4b-d544-e336-66be-21857dcbf274@ideasonboard.com>","date":"2021-03-02T10:46:28","subject":"Re: [libcamera-devel] [PATCH 18/20] libcamera: pipeline: simple:\n\tSupport configuration of multiple streams","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 02/03/2021 06:30, paul.elder@ideasonboard.com wrote:\n> Hi Laurent,\n> \n> On Mon, Feb 01, 2021 at 12:47:00AM +0200, Laurent Pinchart wrote:\n>> Extend the SimpleCameraConfiguration to support multiple streams, using\n>> the multi-stream capability of the SimpleConverter class. Wiring up\n>> multi-stream support in the other pipeline handler operations will come\n>> in further commits.\n>>\n>> To keep the code simple, require all streams to use the converter if any\n>> stream needs it. It would be possible to generate one stream without\n>> conversion (provided the format and size match what the capture device\n>> can generate), and this is left as a future optimization.\n>>\n>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\nSeems you've answered Pauls questions:\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n>> ---\n>>  src/libcamera/pipeline/simple/simple.cpp | 174 ++++++++++++++---------\n>>  1 file changed, 104 insertions(+), 70 deletions(-)\n>>\n>> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n>> index 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> Don't we also needConversion_ if we have only one stream but the\n> format/size doesn't match?>> +\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> \n> Is this the right parameter order (did I miss a change earlier in this\n> series)?\n> \n> \n> Paul\n> \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> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel\n>","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 29F00BD1F1\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  2 Mar 2021 10:46:34 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id AC8C868A95;\n\tTue,  2 Mar 2021 11:46:33 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C970C68A92\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Mar 2021 11:46:31 +0100 (CET)","from [192.168.0.20]\n\t(cpc89244-aztw30-2-0-cust3082.18-1.cable.virginm.net [86.31.172.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 16B8045D;\n\tTue,  2 Mar 2021 11:46:31 +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=\"Yk9folg5\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1614681991;\n\tbh=kD6fP8pzdPSOsmfy1FWXyVrWIcATYOhwQjdKElU8Fns=;\n\th=Reply-To:Subject:To:Cc:References:From:Date:In-Reply-To:From;\n\tb=Yk9folg5FYLWvQaSXnOzFA6Dfbra6GJn+LSz8kmLO7t/PJBYNjoGXJ5ffd2f5Sfwd\n\tXGEHW2DefcXi7eLLhX4ORxt38QR+wjGkfm2LSFR+fd3WGg7CxcjXYQv4dZv4uSWE2v\n\twxPaAs5Ec+iMo2h5dE6t9pBH4VKih4bgkyDIqHZ0=","To":"paul.elder@ideasonboard.com,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20210131224702.8838-1-laurent.pinchart@ideasonboard.com>\n\t<20210131224702.8838-19-laurent.pinchart@ideasonboard.com>\n\t<20210302063016.GD35047@pyrite.rasen.tech>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\tmQINBFYE/WYBEACs1PwjMD9rgCu1hlIiUA1AXR4rv2v+BCLUq//vrX5S5bjzxKAryRf0uHat\n\tV/zwz6hiDrZuHUACDB7X8OaQcwhLaVlq6byfoBr25+hbZG7G3+5EUl9cQ7dQEdvNj6V6y/SC\n\trRanWfelwQThCHckbobWiQJfK9n7rYNcPMq9B8e9F020LFH7Kj6YmO95ewJGgLm+idg1Kb3C\n\tpotzWkXc1xmPzcQ1fvQMOfMwdS+4SNw4rY9f07Xb2K99rjMwZVDgESKIzhsDB5GY465sCsiQ\n\tcSAZRxqE49RTBq2+EQsbrQpIc8XiffAB8qexh5/QPzCmR4kJgCGeHIXBtgRj+nIkCJPZvZtf\n\tKr2EAbc6tgg6DkAEHJb+1okosV09+0+TXywYvtEop/WUOWQ+zo+Y/OBd+8Ptgt1pDRyOBzL8\n\tRXa8ZqRf0Mwg75D+dKntZeJHzPRJyrlfQokngAAs4PaFt6UfS+ypMAF37T6CeDArQC41V3ko\n\tlPn1yMsVD0p+6i3DPvA/GPIksDC4owjnzVX9kM8Zc5Cx+XoAN0w5Eqo4t6qEVbuettxx55gq\n\t8K8FieAjgjMSxngo/HST8TpFeqI5nVeq0/lqtBRQKumuIqDg+Bkr4L1V/PSB6XgQcOdhtd36\n\tOe9X9dXB8YSNt7VjOcO7BTmFn/Z8r92mSAfHXpb07YJWJosQOQARAQABtDBLaWVyYW4gQmlu\n\tZ2hhbSA8a2llcmFuLmJpbmdoYW1AaWRlYXNvbmJvYXJkLmNvbT6JAlcEEwEKAEECGwMFCwkI\n\tBwIGFQgJCgsCBBYCAwECHgECF4ACGQEWIQSQLdeYP70o/eNy1HqhHkZyEKRh/QUCXWTtygUJ\n\tCyJXZAAKCRChHkZyEKRh/f8dEACTDsbLN2nioNZMwyLuQRUAFcXNolDX48xcUXsWS2QjxaPm\n\tVsJx8Uy8aYkS85mdPBh0C83OovQR/OVbr8AxhGvYqBs3nQvbWuTl/+4od7DfK2VZOoKBAu5S\n\tQK2FYuUcikDqYcFWJ8DQnubxfE8dvzojHEkXw0sA4igINHDDFX3HJGZtLio+WpEFQtCbfTAG\n\tYZslasz1YZRbwEdSsmO3/kqy5eMnczlm8a21A3fKUo3g8oAZEFM+f4DUNzqIltg31OAB/kZS\n\tenKZQ/SWC8PmLg/ZXBrReYakxXtkP6w3FwMlzOlhGxqhIRNiAJfXJBaRhuUWzPOpEDE9q5YJ\n\tBmqQL2WJm1VSNNVxbXJHpaWMH1sA2R00vmvRrPXGwyIO0IPYeUYQa3gsy6k+En/aMQJd27dp\n\taScf9am9PFICPY5T4ppneeJLif2lyLojo0mcHOV+uyrds9XkLpp14GfTkeKPdPMrLLTsHRfH\n\tfA4I4OBpRrEPiGIZB/0im98MkGY/Mu6qxeZmYLCcgD6qz4idOvfgVOrNh+aA8HzIVR+RMW8H\n\tQGBN9f0E3kfwxuhl3omo6V7lDw8XOdmuWZNC9zPq1UfryVHANYbLGz9KJ4Aw6M+OgBC2JpkD\n\thXMdHUkC+d20dwXrwHTlrJi1YNp6rBc+xald3wsUPOZ5z8moTHUX/uPA/qhGsbkCDQRWBP1m\n\tARAAzijkb+Sau4hAncr1JjOY+KyFEdUNxRy+hqTJdJfaYihxyaj0Ee0P0zEi35CbE6lgU0Uz\n\ttih9fiUbSV3wfsWqg1Ut3/5rTKu7kLFp15kF7eqvV4uezXRD3Qu4yjv/rMmEJbbD4cTvGCYI\n\td6MDC417f7vK3hCbCVIZSp3GXxyC1LU+UQr3fFcOyCwmP9vDUR9JV0BSqHHxRDdpUXE26Dk6\n\tmhf0V1YkspE5St814ETXpEus2urZE5yJIUROlWPIL+hm3NEWfAP06vsQUyLvr/GtbOT79vXl\n\tEn1aulcYyu20dRRxhkQ6iILaURcxIAVJJKPi8dsoMnS8pB0QW12AHWuirPF0g6DiuUfPmrA5\n\tPKe56IGlpkjc8cO51lIxHkWTpCMWigRdPDexKX+Sb+W9QWK/0JjIc4t3KBaiG8O4yRX8ml2R\n\t+rxfAVKM6V769P/hWoRGdgUMgYHFpHGSgEt80OKK5HeUPy2cngDUXzwrqiM5Sz6Od0qw5pCk\n\tNlXqI0W/who0iSVM+8+RmyY0OEkxEcci7rRLsGnM15B5PjLJjh1f2ULYkv8s4SnDwMZ/kE04\n\t/UqCMK/KnX8pwXEMCjz0h6qWNpGwJ0/tYIgQJZh6bqkvBrDogAvuhf60Sogw+mH8b+PBlx1L\n\toeTK396wc+4c3BfiC6pNtUS5GpsPMMjYMk7kVvEAEQEAAYkCPAQYAQoAJgIbDBYhBJAt15g/\n\tvSj943LUeqEeRnIQpGH9BQJdizzIBQkLSKZiAAoJEKEeRnIQpGH9eYgQAJpjaWNgqNOnMTmD\n\tMJggbwjIotypzIXfhHNCeTkG7+qCDlSaBPclcPGYrTwCt0YWPU2TgGgJrVhYT20ierN8LUvj\n\t6qOPTd+Uk7NFzL65qkh80ZKNBFddx1AabQpSVQKbdcLb8OFs85kuSvFdgqZwgxA1vl4TFhNz\n\tPZ79NAmXLackAx3sOVFhk4WQaKRshCB7cSl+RIng5S/ThOBlwNlcKG7j7W2MC06BlTbdEkUp\n\tECzuuRBv8wX4OQl+hbWbB/VKIx5HKlLu1eypen/5lNVzSqMMIYkkZcjV2SWQyUGxSwq0O/sx\n\tS0A8/atCHUXOboUsn54qdxrVDaK+6jIAuo8JiRWctP16KjzUM7MO0/+4zllM8EY57rXrj48j\n\tsbEYX0YQnzaj+jO6kJtoZsIaYR7rMMq9aUAjyiaEZpmP1qF/2sYenDx0Fg2BSlLvLvXM0vU8\n\tpQk3kgDu7kb/7PRYrZvBsr21EIQoIjXbZxDz/o7z95frkP71EaICttZ6k9q5oxxA5WC6sTXc\n\tMW8zs8avFNuA9VpXt0YupJd2ijtZy2mpZNG02fFVXhIn4G807G7+9mhuC4XG5rKlBBUXTvPU\n\tAfYnB4JBDLmLzBFavQfvonSfbitgXwCG3vS+9HEwAjU30Bar1PEOmIbiAoMzuKeRm2LVpmq4\n\tWZw01QYHU/GUV/zHJSFk","Organization":"Ideas on Board","Message-ID":"<718a0d4b-d544-e336-66be-21857dcbf274@ideasonboard.com>","Date":"Tue, 2 Mar 2021 10:46:28 +0000","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101\n\tThunderbird/68.10.0","MIME-Version":"1.0","In-Reply-To":"<20210302063016.GD35047@pyrite.rasen.tech>","Content-Language":"en-GB","Subject":"Re: [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>","Reply-To":"kieran.bingham@ideasonboard.com","Cc":"Phi-Bang Nguyen <pnguyen@baylibre.com>,\n\tlibcamera-devel@lists.libcamera.org","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>"}}]