[{"id":23411,"web_url":"https://patchwork.libcamera.org/comment/23411/","msgid":"<165533522278.2586493.5806750152885343753@Monstersaurus>","date":"2022-06-15T23:20:22","subject":"Re: [libcamera-devel] [PATCH v2 5/5] libcamera: pipeline: simple:\n\tSupport scaling on the sensor","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Laurent Pinchart via libcamera-devel (2022-06-12 16:23:11)\n> As the simple pipeline handler targets simple pipelines on the SoC side,\n> it often gets used with platforms that have a YUV sensor capable of\n> outputting different sizes. Extend the heuristics used for pipeline\n> discovery and configuration to scale as much as possible on the sensor\n> side, in order to minimize the required bandwidth.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  src/libcamera/pipeline/simple/simple.cpp | 142 +++++++++++++++++------\n>  1 file changed, 109 insertions(+), 33 deletions(-)\n> \n> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n> index 8c48162d7ff0..3a9f4d45e830 100644\n> --- a/src/libcamera/pipeline/simple/simple.cpp\n> +++ b/src/libcamera/pipeline/simple/simple.cpp\n> @@ -115,16 +115,25 @@ LOG_DEFINE_CATEGORY(SimplePipeline)\n>   *\n>   * The simple pipeline handler configures the pipeline by propagating V4L2\n>   * subdev formats from the camera sensor to the video node. The format is first\n> - * set on the camera sensor's output, using the native camera sensor\n> - * resolution. Then, on every link in the pipeline, the format is retrieved on\n> - * the link source and set unmodified on the link sink.\n> + * set on the camera sensor's output, picking a resolution supported by the\n> + * sensor that best matches the needs of the requested streams. Then, on every\n> + * link in the pipeline, the format is retrieved on the link source and set\n> + * unmodified on the link sink.\n>   *\n> - * When initializating the camera data, this above procedure is repeated for\n> - * every media bus format supported by the camera sensor. Upon reaching the\n> - * video node, the pixel formats compatible with the media bus format are\n> - * enumerated. Each of those pixel formats corresponds to one possible pipeline\n> - * configuration, stored as an instance of SimpleCameraData::Configuration in\n> - * the SimpleCameraData::formats_ map.\n> + * The best sensor resolution is selected using a heuristic that tries to\n> + * minimize the required bus and memory bandwidth, as the simple pipeline\n> + * handler is typically used on smaller, less powerful systems. To avoid the\n> + * need to upscale, the pipeline handler picks the smallest sensor resolution\n> + * large enough to accommodate the need of all streams. Resolutions that\n> + * significantly restrict the field of view are ignored.\n> + *\n> + * When initializating the camera data, the above format propagation procedure\n> + * is repeated for every media bus format and size supported by the camera\n> + * sensor. Upon reaching the video node, the pixel formats compatible with the\n> + * media bus format are enumerated. Each combination of the input media bus\n> + * format, output pixel format and output size are recorded in an instance of\n> + * the SimpleCameraData::Configuration structure, stored in the\n> + * SimpleCameraData::configs_ vector.\n\nAll sounds good so far.\n\n>   *\n>   * Format Conversion and Scaling\n>   * -----------------------------\n> @@ -243,7 +252,7 @@ public:\n>         V4L2VideoDevice *video_;\n>  \n>         std::vector<Configuration> configs_;\n> -       std::map<PixelFormat, const Configuration *> formats_;\n> +       std::map<PixelFormat, std::vector<const Configuration *>> formats_;\n>  \n>         std::unique_ptr<SimpleConverter> converter_;\n>         std::vector<std::unique_ptr<FrameBuffer>> converterBuffers_;\n> @@ -470,26 +479,24 @@ int SimpleCameraData::init()\n>  \n>         /*\n>          * Generate the list of possible pipeline configurations by trying each\n> -        * media bus format supported by the sensor.\n> +        * media bus format and size supported by the sensor.\n>          */\n> -       for (unsigned int code : sensor_->mbusCodes())\n> -               tryPipeline(code, sensor_->resolution());\n> +       for (unsigned int code : sensor_->mbusCodes()) {\n> +               for (const Size &size : sensor_->sizes(code))\n> +                       tryPipeline(code, size);\n> +       }\n>  \n>         if (configs_.empty()) {\n>                 LOG(SimplePipeline, Error) << \"No valid configuration found\";\n>                 return -EINVAL;\n>         }\n>  \n> -       /*\n> -        * Map the pixel formats to configurations. Any previously stored value\n> -        * is overwritten, as the pipeline handler currently doesn't care about\n> -        * how a particular PixelFormat is achieved.\n> -        */\n> +       /* Map the pixel formats to configurations. */\n>         for (const Configuration &config : configs_) {\n> -               formats_[config.captureFormat] = &config;\n> +               formats_[config.captureFormat].push_back(&config);\n>  \n>                 for (PixelFormat fmt : config.outputFormats)\n> -                       formats_[fmt] = &config;\n> +                       formats_[fmt].push_back(&config);\n>         }\n>  \n>         properties_ = sensor_->properties();\n> @@ -519,8 +526,10 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size)\n>         int ret = setupFormats(&format, V4L2Subdevice::TryFormat);\n>         if (ret < 0) {\n>                 /* Pipeline configuration failed, skip this configuration. */\n> +               format.mbus_code = code;\n> +               format.size = size;\n>                 LOG(SimplePipeline, Debug)\n> -                       << \"Media bus code \" << utils::hex(code, 4)\n> +                       << \"Sensor format \" << format\n>                         << \" not supported for this pipeline\";\n>                 return;\n>         }\n> @@ -791,22 +800,70 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n>                 status = Adjusted;\n>         }\n>  \n> +       /* Find the largest stream size. */\n> +       Size maxStreamSize;\n> +       for (const StreamConfiguration &cfg : config_)\n> +               maxStreamSize.expandTo(cfg.size);\n> +\n> +       LOG(SimplePipeline, Debug)\n> +               << \"Largest stream size is \" << maxStreamSize;\n> +\n>         /*\n> -        * Pick a configuration for the pipeline based on the pixel format for\n> -        * the streams (ordered from highest to lowest priority). Default to\n> -        * the first pipeline configuration if no streams requests a supported\n> +        * Find the best configuration for the pipeline using a heuristic.\n> +        * First select the pixel format based on the streams (which are\n> +        * considered ordered from highest to lowest priority). Default to the\n> +        * first pipeline configuration if no streams requests a supported\n\neither:\n  no stream reqeusts\nor \n  no streams request\n\n\n\n>          * pixel format.\n>          */\n> -       pipeConfig_ = data_->formats_.begin()->second;\n> +       const std::vector<const SimpleCameraData::Configuration *> *configs =\n> +               &data_->formats_.begin()->second;\n>  \n>         for (const StreamConfiguration &cfg : config_) {\n>                 auto it = data_->formats_.find(cfg.pixelFormat);\n>                 if (it != data_->formats_.end()) {\n> -                       pipeConfig_ = it->second;\n> +                       configs = &it->second;\n>                         break;\n>                 }\n>         }\n>  \n> +       /*\n> +        * \\todo Pick the best sensor output media bus format when the\n> +        * requested pixel format can be produced from multiple sensor media\n> +        * bus formats.\n\nHow would we define 'best' ? Highest bit depth?\n\nAside from the trivial comment fix above this all looks ok to me.\nDorota reports it supports her use cases, but do we need to\ntest/validate this on other platforms supported by the Simple PH - or\nhave you done so already? (perhaps you have done so, by developing on a\ndifferent platform).\n\n--\nKieran\n\n\n> +        */\n> +\n> +       /*\n> +        * Then pick, among the possible configuration for the pixel format,\n> +        * the smallest sensor resolution that can accommodate all streams\n> +        * without upscaling.\n> +        */\n> +       const SimpleCameraData::Configuration *maxPipeConfig = nullptr;\n> +       pipeConfig_ = nullptr;\n> +\n> +       for (const SimpleCameraData::Configuration *pipeConfig : *configs) {\n> +               const Size &size = pipeConfig->captureSize;\n> +\n> +               if (size.width >= maxStreamSize.width &&\n> +                   size.height >= maxStreamSize.height) {\n> +                       if (!pipeConfig_ || size < pipeConfig_->captureSize)\n> +                               pipeConfig_ = pipeConfig;\n> +               }\n> +\n> +               if (!maxPipeConfig || maxPipeConfig->captureSize < size)\n> +                       maxPipeConfig = pipeConfig;\n> +       }\n> +\n> +       /* If no configuration was large enough, select the largest one. */\n> +       if (!pipeConfig_)\n> +               pipeConfig_ = maxPipeConfig;\n> +\n> +       LOG(SimplePipeline, Debug)\n> +               << \"Picked \"\n> +               << V4L2SubdeviceFormat{ pipeConfig_->code, pipeConfig_->sensorSize, {} }\n> +               << \" -> \" << pipeConfig_->captureSize\n> +               << \"-\" << pipeConfig_->captureFormat\n> +               << \" for max stream size \" << maxStreamSize;\n> +\n>         /*\n>          * Adjust the requested streams.\n>          *\n> @@ -899,13 +956,32 @@ CameraConfiguration *SimplePipelineHandler::generateConfiguration(Camera *camera\n>  \n>         /* Create the formats map. */\n>         std::map<PixelFormat, std::vector<SizeRange>> formats;\n> -       std::transform(data->formats_.begin(), data->formats_.end(),\n> -                      std::inserter(formats, formats.end()),\n> -                      [](const auto &format) -> decltype(formats)::value_type {\n> -                              const PixelFormat &pixelFormat = format.first;\n> -                              const Size &size = format.second->captureSize;\n> -                              return { pixelFormat, { size } };\n> -                      });\n> +\n> +       for (const SimpleCameraData::Configuration &cfg : data->configs_) {\n> +               for (PixelFormat format : cfg.outputFormats)\n> +                       formats[format].push_back(cfg.outputSizes);\n> +       }\n> +\n> +       /* Sort the sizes and merge any consecutive overlapping ranges. */\n> +       for (auto &[format, sizes] : formats) {\n> +               std::sort(sizes.begin(), sizes.end(),\n> +                         [](SizeRange &a, SizeRange &b) {\n> +                                 return a.min < b.min;\n> +                         });\n> +\n> +               auto cur = sizes.begin();\n> +               auto next = cur;\n> +\n> +               while (++next != sizes.end()) {\n> +                       if (cur->max.width >= next->min.width &&\n> +                           cur->max.height >= next->min.height)\n> +                               cur->max = next->max;\n> +                       else if (++cur != next)\n> +                               *cur = *next;\n> +               }\n> +\n> +               sizes.erase(++cur, sizes.end());\n> +       }\n>  \n>         /*\n>          * Create the stream configurations. Take the first entry in the formats\n> -- \n> Regards,\n> \n> Laurent Pinchart\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 5DEE4BD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 15 Jun 2022 23:20:27 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A228F65631;\n\tThu, 16 Jun 2022 01:20:26 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 51820601EF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 16 Jun 2022 01:20:25 +0200 (CEST)","from pendragon.ideasonboard.com\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 D7A5C415;\n\tThu, 16 Jun 2022 01:20:24 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1655335226;\n\tbh=HLCZPVnJH9Vqo0z4N3TLtYmEDRFVEG/H7QB86Fde1fQ=;\n\th=In-Reply-To:References:To:Date:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=d8ImYfWn06LnhyRecq57sRzpgM5pwR5hRpkjElbZDZaN8GUiMuupdWYyNyK7XJYpH\n\tMpmfBk7uh9uuKeJaLtxFzbBesgCfUpM1cGteiAOlqsZiL2ByKWpqFJLM5Qc2zcu8n5\n\tb3pve77ctRhERt3GwT2fOAQR5uHma9C9Y3XVwW2LMvLqd+HqAI+P6eN+cqVJBPAVhB\n\tIs9bXSLKYmfmpgCIPR09/q4xEQvkpguLL77FC91WfKNsSRBjbQAQq20kGsaZpBY48f\n\tDtXIwruGD/0cFYvGdXRenoqJqZ3roRXlUw1vfTd4+AknEIsVcKA+xeFS+nHghYpxqx\n\tmGJBGYuwOckIg==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1655335224;\n\tbh=HLCZPVnJH9Vqo0z4N3TLtYmEDRFVEG/H7QB86Fde1fQ=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=kRqvh8UX0JstbRvd1cFPUxXj6rHNGmLfros06RizUhmf1Yx+Sm/7/+QmFeVCD/Lgu\n\tQCfWhuh5XkI5cH9UuHvQi4jkCaPjmowAOLUjOczMcuIeosuTOhjuMscNojgVywNte/\n\tz1q0XQhv6csHV14P2t0y/ZigtWghV7KiQRyelH1Y="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"kRqvh8UX\"; dkim-atps=neutral","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20220612152311.8408-6-laurent.pinchart@ideasonboard.com>","References":"<20220612152311.8408-1-laurent.pinchart@ideasonboard.com>\n\t<20220612152311.8408-6-laurent.pinchart@ideasonboard.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Thu, 16 Jun 2022 00:20:22 +0100","Message-ID":"<165533522278.2586493.5806750152885343753@Monstersaurus>","User-Agent":"alot/0.10","Subject":"Re: [libcamera-devel] [PATCH v2 5/5] libcamera: pipeline: simple:\n\tSupport scaling on the sensor","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>","From":"Kieran Bingham via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>,\n\tBenjamin Schaaf <ben.schaaf@gmail.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23412,"web_url":"https://patchwork.libcamera.org/comment/23412/","msgid":"<YqpwtrJbQo7KKtBN@pendragon.ideasonboard.com>","date":"2022-06-15T23:52:22","subject":"Re: [libcamera-devel] [PATCH v2 5/5] libcamera: pipeline: simple:\n\tSupport scaling on the sensor","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Kieran,\n\nOn Thu, Jun 16, 2022 at 12:20:22AM +0100, Kieran Bingham wrote:\n> Quoting Laurent Pinchart via libcamera-devel (2022-06-12 16:23:11)\n> > As the simple pipeline handler targets simple pipelines on the SoC side,\n> > it often gets used with platforms that have a YUV sensor capable of\n> > outputting different sizes. Extend the heuristics used for pipeline\n> > discovery and configuration to scale as much as possible on the sensor\n> > side, in order to minimize the required bandwidth.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  src/libcamera/pipeline/simple/simple.cpp | 142 +++++++++++++++++------\n> >  1 file changed, 109 insertions(+), 33 deletions(-)\n> > \n> > diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n> > index 8c48162d7ff0..3a9f4d45e830 100644\n> > --- a/src/libcamera/pipeline/simple/simple.cpp\n> > +++ b/src/libcamera/pipeline/simple/simple.cpp\n> > @@ -115,16 +115,25 @@ LOG_DEFINE_CATEGORY(SimplePipeline)\n> >   *\n> >   * The simple pipeline handler configures the pipeline by propagating V4L2\n> >   * subdev formats from the camera sensor to the video node. The format is first\n> > - * set on the camera sensor's output, using the native camera sensor\n> > - * resolution. Then, on every link in the pipeline, the format is retrieved on\n> > - * the link source and set unmodified on the link sink.\n> > + * set on the camera sensor's output, picking a resolution supported by the\n> > + * sensor that best matches the needs of the requested streams. Then, on every\n> > + * link in the pipeline, the format is retrieved on the link source and set\n> > + * unmodified on the link sink.\n> >   *\n> > - * When initializating the camera data, this above procedure is repeated for\n> > - * every media bus format supported by the camera sensor. Upon reaching the\n> > - * video node, the pixel formats compatible with the media bus format are\n> > - * enumerated. Each of those pixel formats corresponds to one possible pipeline\n> > - * configuration, stored as an instance of SimpleCameraData::Configuration in\n> > - * the SimpleCameraData::formats_ map.\n> > + * The best sensor resolution is selected using a heuristic that tries to\n> > + * minimize the required bus and memory bandwidth, as the simple pipeline\n> > + * handler is typically used on smaller, less powerful systems. To avoid the\n> > + * need to upscale, the pipeline handler picks the smallest sensor resolution\n> > + * large enough to accommodate the need of all streams. Resolutions that\n> > + * significantly restrict the field of view are ignored.\n> > + *\n> > + * When initializating the camera data, the above format propagation procedure\n> > + * is repeated for every media bus format and size supported by the camera\n> > + * sensor. Upon reaching the video node, the pixel formats compatible with the\n> > + * media bus format are enumerated. Each combination of the input media bus\n> > + * format, output pixel format and output size are recorded in an instance of\n> > + * the SimpleCameraData::Configuration structure, stored in the\n> > + * SimpleCameraData::configs_ vector.\n> \n> All sounds good so far.\n> \n> >   *\n> >   * Format Conversion and Scaling\n> >   * -----------------------------\n> > @@ -243,7 +252,7 @@ public:\n> >         V4L2VideoDevice *video_;\n> >  \n> >         std::vector<Configuration> configs_;\n> > -       std::map<PixelFormat, const Configuration *> formats_;\n> > +       std::map<PixelFormat, std::vector<const Configuration *>> formats_;\n> >  \n> >         std::unique_ptr<SimpleConverter> converter_;\n> >         std::vector<std::unique_ptr<FrameBuffer>> converterBuffers_;\n> > @@ -470,26 +479,24 @@ int SimpleCameraData::init()\n> >  \n> >         /*\n> >          * Generate the list of possible pipeline configurations by trying each\n> > -        * media bus format supported by the sensor.\n> > +        * media bus format and size supported by the sensor.\n> >          */\n> > -       for (unsigned int code : sensor_->mbusCodes())\n> > -               tryPipeline(code, sensor_->resolution());\n> > +       for (unsigned int code : sensor_->mbusCodes()) {\n> > +               for (const Size &size : sensor_->sizes(code))\n> > +                       tryPipeline(code, size);\n> > +       }\n> >  \n> >         if (configs_.empty()) {\n> >                 LOG(SimplePipeline, Error) << \"No valid configuration found\";\n> >                 return -EINVAL;\n> >         }\n> >  \n> > -       /*\n> > -        * Map the pixel formats to configurations. Any previously stored value\n> > -        * is overwritten, as the pipeline handler currently doesn't care about\n> > -        * how a particular PixelFormat is achieved.\n> > -        */\n> > +       /* Map the pixel formats to configurations. */\n> >         for (const Configuration &config : configs_) {\n> > -               formats_[config.captureFormat] = &config;\n> > +               formats_[config.captureFormat].push_back(&config);\n> >  \n> >                 for (PixelFormat fmt : config.outputFormats)\n> > -                       formats_[fmt] = &config;\n> > +                       formats_[fmt].push_back(&config);\n> >         }\n> >  \n> >         properties_ = sensor_->properties();\n> > @@ -519,8 +526,10 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size)\n> >         int ret = setupFormats(&format, V4L2Subdevice::TryFormat);\n> >         if (ret < 0) {\n> >                 /* Pipeline configuration failed, skip this configuration. */\n> > +               format.mbus_code = code;\n> > +               format.size = size;\n> >                 LOG(SimplePipeline, Debug)\n> > -                       << \"Media bus code \" << utils::hex(code, 4)\n> > +                       << \"Sensor format \" << format\n> >                         << \" not supported for this pipeline\";\n> >                 return;\n> >         }\n> > @@ -791,22 +800,70 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n> >                 status = Adjusted;\n> >         }\n> >  \n> > +       /* Find the largest stream size. */\n> > +       Size maxStreamSize;\n> > +       for (const StreamConfiguration &cfg : config_)\n> > +               maxStreamSize.expandTo(cfg.size);\n> > +\n> > +       LOG(SimplePipeline, Debug)\n> > +               << \"Largest stream size is \" << maxStreamSize;\n> > +\n> >         /*\n> > -        * Pick a configuration for the pipeline based on the pixel format for\n> > -        * the streams (ordered from highest to lowest priority). Default to\n> > -        * the first pipeline configuration if no streams requests a supported\n> > +        * Find the best configuration for the pipeline using a heuristic.\n> > +        * First select the pixel format based on the streams (which are\n> > +        * considered ordered from highest to lowest priority). Default to the\n> > +        * first pipeline configuration if no streams requests a supported\n> \n> either:\n>   no stream reqeusts\n> or \n>   no streams request\n\nOops. Will go for the second option.\n\n> >          * pixel format.\n> >          */\n> > -       pipeConfig_ = data_->formats_.begin()->second;\n> > +       const std::vector<const SimpleCameraData::Configuration *> *configs =\n> > +               &data_->formats_.begin()->second;\n> >  \n> >         for (const StreamConfiguration &cfg : config_) {\n> >                 auto it = data_->formats_.find(cfg.pixelFormat);\n> >                 if (it != data_->formats_.end()) {\n> > -                       pipeConfig_ = it->second;\n> > +                       configs = &it->second;\n> >                         break;\n> >                 }\n> >         }\n> >  \n> > +       /*\n> > +        * \\todo Pick the best sensor output media bus format when the\n> > +        * requested pixel format can be produced from multiple sensor media\n> > +        * bus formats.\n> \n> How would we define 'best' ? Highest bit depth?\n\nIf I knew, I would have implemented it already :-) That's an exercise\nleft for the reader at this point.\n\n> Aside from the trivial comment fix above this all looks ok to me.\n> Dorota reports it supports her use cases, but do we need to\n> test/validate this on other platforms supported by the Simple PH - or\n> have you done so already? (perhaps you have done so, by developing on a\n> different platform).\n\nI've tested this on the i.MX8MP (not upstream yet), and Dorota's tests\ncover the \"imx7-csi\" driver supported by the simple pipeline handler\n(CSI bridge, used in several i.MX7 and i.MX8 SoCs).\n\n> > +        */\n> > +\n> > +       /*\n> > +        * Then pick, among the possible configuration for the pixel format,\n> > +        * the smallest sensor resolution that can accommodate all streams\n> > +        * without upscaling.\n> > +        */\n> > +       const SimpleCameraData::Configuration *maxPipeConfig = nullptr;\n> > +       pipeConfig_ = nullptr;\n> > +\n> > +       for (const SimpleCameraData::Configuration *pipeConfig : *configs) {\n> > +               const Size &size = pipeConfig->captureSize;\n> > +\n> > +               if (size.width >= maxStreamSize.width &&\n> > +                   size.height >= maxStreamSize.height) {\n> > +                       if (!pipeConfig_ || size < pipeConfig_->captureSize)\n> > +                               pipeConfig_ = pipeConfig;\n> > +               }\n> > +\n> > +               if (!maxPipeConfig || maxPipeConfig->captureSize < size)\n> > +                       maxPipeConfig = pipeConfig;\n> > +       }\n> > +\n> > +       /* If no configuration was large enough, select the largest one. */\n> > +       if (!pipeConfig_)\n> > +               pipeConfig_ = maxPipeConfig;\n> > +\n> > +       LOG(SimplePipeline, Debug)\n> > +               << \"Picked \"\n> > +               << V4L2SubdeviceFormat{ pipeConfig_->code, pipeConfig_->sensorSize, {} }\n> > +               << \" -> \" << pipeConfig_->captureSize\n> > +               << \"-\" << pipeConfig_->captureFormat\n> > +               << \" for max stream size \" << maxStreamSize;\n> > +\n> >         /*\n> >          * Adjust the requested streams.\n> >          *\n> > @@ -899,13 +956,32 @@ CameraConfiguration *SimplePipelineHandler::generateConfiguration(Camera *camera\n> >  \n> >         /* Create the formats map. */\n> >         std::map<PixelFormat, std::vector<SizeRange>> formats;\n> > -       std::transform(data->formats_.begin(), data->formats_.end(),\n> > -                      std::inserter(formats, formats.end()),\n> > -                      [](const auto &format) -> decltype(formats)::value_type {\n> > -                              const PixelFormat &pixelFormat = format.first;\n> > -                              const Size &size = format.second->captureSize;\n> > -                              return { pixelFormat, { size } };\n> > -                      });\n> > +\n> > +       for (const SimpleCameraData::Configuration &cfg : data->configs_) {\n> > +               for (PixelFormat format : cfg.outputFormats)\n> > +                       formats[format].push_back(cfg.outputSizes);\n> > +       }\n> > +\n> > +       /* Sort the sizes and merge any consecutive overlapping ranges. */\n> > +       for (auto &[format, sizes] : formats) {\n> > +               std::sort(sizes.begin(), sizes.end(),\n> > +                         [](SizeRange &a, SizeRange &b) {\n> > +                                 return a.min < b.min;\n> > +                         });\n> > +\n> > +               auto cur = sizes.begin();\n> > +               auto next = cur;\n> > +\n> > +               while (++next != sizes.end()) {\n> > +                       if (cur->max.width >= next->min.width &&\n> > +                           cur->max.height >= next->min.height)\n> > +                               cur->max = next->max;\n> > +                       else if (++cur != next)\n> > +                               *cur = *next;\n> > +               }\n> > +\n> > +               sizes.erase(++cur, sizes.end());\n> > +       }\n> >  \n> >         /*\n> >          * Create the stream configurations. Take the first entry in the formats","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 72012BD808\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 15 Jun 2022 23:52:38 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 655B065635;\n\tThu, 16 Jun 2022 01:52:36 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2E2DB601EF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 16 Jun 2022 01:52:34 +0200 (CEST)","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 7105AA49;\n\tThu, 16 Jun 2022 01:52:33 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1655337156;\n\tbh=S/WiOIimC7jJTk7/KxROXA83JDPfV1AEtAfBISv72T0=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=iY2rPGRFzk+zRtwJdKgG3wxG8t5w5+0vynyJvQxoE9GOuUCgR8rJG3jGTyUowzCNT\n\tPbpeIxSaVywJlwh7mn0tTfSqvZflUw6CYnHBPHrDjD0Nv6Drg4+OYSgwfqe5jis1Dz\n\tnvSd96qMkG7wbbQtCi5Qhf69ZbGlscstr6GuDrHj7uaAQutBXSo51imjIMFgcYmh4Q\n\t1oKSWHCriQFlUg30KFhfKQKKOdfomFEt3i84jfJ+pk4eVPvx76tFH63MshQau9ZyBM\n\tfdNbxi04XuvU2trd4IErXuyRP1un8KOX4/u0ptrmmPBF/cjnvDCCj9ruR3muPPvVg7\n\tZcditvPYmIBng==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1655337153;\n\tbh=S/WiOIimC7jJTk7/KxROXA83JDPfV1AEtAfBISv72T0=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=F3oet18lac363RiOH3RavWsXZMB6/ljGdxgycuDkKsSgGRo1UDXjYzmHbOT40miZJ\n\t+qojdgEq3C9eKwnBrNDTb3oi9IRpfGQkDuUi7WblPRdEo098u3hEUhqhgmGqutLXFP\n\tWCipzL6V1PodWUitQNmyIaGb5Sc9k58nz+DjJl2c="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"F3oet18l\"; dkim-atps=neutral","Date":"Thu, 16 Jun 2022 02:52:22 +0300","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<YqpwtrJbQo7KKtBN@pendragon.ideasonboard.com>","References":"<20220612152311.8408-1-laurent.pinchart@ideasonboard.com>\n\t<20220612152311.8408-6-laurent.pinchart@ideasonboard.com>\n\t<165533522278.2586493.5806750152885343753@Monstersaurus>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<165533522278.2586493.5806750152885343753@Monstersaurus>","Subject":"Re: [libcamera-devel] [PATCH v2 5/5] libcamera: pipeline: simple:\n\tSupport scaling on the sensor","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>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>,\n\tlibcamera-devel@lists.libcamera.org,\n\tBenjamin Schaaf <ben.schaaf@gmail.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23413,"web_url":"https://patchwork.libcamera.org/comment/23413/","msgid":"<165536597492.2707671.8036451059793256750@Monstersaurus>","date":"2022-06-16T07:52:54","subject":"Re: [libcamera-devel] [PATCH v2 5/5] libcamera: pipeline: simple:\n\tSupport scaling on the sensor","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Laurent Pinchart (2022-06-16 00:52:22)\n> Hi Kieran,\n> \n> On Thu, Jun 16, 2022 at 12:20:22AM +0100, Kieran Bingham wrote:\n> > Quoting Laurent Pinchart via libcamera-devel (2022-06-12 16:23:11)\n> > > As the simple pipeline handler targets simple pipelines on the SoC side,\n> > > it often gets used with platforms that have a YUV sensor capable of\n> > > outputting different sizes. Extend the heuristics used for pipeline\n> > > discovery and configuration to scale as much as possible on the sensor\n> > > side, in order to minimize the required bandwidth.\n> > > \n> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > ---\n> > >  src/libcamera/pipeline/simple/simple.cpp | 142 +++++++++++++++++------\n> > >  1 file changed, 109 insertions(+), 33 deletions(-)\n> > > \n> > > diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n> > > index 8c48162d7ff0..3a9f4d45e830 100644\n> > > --- a/src/libcamera/pipeline/simple/simple.cpp\n> > > +++ b/src/libcamera/pipeline/simple/simple.cpp\n> > > @@ -115,16 +115,25 @@ LOG_DEFINE_CATEGORY(SimplePipeline)\n> > >   *\n> > >   * The simple pipeline handler configures the pipeline by propagating V4L2\n> > >   * subdev formats from the camera sensor to the video node. The format is first\n> > > - * set on the camera sensor's output, using the native camera sensor\n> > > - * resolution. Then, on every link in the pipeline, the format is retrieved on\n> > > - * the link source and set unmodified on the link sink.\n> > > + * set on the camera sensor's output, picking a resolution supported by the\n> > > + * sensor that best matches the needs of the requested streams. Then, on every\n> > > + * link in the pipeline, the format is retrieved on the link source and set\n> > > + * unmodified on the link sink.\n> > >   *\n> > > - * When initializating the camera data, this above procedure is repeated for\n> > > - * every media bus format supported by the camera sensor. Upon reaching the\n> > > - * video node, the pixel formats compatible with the media bus format are\n> > > - * enumerated. Each of those pixel formats corresponds to one possible pipeline\n> > > - * configuration, stored as an instance of SimpleCameraData::Configuration in\n> > > - * the SimpleCameraData::formats_ map.\n> > > + * The best sensor resolution is selected using a heuristic that tries to\n> > > + * minimize the required bus and memory bandwidth, as the simple pipeline\n> > > + * handler is typically used on smaller, less powerful systems. To avoid the\n> > > + * need to upscale, the pipeline handler picks the smallest sensor resolution\n> > > + * large enough to accommodate the need of all streams. Resolutions that\n> > > + * significantly restrict the field of view are ignored.\n> > > + *\n> > > + * When initializating the camera data, the above format propagation procedure\n> > > + * is repeated for every media bus format and size supported by the camera\n> > > + * sensor. Upon reaching the video node, the pixel formats compatible with the\n> > > + * media bus format are enumerated. Each combination of the input media bus\n> > > + * format, output pixel format and output size are recorded in an instance of\n> > > + * the SimpleCameraData::Configuration structure, stored in the\n> > > + * SimpleCameraData::configs_ vector.\n> > \n> > All sounds good so far.\n> > \n> > >   *\n> > >   * Format Conversion and Scaling\n> > >   * -----------------------------\n> > > @@ -243,7 +252,7 @@ public:\n> > >         V4L2VideoDevice *video_;\n> > >  \n> > >         std::vector<Configuration> configs_;\n> > > -       std::map<PixelFormat, const Configuration *> formats_;\n> > > +       std::map<PixelFormat, std::vector<const Configuration *>> formats_;\n> > >  \n> > >         std::unique_ptr<SimpleConverter> converter_;\n> > >         std::vector<std::unique_ptr<FrameBuffer>> converterBuffers_;\n> > > @@ -470,26 +479,24 @@ int SimpleCameraData::init()\n> > >  \n> > >         /*\n> > >          * Generate the list of possible pipeline configurations by trying each\n> > > -        * media bus format supported by the sensor.\n> > > +        * media bus format and size supported by the sensor.\n> > >          */\n> > > -       for (unsigned int code : sensor_->mbusCodes())\n> > > -               tryPipeline(code, sensor_->resolution());\n> > > +       for (unsigned int code : sensor_->mbusCodes()) {\n> > > +               for (const Size &size : sensor_->sizes(code))\n> > > +                       tryPipeline(code, size);\n> > > +       }\n> > >  \n> > >         if (configs_.empty()) {\n> > >                 LOG(SimplePipeline, Error) << \"No valid configuration found\";\n> > >                 return -EINVAL;\n> > >         }\n> > >  \n> > > -       /*\n> > > -        * Map the pixel formats to configurations. Any previously stored value\n> > > -        * is overwritten, as the pipeline handler currently doesn't care about\n> > > -        * how a particular PixelFormat is achieved.\n> > > -        */\n> > > +       /* Map the pixel formats to configurations. */\n> > >         for (const Configuration &config : configs_) {\n> > > -               formats_[config.captureFormat] = &config;\n> > > +               formats_[config.captureFormat].push_back(&config);\n> > >  \n> > >                 for (PixelFormat fmt : config.outputFormats)\n> > > -                       formats_[fmt] = &config;\n> > > +                       formats_[fmt].push_back(&config);\n> > >         }\n> > >  \n> > >         properties_ = sensor_->properties();\n> > > @@ -519,8 +526,10 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size)\n> > >         int ret = setupFormats(&format, V4L2Subdevice::TryFormat);\n> > >         if (ret < 0) {\n> > >                 /* Pipeline configuration failed, skip this configuration. */\n> > > +               format.mbus_code = code;\n> > > +               format.size = size;\n> > >                 LOG(SimplePipeline, Debug)\n> > > -                       << \"Media bus code \" << utils::hex(code, 4)\n> > > +                       << \"Sensor format \" << format\n> > >                         << \" not supported for this pipeline\";\n> > >                 return;\n> > >         }\n> > > @@ -791,22 +800,70 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()\n> > >                 status = Adjusted;\n> > >         }\n> > >  \n> > > +       /* Find the largest stream size. */\n> > > +       Size maxStreamSize;\n> > > +       for (const StreamConfiguration &cfg : config_)\n> > > +               maxStreamSize.expandTo(cfg.size);\n> > > +\n> > > +       LOG(SimplePipeline, Debug)\n> > > +               << \"Largest stream size is \" << maxStreamSize;\n> > > +\n> > >         /*\n> > > -        * Pick a configuration for the pipeline based on the pixel format for\n> > > -        * the streams (ordered from highest to lowest priority). Default to\n> > > -        * the first pipeline configuration if no streams requests a supported\n> > > +        * Find the best configuration for the pipeline using a heuristic.\n> > > +        * First select the pixel format based on the streams (which are\n> > > +        * considered ordered from highest to lowest priority). Default to the\n> > > +        * first pipeline configuration if no streams requests a supported\n> > \n> > either:\n> >   no stream reqeusts\n> > or \n> >   no streams request\n> \n> Oops. Will go for the second option.\n> \n> > >          * pixel format.\n> > >          */\n> > > -       pipeConfig_ = data_->formats_.begin()->second;\n> > > +       const std::vector<const SimpleCameraData::Configuration *> *configs =\n> > > +               &data_->formats_.begin()->second;\n> > >  \n> > >         for (const StreamConfiguration &cfg : config_) {\n> > >                 auto it = data_->formats_.find(cfg.pixelFormat);\n> > >                 if (it != data_->formats_.end()) {\n> > > -                       pipeConfig_ = it->second;\n> > > +                       configs = &it->second;\n> > >                         break;\n> > >                 }\n> > >         }\n> > >  \n> > > +       /*\n> > > +        * \\todo Pick the best sensor output media bus format when the\n> > > +        * requested pixel format can be produced from multiple sensor media\n> > > +        * bus formats.\n> > \n> > How would we define 'best' ? Highest bit depth?\n> \n> If I knew, I would have implemented it already :-) That's an exercise\n> left for the reader at this point.\n> \n> > Aside from the trivial comment fix above this all looks ok to me.\n> > Dorota reports it supports her use cases, but do we need to\n> > test/validate this on other platforms supported by the Simple PH - or\n> > have you done so already? (perhaps you have done so, by developing on a\n> > different platform).\n> \n> I've tested this on the i.MX8MP (not upstream yet), and Dorota's tests\n> cover the \"imx7-csi\" driver supported by the simple pipeline handler\n> (CSI bridge, used in several i.MX7 and i.MX8 SoCs).\n\nWell, then lets get this merged soon.\n\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n\n> \n> > > +        */\n> > > +\n> > > +       /*\n> > > +        * Then pick, among the possible configuration for the pixel format,\n> > > +        * the smallest sensor resolution that can accommodate all streams\n> > > +        * without upscaling.\n> > > +        */\n> > > +       const SimpleCameraData::Configuration *maxPipeConfig = nullptr;\n> > > +       pipeConfig_ = nullptr;\n> > > +\n> > > +       for (const SimpleCameraData::Configuration *pipeConfig : *configs) {\n> > > +               const Size &size = pipeConfig->captureSize;\n> > > +\n> > > +               if (size.width >= maxStreamSize.width &&\n> > > +                   size.height >= maxStreamSize.height) {\n> > > +                       if (!pipeConfig_ || size < pipeConfig_->captureSize)\n> > > +                               pipeConfig_ = pipeConfig;\n> > > +               }\n> > > +\n> > > +               if (!maxPipeConfig || maxPipeConfig->captureSize < size)\n> > > +                       maxPipeConfig = pipeConfig;\n> > > +       }\n> > > +\n> > > +       /* If no configuration was large enough, select the largest one. */\n> > > +       if (!pipeConfig_)\n> > > +               pipeConfig_ = maxPipeConfig;\n> > > +\n> > > +       LOG(SimplePipeline, Debug)\n> > > +               << \"Picked \"\n> > > +               << V4L2SubdeviceFormat{ pipeConfig_->code, pipeConfig_->sensorSize, {} }\n> > > +               << \" -> \" << pipeConfig_->captureSize\n> > > +               << \"-\" << pipeConfig_->captureFormat\n> > > +               << \" for max stream size \" << maxStreamSize;\n> > > +\n> > >         /*\n> > >          * Adjust the requested streams.\n> > >          *\n> > > @@ -899,13 +956,32 @@ CameraConfiguration *SimplePipelineHandler::generateConfiguration(Camera *camera\n> > >  \n> > >         /* Create the formats map. */\n> > >         std::map<PixelFormat, std::vector<SizeRange>> formats;\n> > > -       std::transform(data->formats_.begin(), data->formats_.end(),\n> > > -                      std::inserter(formats, formats.end()),\n> > > -                      [](const auto &format) -> decltype(formats)::value_type {\n> > > -                              const PixelFormat &pixelFormat = format.first;\n> > > -                              const Size &size = format.second->captureSize;\n> > > -                              return { pixelFormat, { size } };\n> > > -                      });\n> > > +\n> > > +       for (const SimpleCameraData::Configuration &cfg : data->configs_) {\n> > > +               for (PixelFormat format : cfg.outputFormats)\n> > > +                       formats[format].push_back(cfg.outputSizes);\n> > > +       }\n> > > +\n> > > +       /* Sort the sizes and merge any consecutive overlapping ranges. */\n> > > +       for (auto &[format, sizes] : formats) {\n> > > +               std::sort(sizes.begin(), sizes.end(),\n> > > +                         [](SizeRange &a, SizeRange &b) {\n> > > +                                 return a.min < b.min;\n> > > +                         });\n> > > +\n> > > +               auto cur = sizes.begin();\n> > > +               auto next = cur;\n> > > +\n> > > +               while (++next != sizes.end()) {\n> > > +                       if (cur->max.width >= next->min.width &&\n> > > +                           cur->max.height >= next->min.height)\n> > > +                               cur->max = next->max;\n> > > +                       else if (++cur != next)\n> > > +                               *cur = *next;\n> > > +               }\n> > > +\n> > > +               sizes.erase(++cur, sizes.end());\n> > > +       }\n> > >  \n> > >         /*\n> > >          * Create the stream configurations. Take the first entry in the formats\n> \n> -- \n> Regards,\n> \n> Laurent Pinchart","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 C3594BD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 16 Jun 2022 07:53:00 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F3E7B65631;\n\tThu, 16 Jun 2022 09:52:59 +0200 (CEST)","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 5A127601F1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 16 Jun 2022 09:52:58 +0200 (CEST)","from pendragon.ideasonboard.com\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 BF1DB30B;\n\tThu, 16 Jun 2022 09:52:57 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1655365980;\n\tbh=HiYMAo+cBFwVT08T6ZKxwgGzxc3jb1+FAH9y52TtU2M=;\n\th=In-Reply-To:References:To:Date:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=VL6GBZfuGCReEPy1Zh/KXvW9He/sa1jwhltjM6pQE3YCepsVx5OnV3k90NRTqRrhH\n\tYiDNpPA36n4PpK5E7F4gzabNr81ZrFEVvdwfQV0hUWPmCBEg0DC/vwq2ATIZpY3K7D\n\t17ZAtAzs5D12SzmwH08Lg+4qvwCntuOYqB2MyHTLnyW0IHzIZYRDLDou5MaJziSA1j\n\tuBG5aebr39BYqjQYI90u7iHcnAzJ8NZnaQhjtdtXnawYU+U2hw9nwWf02raF5rkSrV\n\tS8L/yZk0hEnzFhHMN3R/DMupwmeGAL8tmz6E7fJ+CNORIU+NZPBOPWh30uVouuorM7\n\tduR/GbYF0qUqA==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1655365977;\n\tbh=HiYMAo+cBFwVT08T6ZKxwgGzxc3jb1+FAH9y52TtU2M=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=TeV/XCWeN9YlKmVxeEQW98e6oM2pbgw61t06tcs8V1Vsi02YQsXMIANXHI4MBKKTp\n\tFNQTafr6Ag/u8xcB2p2IpaFXKQZ4uPUL85LObaoCF2EPEq1b4GKJyP2f0a3s3zkeab\n\t7MEp3CSzBXrSuLxUIOD6vQL0NKJfCBBIs//w71Pk="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"TeV/XCWe\"; dkim-atps=neutral","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<YqpwtrJbQo7KKtBN@pendragon.ideasonboard.com>","References":"<20220612152311.8408-1-laurent.pinchart@ideasonboard.com>\n\t<20220612152311.8408-6-laurent.pinchart@ideasonboard.com>\n\t<165533522278.2586493.5806750152885343753@Monstersaurus>\n\t<YqpwtrJbQo7KKtBN@pendragon.ideasonboard.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Date":"Thu, 16 Jun 2022 08:52:54 +0100","Message-ID":"<165536597492.2707671.8036451059793256750@Monstersaurus>","User-Agent":"alot/0.10","Subject":"Re: [libcamera-devel] [PATCH v2 5/5] libcamera: pipeline: simple:\n\tSupport scaling on the sensor","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>","From":"Kieran Bingham via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>,\n\tlibcamera-devel@lists.libcamera.org,\n\tBenjamin Schaaf <ben.schaaf@gmail.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]